From ee69eaf8b8eacfd1199769ab55fb22147a799faf Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Fri, 25 Oct 2024 10:21:31 -0400 Subject: [PATCH 01/88] adjusted update from google api to use async generator --- .../writing_observer/aggregator.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/modules/writing_observer/writing_observer/aggregator.py b/modules/writing_observer/writing_observer/aggregator.py index f45e06714..158e83cee 100644 --- a/modules/writing_observer/writing_observer/aggregator.py +++ b/modules/writing_observer/writing_observer/aggregator.py @@ -298,12 +298,15 @@ async def fetch_doc_from_google(student, doc_id): await kvs.set(key, text) return text - if learning_observer.settings.module_setting('writing_observer', 'use_google_documents'): - [await fetch_doc_from_google( - learning_observer.util.get_nested_dict_value(d, 'provenance.provenance.value.user_id'), - learning_observer.util.get_nested_dict_value(d, 'doc_id') - ) for d in doc_ids] - return doc_ids + fetch_from_google_documents = learning_observer.settings.module_setting('writing_observer', 'use_google_documents') + async for d in doc_ids: + if fetch_from_google_documents: + yield await fetch_doc_from_google( + learning_observer.util.get_nested_dict_value(d, 'provenance.provenance.value.user_id'), + learning_observer.util.get_nested_dict_value(d, 'doc_id') + ) + else: + yield d def get_last_document_id(s): From 0bac912e6364684eb5c051adca221bb74d6424ee Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Fri, 25 Oct 2024 10:39:20 -0400 Subject: [PATCH 02/88] fixed small bug in yield statements --- modules/writing_observer/writing_observer/aggregator.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/modules/writing_observer/writing_observer/aggregator.py b/modules/writing_observer/writing_observer/aggregator.py index 158e83cee..188de004a 100644 --- a/modules/writing_observer/writing_observer/aggregator.py +++ b/modules/writing_observer/writing_observer/aggregator.py @@ -301,12 +301,11 @@ async def fetch_doc_from_google(student, doc_id): fetch_from_google_documents = learning_observer.settings.module_setting('writing_observer', 'use_google_documents') async for d in doc_ids: if fetch_from_google_documents: - yield await fetch_doc_from_google( - learning_observer.util.get_nested_dict_value(d, 'provenance.provenance.value.user_id'), + await fetch_doc_from_google( + learning_observer.util.get_nested_dict_value(d, 'provenance.provenance.STUDENT.value.user_id'), learning_observer.util.get_nested_dict_value(d, 'doc_id') ) - else: - yield d + yield d def get_last_document_id(s): From 5127ea754b0fb36049d7c5b467699abc19f47039 Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Wed, 30 Oct 2024 14:24:08 -0400 Subject: [PATCH 03/88] fixed possible create_task bug (#192) Addressed weak reference for created tasks in asyncio --- learning_observer/learning_observer/dashboard.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/learning_observer/learning_observer/dashboard.py b/learning_observer/learning_observer/dashboard.py index 8d06bfd3a..6df3dbf75 100644 --- a/learning_observer/learning_observer/dashboard.py +++ b/learning_observer/learning_observer/dashboard.py @@ -588,6 +588,7 @@ async def websocket_dashboard_handler(request): previous_client_query = None batch = [] lock = asyncio.Lock() + background_tasks = set() async def _send_update(update): '''Send an update to our batch @@ -646,7 +647,9 @@ async def _drive_generator(generator, dag_kwargs): item['option_hash'] = dag_kwargs['option_hash'] await _send_update({'op': 'update', 'path': update_path, 'value': item}) - send_batches = asyncio.create_task(_batch_send()) + send_batches_task = asyncio.create_task(_batch_send()) + background_tasks.add(send_batches_task) + send_batches_task.add_done_callback(background_tasks.discard) while True: try: @@ -676,7 +679,9 @@ async def _drive_generator(generator, dag_kwargs): # reschedule timeout). for k, v in client_query.items(): for target in v.get('target_exports', []): - asyncio.create_task(_execute_dag(v, target, client_query)) + execute_dag_task = asyncio.create_task(_execute_dag(v, target, client_query)) + background_tasks.add(execute_dag_task) + execute_dag_task.add_done_callback(background_tasks.discard) # Obsolete code -- we should put this back in after our refactor. Allows us to use From 5ad48abd3eb529a11cc242d3bc32ab32087f09a0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Nov 2024 13:13:15 -0500 Subject: [PATCH 04/88] Bump rollup from 2.79.1 to 2.79.2 in /modules/lo_dash_react_components (#179) Bumps [rollup](https://github.com/rollup/rollup) from 2.79.1 to 2.79.2. - [Release notes](https://github.com/rollup/rollup/releases) - [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md) - [Commits](https://github.com/rollup/rollup/compare/v2.79.1...v2.79.2) --- updated-dependencies: - dependency-name: rollup dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/lo_dash_react_components/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/lo_dash_react_components/package-lock.json b/modules/lo_dash_react_components/package-lock.json index 036c4ca1c..2bab0a655 100644 --- a/modules/lo_dash_react_components/package-lock.json +++ b/modules/lo_dash_react_components/package-lock.json @@ -25367,9 +25367,9 @@ } }, "node_modules/rollup": { - "version": "2.79.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", - "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", + "version": "2.79.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz", + "integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==", "bin": { "rollup": "dist/bin/rollup" }, From 45d1c388223093914f85ac81905c9cff582af3c2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Nov 2024 13:13:54 -0500 Subject: [PATCH 05/88] Bump cookie and express in /modules/lo_dash_react_components (#183) Bumps [cookie](https://github.com/jshttp/cookie) and [express](https://github.com/expressjs/express). These dependencies needed to be updated together. Updates `cookie` from 0.6.0 to 0.7.1 - [Release notes](https://github.com/jshttp/cookie/releases) - [Commits](https://github.com/jshttp/cookie/compare/v0.6.0...v0.7.1) Updates `express` from 4.20.0 to 4.21.1 - [Release notes](https://github.com/expressjs/express/releases) - [Changelog](https://github.com/expressjs/express/blob/4.21.1/History.md) - [Commits](https://github.com/expressjs/express/compare/4.20.0...4.21.1) --- updated-dependencies: - dependency-name: cookie dependency-type: indirect - dependency-name: express dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .../package-lock.json | 130 +++++------------- 1 file changed, 34 insertions(+), 96 deletions(-) diff --git a/modules/lo_dash_react_components/package-lock.json b/modules/lo_dash_react_components/package-lock.json index 2bab0a655..6cf4d12f8 100644 --- a/modules/lo_dash_react_components/package-lock.json +++ b/modules/lo_dash_react_components/package-lock.json @@ -6619,20 +6619,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "node_modules/body-parser/node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "dependencies": { - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/body-parser/node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -7689,9 +7675,9 @@ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" }, "node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", "engines": { "node": ">= 0.6" } @@ -9927,23 +9913,23 @@ } }, "node_modules/express": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/express/-/express-4.20.0.tgz", - "integrity": "sha512-pLdae7I6QqShF5PnNTCVn4hI91Dx0Grkn2+IAsMTgMIKuQVte2dN9PeGSSAME2FR8anOhVA62QDIUaWVfEXVLw==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", + "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.6.0", + "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "1.2.0", + "finalhandler": "1.3.1", "fresh": "0.5.2", "http-errors": "2.0.0", "merge-descriptors": "1.0.3", @@ -9952,11 +9938,11 @@ "parseurl": "~1.3.3", "path-to-regexp": "0.1.10", "proxy-addr": "~2.0.7", - "qs": "6.11.0", + "qs": "6.13.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "0.19.0", - "serve-static": "1.16.0", + "serve-static": "1.16.2", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "~1.6.18", @@ -10367,12 +10353,12 @@ } }, "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", "dependencies": { "debug": "2.6.9", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "2.4.1", "parseurl": "~1.3.3", @@ -10391,6 +10377,14 @@ "ms": "2.0.0" } }, + "node_modules/finalhandler/node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/finalhandler/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -20525,11 +20519,11 @@ } }, "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", "dependencies": { - "side-channel": "^1.0.4" + "side-channel": "^1.0.6" }, "engines": { "node": ">=0.6" @@ -25845,79 +25839,23 @@ "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" }, "node_modules/serve-static": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.0.tgz", - "integrity": "sha512-pDLK8zwl2eKaYrs8mrPZBJua4hMplRWJ1tIFksVC3FtBEBnl8dxgeHtsaMS8DhS9i4fLObaon6ABoc4/hQGdPA==", + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", "dependencies": { - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.18.0" + "send": "0.19.0" }, "engines": { "node": ">= 0.8.0" } }, - "node_modules/serve-static/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/serve-static/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/serve-static/node_modules/http-errors": { + "node_modules/serve-static/node_modules/encodeurl": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/serve-static/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/serve-static/node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/serve-static/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "engines": { "node": ">= 0.8" } From fdf00eaf1389cb4272397daaa41f1d5ece5f032d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Nov 2024 13:14:33 -0500 Subject: [PATCH 06/88] Bump elliptic from 6.5.7 to 6.6.0 in /modules/lo_dash_react_components (#193) Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.7 to 6.6.0. - [Commits](https://github.com/indutny/elliptic/compare/v6.5.7...v6.6.0) --- updated-dependencies: - dependency-name: elliptic dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/lo_dash_react_components/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/lo_dash_react_components/package-lock.json b/modules/lo_dash_react_components/package-lock.json index 6cf4d12f8..528896ff6 100644 --- a/modules/lo_dash_react_components/package-lock.json +++ b/modules/lo_dash_react_components/package-lock.json @@ -8922,9 +8922,9 @@ "integrity": "sha512-ndBQYz3Eyy3rASjjQ9poMJGoAlsZ/aZnq6GBsGL4w/4sWIAwiUHVSsMuADbxa8WJw7pZ0oxLpGbtoDt4vRTdCg==" }, "node_modules/elliptic": { - "version": "6.5.7", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.7.tgz", - "integrity": "sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.0.tgz", + "integrity": "sha512-dpwoQcLc/2WLQvJvLRHKZ+f9FgOdjnq11rurqwekGQygGPsYSK29OMMD2WalatiqQ+XGFDglTNixpPfI+lpaAA==", "dependencies": { "bn.js": "^4.11.9", "brorand": "^1.1.0", From 0105345159b58cb494a3d61bf4115bea8bca0d0d Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Mon, 4 Nov 2024 13:20:35 -0500 Subject: [PATCH 07/88] added communication protocol readme to documentation --- autodocs/system_design.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/autodocs/system_design.rst b/autodocs/system_design.rst index a48558798..43c818d49 100644 --- a/autodocs/system_design.rst +++ b/autodocs/system_design.rst @@ -12,3 +12,5 @@ System Design :parser: myst_parser.sphinx_ .. include:: ../docs/reducers.md :parser: myst_parser.sphinx_ +.. include:: ../learning_observer/learning_observer/communication_protocol/README.md + :parser: myst_parser.sphinx_ From 22bf45097b49ce4f37245c3c3ee2525d3510206b Mon Sep 17 00:00:00 2001 From: Paul Brost Date: Wed, 6 Nov 2024 16:00:01 -0500 Subject: [PATCH 08/88] updated npm packages for lo_event --- modules/lo_event/package.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/lo_event/package.json b/modules/lo_event/package.json index c7aaee715..d62e93855 100644 --- a/modules/lo_event/package.json +++ b/modules/lo_event/package.json @@ -32,8 +32,8 @@ "lodash": "^4.17.21", "react": "^18.3.1", "react-dom": "^18.3.1", - "react-redux": "^9.1.2", - "redux-thunk": "^2.4.2", + "redux": "^5.0.1", + "redux-thunk": "^3.1.0", "sqlite3": "^5.1.6", "uuid": "^10.0.0", "ws": "^8.14.2", @@ -54,7 +54,6 @@ "path-browserify": "^1.0.1", "process": "^0.11.10", "querystring-es3": "^0.2.1", - "redux": "^4.2.1", "stream-browserify": "^3.0.0", "url": "^0.11.3", "util": "^0.12.5", From 4ff4f3a19c0944959e9cff183edac7ef7c7291f4 Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Thu, 7 Nov 2024 09:41:21 -0500 Subject: [PATCH 09/88] Abstracted current gpt responders to their own module (#178) See #195 for future work. Full commit list * abstracted current gpt responders to their own module * updated wo_requirements to install lo_gpt * added a comment about how we ought to handle responders in the future * updated readme with future work --- modules/lo_gpt/README.md | 34 +++ modules/lo_gpt/lo_gpt/__init__.py | 0 modules/lo_gpt/lo_gpt/gpt.py | 238 ++++++++++++++++++ modules/lo_gpt/lo_gpt/module.py | 26 ++ modules/lo_gpt/setup.cfg | 10 + modules/lo_gpt/setup.py | 14 ++ .../wo_bulk_essay_analysis/gpt.py | 184 +------------- wo_requirements.txt | 1 + 8 files changed, 326 insertions(+), 181 deletions(-) create mode 100644 modules/lo_gpt/README.md create mode 100644 modules/lo_gpt/lo_gpt/__init__.py create mode 100644 modules/lo_gpt/lo_gpt/gpt.py create mode 100644 modules/lo_gpt/lo_gpt/module.py create mode 100644 modules/lo_gpt/setup.cfg create mode 100644 modules/lo_gpt/setup.py diff --git a/modules/lo_gpt/README.md b/modules/lo_gpt/README.md new file mode 100644 index 000000000..34aebb103 --- /dev/null +++ b/modules/lo_gpt/README.md @@ -0,0 +1,34 @@ +# Learning Observer GPT + +This module allows users make queries to a GPT model. + +This code is abstracted directly from the `wo_bulk_essay_analysis` module and should be treated as scaffolding as we determine the appropriate way to use query GPT responders. + +## Long-term goals / Future work + +### General Design + +Currently the GPT responders are using an Object Oriented approach which clashes with much of Learning Observer's design. We ought to change this from an OO approach to a Functional Programming approach. + +### Querying Responders + +This package currently initializes a single GPT responder for use. In practice, we need to keep track of multiple responders depending on the context/use case. The different responders ought to be defined and queried via PMSS like + +```css +.wo .group_a { + gpt_responder: openai; + model: gpt-4o; + temperature: 0.5; +} + +.dynamic-assessment { + gpt_responder: ollama; + ollama_server: http://my-ollama-server.learning-observer.org/ +} + +[school=ncsu] { + open_ai_creds: [NCSU-billed account] +} + +// ... and so on +``` diff --git a/modules/lo_gpt/lo_gpt/__init__.py b/modules/lo_gpt/lo_gpt/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/modules/lo_gpt/lo_gpt/gpt.py b/modules/lo_gpt/lo_gpt/gpt.py new file mode 100644 index 000000000..69ee8acf1 --- /dev/null +++ b/modules/lo_gpt/lo_gpt/gpt.py @@ -0,0 +1,238 @@ +''' +This file defines connections to various GPT providers. Each provider +defines a chat completion method. Supported large-language proviers: + +- OpenAI ChatGPT +- Ollama + +TODO OpenAI is built to provide multiple choices. The response from their +API looks like `response['choices'][0]['message']['content']`. The Ollama +API only provides a single completion so the response looks like +`response['message']['content']`. Much of the code is shared between both +APIs so the code can be abstracted to the parent class. +''' +import aiohttp +import json +import loremipsum +import os + +import learning_observer.communication_protocol.integration +from learning_observer.log_event import debug_log +import learning_observer.prestartup +import learning_observer.settings + +gpt_responder = None +SYSTEM_PROMPT_DEFAULT = 'You are a helper agent, please help fulfill user requests.' + + +class GPTAPI: + def chat_completion(self, prompt, system_prompt): + ''' + Respond to user prompt + ''' + raise NotImplementedError + + +class GPTInitializationError(Exception): + '''Raise when GPT fails to initialize. + This usually happens when the users forgets to + provide information in the `creds.yaml` file. + ''' + + +class GPTRequestErorr(Exception): + '''Raise when the GPT chat completion raises + an exception + ''' + + +class OpenAIGPT(GPTAPI): + def __init__(self, **kwargs): + ''' + kwargs: + - `model`: the GPT model we should use, defaults to `gpt-3.5-turbo-16k` + - `api_key`: OpenAI api key, defaults to the `OPENAI_API_KEY` environment variable. + ''' + super().__init__() + self.model = kwargs.get('model', 'gpt-3.5-turbo-16k') + self.api_key = kwargs.get('api_key', os.getenv('OPENAI_API_KEY')) + if self.api_key is None: + exception_text = 'Error while starting openai:\n'\ + 'Please ensure that the API Key is correctly configured in '\ + '`creds.yaml` under `modules.writing_observer.gpt_responders.openai.api_key`, '\ + 'or alternatively, set it as the `OPENAI_API_KEY` environment '\ + 'variable.' + raise GPTInitializationError(exception_text) + + async def chat_completion(self, prompt, system_prompt): + url = 'https://api.openai.com/v1/chat/completions' + headers = {'Content-Type': 'application/json', 'Authorization': f'Bearer {self.api_key}'} + messages = [ + {'role': 'system', 'content': system_prompt}, + {'role': 'user', 'content': prompt} + ] + content = {'model': self.model, 'messages': messages} + async with aiohttp.ClientSession() as session: + async with session.post(url, headers=headers, json=content) as resp: + json_resp = await resp.json() + if resp.status == 200: + return json_resp['choices'][0]['message']['content'] + error = 'Error occured while making OpenAI request' + if 'error' in json_resp: + error += f"\n{json_resp['error']['message']}" + raise GPTRequestErorr(error) + + +class OllamaGPT(GPTAPI): + '''GPT responder for handling request to the Ollama API + ''' + def __init__(self, **kwargs): + ''' + kwargs + - `model`: the GPT model we should use, defaults to `llama2` + - `host`: Ollama server to connect to - the Ollama client will + default to `localhost:11434`. + ''' + super().__init__() + self.model = kwargs.get('model', 'llama2') + # the Ollama client checks for the `OLLAMA_HOST` env variable + # or defaults to `localhost:11434`. We provide a warning when + # a specific host is not found. + self.ollama_host = kwargs.get('host', os.getenv('OLLAMA_HOST', None)) + if self.ollama_host is None: + debug_log('WARNING:: Ollama host not specified. Defaulting to '\ + '`localhost:11434`.\nTo set a specific host, set '\ + '`modules.writing_observer.gpt_responders.ollama.host` '\ + 'in `creds.yaml` or set the `OLLAMA_HOST` environment '\ + 'variable.\n'\ + 'If you wish to install Ollama and download a model, '\ + 'run the following commands:\n'\ + '```bash\ncurl https://ollama.ai/install.sh | sh\n'\ + 'ollama run \n```') + self.ollama_host = 'http://localhost:11434' + + async def chat_completion(self, prompt, system_prompt): + '''Ollama only returns a single item compared to GPT returning a list + ''' + url = f'{self.ollama_host}/api/chat' + messages = [ + {'role': 'system', 'content': system_prompt}, + {'role': 'user', 'content': prompt} + ] + content = {'model': self.model, 'messages': messages, 'stream': False} + async with aiohttp.ClientSession() as session: + async with session.post(url, json=content) as resp: + json_resp = await resp.json(content_type=None) + if resp.status == 200: + return json_resp['message']['content'] + error = 'Error occured while making Ollama request' + if 'error' in json_resp: + error += f"\n{json_resp['error']['message']}" + raise GPTRequestErorr(error) + + +class StubGPT(GPTAPI): + '''GPT responder for handling stub requests + ''' + def __init__(self, **kwargs): + super().__init__() + + async def chat_completion(self, prompt, system_prompt): + return "\n".join(loremipsum.get_paragraphs(1)) + + +GPT_RESPONDERS = { + 'openai': OpenAIGPT, + 'ollama': OllamaGPT, + 'stub': StubGPT +} + + +@learning_observer.prestartup.register_startup_check +def initialize_gpt_responder(): + '''Iterate over the gpt_responders listed in `creds.yaml` + and attempt to initialize it. On successful initialization + of a responder, exit the this startup check. Otherwise, + try the next one. + ''' + global gpt_responder + # TODO change this to use settings.module_settings() instead + # that method now uses pmss which doesn't support lists and + # dictionaries yet. + # TODO think through how we might use different gpt responders + # with different PMSS groups. We may have mutliple responders + # running on the same system for different modules or schools. + responders = learning_observer.settings.settings['modules']['writing_observer'].get('gpt_responders', {}) + exceptions = [] + for key in responders: + if key not in GPT_RESPONDERS: + exceptions.append(KeyError( + f'GPT Responder `{key}` is not yet configured on this system.\n'\ + f'The available responders are [{", ".join(GPT_RESPONDERS.keys())}].' + )) + continue + try: + gpt_responder = GPT_RESPONDERS[key](**responders[key]) + debug_log(f'INFO:: Using GPT responder `{key}` with model `{responders[key]["model"]}`') + return True + except GPTInitializationError as e: + exceptions.append(e) + debug_log(f'WARNING:: Unable to initialize GPT responder `{key}:`.\n{e}') + gpt_responder = None + no_responders = 'No GPT responders found in `creds.yaml`. To add a responder, add either'\ + '`openai` or `ollama` along with any subsettings to `modules.writing_observer.gpt_responders`.\n'\ + 'Example:\n```\ngpt_responders:\n ollama:\n model: llama2\n```' + exception_strings = '\n'.join(str(e) for e in exceptions) if len(exceptions) > 0 else no_responders + exception_text = 'Unable to initialize a GPT responder. Encountered the following errors:\n'\ + f'{exception_strings}' + raise learning_observer.prestartup.StartupCheck("GPT: " + exception_text) + + +async def api_chat_completion(request): + '''This function drieclty interacts with the gpt_responder + chat completion interface. + + Expected Input (Request): + ------------------------- + - Method: POST + - Content-Type: application/json + - JSON Body: + { + "prompt": "", # Required + "system_prompt": "" # Optional, default `You are a helper agent, please help fulfill user requests.` + } + + Expected Output (Response): + --------------------------- + - Status: 200 OK + - Content-Type: application/json + - JSON Body: + { + "response": "" + } + ''' + # TODO add error handling + global gpt_responder + try: + data = await request.json() + except json.JSONDecodeError: + return aiohttp.web.json_response({"error": "Invalid JSON"}, status=400) + + prompt = data['prompt'] + system_prompt = data.get('system_prompt', SYSTEM_PROMPT_DEFAULT) + response_data = {'response': await gpt_responder.chat_completion(prompt, system_prompt)} + return aiohttp.web.json_response(response_data) + + +async def test_responder(): + responder = OllamaGPT('llama2') + response = await responder.chat_completion('Why is the sky blue?') + print('Response:', response) + + +if __name__ == '__main__': + import asyncio + loop = asyncio.get_event_loop() + asyncio.ensure_future(test_responder()) + loop.run_forever() + loop.close() diff --git a/modules/lo_gpt/lo_gpt/module.py b/modules/lo_gpt/lo_gpt/module.py new file mode 100644 index 000000000..46799d74d --- /dev/null +++ b/modules/lo_gpt/lo_gpt/module.py @@ -0,0 +1,26 @@ +''' +Learning Observer GPT +''' +import learning_observer.downloads as d +import learning_observer.communication_protocol.query as q +from learning_observer.dash_integration import thirdparty_url, static_url +from learning_observer.stream_analytics.helpers import KeyField, Scope + +import lo_gpt.gpt + +# Name for the module +NAME = 'Learning Observer GPT' + +# TODO Create a really simple dashboard for interfacing with the GPT api. +# This should be created in NextJS. +# An input for each api parameter, a submit button, and the json output. + +''' +Additional API calls we can define, this one returns the colors of the rainbow +''' +EXTRA_VIEWS = [{ + 'name': 'GPT Chat Completion', + 'suburl': 'api/llm', + 'method': 'POST', + 'handler': lo_gpt.gpt.api_chat_completion +}] diff --git a/modules/lo_gpt/setup.cfg b/modules/lo_gpt/setup.cfg new file mode 100644 index 000000000..7ca78a5ad --- /dev/null +++ b/modules/lo_gpt/setup.cfg @@ -0,0 +1,10 @@ +[metadata] +name = Learning Observer GPT +description = Use this to iterface with GPT models. + +[options] +packages = lo_gpt + +[options.entry_points] +lo_modules = + lo_gpt = lo_gpt.module diff --git a/modules/lo_gpt/setup.py b/modules/lo_gpt/setup.py new file mode 100644 index 000000000..46795a41f --- /dev/null +++ b/modules/lo_gpt/setup.py @@ -0,0 +1,14 @@ +''' +Install script. Everything is handled in setup.cfg + +To set up locally for development, run `python setup.py develop`, in a +virtualenv, preferably. +''' +from setuptools import setup + +setup( + name="lo_gpt", + package_data={ + 'lo_gpt': ['assets/*'], + } +) diff --git a/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/gpt.py b/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/gpt.py index baf06a6a1..c2790fa07 100644 --- a/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/gpt.py +++ b/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/gpt.py @@ -1,175 +1,11 @@ -import aiohttp -import loremipsum -import os - import learning_observer.communication_protocol.integration -from learning_observer.log_event import debug_log import learning_observer.prestartup import learning_observer.settings +import lo_gpt.gpt + template = """[Task]\n{question}\n\n[Essay]\n{text}""" rubric_template = """{task}\n\n[Rubric]\n{rubric}""" -gpt_responder = None - - -class GPTAPI: - def chat_completion(self, prompt, system_prompt): - ''' - Respond to user prompt - ''' - raise NotImplementedError - - -class GPTInitializationError(Exception): - '''Raise when GPT fails to initialize. - This usually happens when the users forgets to - provide information in the `creds.yaml` file. - ''' - - -class GPTRequestErorr(Exception): - '''Raise when the GPT chat completion raises - an exception - ''' - - -class OpenAIGPT(GPTAPI): - def __init__(self, **kwargs): - ''' - kwargs: - - `model`: the GPT model we should use, defaults to `gpt-3.5-turbo-16k` - - `api_key`: OpenAI api key, defaults to the `OPENAI_API_KEY` environment variable. - ''' - super().__init__() - self.model = kwargs.get('model', 'gpt-3.5-turbo-16k') - self.api_key = kwargs.get('api_key', os.getenv('OPENAI_API_KEY')) - if self.api_key is None: - exception_text = 'Error while starting openai:\n'\ - 'Please ensure that the API Key is correctly configured in '\ - '`creds.yaml` under `modules.writing_observer.gpt_responders.openai.api_key`, '\ - 'or alternatively, set it as the `OPENAI_API_KEY` environment '\ - 'variable.' - raise GPTInitializationError(exception_text) - - async def chat_completion(self, prompt, system_prompt): - url = 'https://api.openai.com/v1/chat/completions' - headers = {'Content-Type': 'application/json', 'Authorization': f'Bearer {self.api_key}'} - messages = [ - {'role': 'system', 'content': system_prompt}, - {'role': 'user', 'content': prompt} - ] - content = {'model': self.model, 'messages': messages} - async with aiohttp.ClientSession() as session: - async with session.post(url, headers=headers, json=content) as resp: - json_resp = await resp.json() - if resp.status == 200: - return json_resp['choices'][0]['message']['content'] - error = 'Error occured while making OpenAI request' - if 'error' in json_resp: - error += f"\n{json_resp['error']['message']}" - raise GPTRequestErorr(error) - - -class OllamaGPT(GPTAPI): - '''GPT responder for handling request to the Ollama API - ''' - def __init__(self, **kwargs): - ''' - kwargs - - `model`: the GPT model we should use, defaults to `llama2` - - `host`: Ollama server to connect to - the Ollama client will - default to `localhost:11434`. - ''' - super().__init__() - self.model = kwargs.get('model', 'llama2') - # the Ollama client checks for the `OLLAMA_HOST` env variable - # or defaults to `localhost:11434`. We provide a warning when - # a specific host is not found. - self.ollama_host = kwargs.get('host', os.getenv('OLLAMA_HOST', None)) - if self.ollama_host is None: - debug_log('WARNING:: Ollama host not specified. Defaulting to '\ - '`localhost:11434`.\nTo set a specific host, set '\ - '`modules.writing_observer.gpt_responders.ollama.host` '\ - 'in `creds.yaml` or set the `OLLAMA_HOST` environment '\ - 'variable.\n'\ - 'If you wish to install Ollama and download a model, '\ - 'run the following commands:\n'\ - '```bash\ncurl https://ollama.ai/install.sh | sh\n'\ - 'ollama run \n```') - self.ollama_host = 'http://localhost:11434' - - async def chat_completion(self, prompt, system_prompt): - '''Ollama only returns a single item compared to GPT returning a list - ''' - url = f'{self.ollama_host}/api/chat' - messages = [ - {'role': 'system', 'content': system_prompt}, - {'role': 'user', 'content': prompt} - ] - content = {'model': self.model, 'messages': messages, 'stream': False} - async with aiohttp.ClientSession() as session: - async with session.post(url, json=content) as resp: - json_resp = await resp.json() - if resp.status == 200: - return json_resp['message']['content'] - error = 'Error occured while making Ollama request' - if 'error' in json_resp: - error += f"\n{json_resp['error']['message']}" - raise GPTRequestErorr(error) - - -class StubGPT(GPTAPI): - '''GPT responder for handling stub requests - ''' - def __init__(self, **kwargs): - super().__init__() - - async def chat_completion(self, prompt, system_prompt): - return "\n".join(loremipsum.get_paragraphs(1)) - - -GPT_RESPONDERS = { - 'openai': OpenAIGPT, - 'ollama': OllamaGPT, - 'stub': StubGPT -} - - -@learning_observer.prestartup.register_startup_check -def initialize_gpt_responder(): - '''Iterate over the gpt_responders listed in `creds.yaml` - and attempt to initialize it. On successful initialization - of a responder, exit the this startup check. Otherwise, - try the next one. - ''' - global gpt_responder - # TODO change this to use settings.module_settings() instead - # that method now uses pmss which doesn't support lists and - # dictionaries yet. - responders = learning_observer.settings.settings['modules']['writing_observer'].get('gpt_responders', {}) - exceptions = [] - for key in responders: - if key not in GPT_RESPONDERS: - exceptions.append(KeyError( - f'GPT Responder `{key}` is not yet configured on this system.\n'\ - f'The available responders are [{", ".join(GPT_RESPONDERS.keys())}].' - )) - continue - try: - gpt_responder = GPT_RESPONDERS[key](**responders[key]) - debug_log(f'INFO:: Using GPT responder `{key}` with model `{responders[key]["model"]}`') - return True - except GPTInitializationError as e: - exceptions.append(e) - debug_log(f'WARNING:: Unable to initialize GPT responder `{key}:`.\n{e}') - gpt_responder = None - no_responders = 'No GPT responders found in `creds.yaml`. To add a responder, add either'\ - '`openai` or `ollama` along with any subsettings to `modules.writing_observer.gpt_responders`.\n'\ - 'Example:\n```\ngpt_responders:\n ollama:\n model: llama2\n```' - exception_strings = '\n'.join(str(e) for e in exceptions) if len(exceptions) > 0 else no_responders - exception_text = 'Unable to initialize a GPT responder. Encountered the following errors:\n'\ - f'{exception_strings}' - raise learning_observer.prestartup.StartupCheck("GPT: " + exception_text) @learning_observer.communication_protocol.integration.publish_function('wo_bulk_essay_analysis.gpt_essay_prompt') @@ -183,7 +19,7 @@ async def process_student_essay(text, prompt, system_prompt, tags): @learning_observer.cache.async_memoization() async def gpt(gpt_prompt): - completion = await gpt_responder.chat_completion(gpt_prompt, system_prompt) + completion = await lo_gpt.gpt.gpt_responder.chat_completion(gpt_prompt, system_prompt) return completion if len(prompt) == 0: @@ -208,17 +44,3 @@ async def gpt(gpt_prompt): 'prompt': prompt } return output - - -async def test_responder(): - responder = OllamaGPT() - response = await responder.chat_completion('Why is the sky blue?', 'You are a helper agent, please help fulfill user requests.') - print('Response:', response) - - -if __name__ == '__main__': - import asyncio - loop = asyncio.get_event_loop() - asyncio.ensure_future(test_responder()) - loop.run_forever() - loop.close() diff --git a/wo_requirements.txt b/wo_requirements.txt index 54e26e129..5d42da251 100644 --- a/wo_requirements.txt +++ b/wo_requirements.txt @@ -1,3 +1,4 @@ writing_observer @ git+https://github.com/ETS-Next-Gen/writing_observer.git#subdirectory=modules/writing_observer/ +lo_gpt @ git+https://github.com/ETS-Next-Gen/writing_observer.git#subdirectory=modules/lo_gpt/ wo_bulk_essay_analysis @ git+https://github.com/ETS-Next-Gen/writing_observer.git#subdirectory=modules/wo_bulk_essay_analysis/ wo_classroom_text_highlighter @ git+https://github.com/ETS-Next-Gen/writing_observer.git#subdirectory=modules/wo_classroom_text_highlighter/ From cc3a4b7b7eb4a74f2b5f4ff06da3e8c30028c6a1 Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Thu, 7 Nov 2024 11:00:57 -0500 Subject: [PATCH 10/88] small installation fixes --- Makefile | 3 +++ modules/writing_observer/writing_observer/awe_nlp.py | 1 + 2 files changed, 4 insertions(+) diff --git a/Makefile b/Makefile index f22128c14..019952a31 100644 --- a/Makefile +++ b/Makefile @@ -53,6 +53,9 @@ install-packages: venv # Using the `--no-binary` option includes all files. pip uninstall -y protobuf pip install --no-binary=protobuf protobuf==4.25 + # Numpy v2 usually gets installed but causes issues with a + # mismatch in binary headers, since something wants numpy v1 + pip install -U numpy==1.26.4 # testing commands test: diff --git a/modules/writing_observer/writing_observer/awe_nlp.py b/modules/writing_observer/writing_observer/awe_nlp.py index 838f82d09..8eb616204 100644 --- a/modules/writing_observer/writing_observer/awe_nlp.py +++ b/modules/writing_observer/writing_observer/awe_nlp.py @@ -75,6 +75,7 @@ def init_nlp(): raise OSError(error_text) from e import awe_components.setup.data awe_components.setup.data.download_models() + nlp = spacy.load('en_core_web_lg') # Adding all of the components, since # each of them turns out to be implicated in From 78723f9fc9306f7b8642634163da103532e57f95 Mon Sep 17 00:00:00 2001 From: Paul Brost Date: Thu, 7 Nov 2024 22:42:55 -0500 Subject: [PATCH 11/88] Update to redux-thunk and compile error fix --- modules/lo_event/lo_event/reduxLogger.js | 2 +- modules/lo_event/lo_event/util.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/lo_event/lo_event/reduxLogger.js b/modules/lo_event/lo_event/reduxLogger.js index 1aac21609..c4087d08f 100644 --- a/modules/lo_event/lo_event/reduxLogger.js +++ b/modules/lo_event/lo_event/reduxLogger.js @@ -19,7 +19,7 @@ * use bits and pieces, or to treat this code as an examplar. */ import * as redux from 'redux'; -import thunk from 'redux-thunk'; +import { default as thunk } from 'redux-thunk'; const EMIT_EVENT = 'EMIT_EVENT'; const EMIT_LOCKFIELDS = 'EMIT_LOCKFIELDS'; diff --git a/modules/lo_event/lo_event/util.js b/modules/lo_event/lo_event/util.js index b2f7d4c34..8641d7bcd 100644 --- a/modules/lo_event/lo_event/util.js +++ b/modules/lo_event/lo_event/util.js @@ -447,8 +447,8 @@ export function treeget(tree, key) { else { if (keylist[i] && keylist[i].indexOf('[')>0) { const item = keylist[i].split('[')[0]; - const idx = keylist[i].split('[')[1]; - idx = idx.split(']')[0]; + const idx_orig = keylist[i].split('[')[1]; + const idx = idx.split(']')[0]; if (item in subtree) { if (subtree[item][idx] !== undefined) { subtree =subtree[item][idx]; From e6a922cdbfd9af6a6a63fb21a55db3513f456570 Mon Sep 17 00:00:00 2001 From: Paul Brost Date: Fri, 8 Nov 2024 15:50:28 -0500 Subject: [PATCH 12/88] import thunk statement change --- modules/lo_event/lo_event/reduxLogger.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/lo_event/lo_event/reduxLogger.js b/modules/lo_event/lo_event/reduxLogger.js index c4087d08f..1aac21609 100644 --- a/modules/lo_event/lo_event/reduxLogger.js +++ b/modules/lo_event/lo_event/reduxLogger.js @@ -19,7 +19,7 @@ * use bits and pieces, or to treat this code as an examplar. */ import * as redux from 'redux'; -import { default as thunk } from 'redux-thunk'; +import thunk from 'redux-thunk'; const EMIT_EVENT = 'EMIT_EVENT'; const EMIT_LOCKFIELDS = 'EMIT_LOCKFIELDS'; From d4130300e61220f6b879866bf18e3a302e1c9d25 Mon Sep 17 00:00:00 2001 From: Paul Brost Date: Fri, 8 Nov 2024 16:09:12 -0500 Subject: [PATCH 13/88] Make sure b.storage defaults to thunk if not sync or local --- modules/lo_event/lo_event/browserStorage.js | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/modules/lo_event/lo_event/browserStorage.js b/modules/lo_event/lo_event/browserStorage.js index 1270569c4..613d0fb31 100644 --- a/modules/lo_event/lo_event/browserStorage.js +++ b/modules/lo_event/lo_event/browserStorage.js @@ -107,12 +107,17 @@ if (typeof browser !== 'undefined') { * - window.localStorage * - thunkStorage */ -if (typeof b !== 'undefined' && b.storage && b.storage.sync) { - debug.info('Setting storage to storage.sync'); - storage = b.storage.sync; -} else if (typeof b !== 'undefined' && b.storage && b.storage.local) { - debug.info('Setting storage to storage.local'); - storage = b.storage.local; +if (typeof b !== 'undefined') { + if (b.storage && b.storage.sync) { + debug.info('Setting storage to storage.sync'); + storage = b.storage.sync; + } else if (b.storage && b.storage.local) { + debug.info('Setting storage to storage.local'); + storage = b.storage.local; + } else { + debug.info('Setting storage to default, thunkStorage'); + storage = thunkStorage; + } } else if (typeof localStorage !== 'undefined') { // Add compatibility modifications for localStorage debug.info('Setting storage to localStorage'); From ad9d485a42a40e7cfbd942d71b652c0f957535a6 Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Thu, 14 Nov 2024 12:54:03 -0500 Subject: [PATCH 14/88] code compiling for deployment of toy-sba (#198) Co-authored-by: Paul Brost --- modules/lo_event/lo_event/reduxLogger.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/lo_event/lo_event/reduxLogger.js b/modules/lo_event/lo_event/reduxLogger.js index 1aac21609..c5b74e60f 100644 --- a/modules/lo_event/lo_event/reduxLogger.js +++ b/modules/lo_event/lo_event/reduxLogger.js @@ -19,7 +19,7 @@ * use bits and pieces, or to treat this code as an examplar. */ import * as redux from 'redux'; -import thunk from 'redux-thunk'; +import { thunk } from 'redux-thunk'; const EMIT_EVENT = 'EMIT_EVENT'; const EMIT_LOCKFIELDS = 'EMIT_LOCKFIELDS'; From 83cdafb1574ce0456f6e9386a17658d44bbf3cf9 Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Tue, 19 Nov 2024 10:45:29 -0500 Subject: [PATCH 15/88] LO Event improvements The improvements include, function to compile metadata, fixed sending metadata on reconnects, cleaned up event auth pipeline, various linting improvements, abstracted items to their own files, various loevent bug fixes. Full commit list: * Missing semicolons * Slight clean-up of lo_event, and adding logic for extra metadata for debugging * WiP: Sync between computers: metadata code * Sane way of adding many pieces of metadata * Comment * Removing arrows, since those have a bug. Should be readded later. * Moving lo_dash_react_components to Node 22, newer packages * Missing semicolons * Default host for websocket logger * updated extension to use appropriate metadata functions and added metadata to queue on socket startup * added stringify where needed * updated to proper storage item and fixed storage bug * changed order of dispatch event targets * document retry code * updated lo_event tests * code cleanup * updated extension event * fixed typo * updated docstring * updated auth pipeline and fixed a few bugs * re-added python build steps * added some extra information to the lodrc readme --------- Co-authored-by: Piotr Mitros --- extension/writing-process/src/background.js | 30 +- extension/writing-process/src/writing.js | 2 +- .../learning_observer/auth/events.py | 61 +- .../incoming_student_event.py | 4 +- modules/lo_dash_react_components/.npmrc | 1 - modules/lo_dash_react_components/README.md | 6 + .../package-lock.json | 36049 ++++++---------- modules/lo_dash_react_components/package.json | 102 +- .../lib/components/DAProblemDisplay.react.js | 2 +- .../lib/components/WOAnnotatedText.react.js | 21 +- .../lib/components/WOStudentTextTile.react.js | 2 +- .../src/lib/index.scss | 4 +- modules/lo_event/examples/browser_events.html | 20 + modules/lo_event/lo_event/disabler.js | 20 +- modules/lo_event/lo_event/lo_event.js | 92 +- .../lo_event/lo_event/metadata/browserinfo.js | 112 + .../lo_event/lo_event/metadata/chromeauth.js | 49 + modules/lo_event/lo_event/metadata/storage.js | 61 + modules/lo_event/lo_event/util.js | 210 +- modules/lo_event/lo_event/websocketLogger.js | 85 +- modules/lo_event/tests/lo_event.test.js | 4 +- modules/lo_event/tests/ws.test.js | 5 +- 22 files changed, 13580 insertions(+), 23362 deletions(-) delete mode 100644 modules/lo_dash_react_components/.npmrc create mode 100644 modules/lo_event/lo_event/metadata/browserinfo.js create mode 100644 modules/lo_event/lo_event/metadata/chromeauth.js create mode 100644 modules/lo_event/lo_event/metadata/storage.js diff --git a/extension/writing-process/src/background.js b/extension/writing-process/src/background.js index aa569cfed..5396ce383 100644 --- a/extension/writing-process/src/background.js +++ b/extension/writing-process/src/background.js @@ -17,7 +17,9 @@ import * as loEvent from 'lo_event/lo_event/lo_event.js'; import * as loEventDebug from 'lo_event/lo_event/debugLog.js'; import { websocketLogger } from 'lo_event/lo_event/websocketLogger.js'; import { consoleLogger } from 'lo_event/lo_event/consoleLogger.js'; -import * as loEventUtils from 'lo_event/lo_event/util.js'; +import { browserInfo } from 'lo_event/lo_event/metadata/browserinfo.js'; +import { chromeAuth } from 'lo_event/lo_event/metadata/chromeauth.js'; +import { localStorageInfo, sessionStorageInfo } from 'lo_event/lo_event/metadata/storage.js'; // We would like to support fetching the websocket server from storage @@ -33,8 +35,20 @@ const loggers = [ websocketLogger(WEBSOCKET_SERVER_URL) ] -loEvent.init('org.mitros.writing_analytics', '0.01', loggers, loEventDebug.LEVEL.SIMPLE); -loEvent.setFieldSet([loEventUtils.getBrowserInfo(), loEventUtils.fetchDebuggingIdentifier()]); +loEvent.init( + 'org.mitros.writing_analytics', + '0.01', + loggers, + { + debugLevel: loEventDebug.LEVEL.SIMPLE, + metadata: [ + browserInfo(), + chromeAuth(), + localStorageInfo(), + sessionStorageInfo(), + ] + } +); loEvent.go(); // Function to serve as replacement for @@ -205,16 +219,6 @@ async function reinjectContentScripts() { // Let the server know we've loaded. loEvent.logEvent("extension_loaded", {}); -// Send the server the user info. This might not always be available. -// HACK: this code will be changed pending server side changes to how we -// handle auth and metadata. -loEventUtils.profileInfoWrapper().then((result) => { - if (Object.keys(result).length > 0) { - loEvent.logEvent('chrome_identity', { chrome_identity: result }); - loEvent.logEvent('metadata_finished', {}) - } -}); - // And let the console know we've loaded // chrome.extension.getBackgroundPage().console.log("Loaded"); remove logFromServiceWorker("Loaded"); diff --git a/extension/writing-process/src/writing.js b/extension/writing-process/src/writing.js index 653544a28..1367e8a81 100644 --- a/extension/writing-process/src/writing.js +++ b/extension/writing-process/src/writing.js @@ -37,7 +37,7 @@ function log_event(event_type, event) { "title": google_docs_title(), "id": doc_id(), "url": window.location.href, - } + }; event['event'] = event_type; // We want to track the page status during events. For example, diff --git a/learning_observer/learning_observer/auth/events.py b/learning_observer/learning_observer/auth/events.py index 96b7a60ec..ba20dfdc2 100644 --- a/learning_observer/learning_observer/auth/events.py +++ b/learning_observer/learning_observer/auth/events.py @@ -53,22 +53,6 @@ def wrapper(f): return wrapper -def find_event(event_type, event_list): - ''' - Find the first event of type `event` in the `event_list` - - Return `None` if no event found. - - >>> find_event('this-one', [{'event': 'not-this-one'}, {'event': 'not-this-one'}, {'event': 'this-one'}]) - {'event': 'this-one'} - >>> find_event('missing-event', [{'event': 'not-this-one'}, {'event': 'not-this-one'}, {'event': 'this-one'}]) - ''' - for e in event_list: - if e.get('event', None) == event_type: - return e - return None - - def encode_id(source, unsafe_id): ''' This is a bit of encoding logic to generically encode IDs from @@ -117,7 +101,7 @@ def token_authorize_user(auth_method, user_id_token): @register_event_auth("http_basic") -async def basic_auth(request, headers, first_event, source): +async def basic_auth(request, event, source): ''' Authenticate with HTTP Basic through nginx. ''' @@ -144,7 +128,7 @@ async def basic_auth(request, headers, first_event, source): @register_event_auth("guest") -async def guest_auth(request, headers, first_event, source): +async def guest_auth(request, event, source): ''' Guest users. @@ -169,7 +153,7 @@ async def guest_auth(request, headers, first_event, source): @register_event_auth("local_storage") -async def local_storage_auth(request, headers, first_event, source): +async def local_storage_auth(request, event, source): ''' This authentication method is used by the browser extension, based on configuration options. Each Chromebook is given a unique ID @@ -188,23 +172,21 @@ async def local_storage_auth(request, headers, first_event, source): >>> a {'sec': 'unauthenticated', 'user_id': 'ls-jim', 'providence': 'ls'} ''' - authdata = find_event('local_storage', headers + [first_event]) - - if authdata is None or 'user_tag' not in authdata: + if 'user_tag' not in event: return False - user_id = "ls-" + authdata['user_tag'] + user_id = "ls-" + event['user_tag'] authenticated = token_authorize_user('local_storage', user_id) return { - 'sec': token_authorize_user('local_storage', user_id), + 'sec': authenticated, constants.USER_ID: user_id, 'providence': 'ls' # local storage } @register_event_auth("chromebook") -async def chromebook_auth(request, headers, first_event, source): +async def chromebook_auth(request, event, source): ''' Authenticate student Chromebooks. @@ -212,20 +194,18 @@ async def chromebook_auth(request, headers, first_event, source): the identity token to the Google ID. TODO: See about client-side oauth on Chromebooks ''' - authdata = find_event('chrome_identity', headers + [first_event]) - - if authdata is None or 'chrome_identity' not in authdata: + if 'chrome_identity' not in event: return False # If we have an auth key, we are authenticated! - lsa = await local_storage_auth(request, headers, first_event, source) + lsa = await local_storage_auth(request, event, source) if lsa and lsa['sec'] == 'authenticated': auth = 'authenticated' else: auth = 'unauthenticated' - untrusted_google_id = authdata.get('chrome_identity', {}).get('id', None) + untrusted_google_id = event.get('chrome_identity', {}).get('id', None) debug_log("untrusted_google_id", untrusted_google_id) if untrusted_google_id is None: @@ -241,7 +221,7 @@ async def chromebook_auth(request, headers, first_event, source): @register_event_auth("hash_identify") -async def hash_identify(request, headers, first_event, source): +async def hash_identify(request, event, source): ''' It's sometimes convenient to point folks to pages where the user ID is encoded in the URL e.g. by hash: @@ -258,38 +238,33 @@ async def hash_identify(request, headers, first_event, source): This could be made better by providing an authenticated user list. Then, it'd be okay for the math team example ''' - authdata = find_event('hash_auth', headers + [first_event]) - debug_log("authdata", authdata) - - if authdata is None or 'hash' not in authdata: + if 'hash' not in event: return False return { 'sec': 'unauthenticated', - constants.USER_ID: "hi-" + authdata['hash'], + constants.USER_ID: "hi-" + event['hash'], 'providence': 'mch' # Math contest hash -- toying with plug-in archicture } @register_event_auth("testcase_auth") -async def test_case_identify(request, headers, first_event, source): +async def test_case_identify(request, event, source): ''' This is for test cases. It's quick, easy, insecure, and shouldn't be used in production. ''' - authdata = find_event('test_framework_fake_identity', headers + [first_event]) - - if authdata is None or constants.USER_ID not in authdata: + if constants.USER_ID not in event: return False return { 'sec': "unauthenticated", - constants.USER_ID: "testcase-" + authdata[constants.USER_ID], + constants.USER_ID: "testcase-" + event[constants.USER_ID], 'providence': 'tc' } -async def authenticate(request, headers, first_event, source): +async def authenticate(request, event, source): ''' Authenticate an event stream. @@ -312,7 +287,7 @@ async def authenticate(request, headers, first_event, source): 3. `user_id` -- a unique user identifier ''' for auth_method in learning_observer.settings.settings['event_auth']: - auth_metadata = await AUTH_METHODS[auth_method](request, headers, first_event, source) + auth_metadata = await AUTH_METHODS[auth_method](request, event, source) if auth_metadata: if "safe_user_id" not in auth_metadata: auth_metadata['safe_user_id'] = encode_id( diff --git a/learning_observer/learning_observer/incoming_student_event.py b/learning_observer/learning_observer/incoming_student_event.py index 1ca84d2b0..6cb7de904 100644 --- a/learning_observer/learning_observer/incoming_student_event.py +++ b/learning_observer/learning_observer/incoming_student_event.py @@ -403,12 +403,10 @@ async def handle_auth_events(events): if not authenticated: authenticated = await learning_observer.auth.events.authenticate( request=request, - headers=[event], - first_event={}, + event=event, source='' ) if authenticated: - print(authenticated) await ws.send_json({ 'status': 'auth', constants.USER_ID: authenticated[constants.USER_ID] diff --git a/modules/lo_dash_react_components/.npmrc b/modules/lo_dash_react_components/.npmrc deleted file mode 100644 index b6f27f135..000000000 --- a/modules/lo_dash_react_components/.npmrc +++ /dev/null @@ -1 +0,0 @@ -engine-strict=true diff --git a/modules/lo_dash_react_components/README.md b/modules/lo_dash_react_components/README.md index 69c606bbe..1db5654bb 100644 --- a/modules/lo_dash_react_components/README.md +++ b/modules/lo_dash_react_components/README.md @@ -4,8 +4,14 @@ In Learning Observer, we create React components and generate a Python package for them to be used with the Dash framework. These components are housed in the `lo_dash_react_components` module and enable the creation of highly customizable dashboards. This document guides you through the component development process, the build process, and using the components in your dashboards. +### Pre-built installation + +These components take a bit of extra infrastructure to build (mainly `node`). If you just with to use these components, without developing new ones or changing current ones, we suggest installing the pre-built package available in the [LO Assets github repository](https://github.com/ETS-Next-Gen/lo_assets/tree/main/lo_dash_react_components). + ### Requirements +TODO verify which Node versions work. Previously, we encountered dependency issues with newer versions of node. + A downstream dependency in the build process may cause breaking behavior depending on your node version. The latest version of Node `v16` (tested on `v16.19.1`) should work fine; however, we've noticed errors on Node `v18`. If you already have `v18` on your system, install [nvm: Node Version Manager](https://github.com/nvm-sh/nvm). When running `nvm` commands within the project without specifying a specific version, it will automatically look for the version defined in the `.nvmrc` file in the root directory. diff --git a/modules/lo_dash_react_components/package-lock.json b/modules/lo_dash_react_components/package-lock.json index 528896ff6..785fdec74 100644 --- a/modules/lo_dash_react_components/package-lock.json +++ b/modules/lo_dash_react_components/package-lock.json @@ -9,110 +9,115 @@ "version": "0.0.1", "license": "AGPL-3.0", "dependencies": { - "@testing-library/jest-dom": "^5.16.5", - "@testing-library/react": "^13.4.0", - "@testing-library/user-event": "^13.5.0", - "bootstrap": "^5.2.3", - "node-sass": "^9.0.0", - "nodemon": "^2.0.21", - "ramda": "^0.26.1", - "react": "^18.2.0", - "react-bootstrap": "^2.7.2", - "react-dom": "^18.2.0", - "react-router": "^6.8.2", - "react-router-dom": "^6.8.2", + "bootstrap": "^5.3.3", + "ramda": "^0.30.1", + "react": "^18.3.1", + "react-bootstrap": "^2.10.5", + "react-dom": "^18.3.1", + "react-router-dom": "^6.28.0", "react-scripts": "^5.0.1", - "react-tooltip": "^5.20.0", - "recharts": "^2.4.3", - "web-vitals": "^2.1.4" + "react-tooltip": "^5.28.0", + "recharts": "^2.13.3", + "web-vitals": "^4.2.4" }, "devDependencies": { - "@babel/core": "^7.5.4", - "@babel/plugin-proposal-object-rest-spread": "^7.5.4", - "@babel/preset-env": "^7.5.4", - "@babel/preset-react": "^7.0.0", - "@plotly/dash-component-plugins": "^1.2.0", - "@plotly/webpack-dash-dynamic-import": "^1.2.0", - "babel-eslint": "^10.0.2", - "babel-loader": "^8.0.6", - "copyfiles": "^2.1.1", - "css-loader": "^3.0.0", - "eslint": "^6.0.1", - "eslint-config-prettier": "^6.0.0", - "eslint-plugin-import": "^2.18.0", - "eslint-plugin-react": "^7.14.2", + "@babel/core": "^7.26.0", + "@babel/preset-env": "^7.26.0", + "@babel/preset-react": "^7.25.9", + "autoprefixer": "^10.4.20", + "babel-loader": "^9.2.1", + "css-loader": "^7.1.2", + "eslint-config-prettier": "^9.1.0", + "eslint-config-standard": "^17.1.0", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-promise": "^6.0.0", + "eslint-plugin-standard": "^4.1.0", + "nodemon": "^3.1.7", "npm-run-all": "^4.1.5", - "prop-types": "^15.7.2", - "react-docgen": "^4.1.1", - "style-loader": "^0.23.1", - "styled-jsx": "^5.1.2", - "terser-webpack-plugin": "^2.3.0", - "webpack": "4.37.0", - "webpack-cli": "3.3.6", - "webpack-serve": "3.1.0" + "postcss": "^8.4.47", + "postcss-loader": "^8.1.1", + "prettier": "^3.3.3", + "sass": "^1.80.6", + "style-loader": "^4.0.0", + "tailwindcss": "^3.4.14", + "webpack": "^5.96.1", + "webpack-cli": "^5.1.4", + "webpack-dev-server": "^5.1.0" }, "engines": { - "node": ">=16.0.0 <17.0.0", - "npm": ">=8.11.0" + "node": ">=22.0.0" } }, - "node_modules/@adobe/css-tools": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.2.tgz", - "integrity": "sha512-DA5a1C0gD/pLOvhv33YMrbf2FK3oUzwNl9oOJqE4XVjuEtt6XIakRcsd7eLiOSPkp1kTRQGICTA8cKra/vFbjw==" + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "license": "Apache-2.0", "dependencies": { - "@jridgewell/gen-mapping": "^0.1.0", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" } }, "node_modules/@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "license": "MIT", "dependencies": { - "@babel/highlight": "^7.22.13", - "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.21.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.0.tgz", - "integrity": "sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.2.tgz", + "integrity": "sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==", + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.0.tgz", - "integrity": "sha512-PuxUbxcW6ZYe656yL3EAhpy7qXKq0DmYsrJLpbB8XrsCP9Nm+XCg9XFMb5vIDliPD7+U/+M+QJlH17XOcB7eXA==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", + "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", + "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.21.0", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-module-transforms": "^7.21.0", - "@babel/helpers": "^7.21.0", - "@babel/parser": "^7.21.0", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.0", - "@babel/types": "^7.21.0", - "convert-source-map": "^1.7.0", + "@babel/code-frame": "^7.26.0", + "@babel/generator": "^7.26.0", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.0", + "@babel/parser": "^7.26.0", + "@babel/template": "^7.25.9", + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.26.0", + "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", - "semver": "^6.3.0" + "json5": "^2.2.3", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -122,87 +127,130 @@ "url": "https://opencollective.com/babel" } }, - "node_modules/@babel/generator": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", - "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/eslint-parser": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.25.9.tgz", + "integrity": "sha512-5UXfgpK0j0Xr/xIdgdLEhOFxaDZ0bRPWJJchRpqOSur/3rZoPbqqki5mm0p4NE2cs28krBEiSM2MB7//afRSQQ==", + "license": "MIT", "dependencies": { - "@babel/types": "^7.23.0", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" + "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", + "eslint-visitor-keys": "^2.1.0", + "semver": "^6.3.1" }, "engines": { - "node": ">=6.9.0" + "node": "^10.13.0 || ^12.13.0 || >=14.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.11.0", + "eslint": "^7.5.0 || ^8.0.0 || ^9.0.0" } }, - "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "node_modules/@babel/eslint-parser/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==", + "license": "Apache-2.0", + "engines": { + "node": ">=10" + } + }, + "node_modules/@babel/eslint-parser/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz", + "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==", + "license": "MIT", "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@babel/parser": "^7.26.2", + "@babel/types": "^7.26.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" }, "engines": { - "node": ">=6.0.0" + "node": ">=6.9.0" } }, "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", - "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", + "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", + "license": "MIT", "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz", - "integrity": "sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.25.9.tgz", + "integrity": "sha512-C47lC7LIDCnz0h4vai/tpNOI95tCd5ZT3iBt/DBH5lXKHZsyNQv18yf1wIIg2ntiQNgmAvA+DgZ82iW8Qdym8g==", + "license": "MIT", "dependencies": { - "@babel/helper-explode-assignable-expression": "^7.18.6", - "@babel/types": "^7.18.9" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", - "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", - "dependencies": { - "@babel/compat-data": "^7.20.5", - "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.21.3", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", + "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", + "browserslist": "^4.24.0", "lru-cache": "^5.1.1", - "semver": "^6.3.0" + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.0.tgz", - "integrity": "sha512-Q8wNiMIdwsv5la5SPxNYzzkPnjgC0Sy0i7jLkVOCdllu/xcVNkr3TeZzbHBJrj+XXRqzX5uCyCoV9eu6xUG7KQ==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.21.0", - "@babel/helper-member-expression-to-functions": "^7.21.0", - "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/helper-replace-supers": "^7.20.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", - "@babel/helper-split-export-declaration": "^7.18.6" + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.9.tgz", + "integrity": "sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-member-expression-to-functions": "^7.25.9", + "@babel/helper-optimise-call-expression": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", + "@babel/traverse": "^7.25.9", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -211,13 +259,24 @@ "@babel/core": "^7.0.0" } }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.21.0.tgz", - "integrity": "sha512-N+LaFW/auRSWdx7SHD/HiARwXQju1vXTW4fKr4u5SgBUTm51OKEjKgj+cs00ggW3kEvNqwErnlwuq7Y3xBe4eg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.9.tgz", + "integrity": "sha512-ORPNZ3h6ZRkOyAa/SaHU+XsLZr0UQzRwuDQ0cczIA17nAzZ+85G5cVkOJIj7QavLZGSe8QXUmNFxSZzjcZF9bw==", + "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "regexpu-core": "^5.3.1" + "@babel/helper-annotate-as-pure": "^7.25.9", + "regexpu-core": "^6.1.1", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -226,132 +285,104 @@ "@babel/core": "^7.0.0" } }, + "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz", - "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz", + "integrity": "sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==", + "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.17.7", - "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-plugin-utils": "^7.22.5", "debug": "^4.1.1", "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2", - "semver": "^6.1.2" + "resolve": "^1.14.2" }, "peerDependencies": { - "@babel/core": "^7.4.0-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==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-explode-assignable-expression": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz", - "integrity": "sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==", - "dependencies": { - "@babel/types": "^7.18.6" - }, - "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==", - "dependencies": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" - }, - "engines": { - "node": ">=6.9.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==", - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.21.0.tgz", - "integrity": "sha512-Muu8cdZwNN6mRRNG6lAYErJ5X3bRevgYR2O8wN0yn7jJSnGDu6eG59RfT29JHxGUovyfrh6Pj0XzmR7drNVL3Q==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz", + "integrity": "sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==", + "license": "MIT", "dependencies": { - "@babel/types": "^7.21.0" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "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==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "license": "MIT", "dependencies": { - "@babel/types": "^7.18.6" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz", - "integrity": "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==", + "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==", + "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.20.2", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.19.1", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.2", - "@babel/types": "^7.21.2" + "@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-optimise-call-expression": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz", - "integrity": "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz", + "integrity": "sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==", + "license": "MIT", "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", - "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", + "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==", + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz", - "integrity": "sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.9.tgz", + "integrity": "sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw==", + "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-wrap-function": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-wrap-function": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -361,122 +392,110 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.20.7.tgz", - "integrity": "sha512-vujDMtB6LVfNW13jhlCrp48QNslK6JXi7lQG736HVbHz/mbf4Dc7tIRh1Xf5C0rF7BP8iiSxGMCmY6Ci1ven3A==", - "dependencies": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-member-expression-to-functions": "^7.20.7", - "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.7", - "@babel/types": "^7.20.7" + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.9.tgz", + "integrity": "sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.25.9", + "@babel/helper-optimise-call-expression": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-simple-access": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", - "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.25.9.tgz", + "integrity": "sha512-c6WHXuiaRsJTyHYLJV75t9IqsmTbItYfdj99PnzYGQZkYKvan5/2jKJ7gu31J3/BJ/A18grImSPModuyG/Eo0Q==", + "license": "MIT", "dependencies": { - "@babel/types": "^7.20.2" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz", - "integrity": "sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==", - "dependencies": { - "@babel/types": "^7.20.0" - }, - "engines": { - "node": ">=6.9.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==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz", + "integrity": "sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==", + "license": "MIT", "dependencies": { - "@babel/types": "^7.22.5" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "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==", + "license": "MIT", "engines": { "node": ">=6.9.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==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz", - "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz", - "integrity": "sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.9.tgz", + "integrity": "sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==", + "license": "MIT", "dependencies": { - "@babel/helper-function-name": "^7.19.0", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.20.5", - "@babel/types": "^7.20.5" + "@babel/template": "^7.25.9", + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz", - "integrity": "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", + "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", + "license": "MIT", "dependencies": { - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.0", - "@babel/types": "^7.21.0" + "@babel/template": "^7.25.9", + "@babel/types": "^7.26.0" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/highlight": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", - "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "node_modules/@babel/parser": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", + "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", + "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" + "@babel/types": "^7.26.0" }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", "bin": { "parser": "bin/babel-parser.js" }, @@ -484,12 +503,14 @@ "node": ">=6.0.0" } }, - "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz", - "integrity": "sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==", + "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz", + "integrity": "sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -498,125 +519,78 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.20.7.tgz", - "integrity": "sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", - "@babel/plugin-proposal-optional-chaining": "^7.20.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.13.0" - } - }, - "node_modules/@babel/plugin-proposal-async-generator-functions": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz", - "integrity": "sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==", - "dependencies": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-remap-async-to-generator": "^7.18.9", - "@babel/plugin-syntax-async-generators": "^7.8.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-class-properties": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", - "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-class-static-block": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.21.0.tgz", - "integrity": "sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw==", + "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.9.tgz", + "integrity": "sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw==", + "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.21.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-class-static-block": "^7.14.5" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^7.12.0" + "@babel/core": "^7.0.0" } }, - "node_modules/@babel/plugin-proposal-decorators": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.21.0.tgz", - "integrity": "sha512-MfgX49uRrFUTL/HvWtmx3zmpyzMMr4MTj3d527MLlr/4RTT9G/ytFFP7qet2uM2Ve03b+BkpWUpK+lRXnQ+v9w==", + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz", + "integrity": "sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug==", + "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.21.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-replace-supers": "^7.20.7", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/plugin-syntax-decorators": "^7.21.0" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/core": "^7.0.0" } }, - "node_modules/@babel/plugin-proposal-dynamic-import": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", - "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==", + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.9.tgz", + "integrity": "sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", + "@babel/plugin-transform-optional-chaining": "^7.25.9" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/core": "^7.13.0" } }, - "node_modules/@babel/plugin-proposal-export-namespace-from": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", - "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", + "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.9.tgz", + "integrity": "sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/core": "^7.0.0" } }, - "node_modules/@babel/plugin-proposal-json-strings": { + "node_modules/@babel/plugin-proposal-class-properties": { "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz", - "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", + "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead.", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-json-strings": "^7.8.3" + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -625,13 +599,15 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz", - "integrity": "sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==", + "node_modules/@babel/plugin-proposal-decorators": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.25.9.tgz", + "integrity": "sha512-smkNLL/O1ezy9Nhy4CNosc4Va+1wo5w4gzSZeLe6y6dM4mmHfYOCPolXQPHQxonZCF+ZyebxN9vqOolkYrSn5g==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/plugin-syntax-decorators": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -644,6 +620,8 @@ "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-nullish-coalescing-operator instead.", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.18.6", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" @@ -659,6 +637,8 @@ "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-numeric-separator instead.", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.18.6", "@babel/plugin-syntax-numeric-separator": "^7.10.4" @@ -670,43 +650,12 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-proposal-object-rest-spread": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz", - "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==", - "dependencies": { - "@babel/compat-data": "^7.20.5", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.20.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-optional-catch-binding": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", - "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-proposal-optional-chaining": { "version": "7.21.0", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz", "integrity": "sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-chaining instead.", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.20.2", "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", @@ -723,6 +672,8 @@ "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-methods instead.", + "license": "MIT", "dependencies": { "@babel/helper-create-class-features-plugin": "^7.18.6", "@babel/helper-plugin-utils": "^7.18.6" @@ -735,15 +686,10 @@ } }, "node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0.tgz", - "integrity": "sha512-ha4zfehbJjc5MmXBlHec1igel5TJXXLDDRbuJ4+XT2TJcyD9/V1919BA8gMvsdHcNMBy4WBUBiRb3nw/EQUtBw==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-create-class-features-plugin": "^7.21.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" - }, + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "license": "MIT", "engines": { "node": ">=6.9.0" }, @@ -751,25 +697,11 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-proposal-unicode-property-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", - "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "@babel/core": "^7.0.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==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -781,6 +713,7 @@ "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==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -792,6 +725,7 @@ "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==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.12.13" }, @@ -803,6 +737,7 @@ "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==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" }, @@ -814,11 +749,12 @@ } }, "node_modules/@babel/plugin-syntax-decorators": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.21.0.tgz", - "integrity": "sha512-tIoPpGBR8UuM4++ccWN3gifhVvQu7ZizuR1fklhRJrd5ewgbkUS+0KVFeWWxELtn18NTLoW32XV7zyOgIAiz+w==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.25.9.tgz", + "integrity": "sha512-ryzI0McXUPJnRCvMo4lumIKZUzhYUO/ScI+Mz4YVaTLt04DHNSjEUjKVvbzQjZFLuod/cYEc07mJWhzl6v4DPg==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -827,34 +763,28 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "node_modules/@babel/plugin-syntax-flow": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.26.0.tgz", + "integrity": "sha512-B+O2DnPc0iG+YXFqOxv2WNuNU97ToWjOomUQ78DouOENWUaM5sVrmet9mcomUGQFwpJd//gvUagXBSdzO1fRKg==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@babel/helper-plugin-utils": "^7.25.9" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3" + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-flow": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.18.6.tgz", - "integrity": "sha512-LUbR+KNTBWCUAqRG9ex5Gnzu2IOkt8jRJbHHXFT9q+L9zm7M/QQbEqXyw1n1pohYvOyWC8CjeyjrSaIwiYjK7A==", + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz", + "integrity": "sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -863,12 +793,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz", - "integrity": "sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==", + "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==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.19.0" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -881,6 +812,7 @@ "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==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" }, @@ -892,6 +824,7 @@ "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==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -900,11 +833,12 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz", - "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz", + "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -917,6 +851,7 @@ "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==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" }, @@ -928,6 +863,7 @@ "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==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -939,6 +875,7 @@ "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==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" }, @@ -950,6 +887,7 @@ "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==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -961,6 +899,7 @@ "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==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -972,6 +911,7 @@ "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==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -983,6 +923,7 @@ "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==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" }, @@ -997,6 +938,7 @@ "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==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" }, @@ -1008,11 +950,12 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", - "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz", + "integrity": "sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.19.0" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1021,12 +964,46 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.20.7.tgz", - "integrity": "sha512-3poA5E7dzDomxj9WXWwuD6A5F3kc7VXwIJO+E+J8qtDtS+pXPAhrgEyh+9GBwBgPq1Z+bB+/JD60lp5jsN7JPQ==", + "node_modules/@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz", + "integrity": "sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg==", + "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/plugin-transform-async-generator-functions": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.9.tgz", + "integrity": "sha512-RXV6QAzTBbhDMO9fWwOmwwTuYaiPbggWQ9INdZqAYeSHyG7FzQ+nOZaUUjNwKv9pV3aE4WFqFm1Hnbci5tBCAw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-remap-async-to-generator": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1036,13 +1013,14 @@ } }, "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz", - "integrity": "sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.9.tgz", + "integrity": "sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ==", + "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-remap-async-to-generator": "^7.18.9" + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-remap-async-to-generator": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1052,11 +1030,12 @@ } }, "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz", - "integrity": "sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.9.tgz", + "integrity": "sha512-toHc9fzab0ZfenFpsyYinOX0J/5dgJVA2fm64xPewu7CoYHWEivIWKxkK2rMi4r3yQqLnVmheMXRdG+k239CgA==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1066,11 +1045,28 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.21.0.tgz", - "integrity": "sha512-Mdrbunoh9SxwFZapeHVrwFmri16+oYotcZysSzhNIVDwIAb1UV+kvnxULSYq9J3/q5MDG+4X6w8QVgD1zhBXNQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.9.tgz", + "integrity": "sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg==", + "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/plugin-transform-class-properties": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.9.tgz", + "integrity": "sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1079,19 +1075,33 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.26.0.tgz", + "integrity": "sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.21.0.tgz", - "integrity": "sha512-RZhbYTCEUAe6ntPehC4hlslPWosNHDox+vAs4On/mCLRLfoDVHf6hVEd7kuxr1RnHwJmxFfUM3cZiZRmPxJPXQ==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.21.0", - "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-replace-supers": "^7.20.7", - "@babel/helper-split-export-declaration": "^7.18.6", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz", + "integrity": "sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9", + "@babel/traverse": "^7.25.9", "globals": "^11.1.0" }, "engines": { @@ -1102,12 +1112,13 @@ } }, "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.20.7.tgz", - "integrity": "sha512-Lz7MvBK6DTjElHAmfu6bfANzKcxpyNPeYBGEafyA6E5HtRpjpZwU+u7Qrgz/2OR0z+5TvKYbPdphfSaAcZBrYQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz", + "integrity": "sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/template": "^7.20.7" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/template": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1117,11 +1128,12 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.7.tgz", - "integrity": "sha512-Xwg403sRrZb81IVB79ZPqNQME23yhugYVqgTxAhT99h485F4f+GMELFhhOsscDUB7HCswepKeCKLn/GZvUKoBA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz", + "integrity": "sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1131,12 +1143,13 @@ } }, "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz", - "integrity": "sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.9.tgz", + "integrity": "sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA==", + "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1146,11 +1159,43 @@ } }, "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz", - "integrity": "sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.9.tgz", + "integrity": "sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw==", + "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/plugin-transform-duplicate-named-capturing-groups-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.9.tgz", + "integrity": "sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-dynamic-import": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.9.tgz", + "integrity": "sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1160,12 +1205,28 @@ } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz", - "integrity": "sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.25.9.tgz", + "integrity": "sha512-KRhdhlVk2nObA5AYa7QMgTMTVJdfHprfpAk4DjZVtllqRg9qarilstTKEhpVjyt+Npi8ThRyiV8176Am3CodPA==", + "license": "MIT", "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-export-namespace-from": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.9.tgz", + "integrity": "sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1175,12 +1236,13 @@ } }, "node_modules/@babel/plugin-transform-flow-strip-types": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.21.0.tgz", - "integrity": "sha512-FlFA2Mj87a6sDkW4gfGrQQqwY/dLlBAyJa2dJEZ+FHXUVHBflO2wyKvg+OOEzXfrKYIa4HWl0mgmbCzt0cMb7w==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.25.9.tgz", + "integrity": "sha512-/VVukELzPDdci7UUsWQaSkhgnjIWXnIyRpM02ldxaVoFK96c41So8JcKT3m0gYjyv7j5FNPGS5vfELrWalkbDA==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-flow": "^7.18.6" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/plugin-syntax-flow": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1190,11 +1252,13 @@ } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.21.0.tgz", - "integrity": "sha512-LlUYlydgDkKpIY7mcBWvyPPmMcOphEyYA27Ef4xpbh1IiDNLr0kZsos2nf92vz3IccvJI25QUwp86Eo5s6HmBQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.9.tgz", + "integrity": "sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1204,13 +1268,29 @@ } }, "node_modules/@babel/plugin-transform-function-name": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz", - "integrity": "sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz", + "integrity": "sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA==", + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-json-strings": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.9.tgz", + "integrity": "sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw==", + "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1220,11 +1300,27 @@ } }, "node_modules/@babel/plugin-transform-literals": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz", - "integrity": "sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz", + "integrity": "sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ==", + "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/plugin-transform-logical-assignment-operators": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.9.tgz", + "integrity": "sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1234,11 +1330,12 @@ } }, "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz", - "integrity": "sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz", + "integrity": "sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1248,12 +1345,13 @@ } }, "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.20.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.20.11.tgz", - "integrity": "sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.9.tgz", + "integrity": "sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw==", + "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.20.11", - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1263,13 +1361,14 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.21.2.tgz", - "integrity": "sha512-Cln+Yy04Gxua7iPdj6nOV96smLGjpElir5YwzF0LBPKoPlLDNJePNlrGGaybAJkd0zKRnOVXOgizSqPYMNYkzA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.25.9.tgz", + "integrity": "sha512-dwh2Ol1jWwL2MgkCzUSOvfmKElqQcuswAZypBSUsScMXvgdT8Ekq5YA6TtqpTVWH+4903NmboMuH1o9i8Rxlyg==", + "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.21.2", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-simple-access": "^7.20.2" + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-simple-access": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1279,14 +1378,15 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.20.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.20.11.tgz", - "integrity": "sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.9.tgz", + "integrity": "sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA==", + "license": "MIT", "dependencies": { - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-module-transforms": "^7.20.11", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-validator-identifier": "^7.19.1" + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1296,12 +1396,13 @@ } }, "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz", - "integrity": "sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.9.tgz", + "integrity": "sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw==", + "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1311,12 +1412,13 @@ } }, "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz", - "integrity": "sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.9.tgz", + "integrity": "sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA==", + "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.20.5", - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1326,11 +1428,12 @@ } }, "node_modules/@babel/plugin-transform-new-target": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz", - "integrity": "sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.9.tgz", + "integrity": "sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1339,13 +1442,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-object-super": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz", - "integrity": "sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==", + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.25.9.tgz", + "integrity": "sha512-ENfftpLZw5EItALAD4WsY/KUWvhUlZndm5GC7G3evUsVeSJB6p0pBeLQUnRnBCBx7zV0RKQjR9kCuwrsIrjWog==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-replace-supers": "^7.18.6" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1354,12 +1457,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-parameters": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.7.tgz", - "integrity": "sha512-WiWBIkeHKVOSYPO0pWkxGPfKeWrCJyD3NJ53+Lrp/QMSZbsVPovrVl2aWZ19D/LTVnaDv5Ap7GJ/B2CTOZdrfA==", + "node_modules/@babel/plugin-transform-numeric-separator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.9.tgz", + "integrity": "sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1368,12 +1472,15 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz", - "integrity": "sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==", + "node_modules/@babel/plugin-transform-object-rest-spread": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.9.tgz", + "integrity": "sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/plugin-transform-parameters": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1382,12 +1489,14 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-react-constant-elements": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.20.2.tgz", - "integrity": "sha512-KS/G8YI8uwMGKErLFOHS/ekhqdHhpEloxs43NecQHVgo2QuQSyJhGIY1fL8UGl9wy5ItVwwoUL4YxVqsplGq2g==", + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz", + "integrity": "sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1396,12 +1505,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-react-display-name": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.18.6.tgz", - "integrity": "sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA==", + "node_modules/@babel/plugin-transform-optional-catch-binding": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.9.tgz", + "integrity": "sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1410,16 +1520,14 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.21.0.tgz", - "integrity": "sha512-6OAWljMvQrZjR2DaNhVfRz6dkCAVV+ymcLUmaf8bccGOHn2v5rHJK3tTpij0BuhdYWP4LLaqj5lwcdlpAAPuvg==", + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.9.tgz", + "integrity": "sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A==", + "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-jsx": "^7.18.6", - "@babel/types": "^7.21.0" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1428,12 +1536,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-react-jsx-development": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.18.6.tgz", - "integrity": "sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA==", + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz", + "integrity": "sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g==", + "license": "MIT", "dependencies": { - "@babel/plugin-transform-react-jsx": "^7.18.6" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1442,13 +1551,14 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-react-pure-annotations": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.18.6.tgz", - "integrity": "sha512-I8VfEPg9r2TRDdvnHgPepTKvuRomzA8+u+nhY7qSI1fR2hRNebasZEETLyM5mAUr0Ku56OkXJ0I7NHJnO6cJiQ==", + "node_modules/@babel/plugin-transform-private-methods": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz", + "integrity": "sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw==", + "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1457,13 +1567,15 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.20.5.tgz", - "integrity": "sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ==", + "node_modules/@babel/plugin-transform-private-property-in-object": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.9.tgz", + "integrity": "sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "regenerator-transform": "^0.15.1" + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1472,12 +1584,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz", - "integrity": "sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==", + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz", + "integrity": "sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1486,17 +1599,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-runtime": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.21.0.tgz", - "integrity": "sha512-ReY6pxwSzEU0b3r2/T/VhqMKg/AkceBT19X0UptA3/tYi5Pe2eXgEUH+NNMC5nok6c6XQz5tyVTUpuezRfSMSg==", + "node_modules/@babel/plugin-transform-react-constant-elements": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.25.9.tgz", + "integrity": "sha512-Ncw2JFsJVuvfRsa2lSHiC55kETQVLSnsYGQ1JDDwkUeWGTL/8Tom8aLTnlqgoeuopWrbbGndrc9AlLYrIosrow==", + "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-plugin-utils": "^7.20.2", - "babel-plugin-polyfill-corejs2": "^0.3.3", - "babel-plugin-polyfill-corejs3": "^0.6.0", - "babel-plugin-polyfill-regenerator": "^0.4.1", - "semver": "^6.3.0" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1505,12 +1614,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz", - "integrity": "sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==", + "node_modules/@babel/plugin-transform-react-display-name": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.25.9.tgz", + "integrity": "sha512-KJfMlYIUxQB1CJfO3e0+h0ZHWOTLCPP115Awhaz8U0Zpq36Gl/cXlpoyMRnUWlhNUBAzldnCiAZNvCDj7CrKxQ==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1519,13 +1629,17 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-spread": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz", - "integrity": "sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw==", + "node_modules/@babel/plugin-transform-react-jsx": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.9.tgz", + "integrity": "sha512-s5XwpQYCqGerXl+Pu6VDL3x0j2d82eiV77UJ8a2mDHAW7j9SWRqQ2y1fNo1Z74CdcYipl5Z41zvjj4Nfzq36rw==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0" + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/plugin-syntax-jsx": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1534,12 +1648,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz", - "integrity": "sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==", + "node_modules/@babel/plugin-transform-react-jsx-development": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.25.9.tgz", + "integrity": "sha512-9mj6rm7XVYs4mdLIpbZnHOYdpW42uoiBCTVowg7sP1thUOiANgMb4UtpRivR0pp5iL+ocvUv7X4mZgFRpJEzGw==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/plugin-transform-react-jsx": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1548,12 +1663,14 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz", - "integrity": "sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==", + "node_modules/@babel/plugin-transform-react-pure-annotations": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.25.9.tgz", + "integrity": "sha512-KQ/Takk3T8Qzj5TppkS1be588lkbTp5uj7w6a0LeQaTMSckU/wK0oJ/pih+T690tkgI5jfmg2TqDJvd41Sj1Cg==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1562,12 +1679,14 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz", - "integrity": "sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==", + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.9.tgz", + "integrity": "sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.25.9", + "regenerator-transform": "^0.15.2" }, "engines": { "node": ">=6.9.0" @@ -1576,28 +1695,29 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-typescript": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.21.0.tgz", - "integrity": "sha512-xo///XTPp3mDzTtrqXoBlK9eiAYW3wv9JXglcn/u1bi60RW11dEUxIgA8cbnDhutS1zacjMRmAwxE0gMklLnZg==", + "node_modules/@babel/plugin-transform-regexp-modifiers": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.26.0.tgz", + "integrity": "sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw==", + "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.21.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-typescript": "^7.20.0" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/core": "^7.0.0" } }, - "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz", - "integrity": "sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ==", + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.9.tgz", + "integrity": "sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1606,13 +1726,18 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz", - "integrity": "sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==", + "node_modules/@babel/plugin-transform-runtime": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.25.9.tgz", + "integrity": "sha512-nZp7GlEl+yULJrClz0SwHPqir3lc0zsPrDHQUcxGspSL7AKrexNSEfTbfqnDNJUO13bgKyfuOLMF8Xqtu8j3YQ==", + "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "babel-plugin-polyfill-corejs2": "^0.4.10", + "babel-plugin-polyfill-corejs3": "^0.10.6", + "babel-plugin-polyfill-regenerator": "^0.6.1", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -1621,86 +1746,22 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/preset-env": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.20.2.tgz", - "integrity": "sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg==", + "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz", + "integrity": "sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng==", + "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.20.1", - "@babel/helper-compilation-targets": "^7.20.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-validator-option": "^7.18.6", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9", - "@babel/plugin-proposal-async-generator-functions": "^7.20.1", - "@babel/plugin-proposal-class-properties": "^7.18.6", - "@babel/plugin-proposal-class-static-block": "^7.18.6", - "@babel/plugin-proposal-dynamic-import": "^7.18.6", - "@babel/plugin-proposal-export-namespace-from": "^7.18.9", - "@babel/plugin-proposal-json-strings": "^7.18.6", - "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", - "@babel/plugin-proposal-numeric-separator": "^7.18.6", - "@babel/plugin-proposal-object-rest-spread": "^7.20.2", - "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", - "@babel/plugin-proposal-optional-chaining": "^7.18.9", - "@babel/plugin-proposal-private-methods": "^7.18.6", - "@babel/plugin-proposal-private-property-in-object": "^7.18.6", - "@babel/plugin-proposal-unicode-property-regex": "^7.18.6", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.20.0", - "@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", - "@babel/plugin-transform-arrow-functions": "^7.18.6", - "@babel/plugin-transform-async-to-generator": "^7.18.6", - "@babel/plugin-transform-block-scoped-functions": "^7.18.6", - "@babel/plugin-transform-block-scoping": "^7.20.2", - "@babel/plugin-transform-classes": "^7.20.2", - "@babel/plugin-transform-computed-properties": "^7.18.9", - "@babel/plugin-transform-destructuring": "^7.20.2", - "@babel/plugin-transform-dotall-regex": "^7.18.6", - "@babel/plugin-transform-duplicate-keys": "^7.18.9", - "@babel/plugin-transform-exponentiation-operator": "^7.18.6", - "@babel/plugin-transform-for-of": "^7.18.8", - "@babel/plugin-transform-function-name": "^7.18.9", - "@babel/plugin-transform-literals": "^7.18.9", - "@babel/plugin-transform-member-expression-literals": "^7.18.6", - "@babel/plugin-transform-modules-amd": "^7.19.6", - "@babel/plugin-transform-modules-commonjs": "^7.19.6", - "@babel/plugin-transform-modules-systemjs": "^7.19.6", - "@babel/plugin-transform-modules-umd": "^7.18.6", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.19.1", - "@babel/plugin-transform-new-target": "^7.18.6", - "@babel/plugin-transform-object-super": "^7.18.6", - "@babel/plugin-transform-parameters": "^7.20.1", - "@babel/plugin-transform-property-literals": "^7.18.6", - "@babel/plugin-transform-regenerator": "^7.18.6", - "@babel/plugin-transform-reserved-words": "^7.18.6", - "@babel/plugin-transform-shorthand-properties": "^7.18.6", - "@babel/plugin-transform-spread": "^7.19.0", - "@babel/plugin-transform-sticky-regex": "^7.18.6", - "@babel/plugin-transform-template-literals": "^7.18.9", - "@babel/plugin-transform-typeof-symbol": "^7.18.9", - "@babel/plugin-transform-unicode-escapes": "^7.18.10", - "@babel/plugin-transform-unicode-regex": "^7.18.6", - "@babel/preset-modules": "^0.1.5", - "@babel/types": "^7.20.2", - "babel-plugin-polyfill-corejs2": "^0.3.3", - "babel-plugin-polyfill-corejs3": "^0.6.0", - "babel-plugin-polyfill-regenerator": "^0.4.1", - "core-js-compat": "^3.25.1", - "semver": "^6.3.0" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1709,32 +1770,29 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/preset-modules": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", - "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", + "node_modules/@babel/plugin-transform-spread": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz", + "integrity": "sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-transform-dotall-regex": "^7.4.4", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/preset-react": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.18.6.tgz", - "integrity": "sha512-zXr6atUmyYdiWRVLOZahakYmOBHtWc2WGCkP8PYTgZi0iJXDY2CN180TdrIW4OGOAdLc7TifzDIvtx6izaRIzg==", + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.9.tgz", + "integrity": "sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-validator-option": "^7.18.6", - "@babel/plugin-transform-react-display-name": "^7.18.6", - "@babel/plugin-transform-react-jsx": "^7.18.6", - "@babel/plugin-transform-react-jsx-development": "^7.18.6", - "@babel/plugin-transform-react-pure-annotations": "^7.18.6" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1743,14 +1801,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/preset-typescript": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.21.0.tgz", - "integrity": "sha512-myc9mpoVA5m1rF8K8DgLEatOYFDpwC+RkMkjZ0Du6uI62YvDe8uxIEYVs/VCdSJ097nlALiU/yBC7//3nI+hNg==", + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.9.tgz", + "integrity": "sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-validator-option": "^7.21.0", - "@babel/plugin-transform-typescript": "^7.21.0" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1759,1058 +1816,1169 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/regjsgen": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" - }, - "node_modules/@babel/runtime": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz", - "integrity": "sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==", + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.9.tgz", + "integrity": "sha512-v61XqUMiueJROUv66BVIOi0Fv/CUuZuZMl5NkRoCVxLAnMexZ0A3kMe7vvZ0nulxMuMp0Mk6S5hNh48yki08ZA==", + "license": "MIT", "dependencies": { - "regenerator-runtime": "^0.13.11" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/runtime-corejs3": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.21.0.tgz", - "integrity": "sha512-TDD4UJzos3JJtM+tHX+w2Uc+KWj7GV+VKKFdMVd2Rx8sdA19hcc3P3AHFYd5LVOw+pYuSd5lICC3gm52B6Rwxw==", - "dev": true, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.25.9.tgz", + "integrity": "sha512-7PbZQZP50tzv2KGGnhh82GSyMB01yKY9scIjf1a+GfZCtInOWqUH5+1EBU4t9fyR5Oykkkc9vFTs4OHrhHXljQ==", + "license": "MIT", "dependencies": { - "core-js-pure": "^3.25.1", - "regenerator-runtime": "^0.13.11" + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", + "@babel/plugin-syntax-typescript": "^7.25.9" }, "engines": { "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==", + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.9.tgz", + "integrity": "sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q==", + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/traverse": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", - "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", - "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", - "@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.0", - "@babel/types": "^7.23.0", - "debug": "^4.1.0", - "globals": "^11.1.0" + "node_modules/@babel/plugin-transform-unicode-property-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.9.tgz", + "integrity": "sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.9.tgz", + "integrity": "sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA==", + "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-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==" - }, - "node_modules/@csstools/normalize.css": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-12.0.0.tgz", - "integrity": "sha512-M0qqxAcwCsIVfpFQSlGN5XjXWu8l5JDZN+fPt1LeW5SZexQTgnaEvgXAY+CeygRw0EeppWHi12JxESWiWrB0Sg==" - }, - "node_modules/@eslint/eslintrc": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.0.tgz", - "integrity": "sha512-fluIaaV+GyV24CCu/ggiHdV+j4RNh85yQnAYS/G2mZODZgGmmlrgCydjUcV3YvxCm9x8nMAfThsqTni4KiXT4A==", + "node_modules/@babel/plugin-transform-unicode-sets-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.9.tgz", + "integrity": "sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ==", + "license": "MIT", "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.4.0", - "globals": "^13.19.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" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=6.9.0" }, - "funding": { - "url": "https://opencollective.com/eslint" - } + "peerDependencies": { + "@babel/core": "^7.0.0" + } }, - "node_modules/@eslint/eslintrc/node_modules/acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", - "bin": { - "acorn": "bin/acorn" + "node_modules/@babel/preset-env": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.0.tgz", + "integrity": "sha512-H84Fxq0CQJNdPFT2DrfnylZ3cf5K43rGfWK4LJGPpjKHiZlk0/RzwEus3PDDZZg+/Er7lCA03MVacueUuXdzfw==", + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.26.0", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.9", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.9", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.9", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.25.9", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.9", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", + "@babel/plugin-syntax-import-assertions": "^7.26.0", + "@babel/plugin-syntax-import-attributes": "^7.26.0", + "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.25.9", + "@babel/plugin-transform-async-generator-functions": "^7.25.9", + "@babel/plugin-transform-async-to-generator": "^7.25.9", + "@babel/plugin-transform-block-scoped-functions": "^7.25.9", + "@babel/plugin-transform-block-scoping": "^7.25.9", + "@babel/plugin-transform-class-properties": "^7.25.9", + "@babel/plugin-transform-class-static-block": "^7.26.0", + "@babel/plugin-transform-classes": "^7.25.9", + "@babel/plugin-transform-computed-properties": "^7.25.9", + "@babel/plugin-transform-destructuring": "^7.25.9", + "@babel/plugin-transform-dotall-regex": "^7.25.9", + "@babel/plugin-transform-duplicate-keys": "^7.25.9", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.9", + "@babel/plugin-transform-dynamic-import": "^7.25.9", + "@babel/plugin-transform-exponentiation-operator": "^7.25.9", + "@babel/plugin-transform-export-namespace-from": "^7.25.9", + "@babel/plugin-transform-for-of": "^7.25.9", + "@babel/plugin-transform-function-name": "^7.25.9", + "@babel/plugin-transform-json-strings": "^7.25.9", + "@babel/plugin-transform-literals": "^7.25.9", + "@babel/plugin-transform-logical-assignment-operators": "^7.25.9", + "@babel/plugin-transform-member-expression-literals": "^7.25.9", + "@babel/plugin-transform-modules-amd": "^7.25.9", + "@babel/plugin-transform-modules-commonjs": "^7.25.9", + "@babel/plugin-transform-modules-systemjs": "^7.25.9", + "@babel/plugin-transform-modules-umd": "^7.25.9", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.9", + "@babel/plugin-transform-new-target": "^7.25.9", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.25.9", + "@babel/plugin-transform-numeric-separator": "^7.25.9", + "@babel/plugin-transform-object-rest-spread": "^7.25.9", + "@babel/plugin-transform-object-super": "^7.25.9", + "@babel/plugin-transform-optional-catch-binding": "^7.25.9", + "@babel/plugin-transform-optional-chaining": "^7.25.9", + "@babel/plugin-transform-parameters": "^7.25.9", + "@babel/plugin-transform-private-methods": "^7.25.9", + "@babel/plugin-transform-private-property-in-object": "^7.25.9", + "@babel/plugin-transform-property-literals": "^7.25.9", + "@babel/plugin-transform-regenerator": "^7.25.9", + "@babel/plugin-transform-regexp-modifiers": "^7.26.0", + "@babel/plugin-transform-reserved-words": "^7.25.9", + "@babel/plugin-transform-shorthand-properties": "^7.25.9", + "@babel/plugin-transform-spread": "^7.25.9", + "@babel/plugin-transform-sticky-regex": "^7.25.9", + "@babel/plugin-transform-template-literals": "^7.25.9", + "@babel/plugin-transform-typeof-symbol": "^7.25.9", + "@babel/plugin-transform-unicode-escapes": "^7.25.9", + "@babel/plugin-transform-unicode-property-regex": "^7.25.9", + "@babel/plugin-transform-unicode-regex": "^7.25.9", + "@babel/plugin-transform-unicode-sets-regex": "^7.25.9", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "babel-plugin-polyfill-corejs2": "^0.4.10", + "babel-plugin-polyfill-corejs3": "^0.10.6", + "babel-plugin-polyfill-regenerator": "^0.6.1", + "core-js-compat": "^3.38.1", + "semver": "^6.3.1" }, "engines": { - "node": ">=0.4.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@eslint/eslintrc/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==" - }, - "node_modules/@eslint/eslintrc/node_modules/eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node_modules/@babel/preset-env/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" } }, - "node_modules/@eslint/eslintrc/node_modules/espree": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", - "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", + "node_modules/@babel/preset-modules": { + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "license": "MIT", "dependencies": { - "acorn": "^8.8.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" }, - "funding": { - "url": "https://opencollective.com/eslint" + "peerDependencies": { + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" } }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "node_modules/@babel/preset-react": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.25.9.tgz", + "integrity": "sha512-D3to0uSPiWE7rBrdIICCd0tJSIGpLaaGptna2+w7Pft5xMqLpA1sz99DK5TZ1TjGbdQ/VI1eCSZ06dv3lT4JOw==", + "license": "MIT", "dependencies": { - "type-fest": "^0.20.2" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", + "@babel/plugin-transform-react-display-name": "^7.25.9", + "@babel/plugin-transform-react-jsx": "^7.25.9", + "@babel/plugin-transform-react-jsx-development": "^7.25.9", + "@babel/plugin-transform-react-pure-annotations": "^7.25.9" }, "engines": { - "node": ">=8" + "node": ">=6.9.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/eslintrc/node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", - "engines": { - "node": ">= 4" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@eslint/eslintrc/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/@babel/preset-typescript": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.26.0.tgz", + "integrity": "sha512-NMk1IGZ5I/oHhoXEElcm+xUnL/szL6xflkFZmoEU9xj1qSJXpiS7rsspYo92B4DRCDvZn2erT5LdsCeXAKNCkg==", + "license": "MIT", "dependencies": { - "argparse": "^2.0.1" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", + "@babel/plugin-syntax-jsx": "^7.25.9", + "@babel/plugin-transform-modules-commonjs": "^7.25.9", + "@babel/plugin-transform-typescript": "^7.25.9" }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@eslint/eslintrc/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==", "engines": { - "node": ">=10" + "node": ">=6.9.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/js": { - "version": "8.35.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.35.0.tgz", - "integrity": "sha512-JXdzbRiWclLVoD8sNUjR443VVlYqiYmDVT6rGUEIEHU5YJW0gaVZwV2xgM7D4arkvASqD0IlLUVjHiFuxaftRw==", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@floating-ui/core": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.4.1.tgz", - "integrity": "sha512-jk3WqquEJRlcyu7997NtR5PibI+y5bi+LS3hPmguVClypenMsCY3CBa3LAQnozRCtCrYWSEtAdiskpamuJRFOQ==", - "dependencies": { - "@floating-ui/utils": "^0.1.1" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@floating-ui/dom": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.5.1.tgz", - "integrity": "sha512-KwvVcPSXg6mQygvA1TjbN/gh///36kKtllIF8SUm0qpFj8+rvYrpvlYdL1JoA71SHpDqgSSdGOSoQ0Mp3uY5aw==", + "node_modules/@babel/runtime": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", + "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", + "license": "MIT", "dependencies": { - "@floating-ui/core": "^1.4.1", - "@floating-ui/utils": "^0.1.1" + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@floating-ui/utils": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.1.tgz", - "integrity": "sha512-m0G6wlnhm/AX0H12IOWtK8gASEMffnX08RtKkCgTdHb9JpHKGloI7icFfLg9ZmQeavcvR0PKmzxClyuFPSjKWw==" - }, - "node_modules/@gar/promisify": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", - "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==" - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", - "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", + "node_modules/@babel/template": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", + "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", + "license": "MIT", "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" + "@babel/code-frame": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { - "node": ">=10.10.0" + "node": ">=6.9.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==", - "engines": { - "node": ">=12.22" + "node_modules/@babel/traverse": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", + "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.25.9", + "@babel/generator": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/template": "^7.25.9", + "@babel/types": "^7.25.9", + "debug": "^4.3.1", + "globals": "^11.1.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==" - }, - "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==", + "node_modules/@babel/types": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", + "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "license": "MIT", "dependencies": { - "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" + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" }, "engines": { - "node": ">=8" + "node": ">=6.9.0" } }, - "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==", - "engines": { - "node": ">=8" - } + "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==", + "license": "MIT" }, - "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==", - "engines": { - "node": ">=8" - } + "node_modules/@csstools/normalize.css": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-12.1.1.tgz", + "integrity": "sha512-YAYeJ+Xqh7fUou1d1j9XHl44BmsuThiTr4iNrgCQ3J27IbhXsxXDGZ1cXv8Qvs99d4rBbLiSKy3+WZiet32PcQ==", + "license": "CC0-1.0" }, - "node_modules/@jest/console": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-27.5.1.tgz", - "integrity": "sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg==", + "node_modules/@csstools/postcss-cascade-layers": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-1.1.1.tgz", + "integrity": "sha512-+KdYrpKC5TgomQr2DlZF4lDEpHcoxnj5IGddYYfBWJAKfj1JtuHUIqMa+E1pJJ+z3kvDViWMqyqPlG4Ja7amQA==", + "license": "CC0-1.0", "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^27.5.1", - "jest-util": "^27.5.1", - "slash": "^3.0.0" + "@csstools/selector-specificity": "^2.0.2", + "postcss-selector-parser": "^6.0.10" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" } }, - "node_modules/@jest/console/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "node_modules/@csstools/postcss-color-function": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-1.1.1.tgz", + "integrity": "sha512-Bc0f62WmHdtRDjf5f3e2STwRAl89N2CLb+9iAwzrv4L2hncrbDwnQD9PCq0gtAt7pOI2leIV08HIBUd4jxD8cw==", + "license": "CC0-1.0", "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/console/node_modules/@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", - "dependencies": { - "@types/yargs-parser": "*" + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" } }, - "node_modules/@jest/console/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/@csstools/postcss-font-format-keywords": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-font-format-keywords/-/postcss-font-format-keywords-1.0.1.tgz", + "integrity": "sha512-ZgrlzuUAjXIOc2JueK0X5sZDjCtgimVp/O5CEqTcs5ShWBa6smhWYbS0x5cVc/+rycTDbjjzoP0KTDnUneZGOg==", + "license": "CC0-1.0", "dependencies": { - "color-convert": "^2.0.1" + "postcss-value-parser": "^4.2.0" }, "engines": { - "node": ">=8" + "node": "^12 || ^14 || >=16" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" } }, - "node_modules/@jest/console/node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "node_modules/@csstools/postcss-hwb-function": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-1.0.2.tgz", + "integrity": "sha512-YHdEru4o3Rsbjmu6vHy4UKOXZD+Rn2zmkAmLRfPet6+Jz4Ojw8cbWxe1n42VaXQhD3CQUXXTooIy8OkVbUcL+w==", + "license": "CC0-1.0", "dependencies": { - "fill-range": "^7.0.1" + "postcss-value-parser": "^4.2.0" }, "engines": { - "node": ">=8" + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" } }, - "node_modules/@jest/console/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/@csstools/postcss-ic-unit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-1.0.1.tgz", + "integrity": "sha512-Ot1rcwRAaRHNKC9tAqoqNZhjdYBzKk1POgWfhN4uCOE47ebGcLRqXjKkApVDpjifL6u2/55ekkpnFcp+s/OZUw==", + "license": "CC0-1.0", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" }, "engines": { - "node": ">=10" + "node": "^12 || ^14 || >=16" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" } }, - "node_modules/@jest/console/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/@csstools/postcss-is-pseudo-class": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-2.0.7.tgz", + "integrity": "sha512-7JPeVVZHd+jxYdULl87lvjgvWldYu+Bc62s9vD/ED6/QTGjy0jy0US/f6BG53sVMTBJ1lzKZFpYmofBN9eaRiA==", + "license": "CC0-1.0", "dependencies": { - "color-name": "~1.1.4" + "@csstools/selector-specificity": "^2.0.0", + "postcss-selector-parser": "^6.0.10" }, "engines": { - "node": ">=7.0.0" + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" } }, - "node_modules/@jest/console/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==" - }, - "node_modules/@jest/console/node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "node_modules/@csstools/postcss-nested-calc": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-nested-calc/-/postcss-nested-calc-1.0.0.tgz", + "integrity": "sha512-JCsQsw1wjYwv1bJmgjKSoZNvf7R6+wuHDAbi5f/7MbFhl2d/+v+TvBTU4BJH3G1X1H87dHl0mh6TfYogbT/dJQ==", + "license": "CC0-1.0", "dependencies": { - "to-regex-range": "^5.0.1" + "postcss-value-parser": "^4.2.0" }, "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console/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==", - "engines": { - "node": ">=0.12.0" + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" } }, - "node_modules/@jest/console/node_modules/jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", + "node_modules/@csstools/postcss-normalize-display-values": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-normalize-display-values/-/postcss-normalize-display-values-1.0.1.tgz", + "integrity": "sha512-jcOanIbv55OFKQ3sYeFD/T0Ti7AMXc9nM1hZWu8m/2722gOTxFg7xYu4RDLJLeZmPUVQlGzo4jhzvTUq3x4ZUw==", + "license": "CC0-1.0", "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" + "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" } }, - "node_modules/@jest/console/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", + "node_modules/@csstools/postcss-oklab-function": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-1.1.1.tgz", + "integrity": "sha512-nJpJgsdA3dA9y5pgyb/UfEzE7W5Ka7u0CX0/HIMVBNWzWemdcTH3XwANECU6anWv/ao4vVNLTMxhiPNZsTK6iA==", + "license": "CC0-1.0", "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" } }, - "node_modules/@jest/console/node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "node_modules/@csstools/postcss-progressive-custom-properties": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-1.3.0.tgz", + "integrity": "sha512-ASA9W1aIy5ygskZYuWams4BzafD12ULvSypmaLJT2jvQ8G0M3I8PRQhC0h7mG0Z3LI05+agZjqSR9+K9yaQQjA==", + "license": "CC0-1.0", "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" + "postcss-value-parser": "^4.2.0" }, "engines": { - "node": ">=8.6" + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.3" } }, - "node_modules/@jest/console/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/@csstools/postcss-stepped-value-functions": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-1.0.1.tgz", + "integrity": "sha512-dz0LNoo3ijpTOQqEJLY8nyaapl6umbmDcgj4AD0lgVQ572b2eqA1iGZYTTWhrcrHztWDDRAX2DGYyw2VBjvCvQ==", + "license": "CC0-1.0", "dependencies": { - "has-flag": "^4.0.0" + "postcss-value-parser": "^4.2.0" }, "engines": { - "node": ">=8" + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" } }, - "node_modules/@jest/console/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==", + "node_modules/@csstools/postcss-text-decoration-shorthand": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-1.0.0.tgz", + "integrity": "sha512-c1XwKJ2eMIWrzQenN0XbcfzckOLLJiczqy+YvfGmzoVXd7pT9FfObiSEfzs84bpE/VqfpEuAZ9tCRbZkZxxbdw==", + "license": "CC0-1.0", "dependencies": { - "is-number": "^7.0.0" + "postcss-value-parser": "^4.2.0" }, "engines": { - "node": ">=8.0" + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" } }, - "node_modules/@jest/core": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-27.5.1.tgz", - "integrity": "sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ==", + "node_modules/@csstools/postcss-trigonometric-functions": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-1.0.2.tgz", + "integrity": "sha512-woKaLO///4bb+zZC2s80l+7cm07M7268MsyG3M0ActXXEFi6SuhvriQYcb58iiKGbjwwIU7n45iRLEHypB47Og==", + "license": "CC0-1.0", "dependencies": { - "@jest/console": "^27.5.1", - "@jest/reporters": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.8.1", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^27.5.1", - "jest-config": "^27.5.1", - "jest-haste-map": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-resolve-dependencies": "^27.5.1", - "jest-runner": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "jest-watcher": "^27.5.1", - "micromatch": "^4.0.4", - "rimraf": "^3.0.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" + "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^14 || >=16" }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } + "peerDependencies": { + "postcss": "^8.2" } }, - "node_modules/@jest/core/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, + "node_modules/@csstools/postcss-unset-value": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-1.0.2.tgz", + "integrity": "sha512-c8J4roPBILnelAsdLr4XOAR/GsTm0GJi4XpcfvoWk3U6KiTCqiFYc63KhRMQQX35jYMp4Ao8Ij9+IZRgMfJp1g==", + "license": "CC0-1.0", "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" } }, - "node_modules/@jest/core/node_modules/@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", - "dependencies": { - "@types/yargs-parser": "*" + "node_modules/@csstools/selector-specificity": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.2.0.tgz", + "integrity": "sha512-+OJ9konv95ClSTOJCmMZqpd5+YGsB2S+x6w3E1oaM8UuR5j8nTNHYSz8c9BEPGDOCMQYIEEGlVPj/VY64iTbGw==", + "license": "CC0-1.0", + "engines": { + "node": "^14 || ^16 || >=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss-selector-parser": "^6.0.10" } }, - "node_modules/@jest/core/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/@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=10.0.0" } }, - "node_modules/@jest/core/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/@eslint-community/eslint-utils": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", + "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", + "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "eslint-visitor-keys": "^3.4.3" }, "engines": { - "node": ">=8" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "node_modules/@jest/core/node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dependencies": { - "fill-range": "^7.0.1" - }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "license": "MIT", "engines": { - "node": ">=8" + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, - "node_modules/@jest/core/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/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.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": ">=10" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://opencollective.com/eslint" } }, - "node_modules/@jest/core/node_modules/color-convert": { + "node_modules/@eslint/eslintrc/node_modules/argparse": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "license": "MIT", "dependencies": { - "color-name": "~1.1.4" + "type-fest": "^0.20.2" }, "engines": { - "node": ">=7.0.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@jest/core/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==" - }, - "node_modules/@jest/core/node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "node_modules/@eslint/eslintrc/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==", + "license": "MIT", "dependencies": { - "to-regex-range": "^5.0.1" + "argparse": "^2.0.1" }, - "engines": { - "node": ">=8" + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/@jest/core/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==", + "node_modules/@eslint/eslintrc/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==", + "license": "(MIT OR CC0-1.0)", "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@jest/core/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==", + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "license": "MIT", "engines": { - "node": ">=0.12.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/@jest/core/node_modules/jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", + "node_modules/@floating-ui/core": { + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.8.tgz", + "integrity": "sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA==", + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "@floating-ui/utils": "^0.2.8" } }, - "node_modules/@jest/core/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", + "node_modules/@floating-ui/dom": { + "version": "1.6.12", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.12.tgz", + "integrity": "sha512-NP83c0HjokcGVEMeoStg317VD9W7eDlGK7457dMBANbKA6GJZdc7rjujdgqzTaz93jkGgc5P/jeWbaCHnMNc+w==", + "license": "MIT", "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "@floating-ui/core": "^1.6.0", + "@floating-ui/utils": "^0.2.8" } }, - "node_modules/@jest/core/node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" + "node_modules/@floating-ui/utils": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.8.tgz", + "integrity": "sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==", + "license": "MIT" + }, + "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", + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" }, "engines": { - "node": ">=8.6" + "node": ">=10.10.0" } }, - "node_modules/@jest/core/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" + "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==", + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@jest/core/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==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } + "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", + "license": "BSD-3-Clause" }, - "node_modules/@jest/core/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/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", "dependencies": { - "has-flag": "^4.0.0" + "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": ">=8" + "node": ">=12" } }, - "node_modules/@jest/core/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==", - "dependencies": { - "is-number": "^7.0.0" - }, + "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==", + "license": "MIT", "engines": { - "node": ">=8.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/@jest/environment": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz", - "integrity": "sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==", - "dependencies": { - "@jest/fake-timers": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "jest-mock": "^27.5.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==", + "license": "MIT", "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@jest/environment/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "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==", + "license": "MIT", "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/environment/node_modules/@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", - "dependencies": { - "@types/yargs-parser": "*" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@jest/environment/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/@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==", + "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "ansi-regex": "^6.0.1" }, "engines": { - "node": ">=8" + "node": ">=12" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/@jest/environment/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/@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==", + "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/@jest/environment/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/@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==", + "license": "ISC", "dependencies": { - "color-name": "~1.1.4" + "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": ">=7.0.0" - } - }, - "node_modules/@jest/environment/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==" - }, - "node_modules/@jest/environment/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==", "engines": { "node": ">=8" } }, - "node_modules/@jest/environment/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=6" } }, - "node_modules/@jest/expect-utils": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.4.3.tgz", - "integrity": "sha512-/6JWbkxHOP8EoS8jeeTd9dTfc9Uawi+43oLKHfp6zzux3U2hqOOVnV3ai4RpDYHOccL6g+5nrxpoc8DmJxtXVQ==", - "dependencies": { - "jest-get-type": "^29.4.3" - }, + "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==", + "license": "MIT", "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=8" } }, - "node_modules/@jest/fake-timers": { + "node_modules/@jest/console": { "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz", - "integrity": "sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-27.5.1.tgz", + "integrity": "sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg==", + "license": "MIT", "dependencies": { "@jest/types": "^27.5.1", - "@sinonjs/fake-timers": "^8.0.1", "@types/node": "*", + "chalk": "^4.0.0", "jest-message-util": "^27.5.1", - "jest-mock": "^27.5.1", - "jest-util": "^27.5.1" + "jest-util": "^27.5.1", + "slash": "^3.0.0" }, "engines": { "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/@jest/fake-timers/node_modules/@jest/types": { + "node_modules/@jest/core": { "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-27.5.1.tgz", + "integrity": "sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ==", + "license": "MIT", "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", + "@jest/console": "^27.5.1", + "@jest/reporters": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/fake-timers/node_modules/@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@jest/fake-timers/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==", - "dependencies": { - "color-convert": "^2.0.1" + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.8.1", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^27.5.1", + "jest-config": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-resolve-dependencies": "^27.5.1", + "jest-runner": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "jest-watcher": "^27.5.1", + "micromatch": "^4.0.4", + "rimraf": "^3.0.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">=8" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "node_modules/@jest/fake-timers/node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "node_modules/@jest/environment": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz", + "integrity": "sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==", + "license": "MIT", "dependencies": { - "fill-range": "^7.0.1" + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1" }, "engines": { - "node": ">=8" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/@jest/fake-timers/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/@jest/fake-timers": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz", + "integrity": "sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==", + "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@jest/types": "^27.5.1", + "@sinonjs/fake-timers": "^8.0.1", + "@types/node": "*", + "jest-message-util": "^27.5.1", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/@jest/fake-timers/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/@jest/globals": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.5.1.tgz", + "integrity": "sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==", + "license": "MIT", "dependencies": { - "color-name": "~1.1.4" + "@jest/environment": "^27.5.1", + "@jest/types": "^27.5.1", + "expect": "^27.5.1" }, "engines": { - "node": ">=7.0.0" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/@jest/fake-timers/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==" - }, - "node_modules/@jest/fake-timers/node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "node_modules/@jest/reporters": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-27.5.1.tgz", + "integrity": "sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw==", + "license": "MIT", "dependencies": { - "to-regex-range": "^5.0.1" + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.2", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-haste-map": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "slash": "^3.0.0", + "source-map": "^0.6.0", + "string-length": "^4.0.1", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^8.1.0" }, "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/fake-timers/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==", - "engines": { - "node": ">=8" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "node_modules/@jest/fake-timers/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==", + "node_modules/@jest/reporters/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==", + "license": "BSD-3-Clause", "engines": { - "node": ">=0.12.0" + "node": ">=0.10.0" } }, - "node_modules/@jest/fake-timers/node_modules/jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", + "node_modules/@jest/schemas": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", + "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" + "@sinclair/typebox": "^0.24.1" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/@jest/fake-timers/node_modules/jest-util": { + "node_modules/@jest/source-map": { "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-27.5.1.tgz", + "integrity": "sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==", + "license": "MIT", "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", + "callsites": "^3.0.0", "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" + "source-map": "^0.6.0" }, "engines": { "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/@jest/fake-timers/node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, + "node_modules/@jest/source-map/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==", + "license": "BSD-3-Clause", "engines": { - "node": ">=8.6" + "node": ">=0.10.0" } }, - "node_modules/@jest/fake-timers/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/@jest/test-result": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.5.1.tgz", + "integrity": "sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag==", + "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "@jest/console": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" }, "engines": { - "node": ">=8" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/@jest/fake-timers/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==", + "node_modules/@jest/test-sequencer": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz", + "integrity": "sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ==", + "license": "MIT", "dependencies": { - "is-number": "^7.0.0" + "@jest/test-result": "^27.5.1", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-runtime": "^27.5.1" }, "engines": { - "node": ">=8.0" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/@jest/globals": { + "node_modules/@jest/transform": { "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.5.1.tgz", - "integrity": "sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.5.1.tgz", + "integrity": "sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==", + "license": "MIT", "dependencies": { - "@jest/environment": "^27.5.1", + "@babel/core": "^7.1.0", "@jest/types": "^27.5.1", - "expect": "^27.5.1" + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-util": "^27.5.1", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.0.0" }, "engines": { "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/@jest/globals/node_modules/@jest/types": { + "node_modules/@jest/transform/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "license": "MIT" + }, + "node_modules/@jest/transform/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==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/types": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "license": "MIT", "dependencies": { "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", @@ -2822,4996 +2990,5654 @@ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/@jest/globals/node_modules/@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "license": "MIT", "dependencies": { - "@types/yargs-parser": "*" + "@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/@jest/globals/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=6.0.0" } }, - "node_modules/@jest/globals/node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dependencies": { - "fill-range": "^7.0.1" - }, + "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==", + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=6.0.0" } }, - "node_modules/@jest/globals/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/@jridgewell/source-map": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "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==", + "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==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@jsonjoy.com/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==", + "devOptional": true, + "license": "Apache-2.0", "engines": { - "node": ">=10" + "node": ">=10.0" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" } }, - "node_modules/@jest/globals/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/@jsonjoy.com/json-pack": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.1.0.tgz", + "integrity": "sha512-zlQONA+msXPPwHWZMKFVS78ewFczIll5lXiVPwFPCZUsrOKdxc2AvxU1HoNBmMRhqDZUR9HkC3UOm+6pME6Xsg==", + "devOptional": true, + "license": "Apache-2.0", "dependencies": { - "color-name": "~1.1.4" + "@jsonjoy.com/base64": "^1.1.1", + "@jsonjoy.com/util": "^1.1.2", + "hyperdyperid": "^1.2.0", + "thingies": "^1.20.0" }, "engines": { - "node": ">=7.0.0" + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" } }, - "node_modules/@jest/globals/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==" - }, - "node_modules/@jest/globals/node_modules/diff-sequences": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", - "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", + "node_modules/@jsonjoy.com/util": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.5.0.tgz", + "integrity": "sha512-ojoNsrIuPI9g6o8UxhraZQSyF2ByJanAY4cTFbc8Mf2AXEF4aQRGY1dJxyJpuyav8r9FGflEt/Ff3u5Nt6YMPA==", + "devOptional": true, + "license": "Apache-2.0", "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" } }, - "node_modules/@jest/globals/node_modules/expect": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", - "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", + "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", + "license": "MIT" + }, + "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==", + "license": "MIT", "dependencies": { - "@jest/types": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "eslint-scope": "5.1.1" } }, - "node_modules/@jest/globals/node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "node_modules/@nicolo-ribaudo/eslint-scope-5-internals/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==", + "license": "BSD-2-Clause", "dependencies": { - "to-regex-range": "^5.0.1" + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" }, "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/globals/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==", - "engines": { - "node": ">=8" + "node": ">=8.0.0" } }, - "node_modules/@jest/globals/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==", + "node_modules/@nicolo-ribaudo/eslint-scope-5-internals/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "license": "BSD-2-Clause", "engines": { - "node": ">=0.12.0" + "node": ">=4.0" } }, - "node_modules/@jest/globals/node_modules/jest-diff": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", - "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", + "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==", + "license": "MIT", "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">= 8" } }, - "node_modules/@jest/globals/node_modules/jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", + "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==", + "license": "MIT", "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">= 8" } }, - "node_modules/@jest/globals/node_modules/jest-matcher-utils": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", - "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", + "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==", + "license": "MIT", "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">= 8" } }, - "node_modules/@jest/globals/node_modules/jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", + "node_modules/@parcel/watcher": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.0.tgz", + "integrity": "sha512-i0GV1yJnm2n3Yq1qw6QrUrd/LI9bE8WEBOTtOkpCXHHdyN3TAGgqAK/DAT05z4fq2x04cARXt2pDmjWjL92iTQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.0", + "@parcel/watcher-darwin-arm64": "2.5.0", + "@parcel/watcher-darwin-x64": "2.5.0", + "@parcel/watcher-freebsd-x64": "2.5.0", + "@parcel/watcher-linux-arm-glibc": "2.5.0", + "@parcel/watcher-linux-arm-musl": "2.5.0", + "@parcel/watcher-linux-arm64-glibc": "2.5.0", + "@parcel/watcher-linux-arm64-musl": "2.5.0", + "@parcel/watcher-linux-x64-glibc": "2.5.0", + "@parcel/watcher-linux-x64-musl": "2.5.0", + "@parcel/watcher-win32-arm64": "2.5.0", + "@parcel/watcher-win32-ia32": "2.5.0", + "@parcel/watcher-win32-x64": "2.5.0" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.0.tgz", + "integrity": "sha512-qlX4eS28bUcQCdribHkg/herLe+0A9RyYC+mm2PXpncit8z5b3nSqGVzMNR3CmtAOgRutiZ02eIJJgP/b1iEFQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/@jest/globals/node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.0.tgz", + "integrity": "sha512-hyZ3TANnzGfLpRA2s/4U1kbw2ZI4qGxaRJbBH2DCSREFfubMswheh8TeiC1sGZ3z2jUf3s37P0BBlrD3sjVTUw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=8.6" + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/@jest/globals/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==", - "dependencies": { - "has-flag": "^4.0.0" + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.0.tgz", + "integrity": "sha512-9rhlwd78saKf18fT869/poydQK8YqlU26TMiNg7AIu7eBp9adqbJZqmdFOsbZ5cnLp5XvRo9wcFmNHgHdWaGYA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.0.tgz", + "integrity": "sha512-syvfhZzyM8kErg3VF0xpV8dixJ+RzbUaaGaeb7uDuz0D3FK97/mZ5AJQ3XNnDsXX7KkFNtyQyFrXZzQIcN49Tw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">=8" + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/@jest/globals/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==", - "dependencies": { - "is-number": "^7.0.0" + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.0.tgz", + "integrity": "sha512-0VQY1K35DQET3dVYWpOaPFecqOT9dbuCfzjxoQyif1Wc574t3kOSkKevULddcR9znz1TcklCE7Ht6NIxjvTqLA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.0.tgz", + "integrity": "sha512-6uHywSIzz8+vi2lAzFeltnYbdHsDm3iIB57d4g5oaB9vKwjb6N6dRIgZMujw4nm5r6v9/BQH0noq6DzHrqr2pA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8.0" + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/@jest/reporters": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-27.5.1.tgz", - "integrity": "sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw==", - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.2", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^5.1.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-haste-map": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-util": "^27.5.1", - "jest-worker": "^27.5.1", - "slash": "^3.0.0", - "source-map": "^0.6.0", - "string-length": "^4.0.1", - "terminal-link": "^2.0.0", - "v8-to-istanbul": "^8.1.0" - }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.0.tgz", + "integrity": "sha512-BfNjXwZKxBy4WibDb/LDCriWSKLz+jJRL3cM/DllnHH5QUyoiUNEp3GmL80ZqxeumoADfCCP19+qiYiC8gUBjA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + "node": ">= 10.0.0" }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/@jest/reporters/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.0.tgz", + "integrity": "sha512-S1qARKOphxfiBEkwLUbHjCY9BWPdWnW9j7f7Hb2jPplu8UZ3nes7zpPOW9bkLbHRvWM0WDTsjdOTUgW0xLBN1Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/reporters/node_modules/@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", - "dependencies": { - "@types/yargs-parser": "*" + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/@jest/reporters/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.0.tgz", + "integrity": "sha512-d9AOkusyXARkFD66S6zlGXyzx5RvY+chTP9Jp0ypSTC9d4lzyRs9ovGf/80VCxjKddcUvnsGwCHWuF2EoPgWjw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" + "node": ">= 10.0.0" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/@jest/reporters/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.0.tgz", + "integrity": "sha512-iqOC+GoTDoFyk/VYSFHwjHhYrk8bljW6zOhPuhi5t9ulqiYq1togGJB5e3PwYVFFfeVgc6pbz3JdQyDoBszVaA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=10" + "node": ">= 10.0.0" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/@jest/reporters/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==", - "dependencies": { - "color-name": "~1.1.4" - }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.0.tgz", + "integrity": "sha512-twtft1d+JRNkM5YbmexfcH/N4znDtjgysFaV9zvZmmJezQsKpkfLYJ+JFV3uygugK6AtIM2oADPkB2AdhBrNig==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=7.0.0" + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/@jest/reporters/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==" - }, - "node_modules/@jest/reporters/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==", + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.0.tgz", + "integrity": "sha512-+rgpsNRKwo8A53elqbbHXdOMtY/tAtTzManTWShB5Kk54N8Q9mzNWV7tV+IbGueCbcj826MfWGU3mprWtuf1TA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=8" + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/@jest/reporters/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.0.tgz", + "integrity": "sha512-lPrxve92zEHdgeff3aiu4gDOIt4u7sJYha6wbdEZDCDUhtjTsOMiaJzG5lMY4GkWH8p0fMmO2Ppq5G5XXG+DQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/@jest/reporters/node_modules/jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, + "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==", + "license": "MIT", + "optional": true, "engines": { - "node": ">= 10.13.0" + "node": ">=14" } }, - "node_modules/@jest/reporters/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==", + "node_modules/@pmmmwh/react-refresh-webpack-plugin": { + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.15.tgz", + "integrity": "sha512-LFWllMA55pzB9D34w/wXUCf8+c+IYKuJDgxiZ3qMhl64KRMBHYM1I3VdGaD2BV5FNPV2/S2596bppxHbv2ZydQ==", + "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "ansi-html": "^0.0.9", + "core-js-pure": "^3.23.3", + "error-stack-parser": "^2.0.6", + "html-entities": "^2.1.0", + "loader-utils": "^2.0.4", + "schema-utils": "^4.2.0", + "source-map": "^0.7.3" }, "engines": { - "node": ">=10" + "node": ">= 10.13" + }, + "peerDependencies": { + "@types/webpack": "4.x || 5.x", + "react-refresh": ">=0.10.0 <1.0.0", + "sockjs-client": "^1.4.0", + "type-fest": ">=0.17.0 <5.0.0", + "webpack": ">=4.43.0 <6.0.0", + "webpack-dev-server": "3.x || 4.x || 5.x", + "webpack-hot-middleware": "2.x", + "webpack-plugin-serve": "0.x || 1.x" }, + "peerDependenciesMeta": { + "@types/webpack": { + "optional": true + }, + "sockjs-client": { + "optional": true + }, + "type-fest": { + "optional": true + }, + "webpack-dev-server": { + "optional": true + }, + "webpack-hot-middleware": { + "optional": true + }, + "webpack-plugin-serve": { + "optional": true + } + } + }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/popperjs" } }, - "node_modules/@jest/reporters/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/@react-aria/ssr": { + "version": "3.9.6", + "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.6.tgz", + "integrity": "sha512-iLo82l82ilMiVGy342SELjshuWottlb5+VefO3jOQqQRNYnJBFpUSadswDPbRimSgJUZuFwIEYs6AabkP038fA==", + "license": "Apache-2.0", "dependencies": { - "has-flag": "^4.0.0" + "@swc/helpers": "^0.5.0" }, "engines": { - "node": ">=8" + "node": ">= 12" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0" } }, - "node_modules/@jest/schemas": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.3.tgz", - "integrity": "sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==", - "dependencies": { - "@sinclair/typebox": "^0.25.16" - }, + "node_modules/@remix-run/router": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.21.0.tgz", + "integrity": "sha512-xfSkCAchbdG5PnbrKqFWwia4Bi61nH+wm8wLEqfHDyp7Y3dZzgqS2itV8i4gAq9pC2HsTpwyBC6Ds8VHZ96JlA==", + "license": "MIT", "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=14.0.0" } }, - "node_modules/@jest/source-map": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-27.5.1.tgz", - "integrity": "sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==", + "node_modules/@restart/hooks": { + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.16.tgz", + "integrity": "sha512-f7aCv7c+nU/3mF7NWLtVVr0Ra80RqsO89hO72r+Y/nvQr5+q0UFGkocElTH6MJApvReVh6JHUFYn2cw1WdHF3w==", + "license": "MIT", "dependencies": { - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9", - "source-map": "^0.6.0" + "dequal": "^2.0.3" }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "peerDependencies": { + "react": ">=16.8.0" } }, - "node_modules/@jest/test-result": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.5.1.tgz", - "integrity": "sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag==", + "node_modules/@restart/ui": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@restart/ui/-/ui-1.8.0.tgz", + "integrity": "sha512-xJEOXUOTmT4FngTmhdjKFRrVVF0hwCLNPdatLCHkyS4dkiSK12cEu1Y0fjxktjJrdst9jJIc5J6ihMJCoWEN/g==", + "license": "MIT", "dependencies": { - "@jest/console": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" + "@babel/runtime": "^7.21.0", + "@popperjs/core": "^2.11.6", + "@react-aria/ssr": "^3.5.0", + "@restart/hooks": "^0.4.9", + "@types/warning": "^3.0.0", + "dequal": "^2.0.3", + "dom-helpers": "^5.2.0", + "uncontrollable": "^8.0.1", + "warning": "^4.0.3" }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/test-result/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "peerDependencies": { + "react": ">=16.14.0", + "react-dom": ">=16.14.0" } }, - "node_modules/@jest/test-result/node_modules/@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", - "dependencies": { - "@types/yargs-parser": "*" + "node_modules/@restart/ui/node_modules/uncontrollable": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-8.0.4.tgz", + "integrity": "sha512-ulRWYWHvscPFc0QQXvyJjY6LIXU56f0h8pQFvhxiKk5V1fcI8gp9Ht9leVAhrVjzqMw0BgjspBINx9r6oyJUvQ==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.14.0" } }, - "node_modules/@jest/test-result/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/@rollup/plugin-babel": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", + "integrity": "sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==", + "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "@babel/helper-module-imports": "^7.10.4", + "@rollup/pluginutils": "^3.1.0" }, "engines": { - "node": ">=8" + "node": ">= 10.0.0" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "peerDependencies": { + "@babel/core": "^7.0.0", + "@types/babel__core": "^7.1.9", + "rollup": "^1.20.0||^2.0.0" + }, + "peerDependenciesMeta": { + "@types/babel__core": { + "optional": true + } } }, - "node_modules/@jest/test-result/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/@rollup/plugin-node-resolve": { + "version": "11.2.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.1.tgz", + "integrity": "sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==", + "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@rollup/pluginutils": "^3.1.0", + "@types/resolve": "1.17.1", + "builtin-modules": "^3.1.0", + "deepmerge": "^4.2.2", + "is-module": "^1.0.0", + "resolve": "^1.19.0" }, "engines": { - "node": ">=10" + "node": ">= 10.0.0" }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0" } }, - "node_modules/@jest/test-result/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/@rollup/plugin-replace": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-2.4.2.tgz", + "integrity": "sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==", + "license": "MIT", "dependencies": { - "color-name": "~1.1.4" + "@rollup/pluginutils": "^3.1.0", + "magic-string": "^0.25.7" }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/test-result/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==" - }, - "node_modules/@jest/test-result/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==", - "engines": { - "node": ">=8" + "peerDependencies": { + "rollup": "^1.20.0 || ^2.0.0" } }, - "node_modules/@jest/test-result/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/@rollup/pluginutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", + "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", + "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "@types/estree": "0.0.39", + "estree-walker": "^1.0.1", + "picomatch": "^2.2.2" }, "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/test-sequencer": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz", - "integrity": "sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ==", - "dependencies": { - "@jest/test-result": "^27.5.1", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-runtime": "^27.5.1" + "node": ">= 8.0.0" }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0" } }, - "node_modules/@jest/transform": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.5.1.tgz", - "integrity": "sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==", + "node_modules/@rollup/pluginutils/node_modules/@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", + "license": "MIT" + }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "license": "MIT" + }, + "node_modules/@rushstack/eslint-patch": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.10.4.tgz", + "integrity": "sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA==", + "license": "MIT" + }, + "node_modules/@sinclair/typebox": { + "version": "0.24.51", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", + "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==", + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", + "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", + "license": "BSD-3-Clause", "dependencies": { - "@babel/core": "^7.1.0", - "@jest/types": "^27.5.1", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^1.4.0", - "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-util": "^27.5.1", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "source-map": "^0.6.1", - "write-file-atomic": "^3.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "type-detect": "4.0.8" } }, - "node_modules/@jest/transform/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "node_modules/@sinonjs/fake-timers": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", + "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", + "license": "BSD-3-Clause", "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "@sinonjs/commons": "^1.7.0" } }, - "node_modules/@jest/transform/node_modules/@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", + "node_modules/@surma/rollup-plugin-off-main-thread": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz", + "integrity": "sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==", + "license": "Apache-2.0", "dependencies": { - "@types/yargs-parser": "*" + "ejs": "^3.1.6", + "json5": "^2.2.0", + "magic-string": "^0.25.0", + "string.prototype.matchall": "^4.0.6" } }, - "node_modules/@jest/transform/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, + "node_modules/@svgr/babel-plugin-add-jsx-attribute": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz", + "integrity": "sha512-ZFf2gs/8/6B8PnSofI0inYXr2SDNTDScPXhN7k5EqD4aZ3gi6u+rbmZHVB8IM3wDyx8ntKACZbtXSm7oZGRqVg==", + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "type": "github", + "url": "https://github.com/sponsors/gregberge" } }, - "node_modules/@jest/transform/node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dependencies": { - "fill-range": "^7.0.1" - }, + "node_modules/@svgr/babel-plugin-remove-jsx-attribute": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-5.4.0.tgz", + "integrity": "sha512-yaS4o2PgUtwLFGTKbsiAy6D0o3ugcUhWK0Z45umJ66EPWunAz9fuFw2gJuje6wqQvQWOTJvIahUwndOXb7QCPg==", + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" } }, - "node_modules/@jest/transform/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, + "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-5.0.1.tgz", + "integrity": "sha512-LA72+88A11ND/yFIMzyuLRSMJ+tRKeYKeQ+mR3DcAZ5I4h5CPWN9AHyUzJbWSYp/u2u0xhmgOe0+E41+GjEueA==", + "license": "MIT", "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "type": "github", + "url": "https://github.com/sponsors/gregberge" } }, - "node_modules/@jest/transform/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==", - "dependencies": { - "color-name": "~1.1.4" - }, + "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-5.0.1.tgz", + "integrity": "sha512-PoiE6ZD2Eiy5mK+fjHqwGOS+IXX0wq/YDtNyIgOrc6ejFnxN4b13pRpiIPbtPwHEc+NT2KCjteAcq33/F1Y9KQ==", + "license": "MIT", "engines": { - "node": ">=7.0.0" + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" } }, - "node_modules/@jest/transform/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==" - }, - "node_modules/@jest/transform/node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dependencies": { - "to-regex-range": "^5.0.1" - }, + "node_modules/@svgr/babel-plugin-svg-dynamic-title": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-5.4.0.tgz", + "integrity": "sha512-zSOZH8PdZOpuG1ZVx/cLVePB2ibo3WPpqo7gFIjLV9a0QsuQAzJiwwqmuEdTaW2pegyBE17Uu15mOgOcgabQZg==", + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" } }, - "node_modules/@jest/transform/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==", + "node_modules/@svgr/babel-plugin-svg-em-dimensions": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-5.4.0.tgz", + "integrity": "sha512-cPzDbDA5oT/sPXDCUYoVXEmm3VIoAWAPT6mSPTJNbQaBNUuEKVKyGH93oDY4e42PYHRW67N5alJx/eEol20abw==", + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" } }, - "node_modules/@jest/transform/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==", + "node_modules/@svgr/babel-plugin-transform-react-native-svg": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-5.4.0.tgz", + "integrity": "sha512-3eYP/SaopZ41GHwXma7Rmxcv9uRslRDTY1estspeB1w1ueZWd/tPlMfEOoccYpEMZU3jD4OU7YitnXcF5hLW2Q==", + "license": "MIT", "engines": { - "node": ">=0.12.0" + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" } }, - "node_modules/@jest/transform/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, + "node_modules/@svgr/babel-plugin-transform-svg-component": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-5.5.0.tgz", + "integrity": "sha512-q4jSH1UUvbrsOtlo/tKcgSeiCHRSBdXoIoqX1pgcKK/aU3JD27wmMKwGtpB8qRYUYoyXvfGxUVKchLuR5pB3rQ==", + "license": "MIT", "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" } }, - "node_modules/@jest/transform/node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "node_modules/@svgr/babel-preset": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-5.5.0.tgz", + "integrity": "sha512-4FiXBjvQ+z2j7yASeGPEi8VD/5rrGQk4Xrq3EdJmoZgz/tpqChpo5hgXDvmEauwtvOc52q8ghhZK4Oy7qph4ig==", + "license": "MIT", "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" + "@svgr/babel-plugin-add-jsx-attribute": "^5.4.0", + "@svgr/babel-plugin-remove-jsx-attribute": "^5.4.0", + "@svgr/babel-plugin-remove-jsx-empty-expression": "^5.0.1", + "@svgr/babel-plugin-replace-jsx-attribute-value": "^5.0.1", + "@svgr/babel-plugin-svg-dynamic-title": "^5.4.0", + "@svgr/babel-plugin-svg-em-dimensions": "^5.4.0", + "@svgr/babel-plugin-transform-react-native-svg": "^5.4.0", + "@svgr/babel-plugin-transform-svg-component": "^5.5.0" }, "engines": { - "node": ">=8.6" + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" } }, - "node_modules/@jest/transform/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/@svgr/core": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-5.5.0.tgz", + "integrity": "sha512-q52VOcsJPvV3jO1wkPtzTuKlvX7Y3xIcWRpCMtBF3MrteZJtBfQw/+u0B1BHy5ColpQc1/YVTrPEtSYIMNZlrQ==", + "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "@svgr/plugin-jsx": "^5.5.0", + "camelcase": "^6.2.0", + "cosmiconfig": "^7.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" } }, - "node_modules/@jest/transform/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==", + "node_modules/@svgr/hast-util-to-babel-ast": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-5.5.0.tgz", + "integrity": "sha512-cAaR/CAiZRB8GP32N+1jocovUtvlj0+e65TB50/6Lcime+EA49m/8l+P2ko+XPJ4dw3xaPS3jOL4F2X4KWxoeQ==", + "license": "MIT", "dependencies": { - "is-number": "^7.0.0" + "@babel/types": "^7.12.6" }, "engines": { - "node": ">=8.0" + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" } }, - "node_modules/@jest/types": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.4.3.tgz", - "integrity": "sha512-bPYfw8V65v17m2Od1cv44FH+SiKW7w2Xu7trhcdTLUmSv85rfKsP+qXSjO4KGJr4dtPSzl/gvslZBXctf1qGEA==", + "node_modules/@svgr/plugin-jsx": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-5.5.0.tgz", + "integrity": "sha512-V/wVh33j12hGh05IDg8GpIUXbjAPnTdPTKuP4VNLggnwaHMPNQNae2pRnyTAILWCQdz5GyMqtO488g7CKM8CBA==", + "license": "MIT", "dependencies": { - "@jest/schemas": "^29.4.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" + "@babel/core": "^7.12.3", + "@svgr/babel-preset": "^5.5.0", + "@svgr/hast-util-to-babel-ast": "^5.5.0", + "svg-parser": "^2.0.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" } }, - "node_modules/@jest/types/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/@svgr/plugin-svgo": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-5.5.0.tgz", + "integrity": "sha512-r5swKk46GuQl4RrVejVwpeeJaydoxkdwkM1mBKOgJLBUJPGaLci6ylg/IjhrRsREKDkr4kbMWdgOtbXEh0fyLQ==", + "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "cosmiconfig": "^7.0.0", + "deepmerge": "^4.2.2", + "svgo": "^1.2.2" }, "engines": { - "node": ">=8" + "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "type": "github", + "url": "https://github.com/sponsors/gregberge" } }, - "node_modules/@jest/types/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/@svgr/webpack": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-5.5.0.tgz", + "integrity": "sha512-DOBOK255wfQxguUta2INKkzPj6AIS6iafZYiYmHn6W3pHlycSRRlvWKCfLDG10fXfLWqE3DJHgRUOyJYmARa7g==", + "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@babel/core": "^7.12.3", + "@babel/plugin-transform-react-constant-elements": "^7.12.1", + "@babel/preset-env": "^7.12.1", + "@babel/preset-react": "^7.12.5", + "@svgr/core": "^5.5.0", + "@svgr/plugin-jsx": "^5.5.0", + "@svgr/plugin-svgo": "^5.5.0", + "loader-utils": "^2.0.0" }, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "type": "github", + "url": "https://github.com/sponsors/gregberge" } }, - "node_modules/@jest/types/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/@swc/helpers": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.13.tgz", + "integrity": "sha512-UoKGxQ3r5kYI9dALKJapMmuK+1zWM/H17Z1+iwnNmzcJRnfFuevZs375TA5rW31pu4BS4NoSy1fRsexDXfWn5w==", + "license": "Apache-2.0", "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" + "tslib": "^2.4.0" } }, - "node_modules/@jest/types/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==" - }, - "node_modules/@jest/types/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==", + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "license": "MIT", "engines": { - "node": ">=8" + "node": ">= 6" } }, - "node_modules/@jest/types/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, + "node_modules/@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", + "license": "ISC", "engines": { - "node": ">=8" + "node": ">=10.13.0" } }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", - "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "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==", + "license": "MIT", "dependencies": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" - }, - "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/@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", - "engines": { - "node": ">=6.0.0" + "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==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" } }, - "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==", - "engines": { - "node": ">=6.0.0" + "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==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" } }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", - "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "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==", + "license": "MIT", "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" + "@babel/types": "^7.20.7" } }, - "node_modules/@jridgewell/source-map/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "node_modules/@types/body-parser": { + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "license": "MIT", "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" + "@types/connect": "*", + "@types/node": "*" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", - "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", + "node_modules/@types/bonjour": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz", + "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==", + "license": "MIT", "dependencies": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" + "@types/node": "*" } }, - "node_modules/@leichtgewicht/ip-codec": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", - "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" - }, - "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/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "license": "MIT", "dependencies": { - "eslint-scope": "5.1.1" + "@types/node": "*" } }, - "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/@types/connect-history-api-fallback": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz", + "integrity": "sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==", + "license": "MIT", "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" + "@types/express-serve-static-core": "*", + "@types/node": "*" } }, - "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==", - "engines": { - "node": ">= 8" - } + "node_modules/@types/d3-array": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz", + "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==", + "license": "MIT" }, - "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==", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "license": "MIT" }, - "node_modules/@npmcli/fs": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.2.tgz", - "integrity": "sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==", + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", + "license": "MIT" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "license": "MIT", "dependencies": { - "@gar/promisify": "^1.1.3", - "semver": "^7.3.5" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "@types/d3-color": "*" } }, - "node_modules/@npmcli/fs/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "node_modules/@types/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-P2dlU/q51fkOc/Gfl3Ul9kicV7l+ra934qBFXCFhrZMOL6du1TM0pm1ThYvENukyOn5h9v+yMJ9Fn5JK4QozrQ==", + "license": "MIT" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz", + "integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==", + "license": "MIT", "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" + "@types/d3-time": "*" } }, - "node_modules/@npmcli/fs/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "node_modules/@types/d3-shape": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.6.tgz", + "integrity": "sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==", + "license": "MIT", "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" + "@types/d3-path": "*" } }, - "node_modules/@npmcli/fs/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "node_modules/@types/d3-time": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.3.tgz", + "integrity": "sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==", + "license": "MIT" }, - "node_modules/@npmcli/move-file": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-2.0.1.tgz", - "integrity": "sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==", - "deprecated": "This functionality has been moved to @npmcli/fs", + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "license": "MIT" + }, + "node_modules/@types/eslint": { + "version": "8.56.12", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.12.tgz", + "integrity": "sha512-03ruubjWyOHlmljCVoxSuNDdmfZDzsrrz0P2LeJsOXr+ZwFQ+0yQIwNCwt/GYhV7Z31fgtXJTAEs+FYlEL851g==", + "license": "MIT", "dependencies": { - "mkdirp": "^1.0.4", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "@types/estree": "*", + "@types/json-schema": "*" } }, - "node_modules/@npmcli/move-file/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/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "license": "MIT", "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "@types/eslint": "*", + "@types/estree": "*" } }, - "node_modules/@plotly/dash-component-plugins": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@plotly/dash-component-plugins/-/dash-component-plugins-1.2.3.tgz", - "integrity": "sha512-BjcGvqS+sbp15rnETIWyuPg8imvxboSrHyOlEBGBUurF5v1nHZpfbk4PAYpk8Q6K20ofScbNf1aXp/lOLHAemA==", - "dev": true - }, - "node_modules/@plotly/webpack-dash-dynamic-import": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@plotly/webpack-dash-dynamic-import/-/webpack-dash-dynamic-import-1.3.0.tgz", - "integrity": "sha512-JuleFNu/DqpzjYABv54j2eJ9+bCUut2EjTrEbyPCvAnjdhabOLnJmGl3Tf6ChDadIfao2Q5yH/R1K2q6dElroQ==", - "dev": true + "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==", + "license": "MIT" }, - "node_modules/@popperjs/core": { - "version": "2.11.6", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz", - "integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/popperjs" + "node_modules/@types/express": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" } }, - "node_modules/@react-aria/ssr": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.5.0.tgz", - "integrity": "sha512-h0MJdSWOd1qObLnJ8mprU31wI8tmKFJMuwT22MpWq6psisOOZaga6Ml4u6Ee6M6duWWISjXvqO4Sb/J0PBA+nQ==", + "node_modules/@types/express-serve-static-core": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.1.tgz", + "integrity": "sha512-CRICJIl0N5cXDONAdlTv5ShATZ4HEwk6kDDIW2/w9qOWKg+NU/5F8wYRWCrONad0/UKkloNSmmyN/wX4rtpbVA==", + "license": "MIT", "dependencies": { - "@swc/helpers": "^0.4.14" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0" + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" } }, - "node_modules/@remix-run/router": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.3.3.tgz", - "integrity": "sha512-YRHie1yQEj0kqqCTCJEfHqYSSNlZQ696QJG+MMiW4mxSl9I0ojz/eRhJS4fs88Z5i6D1SmoF9d3K99/QOhI8/w==", - "engines": { - "node": ">=14" + "node_modules/@types/express/node_modules/@types/express-serve-static-core": { + "version": "4.19.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", + "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" } }, - "node_modules/@restart/hooks": { - "version": "0.4.9", - "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.9.tgz", - "integrity": "sha512-3BekqcwB6Umeya+16XPooARn4qEPW6vNvwYnlofIYe6h9qG1/VeD7UvShCWx11eFz5ELYmwIEshz+MkPX3wjcQ==", + "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==", + "license": "MIT", "dependencies": { - "dequal": "^2.0.2" - }, - "peerDependencies": { - "react": ">=16.8.0" + "@types/node": "*" } }, - "node_modules/@restart/ui": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@restart/ui/-/ui-1.6.1.tgz", - "integrity": "sha512-cMI9DdqZV5VGEyANYM4alHK9/2Lh/mKZAMydztMl6PBLm6EetFbwE2RfYqliloR+EtEULlI4TiZk/XPhQAovxw==", + "node_modules/@types/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==", + "license": "MIT" + }, + "node_modules/@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", + "license": "MIT" + }, + "node_modules/@types/http-proxy": { + "version": "1.17.15", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.15.tgz", + "integrity": "sha512-25g5atgiVNTIv0LBDTg1H74Hvayx0ajtJPLLcYE3whFv75J0pWNtOBzaXJQgDTmrX1bx5U9YC2w/n65BN1HwRQ==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.20.7", - "@popperjs/core": "^2.11.6", - "@react-aria/ssr": "^3.4.1", - "@restart/hooks": "^0.4.7", - "@types/warning": "^3.0.0", - "dequal": "^2.0.3", - "dom-helpers": "^5.2.0", - "uncontrollable": "^7.2.1", - "warning": "^4.0.3" - }, - "peerDependencies": { - "react": ">=16.14.0", - "react-dom": ">=16.14.0" + "@types/node": "*" } }, - "node_modules/@rollup/plugin-babel": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", - "integrity": "sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==", + "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==", + "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==", + "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.10.4", - "@rollup/pluginutils": "^3.1.0" - }, - "engines": { - "node": ">= 10.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0", - "@types/babel__core": "^7.1.9", - "rollup": "^1.20.0||^2.0.0" - }, - "peerDependenciesMeta": { - "@types/babel__core": { - "optional": true - } + "@types/istanbul-lib-coverage": "*" } }, - "node_modules/@rollup/plugin-node-resolve": { - "version": "11.2.1", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.1.tgz", - "integrity": "sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==", + "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==", + "license": "MIT", "dependencies": { - "@rollup/pluginutils": "^3.1.0", - "@types/resolve": "1.17.1", - "builtin-modules": "^3.1.0", - "deepmerge": "^4.2.2", - "is-module": "^1.0.0", - "resolve": "^1.19.0" - }, - "engines": { - "node": ">= 10.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0" + "@types/istanbul-lib-report": "*" } }, - "node_modules/@rollup/plugin-replace": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-2.4.2.tgz", - "integrity": "sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==", + "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==", + "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==", + "license": "MIT" + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.9.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz", + "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==", + "license": "MIT", "dependencies": { - "@rollup/pluginutils": "^3.1.0", - "magic-string": "^0.25.7" - }, - "peerDependencies": { - "rollup": "^1.20.0 || ^2.0.0" + "undici-types": "~6.19.8" } }, - "node_modules/@rollup/pluginutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", - "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", + "node_modules/@types/node-forge": { + "version": "1.3.11", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz", + "integrity": "sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==", + "license": "MIT", "dependencies": { - "@types/estree": "0.0.39", - "estree-walker": "^1.0.1", - "picomatch": "^2.2.2" - }, - "engines": { - "node": ">= 8.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0" + "@types/node": "*" } }, - "node_modules/@rushstack/eslint-patch": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz", - "integrity": "sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==" + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", + "license": "MIT" }, - "node_modules/@sinclair/typebox": { - "version": "0.25.24", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz", - "integrity": "sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==" + "node_modules/@types/prettier": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", + "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", + "license": "MIT" }, - "node_modules/@sinonjs/commons": { - "version": "1.8.6", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", - "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", + "node_modules/@types/prop-types": { + "version": "15.7.13", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", + "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==", + "license": "MIT" + }, + "node_modules/@types/q": { + "version": "1.5.8", + "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.8.tgz", + "integrity": "sha512-hroOstUScF6zhIi+5+x0dzqrHA1EJi+Irri6b1fxolMTqqHIV/Cg77EtnQcZqZCu8hR3mX2BzIxN4/GzI68Kfw==", + "license": "MIT" + }, + "node_modules/@types/qs": { + "version": "6.9.17", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.17.tgz", + "integrity": "sha512-rX4/bPcfmvxHDv0XjfJELTTr+iB+tn032nPILqHm5wbthUUUuVtNGGqzhya9XUxjTP8Fpr0qYgSZZKxGY++svQ==", + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "18.3.12", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.12.tgz", + "integrity": "sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==", + "license": "MIT", "dependencies": { - "type-detect": "4.0.8" + "@types/prop-types": "*", + "csstype": "^3.0.2" } }, - "node_modules/@sinonjs/fake-timers": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", - "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", + "node_modules/@types/react-transition-group": { + "version": "4.4.11", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.11.tgz", + "integrity": "sha512-RM05tAniPZ5DZPzzNFP+DmrcOdD0efDUxMy3145oljWSl3x9ZV5vhme98gTxFrj2lhXvmGNnUiuDyJgY9IKkNA==", + "license": "MIT", "dependencies": { - "@sinonjs/commons": "^1.7.0" + "@types/react": "*" } }, - "node_modules/@surma/rollup-plugin-off-main-thread": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz", - "integrity": "sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==", + "node_modules/@types/resolve": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", + "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", + "license": "MIT", "dependencies": { - "ejs": "^3.1.6", - "json5": "^2.2.0", - "magic-string": "^0.25.0", - "string.prototype.matchall": "^4.0.6" + "@types/node": "*" } }, - "node_modules/@svgr/babel-plugin-add-jsx-attribute": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz", - "integrity": "sha512-ZFf2gs/8/6B8PnSofI0inYXr2SDNTDScPXhN7k5EqD4aZ3gi6u+rbmZHVB8IM3wDyx8ntKACZbtXSm7oZGRqVg==", - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" + "node_modules/@types/retry": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz", + "integrity": "sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@types/semver": { + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" } }, - "node_modules/@svgr/babel-plugin-remove-jsx-attribute": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-5.4.0.tgz", - "integrity": "sha512-yaS4o2PgUtwLFGTKbsiAy6D0o3ugcUhWK0Z45umJ66EPWunAz9fuFw2gJuje6wqQvQWOTJvIahUwndOXb7QCPg==", - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" + "node_modules/@types/serve-index": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz", + "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==", + "license": "MIT", + "dependencies": { + "@types/express": "*" } }, - "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-5.0.1.tgz", - "integrity": "sha512-LA72+88A11ND/yFIMzyuLRSMJ+tRKeYKeQ+mR3DcAZ5I4h5CPWN9AHyUzJbWSYp/u2u0xhmgOe0+E41+GjEueA==", - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" + "node_modules/@types/serve-static": { + "version": "1.15.7", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", + "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" } }, - "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-5.0.1.tgz", - "integrity": "sha512-PoiE6ZD2Eiy5mK+fjHqwGOS+IXX0wq/YDtNyIgOrc6ejFnxN4b13pRpiIPbtPwHEc+NT2KCjteAcq33/F1Y9KQ==", - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" + "node_modules/@types/sockjs": { + "version": "0.3.36", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz", + "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==", + "license": "MIT", + "dependencies": { + "@types/node": "*" } }, - "node_modules/@svgr/babel-plugin-svg-dynamic-title": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-5.4.0.tgz", - "integrity": "sha512-zSOZH8PdZOpuG1ZVx/cLVePB2ibo3WPpqo7gFIjLV9a0QsuQAzJiwwqmuEdTaW2pegyBE17Uu15mOgOcgabQZg==", - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" + "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==", + "license": "MIT" + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT" + }, + "node_modules/@types/warning": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.3.tgz", + "integrity": "sha512-D1XC7WK8K+zZEveUPY+cf4+kgauk8N4eHr/XIHXGlGYkHLud6hK9lYfZk1ry1TNh798cZUCgb6MqGEG8DkJt6Q==", + "license": "MIT" + }, + "node_modules/@types/ws": { + "version": "8.5.13", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.13.tgz", + "integrity": "sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA==", + "license": "MIT", + "dependencies": { + "@types/node": "*" } }, - "node_modules/@svgr/babel-plugin-svg-em-dimensions": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-5.4.0.tgz", - "integrity": "sha512-cPzDbDA5oT/sPXDCUYoVXEmm3VIoAWAPT6mSPTJNbQaBNUuEKVKyGH93oDY4e42PYHRW67N5alJx/eEol20abw==", + "node_modules/@types/yargs": { + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", + "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==", + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", + "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/type-utils": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, "engines": { - "node": ">=10" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@svgr/babel-plugin-transform-react-native-svg": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-5.4.0.tgz", - "integrity": "sha512-3eYP/SaopZ41GHwXma7Rmxcv9uRslRDTY1estspeB1w1ueZWd/tPlMfEOoccYpEMZU3jD4OU7YitnXcF5hLW2Q==", + "node_modules/@typescript-eslint/experimental-utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.62.0.tgz", + "integrity": "sha512-RTXpeB3eMkpoclG3ZHft6vG/Z30azNHuqY6wKPBHlVMZFuEvrtlEDe8gMqDb+SO+9hjC/pLekeSCryf9vMZlCw==", + "license": "MIT", + "dependencies": { + "@typescript-eslint/utils": "5.62.0" + }, "engines": { - "node": ">=10" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/@svgr/babel-plugin-transform-svg-component": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-5.5.0.tgz", - "integrity": "sha512-q4jSH1UUvbrsOtlo/tKcgSeiCHRSBdXoIoqX1pgcKK/aU3JD27wmMKwGtpB8qRYUYoyXvfGxUVKchLuR5pB3rQ==", + "node_modules/@typescript-eslint/parser": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", + "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "debug": "^4.3.4" + }, "engines": { - "node": ">=10" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@svgr/babel-preset": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-5.5.0.tgz", - "integrity": "sha512-4FiXBjvQ+z2j7yASeGPEi8VD/5rrGQk4Xrq3EdJmoZgz/tpqChpo5hgXDvmEauwtvOc52q8ghhZK4Oy7qph4ig==", + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", + "license": "MIT", "dependencies": { - "@svgr/babel-plugin-add-jsx-attribute": "^5.4.0", - "@svgr/babel-plugin-remove-jsx-attribute": "^5.4.0", - "@svgr/babel-plugin-remove-jsx-empty-expression": "^5.0.1", - "@svgr/babel-plugin-replace-jsx-attribute-value": "^5.0.1", - "@svgr/babel-plugin-svg-dynamic-title": "^5.4.0", - "@svgr/babel-plugin-svg-em-dimensions": "^5.4.0", - "@svgr/babel-plugin-transform-react-native-svg": "^5.4.0", - "@svgr/babel-plugin-transform-svg-component": "^5.5.0" + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" }, "engines": { - "node": ">=10" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@svgr/core": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/core/-/core-5.5.0.tgz", - "integrity": "sha512-q52VOcsJPvV3jO1wkPtzTuKlvX7Y3xIcWRpCMtBF3MrteZJtBfQw/+u0B1BHy5ColpQc1/YVTrPEtSYIMNZlrQ==", + "node_modules/@typescript-eslint/type-utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", + "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", + "license": "MIT", "dependencies": { - "@svgr/plugin-jsx": "^5.5.0", - "camelcase": "^6.2.0", - "cosmiconfig": "^7.0.0" + "@typescript-eslint/typescript-estree": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" }, "engines": { - "node": ">=10" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@svgr/core/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "node_modules/@typescript-eslint/types": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", + "license": "MIT", "engines": { - "node": ">=10" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@svgr/hast-util-to-babel-ast": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-5.5.0.tgz", - "integrity": "sha512-cAaR/CAiZRB8GP32N+1jocovUtvlj0+e65TB50/6Lcime+EA49m/8l+P2ko+XPJ4dw3xaPS3jOL4F2X4KWxoeQ==", + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", + "license": "BSD-2-Clause", "dependencies": { - "@babel/types": "^7.12.6" + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" }, "engines": { - "node": ">=10" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@svgr/plugin-jsx": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-5.5.0.tgz", - "integrity": "sha512-V/wVh33j12hGh05IDg8GpIUXbjAPnTdPTKuP4VNLggnwaHMPNQNae2pRnyTAILWCQdz5GyMqtO488g7CKM8CBA==", + "node_modules/@typescript-eslint/utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", + "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", + "license": "MIT", "dependencies": { - "@babel/core": "^7.12.3", - "@svgr/babel-preset": "^5.5.0", - "@svgr/hast-util-to-babel-ast": "^5.5.0", - "svg-parser": "^2.0.2" + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" }, "engines": { - "node": ">=10" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/@svgr/plugin-svgo": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-5.5.0.tgz", - "integrity": "sha512-r5swKk46GuQl4RrVejVwpeeJaydoxkdwkM1mBKOgJLBUJPGaLci6ylg/IjhrRsREKDkr4kbMWdgOtbXEh0fyLQ==", + "node_modules/@typescript-eslint/utils/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==", + "license": "BSD-2-Clause", "dependencies": { - "cosmiconfig": "^7.0.0", - "deepmerge": "^4.2.2", - "svgo": "^1.2.2" + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" }, "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" + "node": ">=8.0.0" } }, - "node_modules/@svgr/webpack": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-5.5.0.tgz", - "integrity": "sha512-DOBOK255wfQxguUta2INKkzPj6AIS6iafZYiYmHn6W3pHlycSRRlvWKCfLDG10fXfLWqE3DJHgRUOyJYmARa7g==", + "node_modules/@typescript-eslint/utils/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", + "license": "MIT", "dependencies": { - "@babel/core": "^7.12.3", - "@babel/plugin-transform-react-constant-elements": "^7.12.1", - "@babel/preset-env": "^7.12.1", - "@babel/preset-react": "^7.12.5", - "@svgr/core": "^5.5.0", - "@svgr/plugin-jsx": "^5.5.0", - "@svgr/plugin-svgo": "^5.5.0", - "loader-utils": "^2.0.0" + "@typescript-eslint/types": "5.62.0", + "eslint-visitor-keys": "^3.3.0" }, "engines": { - "node": ">=10" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@swc/helpers": { - "version": "0.4.14", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.4.14.tgz", - "integrity": "sha512-4C7nX/dvpzB7za4Ql9K81xK3HPxCpHMgwTZVyf+9JQ6VUbn9jjZVN7/Nkdz/Ugzs2CSjqnL/UPXroiVBVHUWUw==", + "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==", + "license": "ISC" + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", + "license": "MIT", "dependencies": { - "tslib": "^2.4.0" + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" } }, - "node_modules/@swc/helpers/node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", + "license": "MIT" }, - "node_modules/@testing-library/dom": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.0.0.tgz", - "integrity": "sha512-+/TLgKNFsYUshOY/zXsQOk+PlFQK+eyJ9T13IDVNJEi+M+Un7xlJK+FZKkbGSnf0+7E1G6PlDhkSYQ/GFiruBQ==", - "peer": true, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.10.4", - "@babel/runtime": "^7.12.5", - "@types/aria-query": "^5.0.1", - "aria-query": "^5.0.0", - "chalk": "^4.1.0", - "dom-accessibility-api": "^0.5.9", - "lz-string": "^1.4.4", - "pretty-format": "^27.0.2" - }, - "engines": { - "node": ">=14" + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", + "@xtuc/long": "4.2.2" } }, - "node_modules/@testing-library/dom/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==", - "peer": true, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", + "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" } }, - "node_modules/@testing-library/dom/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "peer": true, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", + "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" + "@xtuc/ieee754": "^1.2.0" } }, - "node_modules/@testing-library/dom/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==", - "peer": true, + "node_modules/@webassemblyjs/leb128": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", + "license": "Apache-2.0", "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" + "@xtuc/long": "4.2.2" } }, - "node_modules/@testing-library/dom/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==", - "peer": true - }, - "node_modules/@testing-library/dom/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==", - "peer": true, - "engines": { - "node": ">=8" - } + "node_modules/@webassemblyjs/utf8": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", + "license": "MIT" }, - "node_modules/@testing-library/dom/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==", - "peer": true, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", + "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" } }, - "node_modules/@testing-library/jest-dom": { - "version": "5.16.5", - "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.16.5.tgz", - "integrity": "sha512-N5ixQ2qKpi5OLYfwQmUb/5mSV9LneAcaUfp32pn4yCnpb8r/Yz0pXFPck21dIicKmi+ta5WRAknkZCfA8refMA==", + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", + "license": "MIT", "dependencies": { - "@adobe/css-tools": "^4.0.1", - "@babel/runtime": "^7.9.2", - "@types/testing-library__jest-dom": "^5.9.1", - "aria-query": "^5.0.0", - "chalk": "^3.0.0", - "css.escape": "^1.5.1", - "dom-accessibility-api": "^0.5.6", - "lodash": "^4.17.15", - "redent": "^3.0.0" - }, - "engines": { - "node": ">=8", - "npm": ">=6", - "yarn": ">=1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" } }, - "node_modules/@testing-library/jest-dom/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/@webassemblyjs/wasm-opt": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", + "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" } }, - "node_modules/@testing-library/jest-dom/node_modules/chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", + "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" } }, - "node_modules/@testing-library/jest-dom/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/@webassemblyjs/wast-printer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", + "license": "MIT", "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" + "@webassemblyjs/ast": "1.14.1", + "@xtuc/long": "4.2.2" } }, - "node_modules/@testing-library/jest-dom/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==" - }, - "node_modules/@testing-library/jest-dom/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==", + "node_modules/@webpack-cli/configtest": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz", + "integrity": "sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=14.15.0" + }, + "peerDependencies": { + "webpack": "5.x.x", + "webpack-cli": "5.x.x" } }, - "node_modules/@testing-library/jest-dom/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, + "node_modules/@webpack-cli/info": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz", + "integrity": "sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=14.15.0" + }, + "peerDependencies": { + "webpack": "5.x.x", + "webpack-cli": "5.x.x" } }, - "node_modules/@testing-library/react": { - "version": "13.4.0", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-13.4.0.tgz", - "integrity": "sha512-sXOGON+WNTh3MLE9rve97ftaZukN3oNf2KjDy7YTx6hcTO2uuLHuCGynMDhFwGw/jYf4OJ2Qk0i4i79qMNNkyw==", - "dependencies": { - "@babel/runtime": "^7.12.5", - "@testing-library/dom": "^8.5.0", - "@types/react-dom": "^18.0.0" - }, + "node_modules/@webpack-cli/serve": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.5.tgz", + "integrity": "sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=12" + "node": ">=14.15.0" }, "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" + "webpack": "5.x.x", + "webpack-cli": "5.x.x" + }, + "peerDependenciesMeta": { + "webpack-dev-server": { + "optional": true + } } }, - "node_modules/@testing-library/react/node_modules/@testing-library/dom": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.20.0.tgz", - "integrity": "sha512-d9ULIT+a4EXLX3UU8FBjauG9NnsZHkHztXoIcTsOKoOw030fyjheN9svkTULjJxtYag9DZz5Jz5qkWZDPxTFwA==", + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "license": "BSD-3-Clause" + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "license": "Apache-2.0" + }, + "node_modules/abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "deprecated": "Use your platform's native atob() and btoa() methods instead", + "license": "BSD-3-Clause" + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.10.4", - "@babel/runtime": "^7.12.5", - "@types/aria-query": "^5.0.1", - "aria-query": "^5.0.0", - "chalk": "^4.1.0", - "dom-accessibility-api": "^0.5.9", - "lz-string": "^1.4.4", - "pretty-format": "^27.0.2" + "mime-types": "~2.1.34", + "negotiator": "0.6.3" }, "engines": { - "node": ">=12" + "node": ">= 0.6" } }, - "node_modules/@testing-library/react/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, + "node_modules/accepts/node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">= 0.6" } }, - "node_modules/@testing-library/react/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=0.4.0" } }, - "node_modules/@testing-library/react/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/acorn-globals": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", + "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "license": "MIT", "dependencies": { - "color-name": "~1.1.4" + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + } + }, + "node_modules/acorn-globals/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" }, "engines": { - "node": ">=7.0.0" + "node": ">=0.4.0" } }, - "node_modules/@testing-library/react/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==" + "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==", + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } }, - "node_modules/@testing-library/react/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==", + "node_modules/acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=0.4.0" } }, - "node_modules/@testing-library/react/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, + "node_modules/address": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/address/-/address-1.2.2.tgz", + "integrity": "sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==", + "license": "MIT", "engines": { - "node": ">=8" + "node": ">= 10.0.0" } }, - "node_modules/@testing-library/user-event": { - "version": "13.5.0", - "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-13.5.0.tgz", - "integrity": "sha512-5Kwtbo3Y/NowpkbRuSepbyMFkZmHgD+vPzYB/RJ4oxt5Gj/avFFBYjhw27cqSVPVw/3a67NK1PbiIr9k4Gwmdg==", + "node_modules/adjust-sourcemap-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", + "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.12.5" + "loader-utils": "^2.0.0", + "regex-parser": "^2.2.11" }, "engines": { - "node": ">=10", - "npm": ">=6" - }, - "peerDependencies": { - "@testing-library/dom": ">=7.21.4" + "node": ">=8.9" } }, - "node_modules/@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, "engines": { - "node": ">= 6" + "node": ">= 6.0.0" } }, - "node_modules/@trysound/sax": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", - "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", - "engines": { - "node": ">=10.13.0" + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "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" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@types/aria-query": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.1.tgz", - "integrity": "sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q==" - }, - "node_modules/@types/babel__core": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.0.tgz", - "integrity": "sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==", + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "license": "MIT", "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } } }, - "node_modules/@types/babel__generator": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", - "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "license": "MIT", "dependencies": { - "@babel/types": "^7.0.0" + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@types/babel__template": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", - "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "license": "MIT", + "peerDependencies": { + "ajv": "^6.9.1" } }, - "node_modules/@types/babel__traverse": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz", - "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==", + "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==", + "license": "MIT", "dependencies": { - "@babel/types": "^7.3.0" + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@types/body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", - "dependencies": { - "@types/connect": "*", - "@types/node": "*" + "node_modules/ansi-html": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.9.tgz", + "integrity": "sha512-ozbS3LuenHVxNRh/wdnN16QapUHzauqSomAl1jwwJRRsGwFwtj644lIhxfWu0Fy0acCij2+AEgHvjscq3dlVXg==", + "engines": [ + "node >= 0.8.0" + ], + "license": "Apache-2.0", + "bin": { + "ansi-html": "bin/ansi-html" } }, - "node_modules/@types/bonjour": { - "version": "3.5.10", - "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", - "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", - "dependencies": { - "@types/node": "*" + "node_modules/ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", + "engines": [ + "node >= 0.8.0" + ], + "license": "Apache-2.0", + "bin": { + "ansi-html": "bin/ansi-html" } }, - "node_modules/@types/connect": { - "version": "3.4.35", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", - "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", - "dependencies": { - "@types/node": "*" + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" } }, - "node_modules/@types/connect-history-api-fallback": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz", - "integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==", + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", "dependencies": { - "@types/express-serve-static-core": "*", - "@types/node": "*" + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@types/d3-array": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.0.4.tgz", - "integrity": "sha512-nwvEkG9vYOc0Ic7G7kwgviY4AQlTfYGIZ0fqB7CQHXGyYM6nO7kJh5EguSNA3jfh4rq7Sb7eMVq8isuvg2/miQ==" - }, - "node_modules/@types/d3-color": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.0.tgz", - "integrity": "sha512-HKuicPHJuvPgCD+np6Se9MQvS6OCbJmOjGvylzMJRlDwUXjKTTXs6Pwgk79O09Vj/ho3u1ofXnhFOaEWWPrlwA==" - }, - "node_modules/@types/d3-ease": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.0.tgz", - "integrity": "sha512-aMo4eaAOijJjA6uU+GIeW018dvy9+oH5Y2VPPzjjfxevvGQ/oRDs+tfYC9b50Q4BygRR8yE2QCLsrT0WtAVseA==" + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "license": "MIT" }, - "node_modules/@types/d3-interpolate": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.1.tgz", - "integrity": "sha512-jx5leotSeac3jr0RePOH1KdR9rISG91QIE4Q2PYTu4OymLTZfA3SrnURSLzKH48HmXVUru50b8nje4E79oQSQw==", + "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==", + "license": "ISC", "dependencies": { - "@types/d3-color": "*" + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" } }, - "node_modules/@types/d3-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.0.0.tgz", - "integrity": "sha512-0g/A+mZXgFkQxN3HniRDbXMN79K3CdTpLsevj+PXiTcb2hVyvkZUBg37StmgCQkaD84cUJ4uaDAWq7UJOQy2Tg==" + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "license": "MIT" }, - "node_modules/@types/d3-scale": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.3.tgz", - "integrity": "sha512-PATBiMCpvHJSMtZAMEhc2WyL+hnzarKzI6wAHYjhsonjWJYGq5BXTzQjv4l8m2jO183/4wZ90rKvSeT7o72xNQ==", + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "license": "MIT", "dependencies": { - "@types/d3-time": "*" + "sprintf-js": "~1.0.2" } }, - "node_modules/@types/d3-shape": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.1.tgz", - "integrity": "sha512-6Uh86YFF7LGg4PQkuO2oG6EMBRLuW9cbavUW46zkIO5kuS2PfTqo2o9SkgtQzguBHbLgNnU90UNsITpsX1My+A==", - "dependencies": { - "@types/d3-path": "*" + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" } }, - "node_modules/@types/d3-time": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.0.tgz", - "integrity": "sha512-sZLCdHvBUcNby1cB6Fd3ZBrABbjz3v1Vm90nysCQ6Vt7vd6e/h9Lt7SiJUoEX0l4Dzc7P5llKyhqSi1ycSf1Hg==" + "node_modules/array-buffer-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", + "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.5", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/@types/d3-timer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.0.tgz", - "integrity": "sha512-HNB/9GHqu7Fo8AQiugyJbv6ZxYz58wef0esl4Mv828w1ZKpAshw/uFWVDUcIB9KKFeFKoxS3cHY07FFgtTRZ1g==" + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" }, - "node_modules/@types/eslint": { - "version": "8.21.1", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.21.1.tgz", - "integrity": "sha512-rc9K8ZpVjNcLs8Fp0dkozd5Pt2Apk1glO4Vgz8ix1u6yFByxfqo5Yavpy65o+93TAe24jr7v+eSBtFLvOQtCRQ==", + "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==", + "license": "MIT", "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" + "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/@types/eslint-scope": { - "version": "3.7.4", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", - "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "license": "MIT", + "engines": { + "node": ">=8" } }, - "node_modules/@types/estree": { - "version": "0.0.39", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", - "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==" - }, - "node_modules/@types/express": { - "version": "4.17.17", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz", - "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==", - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "4.17.33", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.33.tgz", - "integrity": "sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*" - } - }, - "node_modules/@types/graceful-fs": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", - "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==", + "node_modules/array.prototype.findlast": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", + "license": "MIT", "dependencies": { - "@types/node": "*" + "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/@types/html-minifier-terser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", - "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==" - }, - "node_modules/@types/http-proxy": { - "version": "1.17.10", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.10.tgz", - "integrity": "sha512-Qs5aULi+zV1bwKAg5z1PWnDXWmsn+LxIvUGv6E2+OOMYhclZMO+OXd9pYVf2gLykf2I7IV2u7oTHwChPNsvJ7g==", + "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==", + "license": "MIT", "dependencies": { - "@types/node": "*" + "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/@types/istanbul-lib-coverage": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", - "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==" - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "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==", + "license": "MIT", "dependencies": { - "@types/istanbul-lib-coverage": "*" + "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/@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "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==", + "license": "MIT", "dependencies": { - "@types/istanbul-lib-report": "*" + "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/@types/jest": { - "version": "29.4.0", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.4.0.tgz", - "integrity": "sha512-VaywcGQ9tPorCX/Jkkni7RWGFfI11whqzs8dvxF41P17Z+z872thvEvlIbznjPJ02kl1HMX3LmLOonsj2n7HeQ==", + "node_modules/array.prototype.reduce": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.7.tgz", + "integrity": "sha512-mzmiUCVwtiD4lgxYP8g7IYy8El8p2CSMePvIbTS7gchKir/L1fgJrk0yDKmAX6mnRQFKNADYIk8nNlTris5H1Q==", + "license": "MIT", "dependencies": { - "expect": "^29.0.0", - "pretty-format": "^29.0.0" - } - }, - "node_modules/@types/jest/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==", + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-array-method-boxes-properly": "^1.0.0", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "is-string": "^1.0.7" + }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@types/jest/node_modules/pretty-format": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.4.3.tgz", - "integrity": "sha512-cvpcHTc42lcsvOOAzd3XuNWTcvk1Jmnzqeu+WsOuiPmxUJTnkbAcFNsRKvEpBEUFVUgy/GTZLulZDcDEi+CIlA==", + "node_modules/array.prototype.tosorted": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", + "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", + "license": "MIT", "dependencies": { - "@jest/schemas": "^29.4.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-shim-unscopables": "^1.0.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">= 0.4" } }, - "node_modules/@types/jest/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" - }, - "node_modules/@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==" - }, - "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==" - }, - "node_modules/@types/mime": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", - "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==" - }, - "node_modules/@types/minimist": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", - "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==" - }, - "node_modules/@types/node": { - "version": "18.14.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.14.4.tgz", - "integrity": "sha512-VhCw7I7qO2X49+jaKcAUwi3rR+hbxT5VcYF493+Z5kMLI0DL568b7JI4IDJaxWFH0D/xwmGJNoXisyX+w7GH/g==" - }, - "node_modules/@types/normalize-package-data": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", - "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==" - }, - "node_modules/@types/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" - }, - "node_modules/@types/prettier": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz", - "integrity": "sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==" + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", + "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.2.1", + "get-intrinsic": "^1.2.3", + "is-array-buffer": "^3.0.4", + "is-shared-array-buffer": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/@types/prop-types": { - "version": "15.7.5", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", - "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "license": "MIT" }, - "node_modules/@types/q": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.5.tgz", - "integrity": "sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ==" + "node_modules/ast-types-flow": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", + "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", + "license": "MIT" }, - "node_modules/@types/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT" }, - "node_modules/@types/range-parser": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", - "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" }, - "node_modules/@types/react": { - "version": "18.0.28", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.28.tgz", - "integrity": "sha512-RD0ivG1kEztNBdoAK7lekI9M+azSnitIn85h4iOiaLjaTrMjzslhaqCGaI4IyCJ1RljWiLCEu4jyrLLgqxBTew==", - "dependencies": { - "@types/prop-types": "*", - "@types/scheduler": "*", - "csstype": "^3.0.2" + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "license": "ISC", + "engines": { + "node": ">= 4.0.0" } }, - "node_modules/@types/react-dom": { - "version": "18.0.11", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.11.tgz", - "integrity": "sha512-O38bPbI2CWtgw/OoQoY+BRelw7uysmXbWvw3nLWO21H1HSh+GOlqPuXshJfjmpNlKiiSDG9cc1JZAaMmVdcTlw==", + "node_modules/autoprefixer": { + "version": "10.4.20", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", + "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", "dependencies": { - "@types/react": "*" + "browserslist": "^4.23.3", + "caniuse-lite": "^1.0.30001646", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" } }, - "node_modules/@types/react-transition-group": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.5.tgz", - "integrity": "sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA==", + "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==", + "license": "MIT", "dependencies": { - "@types/react": "*" + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@types/resolve": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", - "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", - "dependencies": { - "@types/node": "*" + "node_modules/axe-core": { + "version": "4.10.2", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.2.tgz", + "integrity": "sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w==", + "license": "MPL-2.0", + "engines": { + "node": ">=4" } }, - "node_modules/@types/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" - }, - "node_modules/@types/scheduler": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", - "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" - }, - "node_modules/@types/semver": { - "version": "7.3.13", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", - "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==" - }, - "node_modules/@types/serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==", - "dependencies": { - "@types/express": "*" + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" } }, - "node_modules/@types/serve-static": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.1.tgz", - "integrity": "sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==", + "node_modules/babel-jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz", + "integrity": "sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg==", + "license": "MIT", "dependencies": { - "@types/mime": "*", - "@types/node": "*" + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^27.5.1", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" } }, - "node_modules/@types/sockjs": { - "version": "0.3.33", - "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", - "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==", + "node_modules/babel-loader": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.2.1.tgz", + "integrity": "sha512-fqe8naHt46e0yIdkjUZYqddSXfej3AHajX+CSO5X7oy0EmPc6o5Xh+RClNoHjnieWz9AW4kZxW9yyFMhVB1QLA==", + "dev": true, + "license": "MIT", "dependencies": { - "@types/node": "*" + "find-cache-dir": "^4.0.0", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 14.15.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0", + "webpack": ">=5" } }, - "node_modules/@types/stack-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", - "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==" - }, - "node_modules/@types/testing-library__jest-dom": { - "version": "5.14.5", - "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.5.tgz", - "integrity": "sha512-SBwbxYoyPIvxHbeHxTZX2Pe/74F/tX2/D3mMvzabdeJ25bBojfW0TyB8BHrbq/9zaaKICJZjLP+8r6AeZMFCuQ==", + "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==", + "license": "BSD-3-Clause", "dependencies": { - "@types/jest": "*" + "@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/@types/trusted-types": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.3.tgz", - "integrity": "sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g==" - }, - "node_modules/@types/warning": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.0.tgz", - "integrity": "sha512-t/Tvs5qR47OLOr+4E9ckN8AmP2Tf16gWq+/qA4iUGS/OOyHVO8wv2vjJuX8SNOUTJyWb+2t7wJm6cXILFnOROA==" - }, - "node_modules/@types/ws": { - "version": "8.5.4", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.4.tgz", - "integrity": "sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==", + "node_modules/babel-plugin-jest-hoist": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz", + "integrity": "sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ==", + "license": "MIT", "dependencies": { - "@types/node": "*" + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.0.0", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/@types/yargs": { - "version": "17.0.22", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.22.tgz", - "integrity": "sha512-pet5WJ9U8yPVRhkwuEIp5ktAeAqRZOq4UdAyWLWzxbtpyXnzbtLdKiXAjJzi/KLmPGS9wk86lUFWZFN6sISo4g==", + "node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "license": "MIT", "dependencies": { - "@types/yargs-parser": "*" + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" } }, - "node_modules/@types/yargs-parser": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", - "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==" + "node_modules/babel-plugin-named-asset-import": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.8.tgz", + "integrity": "sha512-WXiAc++qo7XcJ1ZnTYGtLxmBCVbddAml3CEXgWaBzNzLNoxtQ8AiGEFDMOhot9XjTCQbvP5E77Fj9Gk924f00Q==", + "license": "MIT", + "peerDependencies": { + "@babel/core": "^7.1.0" + } }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.54.0.tgz", - "integrity": "sha512-+hSN9BdSr629RF02d7mMtXhAJvDTyCbprNYJKrXETlul/Aml6YZwd90XioVbjejQeHbb3R8Dg0CkRgoJDxo8aw==", + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.11", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz", + "integrity": "sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==", + "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "5.54.0", - "@typescript-eslint/type-utils": "5.54.0", - "@typescript-eslint/utils": "5.54.0", - "debug": "^4.3.4", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "natural-compare-lite": "^1.4.0", - "regexpp": "^3.2.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "@babel/compat-data": "^7.22.6", + "@babel/helper-define-polyfill-provider": "^0.6.2", + "semver": "^6.3.1" }, "peerDependencies": { - "@typescript-eslint/parser": "^5.0.0", - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", - "engines": { - "node": ">= 4" + "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.10.6", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz", + "integrity": "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==", + "license": "MIT", "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "engines": { - "node": ">=8" + "@babel/helper-define-polyfill-provider": "^0.6.2", + "core-js-compat": "^3.38.0" }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz", + "integrity": "sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==", + "license": "MIT", "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" + "@babel/helper-define-polyfill-provider": "^0.6.2" }, - "engines": { - "node": ">=10" + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "node_modules/babel-plugin-transform-react-remove-prop-types": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz", + "integrity": "sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA==", + "license": "MIT" }, - "node_modules/@typescript-eslint/experimental-utils": { - "version": "5.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.54.0.tgz", - "integrity": "sha512-rRYECOTh5V3iWsrOzXi7h1jp3Bi9OkJHrb3wECi3DVqMGTilo9wAYmCbT+6cGdrzUY3MWcAa2mESM6FMik6tVw==", + "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==", + "license": "MIT", "dependencies": { - "@typescript-eslint/utils": "5.54.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "@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": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "@babel/core": "^7.0.0" } }, - "node_modules/@typescript-eslint/parser": { - "version": "5.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.54.0.tgz", - "integrity": "sha512-aAVL3Mu2qTi+h/r04WI/5PfNWvO6pdhpeMRWk9R7rEV4mwJNzoWf5CCU5vDKBsPIFQFjEq1xg7XBI2rjiMXQbQ==", + "node_modules/babel-preset-jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz", + "integrity": "sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag==", + "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "5.54.0", - "@typescript-eslint/types": "5.54.0", - "@typescript-eslint/typescript-estree": "5.54.0", - "debug": "^4.3.4" + "babel-plugin-jest-hoist": "^27.5.1", + "babel-preset-current-node-syntax": "^1.0.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "@babel/core": "^7.0.0" } }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "5.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.54.0.tgz", - "integrity": "sha512-VTPYNZ7vaWtYna9M4oD42zENOBrb+ZYyCNdFs949GcN8Miwn37b8b7eMj+EZaq7VK9fx0Jd+JhmkhjFhvnovhg==", + "node_modules/babel-preset-react-app": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-react-app/-/babel-preset-react-app-10.0.1.tgz", + "integrity": "sha512-b0D9IZ1WhhCWkrTXyFuIIgqGzSkRIH5D5AmB0bXbzYAB1OBAwHcUeyWW2LorutLWF5btNo/N7r/cIdmvvKJlYg==", + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "5.54.0", - "@typescript-eslint/visitor-keys": "5.54.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "@babel/core": "^7.16.0", + "@babel/plugin-proposal-class-properties": "^7.16.0", + "@babel/plugin-proposal-decorators": "^7.16.4", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.0", + "@babel/plugin-proposal-numeric-separator": "^7.16.0", + "@babel/plugin-proposal-optional-chaining": "^7.16.0", + "@babel/plugin-proposal-private-methods": "^7.16.0", + "@babel/plugin-transform-flow-strip-types": "^7.16.0", + "@babel/plugin-transform-react-display-name": "^7.16.0", + "@babel/plugin-transform-runtime": "^7.16.4", + "@babel/preset-env": "^7.16.4", + "@babel/preset-react": "^7.16.0", + "@babel/preset-typescript": "^7.16.0", + "@babel/runtime": "^7.16.3", + "babel-plugin-macros": "^3.1.0", + "babel-plugin-transform-react-remove-prop-types": "^0.4.24" } }, - "node_modules/@typescript-eslint/type-utils": { - "version": "5.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.54.0.tgz", - "integrity": "sha512-WI+WMJ8+oS+LyflqsD4nlXMsVdzTMYTxl16myXPaCXnSgc7LWwMsjxQFZCK/rVmTZ3FN71Ct78ehO9bRC7erYQ==", + "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==", + "license": "MIT" + }, + "node_modules/batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", + "license": "MIT" + }, + "node_modules/bfj": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/bfj/-/bfj-7.1.0.tgz", + "integrity": "sha512-I6MMLkn+anzNdCUp9hMRyui1HaNEUCco50lxbvNS4+EyXg8lN3nJ48PjPWtbH8UVS9CuMoaKE9U2V3l29DaRQw==", + "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "5.54.0", - "@typescript-eslint/utils": "5.54.0", - "debug": "^4.3.4", - "tsutils": "^3.21.0" + "bluebird": "^3.7.2", + "check-types": "^11.2.3", + "hoopy": "^0.1.4", + "jsonpath": "^1.1.1", + "tryer": "^1.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "*" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "node": ">= 8.0.0" } }, - "node_modules/@typescript-eslint/types": { - "version": "5.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.54.0.tgz", - "integrity": "sha512-nExy+fDCBEgqblasfeE3aQ3NuafBUxZxgxXcYfzYRZFHdVvk5q60KhCSkG0noHgHRo/xQ/BOzURLZAafFpTkmQ==", + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "license": "MIT", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": "*" } }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.54.0.tgz", - "integrity": "sha512-X2rJG97Wj/VRo5YxJ8Qx26Zqf0RRKsVHd4sav8NElhbZzhpBI8jU54i6hfo9eheumj4oO4dcRN1B/zIVEqR/MQ==", - "dependencies": { - "@typescript-eslint/types": "5.54.0", - "@typescript-eslint/visitor-keys": "5.54.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "license": "MIT", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=8" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "license": "MIT" + }, + "node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "license": "MIT", "dependencies": { - "yallist": "^4.0.0" + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" }, "engines": { - "node": ">=10" + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" + "ms": "2.0.0" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/@typescript-eslint/utils": { - "version": "5.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.54.0.tgz", - "integrity": "sha512-cuwm8D/Z/7AuyAeJ+T0r4WZmlnlxQ8wt7C7fLpFlKMR+dY6QO79Cq1WpJhvZbMA4ZeZGHiRWnht7ZJ8qkdAunw==", + "node_modules/body-parser/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", "dependencies": { - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.54.0", - "@typescript-eslint/types": "5.54.0", - "@typescript-eslint/typescript-estree": "5.54.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0", - "semver": "^7.3.7" + "safer-buffer": ">= 2.1.2 < 3" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "node": ">=0.10.0" } }, - "node_modules/@typescript-eslint/utils/node_modules/eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/bonjour-service": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.2.1.tgz", + "integrity": "sha512-oSzCS2zV14bh2kji6vNe7vrpJYCHGvcZnlffFQ1MEoX/WOeQ/teD8SYWKR942OI3INjq8OMNJlbPK5LLLUxFDw==", + "license": "MIT", "dependencies": { - "eslint-visitor-keys": "^2.0.0" - }, - "engines": { - "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "license": "ISC" + }, + "node_modules/bootstrap": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.3.tgz", + "integrity": "sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ], + "license": "MIT", "peerDependencies": { - "eslint": ">=5" + "@popperjs/core": "^2.11.8" } }, - "node_modules/@typescript-eslint/utils/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==", - "engines": { - "node": ">=10" + "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==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/@typescript-eslint/utils/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "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==", + "license": "MIT", "dependencies": { - "yallist": "^4.0.0" + "fill-range": "^7.1.1" }, "engines": { - "node": ">=10" + "node": ">=8" } }, - "node_modules/@typescript-eslint/utils/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "node_modules/browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", + "license": "BSD-2-Clause" + }, + "node_modules/browserslist": { + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", + "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", + "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": { - "lru-cache": "^6.0.0" + "caniuse-lite": "^1.0.30001669", + "electron-to-chromium": "^1.5.41", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.1" }, "bin": { - "semver": "bin/semver.js" + "browserslist": "cli.js" }, "engines": { - "node": ">=10" + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/@typescript-eslint/utils/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.54.0.tgz", - "integrity": "sha512-xu4wT7aRCakGINTLGeyGqDn+78BwFlggwBjnHa1ar/KaGagnmwLYmlrXIrgAaQ3AE1Vd6nLfKASm7LrFHNbKGA==", + "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==", + "license": "Apache-2.0", "dependencies": { - "@typescript-eslint/types": "5.54.0", - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node-int64": "^0.4.0" } }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "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==", + "license": "MIT" + }, + "node_modules/builtin-modules": { "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "license": "MIT", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@webassemblyjs/ast": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz", - "integrity": "sha512-aJMfngIZ65+t71C3y2nBBg5FFG0Okt9m0XEgWZ7Ywgn1oMAT8cNwx00Uv1cQyHtidq0Xn94R4TAywO+LCQ+ZAQ==", + "node_modules/builtins": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.1.0.tgz", + "integrity": "sha512-SW9lzGTLvWTP1AY8xeAMZimqDrIaSdLQUcVr9DMef51niJ022Ri87SwRRKYm4A6iHfkPaiVUu/Duw2Wc4J7kKg==", + "dev": true, + "license": "MIT", + "peer": true, "dependencies": { - "@webassemblyjs/helper-module-context": "1.8.5", - "@webassemblyjs/helper-wasm-bytecode": "1.8.5", - "@webassemblyjs/wast-parser": "1.8.5" + "semver": "^7.0.0" } }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.8.5.tgz", - "integrity": "sha512-9p+79WHru1oqBh9ewP9zW95E3XAo+90oth7S5Re3eQnECGq59ly1Ri5tsIipKGpiStHsUYmY3zMLqtk3gTcOtQ==" - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.8.5.tgz", - "integrity": "sha512-Za/tnzsvnqdaSPOUXHyKJ2XI7PDX64kWtURyGiJJZKVEdFOsdKUCPTNEVFZq3zJ2R0G5wc2PZ5gvdTRFgm81zA==" - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.8.5.tgz", - "integrity": "sha512-Ri2R8nOS0U6G49Q86goFIPNgjyl6+oE1abW1pS84BuhP1Qcr5JqMwRFT3Ah3ADDDYGEgGs1iyb1DGX+kAi/c/Q==" - }, - "node_modules/@webassemblyjs/helper-code-frame": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.8.5.tgz", - "integrity": "sha512-VQAadSubZIhNpH46IR3yWO4kZZjMxN1opDrzePLdVKAZ+DFjkGD/rf4v1jap744uPVU6yjL/smZbRIIJTOUnKQ==", + "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==", + "devOptional": true, + "license": "MIT", "dependencies": { - "@webassemblyjs/wast-printer": "1.8.5" + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@webassemblyjs/helper-fsm": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.8.5.tgz", - "integrity": "sha512-kRuX/saORcg8se/ft6Q2UbRpZwP4y7YrWsLXPbbmtepKr22i8Z4O3V5QE9DbZK908dh5Xya4Un57SDIKwB9eow==" - }, - "node_modules/@webassemblyjs/helper-module-context": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.8.5.tgz", - "integrity": "sha512-/O1B236mN7UNEU4t9X7Pj38i4VoU8CcMHyy3l2cV/kIF4U5KoHXDVqcDuOs1ltkac90IM4vZdHc52t1x8Yfs3g==", - "dependencies": { - "@webassemblyjs/ast": "1.8.5", - "mamacro": "^0.0.3" + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" } }, - "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", - "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "license": "MIT", "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@xtuc/long": "4.2.2" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@webassemblyjs/helper-numbers/node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", - "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==" - }, - "node_modules/@webassemblyjs/helper-numbers/node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", - "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==" - }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.8.5.tgz", - "integrity": "sha512-Cu4YMYG3Ddl72CbmpjU/wbP6SACcOPVbHN1dI4VJNJVgFwaKf1ppeFJrwydOG3NDHxVGuCfPlLZNyEdIYlQ6QQ==" - }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.8.5.tgz", - "integrity": "sha512-VV083zwR+VTrIWWtgIUpqfvVdK4ff38loRmrdDBgBT8ADXYsEZ5mPQ4Nde90N3UYatHdYoDIFb7oHzMncI02tA==", - "dependencies": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-buffer": "1.8.5", - "@webassemblyjs/helper-wasm-bytecode": "1.8.5", - "@webassemblyjs/wasm-gen": "1.8.5" + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "license": "MIT", + "engines": { + "node": ">=6" } }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.8.5.tgz", - "integrity": "sha512-aaCvQYrvKbY/n6wKHb/ylAJr27GglahUO89CcGXMItrOBqRarUMxWLJgxm9PJNuKULwN5n1csT9bYoMeZOGF3g==", + "node_modules/camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "license": "MIT", "dependencies": { - "@xtuc/ieee754": "^1.2.0" + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" } }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.8.5.tgz", - "integrity": "sha512-plYUuUwleLIziknvlP8VpTgO4kqNaH57Y3JnNa6DLpu/sGcP6hbVdfdX5aHAV716pQBKrfuU26BJK29qY37J7A==", - "dependencies": { - "@xtuc/long": "4.2.2" + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.8.5.tgz", - "integrity": "sha512-U7zgftmQriw37tfD934UNInokz6yTmn29inT2cAetAsaU9YeVCveWEwhKL1Mg4yS7q//NGdzy79nlXh3bT8Kjw==" - }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.8.5.tgz", - "integrity": "sha512-A41EMy8MWw5yvqj7MQzkDjU29K7UJq1VrX2vWLzfpRHt3ISftOXqrtojn7nlPsZ9Ijhp5NwuODuycSvfAO/26Q==", - "dependencies": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-buffer": "1.8.5", - "@webassemblyjs/helper-wasm-bytecode": "1.8.5", - "@webassemblyjs/helper-wasm-section": "1.8.5", - "@webassemblyjs/wasm-gen": "1.8.5", - "@webassemblyjs/wasm-opt": "1.8.5", - "@webassemblyjs/wasm-parser": "1.8.5", - "@webassemblyjs/wast-printer": "1.8.5" + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "license": "MIT", + "engines": { + "node": ">= 6" } }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.8.5.tgz", - "integrity": "sha512-BCZBT0LURC0CXDzj5FXSc2FPTsxwp3nWcqXQdOZE4U7h7i8FqtFK5Egia6f9raQLpEKT1VL7zr4r3+QX6zArWg==", + "node_modules/caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-wasm-bytecode": "1.8.5", - "@webassemblyjs/ieee754": "1.8.5", - "@webassemblyjs/leb128": "1.8.5", - "@webassemblyjs/utf8": "1.8.5" + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" } }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.8.5.tgz", - "integrity": "sha512-HKo2mO/Uh9A6ojzu7cjslGaHaUU14LdLbGEKqTR7PBKwT6LdPtLLh9fPY33rmr5wcOMrsWDbbdCHq4hQUdd37Q==", - "dependencies": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-buffer": "1.8.5", - "@webassemblyjs/wasm-gen": "1.8.5", - "@webassemblyjs/wasm-parser": "1.8.5" - } + "node_modules/caniuse-lite": { + "version": "1.0.30001679", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001679.tgz", + "integrity": "sha512-j2YqID/YwpLnKzCmBOS4tlZdWprXm3ZmQLBH9ZBXFOhoxLA46fwyBvx6toCBWBmnuwUY/qB3kEU6gFx8qgCroA==", + "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/@webassemblyjs/wasm-parser": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.8.5.tgz", - "integrity": "sha512-pi0SYE9T6tfcMkthwcgCpL0cM9nRYr6/6fjgDtL6q/ZqKHdMWvxitRi5JcZ7RI4SNJJYnYNaWy5UUrHQy998lw==", - "dependencies": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-api-error": "1.8.5", - "@webassemblyjs/helper-wasm-bytecode": "1.8.5", - "@webassemblyjs/ieee754": "1.8.5", - "@webassemblyjs/leb128": "1.8.5", - "@webassemblyjs/utf8": "1.8.5" - } - }, - "node_modules/@webassemblyjs/wast-parser": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.8.5.tgz", - "integrity": "sha512-daXC1FyKWHF1i11obK086QRlsMsY4+tIOKgBqI1lxAnkp9xe9YMcgOxm9kLe+ttjs5aWV2KKE1TWJCN57/Btsg==", - "dependencies": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/floating-point-hex-parser": "1.8.5", - "@webassemblyjs/helper-api-error": "1.8.5", - "@webassemblyjs/helper-code-frame": "1.8.5", - "@webassemblyjs/helper-fsm": "1.8.5", - "@xtuc/long": "4.2.2" + "node_modules/case-sensitive-paths-webpack-plugin": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz", + "integrity": "sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==", + "license": "MIT", + "engines": { + "node": ">=4" } }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.8.5.tgz", - "integrity": "sha512-w0U0pD4EhlnvRyeJzBqaVSJAo9w/ce7/WPogeXLzGkO6hzhr4GnQIZ4W4uUt5b9ooAaXPtnXlj0gzsXEOUNYMg==", + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/wast-parser": "1.8.5", - "@xtuc/long": "4.2.2" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" - }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" - }, - "node_modules/abab": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", - "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==" + "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==", + "license": "MIT", + "engines": { + "node": ">=10" + } }, - "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + "node_modules/check-types": { + "version": "11.2.3", + "resolved": "https://registry.npmjs.org/check-types/-/check-types-11.2.3.tgz", + "integrity": "sha512-+67P1GkJRaxQD6PKK0Et9DhwQB+vGg3PM5+aavopCpZT1lj9jeqfvpgTLAWErNj8qApkkmXlu/Ug74kmhagkXg==", + "license": "MIT" }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "license": "MIT", "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" }, "engines": { - "node": ">= 0.6" + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" } }, - "node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "bin": { - "acorn": "bin/acorn" + "node_modules/chokidar/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==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" }, "engines": { - "node": ">=0.4.0" + "node": ">= 6" } }, - "node_modules/acorn-globals": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", - "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", - "dependencies": { - "acorn": "^7.1.1", - "acorn-walk": "^7.1.1" + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "license": "MIT", + "engines": { + "node": ">=6.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==", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" } }, - "node_modules/acorn-node": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", - "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", - "dependencies": { - "acorn": "^7.0.0", - "acorn-walk": "^7.0.0", - "xtend": "^4.0.2" - } + "node_modules/cjs-module-lexer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz", + "integrity": "sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==", + "license": "MIT" }, - "node_modules/acorn-walk": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", - "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", + "license": "MIT" + }, + "node_modules/clean-css": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", + "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==", + "license": "MIT", + "dependencies": { + "source-map": "~0.6.0" + }, "engines": { - "node": ">=0.4.0" + "node": ">= 10.0" } }, - "node_modules/address": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/address/-/address-1.2.2.tgz", - "integrity": "sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==", + "node_modules/clean-css/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==", + "license": "BSD-3-Clause", "engines": { - "node": ">= 10.0.0" + "node": ">=0.10.0" } }, - "node_modules/adjust-sourcemap-loader": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", - "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "license": "ISC", "dependencies": { - "loader-utils": "^2.0.0", - "regex-parser": "^2.2.11" - }, - "engines": { - "node": ">=8.9" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" } }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "license": "MIT", "dependencies": { - "debug": "4" + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" }, "engines": { - "node": ">= 6.0.0" + "node": ">=6" } }, - "node_modules/agentkeepalive": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.2.1.tgz", - "integrity": "sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA==", - "dependencies": { - "debug": "^4.1.0", - "depd": "^1.1.2", - "humanize-ms": "^1.2.1" - }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", "engines": { - "node": ">= 8.0.0" + "node": ">=6" } }, - "node_modules/agentkeepalive/node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "license": "MIT", "engines": { - "node": ">= 0.6" + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" } }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "node_modules/coa": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", + "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", + "license": "MIT", "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" + "@types/q": "^1.5.1", + "chalk": "^2.4.1", + "q": "^1.1.2" }, "engines": { - "node": ">=8" + "node": ">= 4.0" } }, - "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/coa/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==", + "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" + "color-convert": "^1.9.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-errors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", - "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", - "peerDependencies": { - "ajv": ">=5.0.0" + "engines": { + "node": ">=4" } }, - "node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "node_modules/coa/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==", + "license": "MIT", "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } + "engines": { + "node": ">=4" } }, - "node_modules/ajv-formats/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "node_modules/coa/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==", + "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "color-name": "1.1.3" } }, - "node_modules/ajv-formats/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + "node_modules/coa/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==", + "license": "MIT" }, - "node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "peerDependencies": { - "ajv": "^6.9.1" + "node_modules/coa/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==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" } }, - "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==", + "node_modules/coa/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==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/coa/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==", + "license": "MIT", "dependencies": { - "type-fest": "^0.21.3" + "has-flag": "^3.0.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=4" } }, - "node_modules/ansi-html-community": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", - "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", - "engines": [ - "node >= 0.8.0" - ], - "bin": { - "ansi-html": "bin/ansi-html" - } + "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==", + "license": "MIT" }, - "node_modules/ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, "engines": { - "node": ">=6" + "node": ">=7.0.0" } }, - "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/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/colord": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", + "license": "MIT" + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "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==", + "license": "MIT", "dependencies": { - "color-convert": "^1.9.0" + "delayed-stream": "~1.0.0" }, "engines": { - "node": ">=4" + "node": ">= 0.8" } }, - "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==", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, + "node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "license": "MIT", "engines": { - "node": ">= 8" + "node": ">= 12" } }, - "node_modules/aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" + "node_modules/common-path-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", + "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==", + "dev": true, + "license": "ISC" }, - "node_modules/are-we-there-yet": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", - "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", + "node_modules/common-tags": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", + "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "license": "MIT" + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "license": "MIT", "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" + "mime-db": ">= 1.43.0 < 2" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">= 0.6" } }, - "node_modules/are-we-there-yet/node_modules/readable-stream": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.1.tgz", - "integrity": "sha512-+rQmrWMYGA90yenhTYsLWAsLsqVC8osOw6PKE1HDYiO0gdPeKe/xDHNzIAIn4C91YQ6oenEhfYqqc1883qHbjQ==", + "node_modules/compression": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.5.tgz", + "integrity": "sha512-bQJ0YRck5ak3LgtnpKkiabX5pNF7tMUh1BSy2ZBOTh0Dim0BUu6aPPwByIns6/A5Prh8PufSPerMDUklpzes2Q==", + "license": "MIT", "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "bytes": "3.1.2", + "compressible": "~2.0.18", + "debug": "2.6.9", + "negotiator": "~0.6.4", + "on-headers": "~1.0.2", + "safe-buffer": "5.2.1", + "vary": "~1.1.2" }, "engines": { - "node": ">= 6" + "node": ">= 0.8.0" } }, - "node_modules/are-we-there-yet/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", "dependencies": { - "safe-buffer": "~5.2.0" + "ms": "2.0.0" } }, - "node_modules/arg": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dependencies": { - "sprintf-js": "~1.0.2" - } + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "license": "MIT" }, - "node_modules/aria-query": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", - "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", - "dependencies": { - "deep-equal": "^2.0.5" + "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==", + "license": "MIT" + }, + "node_modules/connect-history-api-fallback": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", + "license": "MIT", + "engines": { + "node": ">=0.8" } }, - "node_modules/aria-query/node_modules/deep-equal": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.0.tgz", - "integrity": "sha512-RdpzE0Hv4lhowpIUKKMJfeH6C1pXdtT1/it80ubgWqwI3qpuxUBpC1S4hnHg+zjnuOoDkzUtUCEEkG+XG5l3Mw==", + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "es-get-iterator": "^1.1.2", - "get-intrinsic": "^1.1.3", - "is-arguments": "^1.1.1", - "is-array-buffer": "^3.0.1", - "is-date-object": "^1.0.5", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "isarray": "^2.0.5", - "object-is": "^1.1.5", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.4.3", - "side-channel": "^1.0.4", - "which-boxed-primitive": "^1.0.2", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.9" + "safe-buffer": "5.2.1" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/aria-query/node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" - }, - "node_modules/arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", "engines": { - "node": ">=0.10.0" + "node": ">= 0.6" } }, - "node_modules/arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 0.6" } }, - "node_modules/arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", + "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==", + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 0.6" } }, - "node_modules/array-flatten": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", - "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==" + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "license": "MIT" }, - "node_modules/array-includes": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", - "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==", + "node_modules/core-js": { + "version": "3.39.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.39.0.tgz", + "integrity": "sha512-raM0ew0/jJUqkJ0E6e8UDtl+y/7ktFivgWvqw8dNSQeNWoSDLvQ1H/RN3aPXB9tBd4/FhyR4RDPGhsNIMsAn7g==", + "hasInstallScript": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-compat": { + "version": "3.39.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.39.0.tgz", + "integrity": "sha512-VgEUx3VwlExr5no0tXlBt+silBvhTryPwCXRI2Id1PN8WTKu7MreethvddqOubrYxkFdv/RnYrqlv1sFNAUelw==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "get-intrinsic": "^1.1.3", - "is-string": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" + "browserslist": "^4.24.2" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/core-js" } }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "engines": { - "node": ">=8" + "node_modules/core-js-pure": { + "version": "3.39.0", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.39.0.tgz", + "integrity": "sha512-7fEcWwKI4rJinnK+wLTezeg2smbFFdSBP6E2kQZNbnzM2s1rpKQ6aaRteZSSg7FLU3P0HGGVo/gbpfanU36urg==", + "hasInstallScript": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" } }, - "node_modules/array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==", - "engines": { - "node": ">=0.10.0" - } + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" }, - "node_modules/array.prototype.flat": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", - "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==", + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0" + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=10" } }, - "node_modules/array.prototype.flatmap": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz", - "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==", + "node_modules/cross-spawn": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.5.tgz", + "integrity": "sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 8" } }, - "node_modules/array.prototype.reduce": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.5.tgz", - "integrity": "sha512-kDdugMl7id9COE8R7MHF5jWk7Dqt/fs4Pv+JXoICnYwqpjjjbUurz6w5fT5IG6brLdJhv6/VoHB0H7oyIBXd+Q==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-array-method-boxes-properly": "^1.0.0", - "is-string": "^1.0.7" - }, + "node_modules/crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "license": "MIT", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/array.prototype.tosorted": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.1.tgz", - "integrity": "sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ==", + "node_modules/css-blank-pseudo": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-3.0.3.tgz", + "integrity": "sha512-VS90XWtsHGqoM0t4KpH053c4ehxZ2E6HtGI7x68YFV0pTo/QmkV/YFA+NnlvK8guxZVNWGQhVNJGC39Q8XF4OQ==", + "license": "CC0-1.0", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0", - "get-intrinsic": "^1.1.3" - } - }, - "node_modules/arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "postcss-selector-parser": "^6.0.9" + }, + "bin": { + "css-blank-pseudo": "dist/cli.cjs" + }, "engines": { - "node": ">=0.10.0" + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" } }, - "node_modules/asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" - }, - "node_modules/asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "optional": true, - "peer": true, - "dependencies": { - "safer-buffer": "~2.1.0" + "node_modules/css-declaration-sorter": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz", + "integrity": "sha512-rtdthzxKuyq6IzqX6jEcIzQF/YqccluefyCYheovBOLhFT/drQA9zj/UbRAa9J7C0o6EG6u3E6g+vKkay7/k3g==", + "license": "ISC", + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.0.9" } }, - "node_modules/asn1.js": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", - "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "node_modules/css-has-pseudo": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-3.0.4.tgz", + "integrity": "sha512-Vse0xpR1K9MNlp2j5w1pgWIJtm1a8qS0JwS9goFYcImjlHEmywP9VUF05aGBXzGpDJF86QXk4L0ypBmwPhGArw==", + "license": "CC0-1.0", "dependencies": { - "bn.js": "^4.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "safer-buffer": "^2.1.0" + "postcss-selector-parser": "^6.0.9" + }, + "bin": { + "css-has-pseudo": "dist/cli.cjs" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" } }, - "node_modules/asn1.js/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" - }, - "node_modules/assert": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", - "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", + "node_modules/css-loader": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.2.tgz", + "integrity": "sha512-6WvYYn7l/XEGN8Xu2vWFt9nVzrCn39vKyTEFf/ExEyoksJjjSZV/0/35XPlMbpnr6VGhZIUg5yJrL8tGfes/FA==", + "dev": true, + "license": "MIT", "dependencies": { - "object-assign": "^4.1.1", - "util": "0.10.3" - } - }, - "node_modules/assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", - "optional": true, - "peer": true, + "icss-utils": "^5.1.0", + "postcss": "^8.4.33", + "postcss-modules-extract-imports": "^3.1.0", + "postcss-modules-local-by-default": "^4.0.5", + "postcss-modules-scope": "^3.2.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.5.4" + }, "engines": { - "node": ">=0.8" + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "webpack": "^5.27.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } } }, - "node_modules/assert/node_modules/inherits": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "integrity": "sha512-8nWq2nLTAwd02jTqJExUYFSD/fKq6VH9Y/oG2accc/kdI0V98Bag8d5a4gi3XHz73rDWa2PvTtvcWYquKqSENA==" - }, - "node_modules/assert/node_modules/util": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", - "integrity": "sha512-5KiHfsmkqacuKjkRkdV7SsfDJ2EGiPsK92s2MhNSY0craxjTdKTtqKsJaCWp4LW33ZZ0OPUv1WO/TFvNQRiQxQ==", + "node_modules/css-minimizer-webpack-plugin": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.4.1.tgz", + "integrity": "sha512-1u6D71zeIfgngN2XNRJefc/hY7Ybsxd74Jm4qngIXyUEk7fss3VUzuHxLAq/R8NAba4QU9OUSaMZlbpRc7bM4Q==", + "license": "MIT", "dependencies": { - "inherits": "2.0.1" - } - }, - "node_modules/assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==", + "cssnano": "^5.0.6", + "jest-worker": "^27.0.2", + "postcss": "^8.3.5", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0", + "source-map": "^0.6.1" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@parcel/css": { + "optional": true + }, + "clean-css": { + "optional": true + }, + "csso": { + "optional": true + }, + "esbuild": { + "optional": true + } } }, - "node_modules/ast-types": { - "version": "0.12.4", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.12.4.tgz", - "integrity": "sha512-ky/YVYCbtVAS8TdMIaTiPFHwEpRB5z1hctepJplTr3UW5q8TDrpIMCILyk8pmLxGtn2KCtC/lSn7zOsaI7nzDw==", - "dev": true, + "node_modules/css-minimizer-webpack-plugin/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==", + "license": "BSD-3-Clause", "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, - "node_modules/ast-types-flow": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", - "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==" - }, - "node_modules/astral-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "node_modules/css-prefers-color-scheme": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz", + "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==", + "license": "CC0-1.0", + "bin": { + "css-prefers-color-scheme": "dist/cli.cjs" + }, "engines": { - "node": ">=4" + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" } }, - "node_modules/async": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", - "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", - "dev": true, + "node_modules/css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "license": "BSD-2-Clause", "dependencies": { - "lodash": "^4.17.14" - } - }, - "node_modules/async-each": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.6.tgz", - "integrity": "sha512-c646jH1avxr+aVpndVMeAfYw7wAa6idufrlN3LPA4PmKS0QEGp6PIC9nwz0WQkkvBGAMEki3pFdtxaF39J9vvg==", - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "optional": true - }, - "node_modules/async-foreach": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz", - "integrity": "sha512-VUeSMD8nEGBWaZK4lizI1sf3yEC7pnAQ/mrI7pC2fBz2s/tq5jWWEngTwaf0Gruu/OoXRGLGg1XFqpYBiGTYJA==", - "engines": { - "node": "*" + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" } }, - "node_modules/async-limiter": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", - "devOptional": true - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + "node_modules/css-select-base-adapter": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", + "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==", + "license": "MIT" }, - "node_modules/at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "node_modules/css-tree": { + "version": "1.0.0-alpha.37", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", + "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.4", + "source-map": "^0.6.1" + }, "engines": { - "node": ">= 4.0.0" + "node": ">=8.0.0" } }, - "node_modules/atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "bin": { - "atob": "bin/atob.js" - }, + "node_modules/css-tree/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==", + "license": "BSD-3-Clause", "engines": { - "node": ">= 4.5.0" + "node": ">=0.10.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/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "license": "BSD-2-Clause", "engines": { - "node": ">= 0.4" + "node": ">= 6" }, "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", - "optional": true, - "peer": true, - "engines": { - "node": "*" + "url": "https://github.com/sponsors/fb55" } }, - "node_modules/aws4": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", - "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==", - "optional": true, - "peer": true + "node_modules/cssdb": { + "version": "7.11.2", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.11.2.tgz", + "integrity": "sha512-lhQ32TFkc1X4eTefGfYPvgovRSzIMofHkigfH8nWtyRL4XJLsRhJFreRvEgKzept7x1rjBuy3J/MurXLaFxW/A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + } + ], + "license": "CC0-1.0" }, - "node_modules/axe-core": { - "version": "4.6.3", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.6.3.tgz", - "integrity": "sha512-/BQzOX780JhsxDnPpH4ZiyrJAzcd8AfzFPkv+89veFSr1rcMjuq2JDCwypKaPeB6ljHp9KjXhPpjgCvQlWYuqg==", + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, "engines": { "node": ">=4" } }, - "node_modules/axobject-query": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz", - "integrity": "sha512-goKlv8DZrK9hUh975fnHzhNIO4jUnFCfv/dszV5VwUGDFjI6vQ2VwoyjYjYNEbBE8AH87TduWP5uyDR1D+Iteg==", - "dependencies": { - "deep-equal": "^2.0.5" - } - }, - "node_modules/axobject-query/node_modules/deep-equal": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.0.tgz", - "integrity": "sha512-RdpzE0Hv4lhowpIUKKMJfeH6C1pXdtT1/it80ubgWqwI3qpuxUBpC1S4hnHg+zjnuOoDkzUtUCEEkG+XG5l3Mw==", + "node_modules/cssnano": { + "version": "5.1.15", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.15.tgz", + "integrity": "sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "es-get-iterator": "^1.1.2", - "get-intrinsic": "^1.1.3", - "is-arguments": "^1.1.1", - "is-array-buffer": "^3.0.1", - "is-date-object": "^1.0.5", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "isarray": "^2.0.5", - "object-is": "^1.1.5", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.4.3", - "side-channel": "^1.0.4", - "which-boxed-primitive": "^1.0.2", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.9" + "cssnano-preset-default": "^5.2.14", + "lilconfig": "^2.0.3", + "yaml": "^1.10.2" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/cssnano" + }, + "peerDependencies": { + "postcss": "^8.2.15" } }, - "node_modules/axobject-query/node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" - }, - "node_modules/babel-eslint": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz", - "integrity": "sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==", - "deprecated": "babel-eslint is now @babel/eslint-parser. This package will no longer receive updates.", - "dev": true, + "node_modules/cssnano-preset-default": { + "version": "5.2.14", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.14.tgz", + "integrity": "sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A==", + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.0", - "@babel/traverse": "^7.7.0", - "@babel/types": "^7.7.0", - "eslint-visitor-keys": "^1.0.0", - "resolve": "^1.12.0" + "css-declaration-sorter": "^6.3.1", + "cssnano-utils": "^3.1.0", + "postcss-calc": "^8.2.3", + "postcss-colormin": "^5.3.1", + "postcss-convert-values": "^5.1.3", + "postcss-discard-comments": "^5.1.2", + "postcss-discard-duplicates": "^5.1.0", + "postcss-discard-empty": "^5.1.1", + "postcss-discard-overridden": "^5.1.0", + "postcss-merge-longhand": "^5.1.7", + "postcss-merge-rules": "^5.1.4", + "postcss-minify-font-values": "^5.1.0", + "postcss-minify-gradients": "^5.1.1", + "postcss-minify-params": "^5.1.4", + "postcss-minify-selectors": "^5.2.1", + "postcss-normalize-charset": "^5.1.0", + "postcss-normalize-display-values": "^5.1.0", + "postcss-normalize-positions": "^5.1.1", + "postcss-normalize-repeat-style": "^5.1.1", + "postcss-normalize-string": "^5.1.0", + "postcss-normalize-timing-functions": "^5.1.0", + "postcss-normalize-unicode": "^5.1.1", + "postcss-normalize-url": "^5.1.0", + "postcss-normalize-whitespace": "^5.1.1", + "postcss-ordered-values": "^5.1.3", + "postcss-reduce-initial": "^5.1.2", + "postcss-reduce-transforms": "^5.1.0", + "postcss-svgo": "^5.1.0", + "postcss-unique-selectors": "^5.1.1" }, "engines": { - "node": ">=6" + "node": "^10 || ^12 || >=14.0" }, "peerDependencies": { - "eslint": ">= 4.12.1" + "postcss": "^8.2.15" } }, - "node_modules/babel-jest": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz", - "integrity": "sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg==", - "dependencies": { - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^27.5.1", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, + "node_modules/cssnano-utils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz", + "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==", + "license": "MIT", "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^10 || ^12 || >=14.0" }, "peerDependencies": { - "@babel/core": "^7.8.0" + "postcss": "^8.2.15" } }, - "node_modules/babel-jest/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "node_modules/csso": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "license": "MIT", "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" + "css-tree": "^1.1.2" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">=8.0.0" } }, - "node_modules/babel-jest/node_modules/@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", + "node_modules/csso/node_modules/css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "license": "MIT", "dependencies": { - "@types/yargs-parser": "*" + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" } }, - "node_modules/babel-jest/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/csso/node_modules/mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", + "license": "CC0-1.0" + }, + "node_modules/csso/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==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cssom": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", + "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", + "license": "MIT" + }, + "node_modules/cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "cssom": "~0.3.6" }, "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/babel-jest/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/cssstyle/node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "license": "MIT" + }, + "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/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "internmap": "1 - 2" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=12" } }, - "node_modules/babel-jest/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==", - "dependencies": { - "color-name": "~1.1.4" - }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", "engines": { - "node": ">=7.0.0" + "node": ">=12" } }, - "node_modules/babel-jest/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==" + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } }, - "node_modules/babel-jest/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==", + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "license": "ISC", "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/babel-jest/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/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", "dependencies": { - "has-flag": "^4.0.0" + "d3-color": "1 - 3" }, "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/babel-loader": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.3.0.tgz", - "integrity": "sha512-H8SvsMF+m9t15HNLMipppzkC+Y2Yq+v3SonZyU70RBL/h1gxPkH08Ot8pEE9Z4Kd+czyWJClmFS8qzIP9OZ04Q==", - "dependencies": { - "find-cache-dir": "^3.3.1", - "loader-utils": "^2.0.0", - "make-dir": "^3.1.0", - "schema-utils": "^2.6.5" - }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "license": "ISC", "engines": { - "node": ">= 8.9" - }, - "peerDependencies": { - "@babel/core": "^7.0.0", - "webpack": ">=2" + "node": ">=12" } }, - "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==", + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", "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" + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" }, "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/babel-plugin-jest-hoist": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz", - "integrity": "sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ==", + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "license": "ISC", "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.0.0", - "@types/babel__traverse": "^7.0.6" + "d3-path": "^3.1.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">=12" } }, - "node_modules/babel-plugin-macros": { + "node_modules/d3-time": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", - "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", "dependencies": { - "@babel/runtime": "^7.12.5", - "cosmiconfig": "^7.0.0", - "resolve": "^1.19.0" + "d3-array": "2 - 3" }, "engines": { - "node": ">=10", - "npm": ">=6" - } - }, - "node_modules/babel-plugin-named-asset-import": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.8.tgz", - "integrity": "sha512-WXiAc++qo7XcJ1ZnTYGtLxmBCVbddAml3CEXgWaBzNzLNoxtQ8AiGEFDMOhot9XjTCQbvP5E77Fj9Gk924f00Q==", - "peerDependencies": { - "@babel/core": "^7.1.0" + "node": ">=12" } }, - "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", - "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", "dependencies": { - "@babel/compat-data": "^7.17.7", - "@babel/helper-define-polyfill-provider": "^0.3.3", - "semver": "^6.1.1" + "d3-time": "1 - 3" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=12" } }, - "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz", - "integrity": "sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.3.3", - "core-js-compat": "^3.25.1" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" } }, - "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz", - "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==", + "node_modules/damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", + "license": "BSD-2-Clause" + }, + "node_modules/data-urls": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", + "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", + "license": "MIT", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.3.3" + "abab": "^2.0.3", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=10" } }, - "node_modules/babel-plugin-transform-react-remove-prop-types": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz", - "integrity": "sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA==" - }, - "node_modules/babel-preset-current-node-syntax": { + "node_modules/data-view-buffer": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", + "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", + "license": "MIT", "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@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-top-level-await": "^7.8.3" + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/babel-preset-jest": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz", - "integrity": "sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag==", + "node_modules/data-view-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", + "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", + "license": "MIT", "dependencies": { - "babel-plugin-jest-hoist": "^27.5.1", - "babel-preset-current-node-syntax": "^1.0.0" + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">= 0.4" }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/babel-preset-react-app": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-react-app/-/babel-preset-react-app-10.0.1.tgz", - "integrity": "sha512-b0D9IZ1WhhCWkrTXyFuIIgqGzSkRIH5D5AmB0bXbzYAB1OBAwHcUeyWW2LorutLWF5btNo/N7r/cIdmvvKJlYg==", - "dependencies": { - "@babel/core": "^7.16.0", - "@babel/plugin-proposal-class-properties": "^7.16.0", - "@babel/plugin-proposal-decorators": "^7.16.4", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.0", - "@babel/plugin-proposal-numeric-separator": "^7.16.0", - "@babel/plugin-proposal-optional-chaining": "^7.16.0", - "@babel/plugin-proposal-private-methods": "^7.16.0", - "@babel/plugin-transform-flow-strip-types": "^7.16.0", - "@babel/plugin-transform-react-display-name": "^7.16.0", - "@babel/plugin-transform-runtime": "^7.16.4", - "@babel/preset-env": "^7.16.4", - "@babel/preset-react": "^7.16.0", - "@babel/preset-typescript": "^7.16.0", - "@babel/runtime": "^7.16.3", - "babel-plugin-macros": "^3.1.0", - "babel-plugin-transform-react-remove-prop-types": "^0.4.24" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "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==" - }, - "node_modules/base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "node_modules/data-view-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", + "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", + "license": "MIT", "dependencies": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/base/node_modules/define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", + "node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", "dependencies": { - "is-descriptor": "^1.0.0" + "ms": "^2.1.3" }, "engines": { - "node": ">=0.10.0" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] + "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==", + "license": "MIT" }, - "node_modules/batch": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", - "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==" + "node_modules/decimal.js-light": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==", + "license": "MIT" }, - "node_modules/bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", - "optional": true, - "peer": true, - "dependencies": { - "tweetnacl": "^0.14.3" + "node_modules/dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", + "license": "MIT" + }, + "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==", + "license": "MIT" + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" } }, - "node_modules/bfj": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/bfj/-/bfj-7.0.2.tgz", - "integrity": "sha512-+e/UqUzwmzJamNF50tBV6tZPTORow7gQ96iFow+8b562OdMpEK0BcJEq2OSPEDmAbSMBQ7PKZ87ubFkgxpYWgw==", + "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==", + "devOptional": true, + "license": "MIT", "dependencies": { - "bluebird": "^3.5.5", - "check-types": "^11.1.1", - "hoopy": "^0.1.4", - "tryer": "^1.0.1" + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" }, "engines": { - "node": ">= 8.0.0" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "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==", + "devOptional": true, + "license": "MIT", "engines": { - "node": "*" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "node_modules/default-gateway": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", + "license": "BSD-2-Clause", + "dependencies": { + "execa": "^5.0.0" + }, "engines": { - "node": ">=8" + "node": ">= 10" } }, - "node_modules/bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "optional": true, + "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==", + "license": "MIT", "dependencies": { - "file-uri-to-path": "1.0.0" + "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/bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" - }, - "node_modules/bn.js": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", - "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "node_modules/body-parser": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", - "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "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==", + "license": "MIT", "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.13.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" }, "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/body-parser/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" } }, - "node_modules/body-parser/node_modules/http-errors": { + "node_modules/depd": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", "engines": { "node": ">= 0.8" } }, - "node_modules/body-parser/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/body-parser/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", "engines": { - "node": ">= 0.8" - } - }, - "node_modules/bonjour-service": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.1.0.tgz", - "integrity": "sha512-LVRinRB3k1/K0XzZ2p58COnWvkQknIY6sf0zF2rpErvcJXpMBttEPQSxK+HEXSS9VmpZlDoDnQWv8ftJT20B0Q==", - "dependencies": { - "array-flatten": "^2.1.2", - "dns-equal": "^1.0.0", - "fast-deep-equal": "^3.1.3", - "multicast-dns": "^7.2.5" + "node": ">=6" } }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" - }, - "node_modules/bootstrap": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.2.3.tgz", - "integrity": "sha512-cEKPM+fwb3cT8NzQZYEu4HilJ3anCrWqh3CHAok1p9jXqMPsPTBhU25fBckEJHJ/p+tTxTFTsFQGM+gaHpi3QQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/twbs" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/bootstrap" - } - ], - "peerDependencies": { - "@popperjs/core": "^2.11.6" + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, - "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==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" } }, - "node_modules/braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dependencies": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, + "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==", + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/braces/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "license": "MIT" + }, + "node_modules/detect-port-alt": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz", + "integrity": "sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==", + "license": "MIT", "dependencies": { - "is-extendable": "^0.1.0" + "address": "^1.0.1", + "debug": "^2.6.0" + }, + "bin": { + "detect": "bin/detect-port", + "detect-port": "bin/detect-port" }, "engines": { - "node": ">=0.10.0" + "node": ">= 4.2.1" } }, - "node_modules/braces/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "engines": { - "node": ">=0.10.0" + "node_modules/detect-port-alt/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" } }, - "node_modules/brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==" + "node_modules/detect-port-alt/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" }, - "node_modules/browser-process-hrtime": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", - "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==" + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "license": "Apache-2.0" }, - "node_modules/browserify-aes": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", - "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", - "dependencies": { - "buffer-xor": "^1.0.3", - "cipher-base": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.3", - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "node_modules/diff-sequences": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", + "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", + "license": "MIT", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/browserify-cipher": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", - "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "license": "MIT", "dependencies": { - "browserify-aes": "^1.0.4", - "browserify-des": "^1.0.0", - "evp_bytestokey": "^1.0.0" + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/browserify-des": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", - "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", - "dependencies": { - "cipher-base": "^1.0.1", - "des.js": "^1.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "license": "MIT" }, - "node_modules/browserify-rsa": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", - "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", + "node_modules/dns-packet": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", + "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", + "license": "MIT", "dependencies": { - "bn.js": "^5.0.0", - "randombytes": "^2.0.1" + "@leichtgewicht/ip-codec": "^2.0.1" + }, + "engines": { + "node": ">=6" } }, - "node_modules/browserify-sign": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.2.tgz", - "integrity": "sha512-1rudGyeYY42Dk6texmv7c4VcQ0EsvVbLwZkA+AQB7SxvXxmcD93jcHie8bzecJ+ChDlmAm2Qyu0+Ccg5uhZXCg==", + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "license": "Apache-2.0", "dependencies": { - "bn.js": "^5.2.1", - "browserify-rsa": "^4.1.0", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "elliptic": "^6.5.4", - "inherits": "^2.0.4", - "parse-asn1": "^5.1.6", - "readable-stream": "^3.6.2", - "safe-buffer": "^5.2.1" + "esutils": "^2.0.2" }, "engines": { - "node": ">= 4" + "node": ">=6.0.0" } }, - "node_modules/browserify-sign/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "node_modules/dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "license": "MIT", "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" + "utila": "~0.4" } }, - "node_modules/browserify-sign/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", "dependencies": { - "safe-buffer": "~5.2.0" + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" } }, - "node_modules/browserify-zlib": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", - "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "license": "MIT", "dependencies": { - "pako": "~1.0.5" + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" } }, - "node_modules/browserslist": { - "version": "4.21.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", - "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", "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/fb55" } ], + "license": "BSD-2-Clause" + }, + "node_modules/domexception": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", + "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", + "deprecated": "Use your platform's native DOMException instead", + "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001449", - "electron-to-chromium": "^1.4.284", - "node-releases": "^2.0.8", - "update-browserslist-db": "^1.0.10" - }, - "bin": { - "browserslist": "cli.js" + "webidl-conversions": "^5.0.0" }, "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + "node": ">=8" } }, - "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==", - "dependencies": { - "node-int64": "^0.4.0" + "node_modules/domexception/node_modules/webidl-conversions": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=8" } }, - "node_modules/buffer": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", - "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "license": "BSD-2-Clause", "dependencies": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" - } - }, - "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==" - }, - "node_modules/buffer-xor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==" - }, - "node_modules/buffer/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, - "node_modules/builtin-modules": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", - "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "domelementtype": "^2.2.0" + }, "engines": { - "node": ">=6" + "node": ">= 4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/builtin-status-codes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", - "integrity": "sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==" - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "engines": { - "node": ">= 0.8" + "url": "https://github.com/fb55/domhandler?sponsor=1" } }, - "node_modules/cacache": { - "version": "13.0.1", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-13.0.1.tgz", - "integrity": "sha512-5ZvAxd05HDDU+y9BVvcqYu2LLXmPnQ0hW62h32g4xBTgL/MppR4/04NHfj/ycM2y6lmTnbw6HVi+1eN0Psba6w==", - "dev": true, + "node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "license": "BSD-2-Clause", "dependencies": { - "chownr": "^1.1.2", - "figgy-pudding": "^3.5.1", - "fs-minipass": "^2.0.0", - "glob": "^7.1.4", - "graceful-fs": "^4.2.2", - "infer-owner": "^1.0.4", - "lru-cache": "^5.1.1", - "minipass": "^3.0.0", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.2", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "p-map": "^3.0.0", - "promise-inflight": "^1.0.1", - "rimraf": "^2.7.1", - "ssri": "^7.0.0", - "unique-filename": "^1.1.1" + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" }, - "engines": { - "node": ">= 8" + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" } }, - "node_modules/cacache/node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dev": true, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "license": "MIT", "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" + "no-case": "^3.0.4", + "tslib": "^2.0.3" } }, - "node_modules/cacache/node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" + "node_modules/dotenv": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", + "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=10" } }, - "node_modules/cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "node_modules/dotenv-expand": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", + "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==", + "license": "BSD-2-Clause" + }, + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "license": "MIT" + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "license": "Apache-2.0", "dependencies": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/cache-content-type": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-content-type/-/cache-content-type-1.0.1.tgz", - "integrity": "sha512-IKufZ1o4Ut42YUrZSo8+qnMTrFuKkvyoLXUywKz9GJ5BrhOFGhLdkx9sG4KAnVvbY6kEcSFjLQul+DVmBm2bgA==", - "devOptional": true, - "dependencies": { - "mime-types": "^2.1.18", - "ylru": "^1.2.0" - }, - "engines": { - "node": ">= 6.0.0" - } + "node_modules/electron-to-chromium": { + "version": "1.5.55", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.55.tgz", + "integrity": "sha512-6maZ2ASDOTBtjt9FhqYPRnbvKU5tjG0IN9SztUOWYw2AzNDNpKJYLJmlK0/En4Hs/aiWnB+JZ+gW19PIGszgKg==", + "license": "ISC" }, - "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" - }, + "node_modules/emittery": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", + "integrity": "sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==", + "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sindresorhus/emittery?sponsor=1" } }, - "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/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "license": "MIT", "engines": { - "node": ">=6" + "node": ">= 4" } }, - "node_modules/camel-case": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", - "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", + "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", + "license": "MIT", "dependencies": { - "pascal-case": "^3.1.2", - "tslib": "^2.0.3" + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" } }, - "node_modules/camel-case/node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + "node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "license": "BSD-2-Clause", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, - "node_modules/camelcase-css": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", - "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "node_modules/envinfo": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.14.0.tgz", + "integrity": "sha512-CO40UI41xDQzhLB1hWyqUKgFhs250pNcGbyGKe1l/e4FSaI/+YE4IMG76GDt0In67WLPACIITC+sOi08x4wIvg==", + "dev": true, + "license": "MIT", + "bin": { + "envinfo": "dist/cli.js" + }, "engines": { - "node": ">= 6" + "node": ">=4" } }, - "node_modules/camelcase-keys": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", - "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "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==", + "license": "MIT", "dependencies": { - "camelcase": "^5.3.1", - "map-obj": "^4.0.0", - "quick-lru": "^4.0.1" + "is-arrayish": "^0.2.1" + } + }, + "node_modules/error-stack-parser": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", + "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", + "license": "MIT", + "dependencies": { + "stackframe": "^1.3.4" + } + }, + "node_modules/es-abstract": { + "version": "1.23.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", + "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "arraybuffer.prototype.slice": "^1.0.3", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "data-view-buffer": "^1.0.1", + "data-view-byte-length": "^1.0.1", + "data-view-byte-offset": "^1.0.0", + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.0.3", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.4", + "get-symbol-description": "^1.0.2", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.3", + "has-symbols": "^1.0.3", + "hasown": "^2.0.2", + "internal-slot": "^1.0.7", + "is-array-buffer": "^3.0.4", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.1", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.3", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.13", + "is-weakref": "^1.0.2", + "object-inspect": "^1.13.1", + "object-keys": "^1.1.1", + "object.assign": "^4.1.5", + "regexp.prototype.flags": "^1.5.2", + "safe-array-concat": "^1.1.2", + "safe-regex-test": "^1.0.3", + "string.prototype.trim": "^1.2.9", + "string.prototype.trimend": "^1.0.8", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.2", + "typed-array-byte-length": "^1.0.1", + "typed-array-byte-offset": "^1.0.2", + "typed-array-length": "^1.0.6", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.15" }, "engines": { - "node": ">=8" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/camelcase-keys/node_modules/quick-lru": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", - "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", - "engines": { - "node": ">=8" - } + "node_modules/es-array-method-boxes-properly": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", + "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", + "license": "MIT" }, - "node_modules/caniuse-api": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", - "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "license": "MIT", "dependencies": { - "browserslist": "^4.0.0", - "caniuse-lite": "^1.0.0", - "lodash.memoize": "^4.1.2", - "lodash.uniq": "^4.5.0" + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" } }, - "node_modules/caniuse-lite": { - "version": "1.0.30001458", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001458.tgz", - "integrity": "sha512-lQ1VlUUq5q9ro9X+5gOEyH7i3vm+AYVT1WDCVB69XOZ17KZRhnZ9J0Sqz7wTHQaLBJccNCHq8/Ww5LlOIZbB0w==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - } - ] + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } }, - "node_modules/case-sensitive-paths-webpack-plugin": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz", - "integrity": "sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==", + "node_modules/es-iterator-helpers": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.0.tgz", + "integrity": "sha512-tpxqxncxnpw3c93u8n3VOzACmRFoVmWJqbWXvX/JfKbkhBw1oslgPrUfeSt2psuqyEJFD6N/9lg5i7bsKpoq+Q==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.0.3", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "globalthis": "^1.0.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.3", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.7", + "iterator.prototype": "^1.1.3", + "safe-array-concat": "^1.1.2" + }, "engines": { - "node": ">=4" + "node": ">= 0.4" } }, - "node_modules/caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", - "optional": true, - "peer": true + "node_modules/es-module-lexer": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz", + "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==", + "license": "MIT" }, - "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/es-object-atoms": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "license": "MIT", "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "es-errors": "^1.3.0" }, "engines": { - "node": ">=4" + "node": ">= 0.4" } }, - "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==", + "node_modules/es-set-tostringtag": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", + "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.4", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.1" + }, "engines": { - "node": ">=10" + "node": ">= 0.4" } }, - "node_modules/chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" - }, - "node_modules/check-types": { - "version": "11.2.2", - "resolved": "https://registry.npmjs.org/check-types/-/check-types-11.2.2.tgz", - "integrity": "sha512-HBiYvXvn9Z70Z88XKjz3AEKd4HJhBXsa3j7xFnITAzoS8+q6eIGi8qDB8FKPBAjtuxjI/zFpwuiCb8oDtKOYrA==" + "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==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.0" + } }, - "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], + "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==", + "license": "MIT", "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" }, "engines": { - "node": ">= 8.10.0" + "node": ">= 0.4" }, - "optionalDependencies": { - "fsevents": "~2.3.2" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/chokidar/node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dependencies": { - "fill-range": "^7.0.1" - }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=6" } }, - "node_modules/chokidar/node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/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==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "license": "BSD-2-Clause", "dependencies": { - "to-regex-range": "^5.0.1" + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" }, "engines": { - "node": ">=8" + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" } }, - "node_modules/chokidar/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==", + "node_modules/escodegen/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==", + "license": "BSD-3-Clause", + "optional": true, "engines": { - "node": ">=0.12.0" + "node": ">=0.10.0" } }, - "node_modules/chokidar/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==", + "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.", + "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-compat-utils": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.5.1.tgz", + "integrity": "sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==", + "dev": true, + "license": "MIT", + "peer": true, "dependencies": { - "is-number": "^7.0.0" + "semver": "^7.5.4" }, "engines": { - "node": ">=8.0" + "node": ">=12" + }, + "peerDependencies": { + "eslint": ">=6.0.0" } }, - "node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + "node_modules/eslint-config-prettier": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", + "dev": true, + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } }, - "node_modules/chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "node_modules/eslint-config-react-app": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-7.0.1.tgz", + "integrity": "sha512-K6rNzvkIeHaTd8m/QEh1Zko0KI7BACWkkneSs6s9cKZC/J27X3eZR6Upt1jkmZ/4FK+XUOPPxMEN7+lbUXfSlA==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.16.0", + "@babel/eslint-parser": "^7.16.3", + "@rushstack/eslint-patch": "^1.1.0", + "@typescript-eslint/eslint-plugin": "^5.5.0", + "@typescript-eslint/parser": "^5.5.0", + "babel-preset-react-app": "^10.0.1", + "confusing-browser-globals": "^1.0.11", + "eslint-plugin-flowtype": "^8.0.3", + "eslint-plugin-import": "^2.25.3", + "eslint-plugin-jest": "^25.3.0", + "eslint-plugin-jsx-a11y": "^6.5.1", + "eslint-plugin-react": "^7.27.1", + "eslint-plugin-react-hooks": "^4.3.0", + "eslint-plugin-testing-library": "^5.0.1" + }, "engines": { - "node": ">=6.0" + "node": ">=14.0.0" + }, + "peerDependencies": { + "eslint": "^8.0.0" } }, - "node_modules/ci-info": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", - "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", + "node_modules/eslint-config-standard": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-17.1.0.tgz", + "integrity": "sha512-IwHwmaBNtDK4zDHQukFDW5u/aTb8+meQWZvNFWkiGmbWjD6bqyuSSBxxXKkCftCUzc1zwCH2m/baCNDLGmuO5Q==", + "dev": true, "funding": [ { "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" } ], + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12.0.0" + }, + "peerDependencies": { + "eslint": "^8.0.1", + "eslint-plugin-import": "^2.25.2", + "eslint-plugin-n": "^15.0.0 || ^16.0.0 ", + "eslint-plugin-promise": "^6.0.0" } }, - "node_modules/cipher-base": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "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==", + "license": "MIT", "dependencies": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" } }, - "node_modules/cjs-module-lexer": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", - "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==" - }, - "node_modules/class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "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==", + "license": "MIT", "dependencies": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, - "engines": { - "node": ">=0.10.0" + "ms": "^2.1.1" } }, - "node_modules/class-utils/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "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==", + "license": "MIT", "dependencies": { - "is-descriptor": "^0.1.0" + "debug": "^3.2.7" }, "engines": { - "node": ">=0.10.0" + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } } }, - "node_modules/class-utils/node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", + "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==", + "license": "MIT", "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" + "ms": "^2.1.1" } }, - "node_modules/class-utils/node_modules/is-accessor-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "node_modules/eslint-plugin-es": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", + "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", + "dev": true, + "license": "MIT", "dependencies": { - "is-buffer": "^1.1.5" + "eslint-utils": "^2.0.0", + "regexpp": "^3.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=8.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=4.19.1" } }, - "node_modules/class-utils/node_modules/is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", + "node_modules/eslint-plugin-es-x": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-es-x/-/eslint-plugin-es-x-7.8.0.tgz", + "integrity": "sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/ota-meshi", + "https://opencollective.com/eslint" + ], + "license": "MIT", + "peer": true, "dependencies": { - "kind-of": "^3.0.2" + "@eslint-community/eslint-utils": "^4.1.2", + "@eslint-community/regexpp": "^4.11.0", + "eslint-compat-utils": "^0.5.1" }, "engines": { - "node": ">=0.10.0" + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": ">=8" } }, - "node_modules/class-utils/node_modules/is-data-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "node_modules/eslint-plugin-flowtype": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-8.0.3.tgz", + "integrity": "sha512-dX8l6qUL6O+fYPtpNRideCFSpmWOUVx5QcaGLVqe/vlDiBSe4vYljDWDETwnyFzpl7By/WVIu6rcrniCgH9BqQ==", + "license": "BSD-3-Clause", "dependencies": { - "is-buffer": "^1.1.5" + "lodash": "^4.17.21", + "string-natural-compare": "^3.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=12.0.0" + }, + "peerDependencies": { + "@babel/plugin-syntax-flow": "^7.14.5", + "@babel/plugin-transform-react-jsx": "^7.14.9", + "eslint": "^8.1.0" } }, - "node_modules/class-utils/node_modules/is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" + "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==", + "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": ">=0.10.0" + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" } }, - "node_modules/class-utils/node_modules/kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/classnames": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", - "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==" + "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==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } }, - "node_modules/clean-css": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.2.tgz", - "integrity": "sha512-JVJbM+f3d3Q704rF4bqQ5UUyTtuJ0JRKNbTKVEeujCCBoMdkEi+V+e8oktO9qGQNSvHrFTM6JZRXrUvGR1czww==", + "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==", + "license": "Apache-2.0", "dependencies": { - "source-map": "~0.6.0" + "esutils": "^2.0.2" }, "engines": { - "node": ">= 10.0" + "node": ">=0.10.0" } }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "engines": { - "node": ">=6" + "node_modules/eslint-plugin-import/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" } }, - "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "node_modules/eslint-plugin-jest": { + "version": "25.7.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-25.7.0.tgz", + "integrity": "sha512-PWLUEXeeF7C9QGKqvdSbzLOiLTx+bno7/HC9eefePfEb257QFHg7ye3dh80AZVkaa/RQsBB1Q/ORQvg2X7F0NQ==", + "license": "MIT", "dependencies": { - "restore-cursor": "^3.1.0" + "@typescript-eslint/experimental-utils": "^5.0.0" }, "engines": { - "node": ">=8" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^4.0.0 || ^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + }, + "jest": { + "optional": true + } } }, - "node_modules/cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "node_modules/eslint-plugin-jsx-a11y": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", + "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==", + "license": "MIT", + "dependencies": { + "aria-query": "^5.3.2", + "array-includes": "^3.1.8", + "array.prototype.flatmap": "^1.3.2", + "ast-types-flow": "^0.0.8", + "axe-core": "^4.10.0", + "axobject-query": "^4.1.0", + "damerau-levenshtein": "^1.0.8", + "emoji-regex": "^9.2.2", + "hasown": "^2.0.2", + "jsx-ast-utils": "^3.3.5", + "language-tags": "^1.0.9", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "safe-regex-test": "^1.0.3", + "string.prototype.includes": "^2.0.1" + }, "engines": { - "node": ">= 10" + "node": ">=4.0" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" } }, - "node_modules/client-only": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", - "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", - "dev": true - }, - "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "node_modules/eslint-plugin-n": { + "version": "16.6.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-16.6.2.tgz", + "integrity": "sha512-6TyDmZ1HXoFQXnhCTUjVFULReoBPOAjpuiKELMkeP40yffI/1ZRO+d9ug/VC6fqISo2WkuIBk3cvuRPALaWlOQ==", + "dev": true, + "license": "MIT", + "peer": true, "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/cliui/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==", + "@eslint-community/eslint-utils": "^4.4.0", + "builtins": "^5.0.1", + "eslint-plugin-es-x": "^7.5.0", + "get-tsconfig": "^4.7.0", + "globals": "^13.24.0", + "ignore": "^5.2.4", + "is-builtin-module": "^3.2.1", + "is-core-module": "^2.12.1", + "minimatch": "^3.1.2", + "resolve": "^1.22.2", + "semver": "^7.5.3" + }, "engines": { - "node": ">=8" + "node": ">=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=7.0.0" } }, - "node_modules/cliui/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==", + "node_modules/eslint-plugin-n/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "license": "MIT", + "peer": true, "dependencies": { - "ansi-regex": "^5.0.1" + "type-fest": "^0.20.2" }, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "node_modules/eslint-plugin-n/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, + "license": "(MIT OR CC0-1.0)", + "peer": true, "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/coa": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", - "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", - "dependencies": { - "@types/q": "^1.5.1", - "chalk": "^2.4.1", - "q": "^1.1.2" + "node": ">=10" }, - "engines": { - "node": ">= 4.0" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/collect-v8-coverage": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", - "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==" - }, - "node_modules/collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==", + "node_modules/eslint-plugin-node": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", + "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", + "dev": true, + "license": "MIT", "dependencies": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" + "eslint-plugin-es": "^3.0.0", + "eslint-utils": "^2.0.0", + "ignore": "^5.1.1", + "minimatch": "^3.0.4", + "resolve": "^1.10.1", + "semver": "^6.1.0" }, "engines": { - "node": ">=0.10.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==", - "dependencies": { - "color-name": "1.1.3" + "node": ">=8.10.0" + }, + "peerDependencies": { + "eslint": ">=5.16.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/color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "node_modules/eslint-plugin-node/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, + "license": "ISC", "bin": { - "color-support": "bin.js" + "semver": "bin/semver.js" } }, - "node_modules/colord": { - "version": "2.9.3", - "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", - "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==" - }, - "node_modules/colorette": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", - "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==" + "node_modules/eslint-plugin-promise": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.0.0.tgz", + "integrity": "sha512-7GPezalm5Bfi/E22PnQxDWH2iW9GTvAlUNTztemeHb6c1BniSyoeTrM87JkC0wYdi6aQrZX9p2qEiAno8aTcbw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } }, - "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==", - "dependencies": { - "delayed-stream": "~1.0.0" + "node_modules/eslint-plugin-react": { + "version": "7.37.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.2.tgz", + "integrity": "sha512-EsTAnj9fLVr/GZleBLFbj/sSuXeWmp1eXIN60ceYnZveqEaUCyW4X+Vh4WTdUhCkW4xutXYqTXCUSyqD4rB75w==", + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.8", + "array.prototype.findlast": "^1.2.5", + "array.prototype.flatmap": "^1.3.2", + "array.prototype.tosorted": "^1.1.4", + "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.1.0", + "estraverse": "^5.3.0", + "hasown": "^2.0.2", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.8", + "object.fromentries": "^2.0.8", + "object.values": "^1.2.0", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.5", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.11", + "string.prototype.repeat": "^1.0.0" }, "engines": { - "node": ">= 0.8" + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" } }, - "node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" - }, - "node_modules/common-path-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", - "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==" - }, - "node_modules/common-tags": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", - "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", + "node_modules/eslint-plugin-react-hooks": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz", + "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==", + "license": "MIT", "engines": { - "node": ">=4.0.0" + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" } }, - "node_modules/commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==" - }, - "node_modules/component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" - }, - "node_modules/compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "node_modules/eslint-plugin-react/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "license": "Apache-2.0", "dependencies": { - "mime-db": ">= 1.43.0 < 2" + "esutils": "^2.0.2" }, "engines": { - "node": ">= 0.6" + "node": ">=0.10.0" } }, - "node_modules/compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "node_modules/eslint-plugin-react/node_modules/resolve": { + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", + "license": "MIT", "dependencies": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", - "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", - "vary": "~1.1.2" + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" }, - "engines": { - "node": ">= 0.8.0" + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/compression/node_modules/bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", - "engines": { - "node": ">= 0.8" + "node_modules/eslint-plugin-react/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" } }, - "node_modules/compression/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" + "node_modules/eslint-plugin-standard": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-4.1.0.tgz", + "integrity": "sha512-ZL7+QRixjTR6/528YNGyDotyffm5OQst/sGxKDwGb9Uqs4In5Egi4+jbobhqJoyoCM6/7v/1A5fhQ7ScMtDjaQ==", + "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", + "peerDependencies": { + "eslint": ">=5.0.0" } }, - "node_modules/compression/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/compression/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, - "node_modules/concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "engines": [ - "node >= 0.8" - ], - "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, - "node_modules/concat-stream/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, - "node_modules/concat-stream/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "node_modules/eslint-plugin-testing-library": { + "version": "5.11.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-5.11.1.tgz", + "integrity": "sha512-5eX9e1Kc2PqVRed3taaLnAAqPZGEX75C+M/rXzUAI3wIg/ZxzUm1OVAwfe/O+vE+6YXOLetSe9g5GKD2ecXipw==", + "license": "MIT", "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "@typescript-eslint/utils": "^5.58.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0", + "npm": ">=6" + }, + "peerDependencies": { + "eslint": "^7.5.0 || ^8.0.0" } }, - "node_modules/concat-stream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/concat-stream/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "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==", + "license": "BSD-2-Clause", "dependencies": { - "safe-buffer": "~5.1.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/connect-history-api-fallback": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", - "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", - "devOptional": true, + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, "engines": { - "node": ">=0.8" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/console-browserify": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", - "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==" - }, - "node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" - }, - "node_modules/constants-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", - "integrity": "sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==" - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "node_modules/eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "license": "MIT", "dependencies": { - "safe-buffer": "5.2.1" + "eslint-visitor-keys": "^1.1.0" }, "engines": { - "node": ">= 0.6" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" } }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">= 0.6" + "node": ">=4" } }, - "node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" - }, - "node_modules/cookie": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", - "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "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==", + "license": "Apache-2.0", "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" - }, - "node_modules/cookies": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.8.0.tgz", - "integrity": "sha512-8aPsApQfebXnuI+537McwYsDtjVxGm8gTIzQI3FDW6t5t/DAhERxtnbEPN/8RX+uZthoz4eCOgloXaE5cYyNow==", - "devOptional": true, - "dependencies": { - "depd": "~2.0.0", - "keygrip": "~1.1.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/copy-concurrently": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", - "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", - "dependencies": { - "aproba": "^1.1.1", - "fs-write-stream-atomic": "^1.0.8", - "iferr": "^0.1.5", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.0" + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/copy-concurrently/node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "node_modules/eslint-webpack-plugin": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/eslint-webpack-plugin/-/eslint-webpack-plugin-3.2.0.tgz", + "integrity": "sha512-avrKcGncpPbPSUHX6B3stNGzkKFto3eL+DKM4+VyMrVnhPc3vRczVlCq3uhuFOdRvDHTVXuzwk1ZKUrqDQHQ9w==", + "license": "MIT", "dependencies": { - "minimist": "^1.2.6" + "@types/eslint": "^7.29.0 || ^8.4.1", + "jest-worker": "^28.0.2", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0" }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==", "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/copyfiles": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/copyfiles/-/copyfiles-2.4.1.tgz", - "integrity": "sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg==", - "dev": true, - "dependencies": { - "glob": "^7.0.5", - "minimatch": "^3.0.3", - "mkdirp": "^1.0.4", - "noms": "0.0.0", - "through2": "^2.0.1", - "untildify": "^4.0.0", - "yargs": "^16.1.0" + "node": ">= 12.13.0" }, - "bin": { - "copyfiles": "copyfiles", - "copyup": "copyfiles" - } - }, - "node_modules/core-js": { - "version": "3.29.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.29.0.tgz", - "integrity": "sha512-VG23vuEisJNkGl6XQmFJd3rEG/so/CNatqeE+7uZAwTSwFeB/qaO0be8xZYUNWprJ/GIwL8aMt9cj1kvbpTZhg==", - "hasInstallScript": true, "funding": { "type": "opencollective", - "url": "https://opencollective.com/core-js" + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0", + "webpack": "^5.0.0" } }, - "node_modules/core-js-compat": { - "version": "3.29.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.29.0.tgz", - "integrity": "sha512-ScMn3uZNAFhK2DGoEfErguoiAHhV2Ju+oJo/jK08p7B3f3UhocUrCCkTvnZaiS+edl5nlIoiBXKcwMc6elv4KQ==", + "node_modules/eslint-webpack-plugin/node_modules/jest-worker": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz", + "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==", + "license": "MIT", "dependencies": { - "browserslist": "^4.21.5" + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/core-js-pure": { - "version": "3.29.0", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.29.0.tgz", - "integrity": "sha512-v94gUjN5UTe1n0yN/opTihJ8QBWD2O8i19RfTZR7foONPWArnjB96QA/wk5ozu1mm6ja3udQCzOzwQXTxi3xOQ==", - "hasInstallScript": true, + "node_modules/eslint-webpack-plugin/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + "node_modules/eslint/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==", + "license": "Python-2.0" }, - "node_modules/cosmiconfig": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", - "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "node_modules/eslint/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==", + "license": "MIT", "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cosmiconfig/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==", + "node_modules/eslint/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "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" + "type-fest": "^0.20.2" }, "engines": { "node": ">=8" @@ -7820,1330 +8646,1291 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/create-ecdh": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", - "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", - "dependencies": { - "bn.js": "^4.1.0", - "elliptic": "^6.5.3" - } - }, - "node_modules/create-ecdh/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" - }, - "node_modules/create-hash": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", - "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "node_modules/eslint/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==", + "license": "MIT", "dependencies": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "md5.js": "^1.3.4", - "ripemd160": "^2.0.1", - "sha.js": "^2.4.0" + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/create-hmac": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", - "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "node_modules/eslint/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==", + "license": "MIT", "dependencies": { - "cipher-base": "^1.0.3", - "create-hash": "^1.1.0", - "inherits": "^2.0.1", - "ripemd160": "^2.0.0", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "node_modules/eslint/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==", + "license": "MIT", "dependencies": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "yocto-queue": "^0.1.0" }, "engines": { - "node": ">=4.8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cross-spawn/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/crypto-browserify": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", - "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", - "dependencies": { - "browserify-cipher": "^1.0.0", - "browserify-sign": "^4.0.0", - "create-ecdh": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.0", - "diffie-hellman": "^5.0.0", - "inherits": "^2.0.1", - "pbkdf2": "^3.0.3", - "public-encrypt": "^4.0.0", - "randombytes": "^2.0.0", - "randomfill": "^1.0.3" - }, - "engines": { - "node": "*" - } - }, - "node_modules/crypto-random-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", - "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/css-loader": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-3.6.0.tgz", - "integrity": "sha512-M5lSukoWi1If8dhQAUCvj4H8vUt3vOnwbQBH9DdTm/s4Ym2B/3dPMtYZeJmq7Q3S3Pa+I94DcZ7pc9bP14cWIQ==", - "dev": true, + "node_modules/eslint/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==", + "license": "MIT", "dependencies": { - "camelcase": "^5.3.1", - "cssesc": "^3.0.0", - "icss-utils": "^4.1.1", - "loader-utils": "^1.2.3", - "normalize-path": "^3.0.0", - "postcss": "^7.0.32", - "postcss-modules-extract-imports": "^2.0.0", - "postcss-modules-local-by-default": "^3.0.2", - "postcss-modules-scope": "^2.2.0", - "postcss-modules-values": "^3.0.0", - "postcss-value-parser": "^4.1.0", - "schema-utils": "^2.7.0", - "semver": "^6.3.0" + "p-limit": "^3.0.2" }, "engines": { - "node": ">= 8.9.0" + "node": ">=10" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" - } - }, - "node_modules/css-loader/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, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/css-loader/node_modules/loader-utils": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", - "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", - "dev": true, - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - }, + "node_modules/eslint/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==", + "license": "(MIT OR CC0-1.0)", "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/css-select": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", - "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^3.2.1", - "domutils": "^1.7.0", - "nth-check": "^1.0.2" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/css-select-base-adapter": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", - "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==" - }, - "node_modules/css-tree": { - "version": "1.0.0-alpha.37", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", - "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", + "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==", + "license": "BSD-2-Clause", "dependencies": { - "mdn-data": "2.0.4", - "source-map": "^0.6.1" + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" }, "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/css-unit-converter": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.2.tgz", - "integrity": "sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA==" - }, - "node_modules/css-what": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz", - "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==", - "engines": { - "node": ">= 6" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css.escape": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", - "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==" - }, - "node_modules/cssdb": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.4.1.tgz", - "integrity": "sha512-0Q8NOMpXJ3iTDDbUv9grcmQAfdDx4qz+fN/+Md2FGbevT+6+bJNQ2LjB2YIUlLbpBTM32idU1Sb+tb/uGt6/XQ==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" + "url": "https://opencollective.com/eslint" } }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "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==", + "license": "BSD-2-Clause", "bin": { - "cssesc": "bin/cssesc" + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" }, "engines": { "node": ">=4" } }, - "node_modules/csso": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", - "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "license": "BSD-3-Clause", "dependencies": { - "css-tree": "^1.1.2" + "estraverse": "^5.1.0" }, "engines": { - "node": ">=8.0.0" + "node": ">=0.10" } }, - "node_modules/csso/node_modules/css-tree": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", - "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "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==", + "license": "BSD-2-Clause", "dependencies": { - "mdn-data": "2.0.14", - "source-map": "^0.6.1" + "estraverse": "^5.2.0" }, "engines": { - "node": ">=8.0.0" + "node": ">=4.0" } }, - "node_modules/csso/node_modules/mdn-data": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", - "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" - }, - "node_modules/cssom": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", - "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==" - }, - "node_modules/cssstyle": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", - "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", - "dependencies": { - "cssom": "~0.3.6" - }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "license": "BSD-2-Clause", "engines": { - "node": ">=8" + "node": ">=4.0" } }, - "node_modules/cssstyle/node_modules/cssom": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==" - }, - "node_modules/csstype": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", - "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" - }, - "node_modules/cyclist": { + "node_modules/estree-walker": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", - "integrity": "sha512-NJGVKPS81XejHcLhaLJS7plab0fK3slPh11mESeeDq2W4ZI5kUKK/LRRdVDvjJseojbPB7ZwjnyOybg3Igea/A==" + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", + "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", + "license": "MIT" }, - "node_modules/d3-array": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.2.tgz", - "integrity": "sha512-yEEyEAbDrF8C6Ob2myOBLjwBLck1Z89jMGFee0oPsn95GqjerpaOA4ch+vc2l0FNFFwMD5N7OCSEN5eAlsUbgQ==", - "dependencies": { - "internmap": "1 - 2" - }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "license": "BSD-2-Clause", "engines": { - "node": ">=12" + "node": ">=0.10.0" } }, - "node_modules/d3-color": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", - "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", "engines": { - "node": ">=12" + "node": ">= 0.6" } }, - "node_modules/d3-ease": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", - "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", - "engines": { - "node": ">=12" - } + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "license": "MIT" }, - "node_modules/d3-format": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", - "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "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": ">=12" + "node": ">=0.8.x" } }, - "node_modules/d3-interpolate": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", - "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "license": "MIT", "dependencies": { - "d3-color": "1 - 3" + "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": ">=12" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/d3-path": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", - "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", "engines": { - "node": ">=12" + "node": ">= 0.8.0" } }, - "node_modules/d3-scale": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", - "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", - "dependencies": { - "d3-array": "2.10.0 - 3", - "d3-format": "1 - 3", - "d3-interpolate": "1.2.0 - 3", - "d3-time": "2.1.1 - 3", - "d3-time-format": "2 - 4" + "node_modules/expect": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", + "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", + "license": "MIT", + "dependencies": { + "@jest/types": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1" }, "engines": { - "node": ">=12" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/d3-shape": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", - "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "node_modules/express": { + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", + "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", + "license": "MIT", "dependencies": { - "d3-path": "^3.1.0" + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.10", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" }, "engines": { - "node": ">=12" + "node": ">= 0.10.0" } }, - "node_modules/d3-time": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", - "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", "dependencies": { - "d3-array": "2 - 3" + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "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==", + "license": "MIT" + }, + "node_modules/fast-equals": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.0.1.tgz", + "integrity": "sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "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.4" }, "engines": { - "node": ">=12" + "node": ">=8.6.0" } }, - "node_modules/d3-time-format": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", - "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "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==", + "license": "ISC", "dependencies": { - "d3-time": "1 - 3" + "is-glob": "^4.0.1" }, "engines": { - "node": ">=12" + "node": ">= 6" } }, - "node_modules/d3-timer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", - "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "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==", + "license": "MIT" + }, + "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==", + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.3.tgz", + "integrity": "sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==", + "license": "BSD-3-Clause" + }, + "node_modules/fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=12" + "node": ">= 4.9.1" } }, - "node_modules/damerau-levenshtein": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", - "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==" + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } }, - "node_modules/dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", - "optional": true, - "peer": true, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "license": "Apache-2.0", "dependencies": { - "assert-plus": "^1.0.0" + "websocket-driver": ">=0.5.1" }, "engines": { - "node": ">=0.10" + "node": ">=0.8.0" } }, - "node_modules/data-urls": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", - "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", + "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==", + "license": "Apache-2.0", "dependencies": { - "abab": "^2.0.3", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.0.0" - }, - "engines": { - "node": ">=10" + "bser": "2.1.1" } }, - "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/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==", + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "flat-cache": "^3.0.4" }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/decamelize": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-3.2.0.tgz", - "integrity": "sha512-4TgkVUsmmu7oCSyGBm5FvfMoACuoh9EOidm7V5/J2X2djAwwt57qb3F2KMP2ITqODTCSwb+YRV+0Zqrv18k/hw==", - "dev": true, + "node_modules/file-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", + "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", + "license": "MIT", "dependencies": { - "xregexp": "^4.2.4" + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" }, "engines": { - "node": ">=6" + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" } }, - "node_modules/decamelize-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", - "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", + "node_modules/file-loader/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "license": "MIT", "dependencies": { - "decamelize": "^1.1.0", - "map-obj": "^1.0.0" + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" }, "engines": { - "node": ">=0.10.0" + "node": ">= 10.13.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, - "node_modules/decamelize-keys/node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "engines": { - "node": ">=0.10.0" + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" } }, - "node_modules/decamelize-keys/node_modules/map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", - "engines": { - "node": ">=0.10.0" + "node_modules/filelist/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==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" } }, - "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==" - }, - "node_modules/decimal.js-light": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", - "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==" - }, - "node_modules/decode-uri-component": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", - "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, "engines": { - "node": ">=0.10" + "node": ">=10" } }, - "node_modules/dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==" - }, - "node_modules/deep-equal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", - "integrity": "sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw==", - "devOptional": 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==" - }, - "node_modules/deepmerge": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.0.tgz", - "integrity": "sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og==", + "node_modules/filesize": { + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz", + "integrity": "sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==", + "license": "BSD-3-Clause", "engines": { - "node": ">=0.10.0" + "node": ">= 0.4.0" } }, - "node_modules/default-gateway": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", - "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", + "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==", + "license": "MIT", "dependencies": { - "execa": "^5.0.0" + "to-regex-range": "^5.0.1" }, "engines": { - "node": ">= 10" + "node": ">=8" } }, - "node_modules/default-gateway/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/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "license": "MIT", "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" }, "engines": { - "node": ">= 8" + "node": ">= 0.8" } }, - "node_modules/default-gateway/node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "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" + "ms": "2.0.0" } }, - "node_modules/default-gateway/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==", + "node_modules/finalhandler/node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.8" } }, - "node_modules/default-gateway/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==", + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/find-cache-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-4.0.0.tgz", + "integrity": "sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "common-path-prefix": "^3.0.0", + "pkg-dir": "^7.0.0" + }, "engines": { - "node": ">=8" + "node": ">=14.16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/default-gateway/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==", + "node_modules/find-cache-dir/node_modules/find-up": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", + "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", + "dev": true, + "license": "MIT", "dependencies": { - "path-key": "^3.0.0" + "locate-path": "^7.1.0", + "path-exists": "^5.0.0" }, "engines": { - "node": ">=8" - } - }, - "node_modules/default-gateway/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==", - "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/default-gateway/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/find-cache-dir/node_modules/locate-path": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", + "dev": true, + "license": "MIT", "dependencies": { - "shebang-regex": "^3.0.0" + "p-locate": "^6.0.0" }, "engines": { - "node": ">=8" - } - }, - "node_modules/default-gateway/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==", - "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/default-gateway/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "node_modules/find-cache-dir/node_modules/p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "dev": true, + "license": "MIT", "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" + "yocto-queue": "^1.0.0" }, "engines": { - "node": ">= 8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "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==", + "node_modules/find-cache-dir/node_modules/p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "dev": true, + "license": "MIT", "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" + "p-limit": "^4.0.0" }, "engines": { - "node": ">= 0.4" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "node_modules/find-cache-dir/node_modules/path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, - "node_modules/define-properties": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", - "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", + "node_modules/find-cache-dir/node_modules/pkg-dir": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-7.0.0.tgz", + "integrity": "sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==", + "dev": true, + "license": "MIT", "dependencies": { - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" + "find-up": "^6.3.0" }, "engines": { - "node": ">= 0.4" + "node": ">=14.16" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "dependencies": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, + "node_modules/find-cache-dir/node_modules/yocto-queue": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.1.1.tgz", + "integrity": "sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/defined": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.1.tgz", - "integrity": "sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==", + "node": ">=12.20" + }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "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==", + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, "engines": { - "node": ">=0.4.0" + "node": ">=8" } }, - "node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + } }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "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==", + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, "engines": { - "node": ">= 0.8" + "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "license": "ISC" + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", "engines": { - "node": ">=6" + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } } }, - "node_modules/des.js": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", - "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "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==", + "license": "MIT", "dependencies": { - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" + "is-callable": "^1.1.3" } }, - "node_modules/detect-file": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", - "integrity": "sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==", - "dev": true, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, "engines": { - "node": ">=0.10.0" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "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==", + "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==", + "license": "ISC", "engines": { - "node": ">=8" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/detect-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", - "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==" - }, - "node_modules/detect-port-alt": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz", - "integrity": "sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==", + "node_modules/fork-ts-checker-webpack-plugin": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.3.tgz", + "integrity": "sha512-SbH/l9ikmMWycd5puHJKTkZJKddF4iRLyW3DeZ08HTI7NGyLS38MXd/KGgeWumQO7YNQbW2u/NtPT2YowbPaGQ==", + "license": "MIT", "dependencies": { - "address": "^1.0.1", - "debug": "^2.6.0" - }, - "bin": { - "detect": "bin/detect-port", - "detect-port": "bin/detect-port" + "@babel/code-frame": "^7.8.3", + "@types/json-schema": "^7.0.5", + "chalk": "^4.1.0", + "chokidar": "^3.4.2", + "cosmiconfig": "^6.0.0", + "deepmerge": "^4.2.2", + "fs-extra": "^9.0.0", + "glob": "^7.1.6", + "memfs": "^3.1.2", + "minimatch": "^3.0.4", + "schema-utils": "2.7.0", + "semver": "^7.3.2", + "tapable": "^1.0.0" }, "engines": { - "node": ">= 4.2.1" - } - }, - "node_modules/detect-port-alt/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/detect-port-alt/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/detective": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.1.tgz", - "integrity": "sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==", - "dependencies": { - "acorn-node": "^1.8.2", - "defined": "^1.0.0", - "minimist": "^1.2.6" + "node": ">=10", + "yarn": ">=1.0.0" }, - "bin": { - "detective": "bin/detective.js" + "peerDependencies": { + "eslint": ">= 6", + "typescript": ">= 2.7", + "vue-template-compiler": "*", + "webpack": ">= 4" }, - "engines": { - "node": ">=0.8.0" + "peerDependenciesMeta": { + "eslint": { + "optional": true + }, + "vue-template-compiler": { + "optional": true + } } }, - "node_modules/didyoumean": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" - }, - "node_modules/diff-sequences": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz", - "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==", + "node_modules/fork-ts-checker-webpack-plugin/node_modules/cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "license": "MIT", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" + }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=8" } }, - "node_modules/diffie-hellman": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", - "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "node_modules/fork-ts-checker-webpack-plugin/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "license": "MIT", "dependencies": { - "bn.js": "^4.1.0", - "miller-rabin": "^4.0.0", - "randombytes": "^2.0.0" + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" } }, - "node_modules/diffie-hellman/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "node_modules/fork-ts-checker-webpack-plugin/node_modules/schema-utils": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", + "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", + "license": "MIT", "dependencies": { - "path-type": "^4.0.0" + "@types/json-schema": "^7.0.4", + "ajv": "^6.12.2", + "ajv-keywords": "^3.4.1" }, "engines": { - "node": ">=8" + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, - "node_modules/dlv": { + "node_modules/fork-ts-checker-webpack-plugin/node_modules/tapable": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" - }, - "node_modules/dns-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", - "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==" - }, - "node_modules/dns-packet": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.4.0.tgz", - "integrity": "sha512-EgqGeaBB8hLiHLZtp/IbaDQTL8pZ0+IvwzSHA6d7VyMDM+B9hgddEMa9xjK5oYnw0ci0JQ6g2XCD7/f6cafU6g==", - "dependencies": { - "@leichtgewicht/ip-codec": "^2.0.1" - }, + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "license": "MIT", "engines": { "node": ">=6" } }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "node_modules/form-data": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.2.tgz", + "integrity": "sha512-sJe+TQb2vIaIyO783qN6BlMYWMw3WBOHA1Ay2qxsnjuafEOQFJ2JakedOQirT6D5XPRxDvS7AHYyem9fTpb4LQ==", + "license": "MIT", "dependencies": { - "esutils": "^2.0.2" + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" }, "engines": { - "node": ">=6.0.0" + "node": ">= 6" } }, - "node_modules/dom-accessibility-api": { - "version": "0.5.16", - "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", - "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==" - }, - "node_modules/dom-converter": { + "node_modules/forwarded": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", - "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", - "dependencies": { - "utila": "~0.4" - } - }, - "node_modules/dom-helpers": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", - "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", - "dependencies": { - "@babel/runtime": "^7.8.7", - "csstype": "^3.0.2" + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" } }, - "node_modules/dom-serializer": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", - "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", - "dependencies": { - "domelementtype": "^2.0.1", - "entities": "^2.0.0" + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" } }, - "node_modules/dom-serializer/node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ] - }, - "node_modules/domain-browser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", - "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", "engines": { - "node": ">=0.4", - "npm": ">=1.2" + "node": ">= 0.6" } }, - "node_modules/domelementtype": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" - }, - "node_modules/domexception": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", - "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "license": "MIT", "dependencies": { - "webidl-conversions": "^5.0.0" + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/domexception/node_modules/webidl-conversions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", - "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "node_modules/fs-monkey": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.6.tgz", + "integrity": "sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==", + "license": "Unlicense" + }, + "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==", + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=8" + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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==", + "license": "MIT", "dependencies": { - "domelementtype": "^2.2.0" + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" }, "engines": { - "node": ">= 4" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/domhandler/node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ] - }, - "node_modules/domutils": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", - "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", - "dependencies": { - "dom-serializer": "0", - "domelementtype": "1" + "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==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/dot-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", - "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" + "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==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" } }, - "node_modules/dot-case/node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" - }, - "node_modules/dotenv": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", - "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", "engines": { - "node": ">=10" + "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/dotenv-expand": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", - "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==" - }, - "node_modules/duplexer": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", - "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" - }, - "node_modules/duplexify": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", - "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "license": "MIT", "dependencies": { - "end-of-stream": "^1.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.0.0", - "stream-shift": "^1.0.0" + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/duplexify/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + "node_modules/get-own-enumerable-property-symbols": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", + "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", + "license": "ISC" }, - "node_modules/duplexify/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "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==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" } }, - "node_modules/duplexify/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "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==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "node_modules/duplexify/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "node_modules/get-symbol-description": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", + "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", + "license": "MIT", "dependencies": { - "safe-buffer": "~5.1.0" + "call-bind": "^1.0.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", - "optional": true, + "node_modules/get-tsconfig": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.1.tgz", + "integrity": "sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==", + "dev": true, + "license": "MIT", "peer": true, "dependencies": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" - }, - "node_modules/ejs": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", - "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", "dependencies": { - "jake": "^10.8.5" - }, - "bin": { - "ejs": "bin/cli.js" + "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.10.0" + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/electron-to-chromium": { - "version": "1.4.315", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.315.tgz", - "integrity": "sha512-ndBQYz3Eyy3rASjjQ9poMJGoAlsZ/aZnq6GBsGL4w/4sWIAwiUHVSsMuADbxa8WJw7pZ0oxLpGbtoDt4vRTdCg==" - }, - "node_modules/elliptic": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.0.tgz", - "integrity": "sha512-dpwoQcLc/2WLQvJvLRHKZ+f9FgOdjnq11rurqwekGQygGPsYSK29OMMD2WalatiqQ+XGFDglTNixpPfI+lpaAA==", + "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==", + "license": "ISC", "dependencies": { - "bn.js": "^4.11.9", - "brorand": "^1.1.0", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.1", - "inherits": "^2.0.4", - "minimalistic-assert": "^1.0.1", - "minimalistic-crypto-utils": "^1.0.1" + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" } }, - "node_modules/elliptic/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "license": "BSD-2-Clause" }, - "node_modules/emittery": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", - "integrity": "sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==", - "engines": { - "node": ">=10" + "node_modules/global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "license": "MIT", + "dependencies": { + "global-prefix": "^3.0.0" }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" + "engines": { + "node": ">=6" } }, - "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==" - }, - "node_modules/emojis-list": { + "node_modules/global-prefix": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "license": "MIT", + "dependencies": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, "engines": { - "node": ">= 4" + "node": ">=6" } }, - "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "engines": { - "node": ">= 0.8" + "node_modules/global-prefix/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" } }, - "node_modules/encoding": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", - "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "optional": true, - "dependencies": { - "iconv-lite": "^0.6.2" + "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==", + "license": "MIT", + "engines": { + "node": ">=4" } }, - "node_modules/encoding/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "optional": true, + "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==", + "license": "MIT", "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" + "define-properties": "^1.2.1", + "gopd": "^1.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "license": "MIT", "dependencies": { - "once": "^1.4.0" + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/enhanced-resolve": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz", - "integrity": "sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==", + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "license": "MIT", "dependencies": { - "graceful-fs": "^4.1.2", - "memory-fs": "^0.5.0", - "tapable": "^1.0.0" + "get-intrinsic": "^1.1.3" }, - "engines": { - "node": ">=6.9.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/enhanced-resolve/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + "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==", + "license": "ISC" }, - "node_modules/enhanced-resolve/node_modules/memory-fs": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", - "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "license": "MIT" + }, + "node_modules/gzip-size": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", + "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", + "license": "MIT", "dependencies": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" + "duplexer": "^0.1.2" }, "engines": { - "node": ">=4.3.0 <5.0.0 || >=5.10" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/enhanced-resolve/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/enhanced-resolve/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "node_modules/handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "license": "MIT" }, - "node_modules/enhanced-resolve/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } + "node_modules/harmony-reflect": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz", + "integrity": "sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==", + "license": "(Apache-2.0 OR MPL-1.1)" }, - "node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "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==", + "license": "MIT", "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/env-paths": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/err-code": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", - "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==" - }, - "node_modules/errno": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", - "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "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==", + "license": "MIT", "dependencies": { - "prr": "~1.0.1" + "es-define-property": "^1.0.0" }, - "bin": { - "errno": "cli.js" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "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==", - "dependencies": { - "is-arrayish": "^0.2.1" + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/error-stack-parser": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", - "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", - "dependencies": { - "stackframe": "^1.3.4" + "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==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es-abstract": { - "version": "1.21.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.1.tgz", - "integrity": "sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg==", + "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==", + "license": "MIT", "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-set-tostringtag": "^2.0.1", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.1.3", - "get-symbol-description": "^1.0.0", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.4", - "is-array-buffer": "^3.0.1", - "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.10", - "is-weakref": "^1.0.2", - "object-inspect": "^1.12.2", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.4.3", - "safe-regex-test": "^1.0.0", - "string.prototype.trimend": "^1.0.6", - "string.prototype.trimstart": "^1.0.6", - "typed-array-length": "^1.0.4", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.9" + "has-symbols": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -9152,463 +9939,420 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es-array-method-boxes-properly": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", - "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==" - }, - "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", "dependencies": { - "get-intrinsic": "^1.2.4" + "function-bind": "^1.1.2" }, "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==", - "engines": { - "node": ">= 0.4" + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "license": "MIT", + "bin": { + "he": "bin/he" } }, - "node_modules/es-get-iterator": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", - "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "has-symbols": "^1.0.3", - "is-arguments": "^1.1.1", - "is-map": "^2.0.2", - "is-set": "^2.0.2", - "is-string": "^1.0.7", - "isarray": "^2.0.5", - "stop-iteration-iterator": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node_modules/hoopy": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", + "integrity": "sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0" } }, - "node_modules/es-get-iterator/node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" - }, - "node_modules/es-module-lexer": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", - "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==" + "node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true, + "license": "ISC" }, - "node_modules/es-set-tostringtag": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", - "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", + "node_modules/hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", + "license": "MIT", "dependencies": { - "get-intrinsic": "^1.1.3", - "has": "^1.0.3", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" } }, - "node_modules/es-shim-unscopables": { + "node_modules/hpack.js/node_modules/isarray": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", - "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/hpack.js/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", "dependencies": { - "has": "^1.0.3" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, - "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/hpack.js/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/hpack.js/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "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" + "safe-buffer": "~5.1.0" } }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "node_modules/html-encoding-sniffer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", + "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", + "license": "MIT", + "dependencies": { + "whatwg-encoding": "^1.0.5" + }, "engines": { - "node": ">=6" + "node": ">=10" } }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + "node_modules/html-entities": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz", + "integrity": "sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ], + "license": "MIT" }, - "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==", - "engines": { - "node": ">=0.8.0" - } + "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==", + "license": "MIT" }, - "node_modules/escodegen": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", - "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", + "node_modules/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", + "license": "MIT", "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1" + "camel-case": "^4.1.2", + "clean-css": "^5.2.2", + "commander": "^8.3.0", + "he": "^1.2.0", + "param-case": "^3.0.4", + "relateurl": "^0.2.7", + "terser": "^5.10.0" }, "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" + "html-minifier-terser": "cli.js" }, "engines": { - "node": ">=6.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" + "node": ">=12" } }, - "node_modules/eslint": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz", - "integrity": "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==", + "node_modules/html-webpack-plugin": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.3.tgz", + "integrity": "sha512-QSf1yjtSAsmf7rYBV7XX86uua4W/vkhIt0xNXKbsi2foEeW7vjJQz4bhnpL3xH+l1ryl1680uNv968Z+X6jSYg==", + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.0.0", - "ajv": "^6.10.0", - "chalk": "^2.1.0", - "cross-spawn": "^6.0.5", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "eslint-scope": "^5.0.0", - "eslint-utils": "^1.4.3", - "eslint-visitor-keys": "^1.1.0", - "espree": "^6.1.2", - "esquery": "^1.0.1", - "esutils": "^2.0.2", - "file-entry-cache": "^5.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.0.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "inquirer": "^7.0.0", - "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.3.0", - "lodash": "^4.17.14", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "optionator": "^0.8.3", - "progress": "^2.0.0", - "regexpp": "^2.0.1", - "semver": "^6.1.2", - "strip-ansi": "^5.2.0", - "strip-json-comments": "^3.0.1", - "table": "^5.2.3", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "bin": { - "eslint": "bin/eslint.js" + "@types/html-minifier-terser": "^6.0.0", + "html-minifier-terser": "^6.0.2", + "lodash": "^4.17.21", + "pretty-error": "^4.0.0", + "tapable": "^2.0.0" }, "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + "node": ">=10.13.0" }, "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-config-prettier": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.15.0.tgz", - "integrity": "sha512-a1+kOYLR8wMGustcgAjdydMsQ2A/2ipRPwRKUmfYaSxc9ZPcrku080Ctl6zrZzZNs/U82MjSv+qKREkoq3bJaw==", - "dev": true, - "dependencies": { - "get-stdin": "^6.0.0" - }, - "bin": { - "eslint-config-prettier-check": "bin/cli.js" + "type": "opencollective", + "url": "https://opencollective.com/html-webpack-plugin" }, "peerDependencies": { - "eslint": ">=3.14.1" - } - }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz", - "integrity": "sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==", - "dependencies": { - "debug": "^3.2.7", - "is-core-module": "^2.11.0", - "resolve": "^1.22.1" - } - }, - "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==", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-module-utils": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz", - "integrity": "sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==", - "dependencies": { - "debug": "^3.2.7" - }, - "engines": { - "node": ">=4" + "@rspack/core": "0.x || 1.x", + "webpack": "^5.20.0" }, "peerDependenciesMeta": { - "eslint": { + "@rspack/core": { + "optional": true + }, + "webpack": { "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==", + "node_modules/htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", "dependencies": { - "ms": "^2.1.1" + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" } }, - "node_modules/eslint-plugin-import": { - "version": "2.27.5", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz", - "integrity": "sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==", + "node_modules/http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", + "license": "MIT" + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "array.prototype.flatmap": "^1.3.1", - "debug": "^3.2.7", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.7", - "eslint-module-utils": "^2.7.4", - "has": "^1.0.3", - "is-core-module": "^2.11.0", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.values": "^1.1.6", - "resolve": "^1.22.1", - "semver": "^6.3.0", - "tsconfig-paths": "^3.14.1" + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" }, "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + "node": ">= 0.8" } }, - "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/http-parser-js": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", + "license": "MIT" + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "license": "MIT", "dependencies": { - "ms": "^2.1.1" + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" } }, - "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/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "license": "MIT", "dependencies": { - "esutils": "^2.0.2" + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" }, "engines": { - "node": ">=0.10.0" + "node": ">= 6" } }, - "node_modules/eslint-plugin-jest": { - "version": "25.7.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-25.7.0.tgz", - "integrity": "sha512-PWLUEXeeF7C9QGKqvdSbzLOiLTx+bno7/HC9eefePfEb257QFHg7ye3dh80AZVkaa/RQsBB1Q/ORQvg2X7F0NQ==", + "node_modules/http-proxy-middleware": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.7.tgz", + "integrity": "sha512-fgVY8AV7qU7z/MmXJ/rxwbrtQH4jBQ9m7kp3llF0liB7glmFeVZFBepQb32T3y8n8k2+AEYuMPCpinYW+/CuRA==", + "license": "MIT", "dependencies": { - "@typescript-eslint/experimental-utils": "^5.0.0" + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=12.0.0" }, "peerDependencies": { - "@typescript-eslint/eslint-plugin": "^4.0.0 || ^5.0.0", - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "@types/express": "^4.17.13" }, "peerDependenciesMeta": { - "@typescript-eslint/eslint-plugin": { - "optional": true - }, - "jest": { + "@types/express": { "optional": true } } }, - "node_modules/eslint-plugin-jsx-a11y": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.7.1.tgz", - "integrity": "sha512-63Bog4iIethyo8smBklORknVjB0T2dwB8Mr/hIC+fBS0uyHdYYpzM/Ed+YC8VxTjlXHEWFOdmgwcDn1U2L9VCA==", + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.20.7", - "aria-query": "^5.1.3", - "array-includes": "^3.1.6", - "array.prototype.flatmap": "^1.3.1", - "ast-types-flow": "^0.0.7", - "axe-core": "^4.6.2", - "axobject-query": "^3.1.1", - "damerau-levenshtein": "^1.0.8", - "emoji-regex": "^9.2.2", - "has": "^1.0.3", - "jsx-ast-utils": "^3.3.3", - "language-tags": "=1.0.5", - "minimatch": "^3.1.2", - "object.entries": "^1.1.6", - "object.fromentries": "^2.0.6", - "semver": "^6.3.0" + "agent-base": "6", + "debug": "4" }, "engines": { - "node": ">=4.0" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + "node": ">= 6" } }, - "node_modules/eslint-plugin-jsx-a11y/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==" - }, - "node_modules/eslint-plugin-react": { - "version": "7.32.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.32.2.tgz", - "integrity": "sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg==", - "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.flatmap": "^1.3.1", - "array.prototype.tosorted": "^1.1.1", - "doctrine": "^2.1.0", - "estraverse": "^5.3.0", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.1.2", - "object.entries": "^1.1.6", - "object.fromentries": "^2.0.6", - "object.hasown": "^1.1.2", - "object.values": "^1.1.6", - "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.4", - "semver": "^6.3.0", - "string.prototype.matchall": "^4.0.8" - }, + "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==", + "license": "Apache-2.0", "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + "node": ">=10.17.0" } }, - "node_modules/eslint-plugin-react-hooks": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", - "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", + "node_modules/hyperdyperid": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/hyperdyperid/-/hyperdyperid-1.2.0.tgz", + "integrity": "sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==", + "devOptional": true, + "license": "MIT", "engines": { - "node": ">=10" - }, - "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + "node": ">=10.18" } }, - "node_modules/eslint-plugin-react/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "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==", + "license": "MIT", "dependencies": { - "esutils": "^2.0.2" + "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/eslint-plugin-react/node_modules/resolve": { - "version": "2.0.0-next.4", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", - "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==", - "dependencies": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" + "node_modules/icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "license": "ISC", + "engines": { + "node": "^10 || ^12 || >= 14" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "postcss": "^8.1.0" } }, - "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/idb": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", + "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==", + "license": "ISC" + }, + "node_modules/identity-obj-proxy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz", + "integrity": "sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==", + "license": "MIT", "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" + "harmony-reflect": "^1.4.6" }, "engines": { - "node": ">=8.0.0" + "node": ">=4" } }, - "node_modules/eslint-scope/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "license": "MIT", "engines": { - "node": ">=4.0" + "node": ">= 4" } }, - "node_modules/eslint-utils": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", - "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", - "dependencies": { - "eslint-visitor-keys": "^1.1.0" + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true, + "license": "ISC" + }, + "node_modules/immer": { + "version": "9.0.21", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", + "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/immutable": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", + "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==", + "devOptional": true, + "license": "MIT" + }, + "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==", + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" }, "engines": { "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "node_modules/import-fresh/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==", + "license": "MIT", "engines": { "node": ">=4" } }, - "node_modules/eslint/node_modules/globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "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==", + "license": "MIT", "dependencies": { - "type-fest": "^0.8.1" + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" }, "engines": { "node": ">=8" @@ -9617,613 +10361,538 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/eslint/node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=0.8.19" } }, - "node_modules/espree": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz", - "integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==", + "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==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "license": "ISC", "dependencies": { - "acorn": "^7.1.1", - "acorn-jsx": "^5.2.0", - "eslint-visitor-keys": "^1.1.0" - }, - "engines": { - "node": ">=6.0.0" + "once": "^1.3.0", + "wrappy": "1" } }, - "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==", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" }, - "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" }, - "node_modules/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/internal-slot": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", + "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", + "license": "MIT", "dependencies": { - "estraverse": "^5.2.0" + "es-errors": "^1.3.0", + "hasown": "^2.0.0", + "side-channel": "^1.0.4" }, "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "engines": { - "node": ">=4.0" + "node": ">= 0.4" } }, - "node_modules/estree-walker": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", - "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==" - }, - "node_modules/esutils": { + "node_modules/internmap": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "license": "ISC", "engines": { - "node": ">=0.10.0" + "node": ">=12" } }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "node_modules/interpret": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", + "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", + "dev": true, + "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">=10.13.0" } }, - "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "engines": { - "node": ">=0.8.x" + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.0.0" } }, - "node_modules/evp_bytestokey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", - "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", - "dependencies": { - "md5.js": "^1.3.4", - "safe-buffer": "^5.1.1" + "node_modules/ipaddr.js": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", + "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", + "license": "MIT", + "engines": { + "node": ">= 10" } }, - "node_modules/execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, + "node_modules/is-array-buffer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", + "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", + "license": "MIT", "dependencies": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1" }, "engines": { - "node": ">=6" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "engines": { - "node": ">= 0.8.0" - } + "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==", + "license": "MIT" }, - "node_modules/expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==", + "node_modules/is-async-function": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", + "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", + "license": "MIT", "dependencies": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/expand-brackets/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "license": "MIT", "dependencies": { - "ms": "2.0.0" + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/expand-brackets/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "license": "MIT", "dependencies": { - "is-descriptor": "^0.1.0" + "binary-extensions": "^2.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/expand-brackets/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "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==", + "license": "MIT", "dependencies": { - "is-extendable": "^0.1.0" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/expand-brackets/node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", + "node_modules/is-builtin-module": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", + "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", + "dev": true, + "license": "MIT", + "peer": true, "dependencies": { - "kind-of": "^3.0.2" + "builtin-modules": "^3.3.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/expand-brackets/node_modules/is-accessor-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dependencies": { - "is-buffer": "^1.1.5" - }, + "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==", + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/expand-brackets/node_modules/is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", + "node_modules/is-core-module": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", + "license": "MIT", "dependencies": { - "kind-of": "^3.0.2" + "hasown": "^2.0.2" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/expand-brackets/node_modules/is-data-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "node_modules/is-data-view": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", + "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", + "license": "MIT", "dependencies": { - "is-buffer": "^1.1.5" + "is-typed-array": "^1.1.13" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/expand-brackets/node_modules/is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "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==", + "license": "MIT", "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/expand-tilde": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", - "integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==", - "dev": true, - "dependencies": { - "homedir-polyfill": "^1.0.1" + "node": ">= 0.4" }, - "engines": { - "node": ">=0.10.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/expect": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.4.3.tgz", - "integrity": "sha512-uC05+Q7eXECFpgDrHdXA4k2rpMyStAYPItEDLyQDo5Ta7fVkJnNA/4zh/OIVkVVNZ1oOK1PipQoyNjuZ6sz6Dg==", - "dependencies": { - "@jest/expect-utils": "^29.4.3", - "jest-get-type": "^29.4.3", - "jest-matcher-utils": "^29.4.3", - "jest-message-util": "^29.4.3", - "jest-util": "^29.4.3" + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/express": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", - "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.3", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.7.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.3.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.10", - "proxy-addr": "~2.0.7", - "qs": "6.13.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" + "node": ">=8" }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/express/node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" - }, - "node_modules/express/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/express/node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", "engines": { - "node": ">= 0.8" + "node": ">=0.10.0" } }, - "node_modules/express/node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "node_modules/is-finalizationregistry": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz", + "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==", + "license": "MIT", "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" + "call-bind": "^1.0.2" }, - "engines": { - "node": ">= 0.8" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/express/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/express/node_modules/path-to-regexp": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", - "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==" - }, - "node_modules/express/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", "engines": { - "node": ">= 0.8" + "node": ">=8" } }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "optional": true, - "peer": true - }, - "node_modules/extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", - "dependencies": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, + "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==", + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=6" } }, - "node_modules/external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "license": "MIT", "dependencies": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">=4" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "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==", + "license": "MIT", "dependencies": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" + "is-extglob": "^2.1.1" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/extglob/node_modules/define-property": { + "node_modules/is-inside-container": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "devOptional": true, + "license": "MIT", "dependencies": { - "is-descriptor": "^1.0.0" + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" }, "engines": { - "node": ">=0.10.0" + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/extglob/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dependencies": { - "is-extendable": "^0.1.0" + "node_modules/is-inside-container/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==", + "devOptional": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" }, "engines": { - "node": ">=0.10.0" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/extglob/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "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==", + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", - "engines": [ - "node >=0.6.0" - ], - "optional": true, - "peer": true - }, - "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==" - }, - "node_modules/fast-equals": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-4.0.3.tgz", - "integrity": "sha512-G3BSX9cfKttjr+2o1O22tYMLq0DPluZnYtq1rXumE1SpL/F/SLIfHx08WYQoWSIpeMYf8sRbJ8++71+v6Pnxfg==" + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", + "license": "MIT" }, - "node_modules/fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", - "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.4" - }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "license": "MIT", "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dependencies": { - "fill-range": "^7.0.1" + "node": ">= 0.4" }, - "engines": { - "node": ">=8" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/fast-glob/node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dependencies": { - "to-regex-range": "^5.0.1" - }, + "node_modules/is-network-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.1.0.tgz", + "integrity": "sha512-tUdRRAnhT+OtCZR/LxZelH/C7QtjtFrTu5tXCA8pl55eTUElUHT+GPYV8MBMBvea/j+NxQqVt3LbWMRir7Gx9g==", + "devOptional": true, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/fast-glob/node_modules/is-number": { + "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==", + "license": "MIT", "engines": { "node": ">=0.12.0" } }, - "node_modules/fast-glob/node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "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==", + "license": "MIT", "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">=8.6" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/fast-glob/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==", - "dependencies": { - "is-number": "^7.0.0" - }, + "node_modules/is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", + "license": "MIT", "engines": { - "node": ">=8.0" + "node": ">=0.10.0" } }, - "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==" + "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==", + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "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==", - "dependencies": { - "reusify": "^1.0.4" + "node_modules/is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/faye-websocket": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", - "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "license": "MIT", "dependencies": { - "websocket-driver": ">=0.5.1" + "isobject": "^3.0.1" }, "engines": { - "node": ">=0.8.0" + "node": ">=0.10.0" } }, - "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==", + "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==", + "license": "MIT" + }, + "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==", + "license": "MIT", "dependencies": { - "bser": "2.1.1" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/figgy-pudding": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz", - "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==" + "node_modules/is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } }, - "node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "node_modules/is-root": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz", + "integrity": "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", + "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", + "license": "MIT", "dependencies": { - "escape-string-regexp": "^1.0.5" + "call-bind": "^1.0.7" + }, + "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==", + "license": "MIT", "engines": { "node": ">=8" }, @@ -10231,11616 +10900,1796 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/file-entry-cache": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", - "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "license": "MIT", "dependencies": { - "flat-cache": "^2.0.1" + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">=4" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/file-loader": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", - "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", + "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==", + "license": "MIT", "dependencies": { - "loader-utils": "^2.0.0", - "schema-utils": "^3.0.0" + "has-symbols": "^1.0.2" }, "engines": { - "node": ">= 10.13.0" + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/file-loader/node_modules/schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "node_modules/is-typed-array": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", + "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", + "license": "MIT", "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" + "which-typed-array": "^1.1.14" }, "engines": { - "node": ">= 10.13.0" + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/file-uri-to-path": { + "node_modules/is-typedarray": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "optional": true + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "license": "MIT" }, - "node_modules/filelist": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", - "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", - "dependencies": { - "minimatch": "^5.0.1" + "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==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/filelist/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==", + "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==", + "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/filelist/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "node_modules/is-weakset": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz", + "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==", + "license": "MIT", "dependencies": { - "brace-expansion": "^2.0.1" + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4" }, "engines": { - "node": ">=10" - } - }, - "node_modules/filesize": { - "version": "8.0.7", - "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz", - "integrity": "sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==", - "engines": { - "node": ">= 0.4.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "license": "MIT", "dependencies": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" + "is-docker": "^2.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/fill-range/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dependencies": { - "is-extendable": "^0.1.0" - }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/fill-range/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "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==", + "license": "BSD-3-Clause", "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/finalhandler": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", - "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "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==", + "license": "BSD-3-Clause", "dependencies": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" + "@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": ">= 0.8" + "node": ">=8" } }, - "node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "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==", + "license": "BSD-3-Clause", "dependencies": { - "ms": "2.0.0" + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" } }, - "node_modules/finalhandler/node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/finalhandler/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/find-cache-dir": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", - "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "node_modules/istanbul-lib-report/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==", + "license": "MIT", "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" + "semver": "^7.5.3" }, "engines": { - "node": ">=8" + "node": ">=10" }, "funding": { - "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "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==", + "license": "BSD-3-Clause", "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" }, "engines": { - "node": ">=8" + "node": ">=10" } }, - "node_modules/findup-sync": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", - "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==", - "dev": true, - "dependencies": { - "detect-file": "^1.0.0", - "is-glob": "^4.0.0", - "micromatch": "^3.0.4", - "resolve-dir": "^1.0.1" - }, + "node_modules/istanbul-lib-source-maps/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==", + "license": "BSD-3-Clause", "engines": { - "node": ">= 0.10" + "node": ">=0.10.0" } }, - "node_modules/flat-cache": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", - "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "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==", + "license": "BSD-3-Clause", "dependencies": { - "flatted": "^2.0.0", - "rimraf": "2.6.3", - "write": "1.0.3" + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" }, "engines": { - "node": ">=4" - } - }, - "node_modules/flatted": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", - "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==" - }, - "node_modules/flush-write-stream": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", - "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", - "dependencies": { - "inherits": "^2.0.3", - "readable-stream": "^2.3.6" + "node": ">=8" } }, - "node_modules/flush-write-stream/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, - "node_modules/flush-write-stream/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "node_modules/iterator.prototype": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.3.tgz", + "integrity": "sha512-FW5iMbeQ6rBGm/oKgzq2aW4KvAGpxPzYES8N4g4xNXUKpL1mclMvOe+76AcLDTvD+Ze+sOpVhgdAQEKF4L9iGQ==", + "license": "MIT", "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "define-properties": "^1.2.1", + "get-intrinsic": "^1.2.1", + "has-symbols": "^1.0.3", + "reflect.getprototypeof": "^1.0.4", + "set-function-name": "^2.0.1" + }, + "engines": { + "node": ">= 0.4" } }, - "node_modules/flush-write-stream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/flush-write-stream/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "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==", + "license": "BlueOak-1.0.0", "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/follow-redirects": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", - "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" + "@isaacs/cliui": "^8.0.2" }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" } }, - "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/jake": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", + "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", + "license": "Apache-2.0", "dependencies": { - "is-callable": "^1.1.3" - } - }, - "node_modules/for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", - "optional": true, - "peer": true, + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + }, + "bin": { + "jake": "bin/cli.js" + }, "engines": { - "node": "*" + "node": ">=10" } }, - "node_modules/fork-ts-checker-webpack-plugin": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.2.tgz", - "integrity": "sha512-m5cUmF30xkZ7h4tWUgTAcEaKmUW7tfyUyTqNNOz7OxWJ0v1VWKTcOvH8FWHUwSjlW/356Ijc9vi3XfcPstpQKA==", + "node_modules/jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", + "integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==", + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.8.3", - "@types/json-schema": "^7.0.5", - "chalk": "^4.1.0", - "chokidar": "^3.4.2", - "cosmiconfig": "^6.0.0", - "deepmerge": "^4.2.2", - "fs-extra": "^9.0.0", - "glob": "^7.1.6", - "memfs": "^3.1.2", - "minimatch": "^3.0.4", - "schema-utils": "2.7.0", - "semver": "^7.3.2", - "tapable": "^1.0.0" + "@jest/core": "^27.5.1", + "import-local": "^3.0.2", + "jest-cli": "^27.5.1" + }, + "bin": { + "jest": "bin/jest.js" }, "engines": { - "node": ">=10", - "yarn": ">=1.0.0" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" }, "peerDependencies": { - "eslint": ">= 6", - "typescript": ">= 2.7", - "vue-template-compiler": "*", - "webpack": ">= 4" + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" }, "peerDependenciesMeta": { - "eslint": { - "optional": true - }, - "vue-template-compiler": { + "node-notifier": { "optional": true } } }, - "node_modules/fork-ts-checker-webpack-plugin/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/jest-changed-files": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.5.1.tgz", + "integrity": "sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw==", + "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "@jest/types": "^27.5.1", + "execa": "^5.0.0", + "throat": "^6.0.1" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/fork-ts-checker-webpack-plugin/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/jest-circus": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.5.1.tgz", + "integrity": "sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw==", + "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@jest/environment": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "expect": "^27.5.1", + "is-generator-fn": "^2.0.0", + "jest-each": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3", + "throat": "^6.0.1" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/fork-ts-checker-webpack-plugin/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/jest-cli": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.5.1.tgz", + "integrity": "sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw==", + "license": "MIT", "dependencies": { - "color-name": "~1.1.4" + "@jest/core": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "import-local": "^3.0.2", + "jest-config": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "prompts": "^2.0.1", + "yargs": "^16.2.0" + }, + "bin": { + "jest": "bin/jest.js" }, "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/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==" - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/cosmiconfig": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", - "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", - "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.1.0", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.7.2" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" }, - "engines": { - "node": ">=10" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/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==", - "engines": { - "node": ">=8" + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "node_modules/jest-config": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-27.5.1.tgz", + "integrity": "sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA==", + "license": "MIT", "dependencies": { - "yallist": "^4.0.0" + "@babel/core": "^7.8.0", + "@jest/test-sequencer": "^27.5.1", + "@jest/types": "^27.5.1", + "babel-jest": "^27.5.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.1", + "graceful-fs": "^4.2.9", + "jest-circus": "^27.5.1", + "jest-environment-jsdom": "^27.5.1", + "jest-environment-node": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-jasmine2": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-runner": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" }, "engines": { - "node": ">=10" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "ts-node": { + "optional": true + } } }, - "node_modules/fork-ts-checker-webpack-plugin/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==", + "node_modules/jest-diff": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", + "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", + "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" + "chalk": "^4.0.0", + "diff-sequences": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/schema-utils": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", - "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", + "node_modules/jest-docblock": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.5.1.tgz", + "integrity": "sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ==", + "license": "MIT", "dependencies": { - "@types/json-schema": "^7.0.4", - "ajv": "^6.12.2", - "ajv-keywords": "^3.4.1" + "detect-newline": "^3.0.0" }, "engines": { - "node": ">= 8.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "node_modules/jest-each": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.5.1.tgz", + "integrity": "sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==", + "license": "MIT", "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "jest-get-type": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1" }, "engines": { - "node": ">=10" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/fork-ts-checker-webpack-plugin/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/jest-environment-jsdom": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz", + "integrity": "sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==", + "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1", + "jsdom": "^16.6.0" }, "engines": { - "node": ">=8" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "node_modules/jest-environment-node": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.5.1.tgz", + "integrity": "sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw==", + "license": "MIT", "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1" }, "engines": { - "node": ">= 6" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "node_modules/jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", + "license": "MIT", "engines": { - "node": ">= 0.6" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/fraction.js": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", - "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", + "node_modules/jest-haste-map": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.5.1.tgz", + "integrity": "sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng==", + "license": "MIT", + "dependencies": { + "@jest/types": "^27.5.1", + "@types/graceful-fs": "^4.1.2", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^27.5.1", + "jest-serializer": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "micromatch": "^4.0.4", + "walker": "^1.0.7" + }, "engines": { - "node": "*" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" }, - "funding": { - "type": "patreon", - "url": "https://www.patreon.com/infusion" + "optionalDependencies": { + "fsevents": "^2.3.2" } }, - "node_modules/fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==", + "node_modules/jest-jasmine2": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz", + "integrity": "sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ==", + "license": "MIT", "dependencies": { - "map-cache": "^0.2.2" + "@jest/environment": "^27.5.1", + "@jest/source-map": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "expect": "^27.5.1", + "is-generator-fn": "^2.0.0", + "jest-each": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1", + "throat": "^6.0.1" }, "engines": { - "node": ">=0.10.0" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "node_modules/jest-leak-detector": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz", + "integrity": "sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ==", + "license": "MIT", + "dependencies": { + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, "engines": { - "node": ">= 0.6" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/from2": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", - "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==", + "node_modules/jest-matcher-utils": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", + "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", + "license": "MIT", "dependencies": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.0" + "chalk": "^4.0.0", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/from2/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, - "node_modules/from2/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/from2/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/from2/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "node_modules/jest-message-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", + "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", + "license": "MIT", "dependencies": { - "minipass": "^3.0.0" + "@babel/code-frame": "^7.12.13", + "@jest/types": "^27.5.1", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" }, "engines": { - "node": ">= 8" - } - }, - "node_modules/fs-monkey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", - "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==" - }, - "node_modules/fs-write-stream-atomic": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", - "integrity": "sha512-gehEzmPn2nAwr39eay+x3X34Ra+M2QlVUTLhkXPjWdeO8RF9kszk116avgBJM3ZyNHgHXBNx+VmPaFC36k0PzA==", - "dependencies": { - "graceful-fs": "^4.1.2", - "iferr": "^0.1.5", - "imurmurhash": "^0.1.4", - "readable-stream": "1 || 2" - } - }, - "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==" - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/function.prototype.name": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", - "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "node_modules/jest-mock": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz", + "integrity": "sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "functions-have-names": "^1.2.2" + "@jest/types": "^27.5.1", + "@types/node": "*" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==" - }, - "node_modules/functions-have-names": { + "node_modules/jest-pnp-resolver": { "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gauge": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", - "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", - "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.3", - "console-control-strings": "^1.1.0", - "has-unicode": "^2.0.1", - "signal-exit": "^3.0.7", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.5" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/gauge/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/gauge/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==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/gaze": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", - "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", - "dependencies": { - "globule": "^1.0.0" - }, - "engines": { - "node": ">= 4.0.0" - } - }, - "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==", - "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==", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-own-enumerable-property-symbols": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", - "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==" - }, - "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==", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/get-stdin": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", - "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "dependencies": { - "pump": "^3.0.0" - }, + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "license": "MIT", "engines": { "node": ">=6" - } - }, - "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==", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" }, - "engines": { - "node": ">= 0.4" + "peerDependencies": { + "jest-resolve": "*" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", - "optional": true, - "peer": true, - "dependencies": { - "assert-plus": "^1.0.0" + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } } }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "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" - }, + "node_modules/jest-regex-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz", + "integrity": "sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==", + "license": "MIT", "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "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==", + "node_modules/jest-resolve": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz", + "integrity": "sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==", + "license": "MIT", "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" - }, - "node_modules/global-modules": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", - "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", - "dependencies": { - "global-prefix": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/global-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", - "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", - "dependencies": { - "ini": "^1.3.5", - "kind-of": "^6.0.2", - "which": "^1.3.1" - }, - "engines": { - "node": ">=6" - } - }, - "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==", - "engines": { - "node": ">=4" - } - }, - "node_modules/globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", - "dependencies": { - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "resolve": "^1.20.0", + "resolve.exports": "^1.1.0", "slash": "^3.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globby/node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", - "engines": { - "node": ">= 4" - } - }, - "node_modules/globule": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.4.tgz", - "integrity": "sha512-OPTIfhMBh7JbBYDpa5b+Q5ptmMWKwcNcFSR/0c6t8V4f3ZAVBEsKNY37QdVqmLRYSMhOUGYrY0QhSoEpzGr/Eg==", - "dependencies": { - "glob": "~7.1.1", - "lodash": "^4.17.21", - "minimatch": "~3.0.2" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/globule/node_modules/glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/globule/node_modules/minimatch": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz", - "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" - }, - "node_modules/grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==" - }, - "node_modules/gzip-size": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", - "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", - "dependencies": { - "duplexer": "^0.1.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/handle-thing": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", - "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==" - }, - "node_modules/har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", - "optional": true, - "peer": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "deprecated": "this library is no longer supported", - "optional": true, - "peer": true, - "dependencies": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/hard-rejection": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", - "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/harmony-reflect": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz", - "integrity": "sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==" - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "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==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "engines": { - "node": ">=4" - } - }, - "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==", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "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==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "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==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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==", - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" - }, - "node_modules/has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==", - "dependencies": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==", - "dependencies": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-values/node_modules/kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/hash-base": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", - "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", - "dependencies": { - "inherits": "^2.0.4", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/hash-base/node_modules/readable-stream": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.1.tgz", - "integrity": "sha512-+rQmrWMYGA90yenhTYsLWAsLsqVC8osOw6PKE1HDYiO0gdPeKe/xDHNzIAIn4C91YQ6oenEhfYqqc1883qHbjQ==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/hash-base/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", - "dependencies": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "bin": { - "he": "bin/he" - } - }, - "node_modules/hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", - "dependencies": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "node_modules/hoek": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-6.1.3.tgz", - "integrity": "sha512-YXXAAhmF9zpQbC7LEcREFtXfGq5K1fmd+4PHkBq8NUqmzW3G+Dq10bI/i0KucLRwss3YYFQ0fSfoxBZYiGUqtQ==", - "deprecated": "This module has moved and is now available at @hapi/hoek. Please update your dependencies as this version is no longer maintained an may contain bugs and security issues.", - "devOptional": true - }, - "node_modules/homedir-polyfill": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", - "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", - "dev": true, - "dependencies": { - "parse-passwd": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/hoopy": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", - "integrity": "sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==", - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/hosted-git-info": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", - "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/hosted-git-info/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/hosted-git-info/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/hpack.js": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", - "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", - "dependencies": { - "inherits": "^2.0.1", - "obuf": "^1.0.0", - "readable-stream": "^2.0.1", - "wbuf": "^1.1.0" - } - }, - "node_modules/hpack.js/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, - "node_modules/hpack.js/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/hpack.js/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/hpack.js/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/html-encoding-sniffer": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", - "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", - "dependencies": { - "whatwg-encoding": "^1.0.5" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/html-entities": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", - "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==" - }, - "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==" - }, - "node_modules/html-minifier-terser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", - "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", - "dependencies": { - "camel-case": "^4.1.2", - "clean-css": "^5.2.2", - "commander": "^8.3.0", - "he": "^1.2.0", - "param-case": "^3.0.4", - "relateurl": "^0.2.7", - "terser": "^5.10.0" - }, - "bin": { - "html-minifier-terser": "cli.js" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/html-minifier-terser/node_modules/acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/html-minifier-terser/node_modules/commander": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", - "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", - "engines": { - "node": ">= 12" - } - }, - "node_modules/html-minifier-terser/node_modules/terser": { - "version": "5.16.5", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.5.tgz", - "integrity": "sha512-qcwfg4+RZa3YvlFh0qjifnzBHjKGNbtDo9yivMqMFDy9Q6FSaQWSB/j1xKhsoUFJIqDOM3TsN6D5xbrMrFcHbg==", - "dependencies": { - "@jridgewell/source-map": "^0.3.2", - "acorn": "^8.5.0", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/html-minifier-terser/node_modules/terser/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" - }, - "node_modules/htmlparser2": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", - "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.0.0", - "domutils": "^2.5.2", - "entities": "^2.0.0" - } - }, - "node_modules/htmlparser2/node_modules/dom-serializer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", - "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/htmlparser2/node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ] - }, - "node_modules/htmlparser2/node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/http-assert": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/http-assert/-/http-assert-1.5.0.tgz", - "integrity": "sha512-uPpH7OKX4H25hBmU6G1jWNaqJGpTXxey+YOUizJUAgu0AjLUeC8D73hTrhvDS5D+GJN1DN1+hhc/eF/wpxtp0w==", - "devOptional": true, - "dependencies": { - "deep-equal": "~1.0.1", - "http-errors": "~1.8.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/http-cache-semantics": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", - "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==" - }, - "node_modules/http-deceiver": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", - "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==" - }, - "node_modules/http-errors": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", - "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", - "devOptional": true, - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/http-errors/node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", - "devOptional": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/http-parser-js": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", - "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==" - }, - "node_modules/http-proxy": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", - "dependencies": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "dependencies": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/http-proxy-middleware": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.2.tgz", - "integrity": "sha512-aYk1rTKqLTus23X3L96LGNCGNgWpG4cG0XoZIT1GUPhhulEHX/QalnO6Vbo+WmKWi4AL2IidjuC0wZtbpg0yhQ==", - "devOptional": true, - "dependencies": { - "http-proxy": "^1.18.1", - "is-glob": "^4.0.0", - "lodash": "^4.17.11", - "micromatch": "^3.1.10" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", - "optional": true, - "peer": true, - "dependencies": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - }, - "engines": { - "node": ">=0.8", - "npm": ">=1.3.7" - } - }, - "node_modules/https-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", - "integrity": "sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==" - }, - "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "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==", - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/humanize-ms": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", - "dependencies": { - "ms": "^2.0.0" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/icss-utils": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-4.1.1.tgz", - "integrity": "sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA==", - "dev": true, - "dependencies": { - "postcss": "^7.0.14" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/idb": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", - "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==" - }, - "node_modules/identity-obj-proxy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz", - "integrity": "sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==", - "dependencies": { - "harmony-reflect": "^1.4.6" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/iferr": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", - "integrity": "sha512-DUNFN5j7Tln0D+TxzloUjKB+CtVu6myn0JEFak6dG18mNt9YkQ6lzGCdafwofISZ1lLF3xRHJ98VKy9ynkcFaA==" - }, - "node_modules/ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "engines": { - "node": ">= 4" - } - }, - "node_modules/ignore-by-default": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", - "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==" - }, - "node_modules/immer": { - "version": "9.0.19", - "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.19.tgz", - "integrity": "sha512-eY+Y0qcsB4TZKwgQzLaE/lqYMlKhv5J9dyd2RhhtGhNo2njPXDqU9XPfcNfa3MIDsdtZt5KlkIsirlo4dHsWdQ==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/immer" - } - }, - "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==", - "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": "2.0.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", - "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", - "dev": true, - "dependencies": { - "pkg-dir": "^3.0.0", - "resolve-cwd": "^2.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/import-local/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/import-local/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/import-local/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/import-local/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/import-local/node_modules/pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "dev": true, - "dependencies": { - "find-up": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/infer-owner": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", - "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==" - }, - "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==", - "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==" - }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" - }, - "node_modules/inquirer": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", - "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==", - "dependencies": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.19", - "mute-stream": "0.0.8", - "run-async": "^2.4.0", - "rxjs": "^6.6.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/inquirer/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/inquirer/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/inquirer/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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/inquirer/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/inquirer/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==" - }, - "node_modules/inquirer/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/inquirer/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==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/inquirer/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/internal-slot": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", - "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", - "dependencies": { - "get-intrinsic": "^1.2.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/internmap": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", - "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", - "engines": { - "node": ">=12" - } - }, - "node_modules/interpret": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz", - "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==", - "dev": true, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "dependencies": { - "loose-envify": "^1.0.0" - } - }, - "node_modules/invert-kv": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", - "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/ip": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.1.tgz", - "integrity": "sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ==" - }, - "node_modules/ipaddr.js": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", - "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==", - "engines": { - "node": ">= 10" - } - }, - "node_modules/is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-array-buffer": { - "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==", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "is-typed-array": "^1.1.10" - }, - "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==" - }, - "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dependencies": { - "has-bigints": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "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==", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "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==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", - "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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==", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dependencies": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dependencies": { - "is-plain-object": "^2.0.4" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "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==", - "engines": { - "node": ">=6" - } - }, - "node_modules/is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", - "devOptional": true, - "dependencies": { - "has-tostringtag": "^1.0.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==", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-lambda": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", - "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==" - }, - "node_modules/is-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", - "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", - "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==" - }, - "node_modules/is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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==", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-number/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", - "engines": { - "node": ">=0.10.0" - } - }, - "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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-potential-custom-element-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==" - }, - "node_modules/is-promise": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", - "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", - "devOptional": true - }, - "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==", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", - "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-root": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz", - "integrity": "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/is-set": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", - "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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==", - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "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==", - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", - "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" - }, - "node_modules/is-weakmap": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", - "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", - "funding": { - "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==", - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakset": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", - "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", - "engines": { - "node": ">=4" - } - }, - "node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" - }, - "node_modules/isemail": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/isemail/-/isemail-3.2.0.tgz", - "integrity": "sha512-zKqkK+O+dGqevc93KNsbZ/TqTUFd46MwWjYOoMrjIMZ51eU7DtQG3Wmd9SQQT7i7RVnuTPEiYEWHU3MSbxC1Tg==", - "devOptional": true, - "dependencies": { - "punycode": "2.x.x" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" - }, - "node_modules/isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", - "optional": true, - "peer": true - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", - "engines": { - "node": ">=8" - } - }, - "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==", - "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/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "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==", - "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.5", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", - "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jake": { - "version": "10.8.5", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz", - "integrity": "sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==", - "dependencies": { - "async": "^3.2.3", - "chalk": "^4.0.2", - "filelist": "^1.0.1", - "minimatch": "^3.0.4" - }, - "bin": { - "jake": "bin/cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jake/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jake/node_modules/async": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", - "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" - }, - "node_modules/jake/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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/jake/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jake/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==" - }, - "node_modules/jake/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jake/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", - "integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==", - "dependencies": { - "@jest/core": "^27.5.1", - "import-local": "^3.0.2", - "jest-cli": "^27.5.1" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.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": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.5.1.tgz", - "integrity": "sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw==", - "dependencies": { - "@jest/types": "^27.5.1", - "execa": "^5.0.0", - "throat": "^6.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-changed-files/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-changed-files/node_modules/@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-changed-files/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-changed-files/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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/jest-changed-files/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-changed-files/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==" - }, - "node_modules/jest-changed-files/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==", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/jest-changed-files/node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "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/jest-changed-files/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==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-changed-files/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-changed-files/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==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-changed-files/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==", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-changed-files/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-changed-files/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==", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-changed-files/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-changed-files/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-changed-files/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/jest-circus": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.5.1.tgz", - "integrity": "sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw==", - "dependencies": { - "@jest/environment": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^0.7.0", - "expect": "^27.5.1", - "is-generator-fn": "^2.0.0", - "jest-each": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3", - "throat": "^6.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-circus/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-circus/node_modules/@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-circus/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-circus/node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-circus/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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/jest-circus/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-circus/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==" - }, - "node_modules/jest-circus/node_modules/diff-sequences": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", - "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-circus/node_modules/expect": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", - "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", - "dependencies": { - "@jest/types": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-circus/node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-circus/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-circus/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==", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/jest-circus/node_modules/jest-diff": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", - "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-circus/node_modules/jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-circus/node_modules/jest-matcher-utils": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", - "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-circus/node_modules/jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-circus/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-circus/node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/jest-circus/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-circus/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==", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/jest-cli": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.5.1.tgz", - "integrity": "sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw==", - "dependencies": { - "@jest/core": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "import-local": "^3.0.2", - "jest-config": "^27.5.1", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "prompts": "^2.0.1", - "yargs": "^16.2.0" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-cli/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-cli/node_modules/@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-cli/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-cli/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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/jest-cli/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-cli/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==" - }, - "node_modules/jest-cli/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-cli/node_modules/import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "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/jest-cli/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-cli/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==", - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-cli/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-cli/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-config": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-27.5.1.tgz", - "integrity": "sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA==", - "dependencies": { - "@babel/core": "^7.8.0", - "@jest/test-sequencer": "^27.5.1", - "@jest/types": "^27.5.1", - "babel-jest": "^27.5.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.1", - "graceful-fs": "^4.2.9", - "jest-circus": "^27.5.1", - "jest-environment-jsdom": "^27.5.1", - "jest-environment-node": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-jasmine2": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-runner": "^27.5.1", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "ts-node": { - "optional": true - } - } - }, - "node_modules/jest-config/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-config/node_modules/@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-config/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-config/node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-config/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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/jest-config/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-config/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==" - }, - "node_modules/jest-config/node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-config/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-config/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==", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/jest-config/node_modules/jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-config/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-config/node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/jest-config/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==", - "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": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-config/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-config/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==", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/jest-diff": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.4.3.tgz", - "integrity": "sha512-YB+ocenx7FZ3T5O9lMVMeLYV4265socJKtkwgk/6YUz/VsEzYDkiMuMhWzZmxm3wDRQvayJu/PjkjjSkjoHsCA==", - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.4.3", - "jest-get-type": "^29.4.3", - "pretty-format": "^29.4.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-diff/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-diff/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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/jest-diff/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-diff/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==" - }, - "node_modules/jest-diff/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-diff/node_modules/pretty-format": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.4.3.tgz", - "integrity": "sha512-cvpcHTc42lcsvOOAzd3XuNWTcvk1Jmnzqeu+WsOuiPmxUJTnkbAcFNsRKvEpBEUFVUgy/GTZLulZDcDEi+CIlA==", - "dependencies": { - "@jest/schemas": "^29.4.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-diff/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==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-diff/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" - }, - "node_modules/jest-diff/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-docblock": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.5.1.tgz", - "integrity": "sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ==", - "dependencies": { - "detect-newline": "^3.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-each": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.5.1.tgz", - "integrity": "sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==", - "dependencies": { - "@jest/types": "^27.5.1", - "chalk": "^4.0.0", - "jest-get-type": "^27.5.1", - "jest-util": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-each/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-each/node_modules/@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-each/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-each/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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/jest-each/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-each/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==" - }, - "node_modules/jest-each/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-each/node_modules/jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-each/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-each/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-environment-jsdom": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz", - "integrity": "sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==", - "dependencies": { - "@jest/environment": "^27.5.1", - "@jest/fake-timers": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "jest-mock": "^27.5.1", - "jest-util": "^27.5.1", - "jsdom": "^16.6.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-environment-jsdom/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-environment-jsdom/node_modules/@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-environment-jsdom/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-environment-jsdom/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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/jest-environment-jsdom/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-environment-jsdom/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==" - }, - "node_modules/jest-environment-jsdom/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-environment-jsdom/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-environment-jsdom/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-environment-node": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.5.1.tgz", - "integrity": "sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw==", - "dependencies": { - "@jest/environment": "^27.5.1", - "@jest/fake-timers": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "jest-mock": "^27.5.1", - "jest-util": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-environment-node/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-environment-node/node_modules/@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-environment-node/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-environment-node/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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/jest-environment-node/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-environment-node/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==" - }, - "node_modules/jest-environment-node/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-environment-node/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-environment-node/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-get-type": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.3.tgz", - "integrity": "sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-haste-map": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.5.1.tgz", - "integrity": "sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng==", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/graceful-fs": "^4.1.2", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^27.5.1", - "jest-serializer": "^27.5.1", - "jest-util": "^27.5.1", - "jest-worker": "^27.5.1", - "micromatch": "^4.0.4", - "walker": "^1.0.7" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - } - }, - "node_modules/jest-haste-map/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-haste-map/node_modules/@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-haste-map/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-haste-map/node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-haste-map/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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/jest-haste-map/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-haste-map/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==" - }, - "node_modules/jest-haste-map/node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-haste-map/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-haste-map/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==", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/jest-haste-map/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-haste-map/node_modules/jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/jest-haste-map/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/jest-haste-map/node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/jest-haste-map/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-haste-map/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==", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/jest-jasmine2": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz", - "integrity": "sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ==", - "dependencies": { - "@jest/environment": "^27.5.1", - "@jest/source-map": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "expect": "^27.5.1", - "is-generator-fn": "^2.0.0", - "jest-each": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "pretty-format": "^27.5.1", - "throat": "^6.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-jasmine2/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-jasmine2/node_modules/@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-jasmine2/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-jasmine2/node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-jasmine2/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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/jest-jasmine2/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-jasmine2/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==" - }, - "node_modules/jest-jasmine2/node_modules/diff-sequences": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", - "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-jasmine2/node_modules/expect": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", - "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", - "dependencies": { - "@jest/types": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-jasmine2/node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-jasmine2/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-jasmine2/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==", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/jest-jasmine2/node_modules/jest-diff": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", - "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-jasmine2/node_modules/jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-jasmine2/node_modules/jest-matcher-utils": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", - "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-jasmine2/node_modules/jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-jasmine2/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-jasmine2/node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/jest-jasmine2/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-jasmine2/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==", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/jest-leak-detector": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz", - "integrity": "sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ==", - "dependencies": { - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-leak-detector/node_modules/jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-matcher-utils": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.4.3.tgz", - "integrity": "sha512-TTciiXEONycZ03h6R6pYiZlSkvYgT0l8aa49z/DLSGYjex4orMUcafuLXYyyEDWB1RKglq00jzwY00Ei7yFNVg==", - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.4.3", - "jest-get-type": "^29.4.3", - "pretty-format": "^29.4.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-matcher-utils/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-matcher-utils/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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/jest-matcher-utils/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-matcher-utils/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==" - }, - "node_modules/jest-matcher-utils/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-matcher-utils/node_modules/pretty-format": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.4.3.tgz", - "integrity": "sha512-cvpcHTc42lcsvOOAzd3XuNWTcvk1Jmnzqeu+WsOuiPmxUJTnkbAcFNsRKvEpBEUFVUgy/GTZLulZDcDEi+CIlA==", - "dependencies": { - "@jest/schemas": "^29.4.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-matcher-utils/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==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-matcher-utils/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" - }, - "node_modules/jest-matcher-utils/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-message-util": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.4.3.tgz", - "integrity": "sha512-1Y8Zd4ZCN7o/QnWdMmT76If8LuDv23Z1DRovBj/vcSFNlGCJGoO8D1nJDw1AdyAGUk0myDLFGN5RbNeJyCRGCw==", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.4.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.4.3", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-message-util/node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-message-util/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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/jest-message-util/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-message-util/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==" - }, - "node_modules/jest-message-util/node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-message-util/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-message-util/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==", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/jest-message-util/node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/jest-message-util/node_modules/pretty-format": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.4.3.tgz", - "integrity": "sha512-cvpcHTc42lcsvOOAzd3XuNWTcvk1Jmnzqeu+WsOuiPmxUJTnkbAcFNsRKvEpBEUFVUgy/GTZLulZDcDEi+CIlA==", - "dependencies": { - "@jest/schemas": "^29.4.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util/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==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-message-util/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" - }, - "node_modules/jest-message-util/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-message-util/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==", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/jest-mock": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz", - "integrity": "sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-mock/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-mock/node_modules/@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-mock/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-mock/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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/jest-mock/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-mock/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==" - }, - "node_modules/jest-mock/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-mock/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "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==", - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } - } - }, - "node_modules/jest-regex-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz", - "integrity": "sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==", - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-resolve": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz", - "integrity": "sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==", - "dependencies": { - "@jest/types": "^27.5.1", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "resolve": "^1.20.0", - "resolve.exports": "^1.1.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-resolve-dependencies": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz", - "integrity": "sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg==", - "dependencies": { - "@jest/types": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-snapshot": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-resolve-dependencies/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-resolve-dependencies/node_modules/@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-resolve-dependencies/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-resolve-dependencies/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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/jest-resolve-dependencies/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-resolve-dependencies/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==" - }, - "node_modules/jest-resolve-dependencies/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-resolve-dependencies/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-resolve/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-resolve/node_modules/@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-resolve/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-resolve/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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/jest-resolve/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-resolve/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==" - }, - "node_modules/jest-resolve/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-resolve/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-resolve/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runner": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.5.1.tgz", - "integrity": "sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ==", - "dependencies": { - "@jest/console": "^27.5.1", - "@jest/environment": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.8.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^27.5.1", - "jest-environment-jsdom": "^27.5.1", - "jest-environment-node": "^27.5.1", - "jest-haste-map": "^27.5.1", - "jest-leak-detector": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-util": "^27.5.1", - "jest-worker": "^27.5.1", - "source-map-support": "^0.5.6", - "throat": "^6.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-runner/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-runner/node_modules/@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-runner/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-runner/node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runner/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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/jest-runner/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-runner/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==" - }, - "node_modules/jest-runner/node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runner/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runner/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==", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/jest-runner/node_modules/jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-runner/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-runner/node_modules/jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/jest-runner/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/jest-runner/node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/jest-runner/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runner/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==", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/jest-runtime": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.5.1.tgz", - "integrity": "sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==", - "dependencies": { - "@jest/environment": "^27.5.1", - "@jest/fake-timers": "^27.5.1", - "@jest/globals": "^27.5.1", - "@jest/source-map": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "execa": "^5.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-mock": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-runtime/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-runtime/node_modules/@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-runtime/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-runtime/node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runtime/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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/jest-runtime/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-runtime/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==" - }, - "node_modules/jest-runtime/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==", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/jest-runtime/node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "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/jest-runtime/node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runtime/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==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-runtime/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runtime/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==", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/jest-runtime/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==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-runtime/node_modules/jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-runtime/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-runtime/node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/jest-runtime/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==", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runtime/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runtime/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==", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runtime/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runtime/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runtime/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runtime/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==", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/jest-runtime/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/jest-serializer": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.5.1.tgz", - "integrity": "sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==", - "dependencies": { - "@types/node": "*", - "graceful-fs": "^4.2.9" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-snapshot": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.5.1.tgz", - "integrity": "sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==", - "dependencies": { - "@babel/core": "^7.7.2", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/traverse": "^7.7.2", - "@babel/types": "^7.0.0", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/babel__traverse": "^7.0.4", - "@types/prettier": "^2.1.5", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^27.5.1", - "graceful-fs": "^4.2.9", - "jest-diff": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-haste-map": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-util": "^27.5.1", - "natural-compare": "^1.4.0", - "pretty-format": "^27.5.1", - "semver": "^7.3.2" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-snapshot/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-snapshot/node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-snapshot/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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/jest-snapshot/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-snapshot/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==" - }, - "node_modules/jest-snapshot/node_modules/diff-sequences": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", - "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/expect": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", - "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", - "dependencies": { - "@jest/types": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-snapshot/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-snapshot/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==", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/jest-snapshot/node_modules/jest-diff": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", - "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/jest-matcher-utils": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", - "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-snapshot/node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-snapshot/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-snapshot/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==", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/jest-snapshot/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/jest-util": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.4.3.tgz", - "integrity": "sha512-ToSGORAz4SSSoqxDSylWX8JzkOQR7zoBtNRsA7e+1WUX5F8jrOwaNpuh1YfJHJKDHXLHmObv5eOjejUd+/Ws+Q==", - "dependencies": { - "@jest/types": "^29.4.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-util/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-util/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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/jest-util/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-util/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==" - }, - "node_modules/jest-util/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-util/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-validate": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.5.1.tgz", - "integrity": "sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==", - "dependencies": { - "@jest/types": "^27.5.1", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^27.5.1", - "leven": "^3.1.0", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-validate/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-validate/node_modules/@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-validate/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "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==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-validate/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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/jest-validate/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-validate/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==" - }, - "node_modules/jest-validate/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-validate/node_modules/jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-validate/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watch-typeahead": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/jest-watch-typeahead/-/jest-watch-typeahead-1.1.0.tgz", - "integrity": "sha512-Va5nLSJTN7YFtC2jd+7wsoe1pNe5K4ShLux/E5iHEwlB9AxaxmggY7to9KUqKojhaJw3aXqt5WAb4jGPOolpEw==", - "dependencies": { - "ansi-escapes": "^4.3.1", - "chalk": "^4.0.0", - "jest-regex-util": "^28.0.0", - "jest-watcher": "^28.0.0", - "slash": "^4.0.0", - "string-length": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "jest": "^27.0.0 || ^28.0.0" - } - }, - "node_modules/jest-watch-typeahead/node_modules/@jest/console": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-28.1.3.tgz", - "integrity": "sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw==", - "dependencies": { - "@jest/types": "^28.1.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^28.1.3", - "jest-util": "^28.1.3", - "slash": "^3.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-watch-typeahead/node_modules/@jest/console/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watch-typeahead/node_modules/@jest/schemas": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", - "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", - "dependencies": { - "@sinclair/typebox": "^0.24.1" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-watch-typeahead/node_modules/@jest/test-result": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-28.1.3.tgz", - "integrity": "sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg==", - "dependencies": { - "@jest/console": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-watch-typeahead/node_modules/@jest/types": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", - "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", - "dependencies": { - "@jest/schemas": "^28.1.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": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-watch-typeahead/node_modules/@sinclair/typebox": { - "version": "0.24.51", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", - "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==" - }, - "node_modules/jest-watch-typeahead/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watch-typeahead/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-watch-typeahead/node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watch-typeahead/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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/jest-watch-typeahead/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-watch-typeahead/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==" - }, - "node_modules/jest-watch-typeahead/node_modules/emittery": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz", - "integrity": "sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } - }, - "node_modules/jest-watch-typeahead/node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watch-typeahead/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watch-typeahead/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==", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/jest-watch-typeahead/node_modules/jest-message-util": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz", - "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^28.1.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^28.1.3", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-watch-typeahead/node_modules/jest-message-util/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watch-typeahead/node_modules/jest-regex-util": { - "version": "28.0.2", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz", - "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==", - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-watch-typeahead/node_modules/jest-util": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", - "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", - "dependencies": { - "@jest/types": "^28.1.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-watch-typeahead/node_modules/jest-watcher": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-28.1.3.tgz", - "integrity": "sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==", - "dependencies": { - "@jest/test-result": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.10.2", - "jest-util": "^28.1.3", - "string-length": "^4.0.1" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-watch-typeahead/node_modules/jest-watcher/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==", - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-watch-typeahead/node_modules/jest-watcher/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==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watch-typeahead/node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/jest-watch-typeahead/node_modules/pretty-format": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", - "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", - "dependencies": { - "@jest/schemas": "^28.1.3", - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-watch-typeahead/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==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-watch-typeahead/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" - }, - "node_modules/jest-watch-typeahead/node_modules/slash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-watch-typeahead/node_modules/string-length": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-5.0.1.tgz", - "integrity": "sha512-9Ep08KAMUn0OadnVaBuRdE2l615CQ508kr0XMadjClfYpdCyvrbFp6Taebo8yyxokQ4viUd/xPPUA4FGgUa0ow==", - "dependencies": { - "char-regex": "^2.0.0", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-watch-typeahead/node_modules/string-length/node_modules/char-regex": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-2.0.1.tgz", - "integrity": "sha512-oSvEeo6ZUD7NepqAat3RqoucZ5SeqLJgOvVIwkafu6IP3V0pO38s/ypdVUmDDK6qIIHNlYHJAKX9E7R7HoKElw==", - "engines": { - "node": ">=12.20" - } - }, - "node_modules/jest-watch-typeahead/node_modules/strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/jest-watch-typeahead/node_modules/strip-ansi/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/jest-watch-typeahead/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watch-typeahead/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==", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/jest-watcher": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.5.1.tgz", - "integrity": "sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw==", - "dependencies": { - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "jest-util": "^27.5.1", - "string-length": "^4.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-watcher/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-watcher/node_modules/@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-watcher/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-watcher/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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/jest-watcher/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-watcher/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==" - }, - "node_modules/jest-watcher/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watcher/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-watcher/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-worker": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.5.0.tgz", - "integrity": "sha512-/dsSmUkIy5EBGfv/IjjqmFxrNAUpBERfGs1oHROyD7yxjG/w+t0GOJDX8O1k32ySmd7+a5IhnJU2qQFcJ4n1vw==", - "dev": true, - "dependencies": { - "merge-stream": "^2.0.0", - "supports-color": "^7.0.0" - }, - "engines": { - "node": ">= 8.3" - } - }, - "node_modules/jest-worker/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, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-worker/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, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest/node_modules/import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "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/jest/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==", - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/joi": { - "version": "14.3.1", - "resolved": "https://registry.npmjs.org/joi/-/joi-14.3.1.tgz", - "integrity": "sha512-LQDdM+pkOrpAn4Lp+neNIFV3axv1Vna3j38bisbQhETPMANYRbFJFUyOZcOClYvM/hppMhGWuKSFEK9vjrB+bQ==", - "deprecated": "This module has moved and is now available at @hapi/joi. Please update your dependencies as this version is no longer maintained an may contain bugs and security issues.", - "devOptional": true, - "dependencies": { - "hoek": "6.x.x", - "isemail": "3.x.x", - "topo": "3.x.x" - } - }, - "node_modules/js-base64": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz", - "integrity": "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==" - }, - "node_modules/js-sdsl": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", - "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/js-sdsl" - } - }, - "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/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==", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", - "optional": true, - "peer": true - }, - "node_modules/jsdom": { - "version": "16.7.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", - "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", - "dependencies": { - "abab": "^2.0.5", - "acorn": "^8.2.4", - "acorn-globals": "^6.0.0", - "cssom": "^0.4.4", - "cssstyle": "^2.3.0", - "data-urls": "^2.0.0", - "decimal.js": "^10.2.1", - "domexception": "^2.0.1", - "escodegen": "^2.0.0", - "form-data": "^3.0.0", - "html-encoding-sniffer": "^2.0.1", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.0", - "parse5": "6.0.1", - "saxes": "^5.0.1", - "symbol-tree": "^3.2.4", - "tough-cookie": "^4.0.0", - "w3c-hr-time": "^1.0.2", - "w3c-xmlserializer": "^2.0.0", - "webidl-conversions": "^6.1.0", - "whatwg-encoding": "^1.0.5", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.5.0", - "ws": "^7.4.6", - "xml-name-validator": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "canvas": "^2.5.0" - }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } - } - }, - "node_modules/jsdom/node_modules/acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/jsdom/node_modules/ws": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" - }, - "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==" - }, - "node_modules/json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" - }, - "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==" - }, - "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==" - }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "optional": true, - "peer": 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==", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/jsonpointer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", - "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/jsprim": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", - "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", - "optional": true, - "peer": true, - "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/jsx-ast-utils": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz", - "integrity": "sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==", - "dependencies": { - "array-includes": "^3.1.5", - "object.assign": "^4.1.3" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/keygrip": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz", - "integrity": "sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==", - "devOptional": true, - "dependencies": { - "tsscmp": "1.0.6" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "engines": { - "node": ">=6" - } - }, - "node_modules/klona": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", - "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/koa": { - "version": "2.14.1", - "resolved": "https://registry.npmjs.org/koa/-/koa-2.14.1.tgz", - "integrity": "sha512-USJFyZgi2l0wDgqkfD27gL4YGno7TfUkcmOe6UOLFOVuN+J7FwnNu4Dydl4CUQzraM1lBAiGed0M9OVJoT0Kqw==", - "devOptional": true, - "dependencies": { - "accepts": "^1.3.5", - "cache-content-type": "^1.0.0", - "content-disposition": "~0.5.2", - "content-type": "^1.0.4", - "cookies": "~0.8.0", - "debug": "^4.3.2", - "delegates": "^1.0.0", - "depd": "^2.0.0", - "destroy": "^1.0.4", - "encodeurl": "^1.0.2", - "escape-html": "^1.0.3", - "fresh": "~0.5.2", - "http-assert": "^1.3.0", - "http-errors": "^1.6.3", - "is-generator-function": "^1.0.7", - "koa-compose": "^4.1.0", - "koa-convert": "^2.0.0", - "on-finished": "^2.3.0", - "only": "~0.0.2", - "parseurl": "^1.3.2", - "statuses": "^1.5.0", - "type-is": "^1.6.16", - "vary": "^1.1.2" - }, - "engines": { - "node": "^4.8.4 || ^6.10.1 || ^7.10.1 || >= 8.1.4" - } - }, - "node_modules/koa-compose": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/koa-compose/-/koa-compose-4.1.0.tgz", - "integrity": "sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw==", - "devOptional": true - }, - "node_modules/koa-compress": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/koa-compress/-/koa-compress-3.1.0.tgz", - "integrity": "sha512-0m24/yS/GbhWI+g9FqtvStY+yJwTObwoxOvPok6itVjRen7PBWkjsJ8pre76m+99YybXLKhOJ62mJ268qyBFMQ==", - "devOptional": true, - "dependencies": { - "bytes": "^3.0.0", - "compressible": "^2.0.0", - "koa-is-json": "^1.0.0", - "statuses": "^1.0.0" - }, - "engines": { - "node": ">= 8.0.0" - } - }, - "node_modules/koa-connect": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/koa-connect/-/koa-connect-2.1.0.tgz", - "integrity": "sha512-O9pcFafHk0oQsBevlbTBlB9co+2RUQJ4zCzu3qJPmGlGoeEZkne+7gWDkecqDPSbCtED6LmhlQladxs6NjOnMQ==", - "devOptional": true - }, - "node_modules/koa-convert": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/koa-convert/-/koa-convert-2.0.0.tgz", - "integrity": "sha512-asOvN6bFlSnxewce2e/DK3p4tltyfC4VM7ZwuTuepI7dEQVcvpyFuBcEARu1+Hxg8DIwytce2n7jrZtRlPrARA==", - "devOptional": true, - "dependencies": { - "co": "^4.6.0", - "koa-compose": "^4.1.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/koa-is-json": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/koa-is-json/-/koa-is-json-1.0.0.tgz", - "integrity": "sha512-+97CtHAlWDx0ndt0J8y3P12EWLwTLMXIfMnYDev3wOTwH/RpBGMlfn4bDXlMEg1u73K6XRE9BbUp+5ZAYoRYWw==", - "devOptional": true - }, - "node_modules/koa-route": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/koa-route/-/koa-route-3.2.0.tgz", - "integrity": "sha512-8FsuWw/L+CUWJfpgN6vrlYUDNTheEinG8Zkm97GyuLJNyWjCVUs9p10Ih3jTIWwmDVQcz6827l0RKadAS5ibqA==", - "devOptional": true, - "dependencies": { - "debug": "*", - "methods": "~1.1.0", - "path-to-regexp": "^1.2.0" - } - }, - "node_modules/koa-send": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/koa-send/-/koa-send-5.0.1.tgz", - "integrity": "sha512-tmcyQ/wXXuxpDxyNXv5yNNkdAMdFRqwtegBXUaowiQzUKqJehttS0x2j0eOZDQAyloAth5w6wwBImnFzkUz3pQ==", - "devOptional": true, - "dependencies": { - "debug": "^4.1.1", - "http-errors": "^1.7.3", - "resolve-path": "^1.4.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/koa-static": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/koa-static/-/koa-static-5.0.0.tgz", - "integrity": "sha512-UqyYyH5YEXaJrf9S8E23GoJFQZXkBVJ9zYYMPGz919MSX1KuvAcycIuS0ci150HCoPf4XQVhQ84Qf8xRPWxFaQ==", - "devOptional": true, - "dependencies": { - "debug": "^3.1.0", - "koa-send": "^5.0.0" - }, - "engines": { - "node": ">= 7.6.0" - } - }, - "node_modules/koa-static/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "devOptional": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/language-subtag-registry": { - "version": "0.3.22", - "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz", - "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==" - }, - "node_modules/language-tags": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz", - "integrity": "sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ==", - "dependencies": { - "language-subtag-registry": "~0.3.2" - } - }, - "node_modules/lcid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", - "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", - "dev": true, - "dependencies": { - "invert-kv": "^2.0.0" - }, - "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==", - "engines": { - "node": ">=6" - } - }, - "node_modules/levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", - "dependencies": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lilconfig": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", - "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==", - "engines": { - "node": ">=10" - } - }, - "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==" - }, - "node_modules/load-json-file": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-5.3.0.tgz", - "integrity": "sha512-cJGP40Jc/VXUsp8/OrnyKyTZ1y6v/dphm3bioS+RrKXjK2BB6wHUd6JptZEFDGgGahMT+InnZO5i1Ei9mpC8Bw==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.15", - "parse-json": "^4.0.0", - "pify": "^4.0.1", - "strip-bom": "^3.0.0", - "type-fest": "^0.3.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/load-json-file/node_modules/type-fest": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz", - "integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/loader-runner": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", - "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", - "engines": { - "node": ">=4.3.0 <5.0.0 || >=5.10" - } - }, - "node_modules/loader-utils": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", - "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - }, - "engines": { - "node": ">=8.9.0" - } - }, - "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==", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==" - }, - "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==" - }, - "node_modules/lodash.sortby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==" - }, - "node_modules/lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==" - }, - "node_modules/loglevelnext": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/loglevelnext/-/loglevelnext-3.0.1.tgz", - "integrity": "sha512-JpjaJhIN1reaSb26SIxDGtE0uc67gPl19OMVHrr+Ggt6b/Vy60jmCtKgQBrygAH0bhRA2nkxgDvM+8QvR8r0YA==", - "devOptional": true, - "engines": { - "node": ">= 6.14.4" - } - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/lower-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", - "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", - "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/lower-case/node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" - }, - "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==", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/lz-string": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz", - "integrity": "sha512-0ckx7ZHRPqb0oUm8zNr+90mtf9DQB60H1wMCjBtfi62Kl3a7JbHob6gA2bC+xRvZoOL+1hzUK8jeuEIQE8svEQ==", - "bin": { - "lz-string": "bin/bin.js" - } - }, - "node_modules/magic-string": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", - "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", - "dependencies": { - "sourcemap-codec": "^1.4.8" - } - }, - "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-fetch-happen": { - "version": "10.2.1", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz", - "integrity": "sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==", - "dependencies": { - "agentkeepalive": "^4.2.1", - "cacache": "^16.1.0", - "http-cache-semantics": "^4.1.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", - "is-lambda": "^1.0.1", - "lru-cache": "^7.7.1", - "minipass": "^3.1.6", - "minipass-collect": "^1.0.2", - "minipass-fetch": "^2.0.3", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.3", - "promise-retry": "^2.0.1", - "socks-proxy-agent": "^7.0.0", - "ssri": "^9.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/make-fetch-happen/node_modules/@tootallnate/once": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", - "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", - "engines": { - "node": ">= 10" - } - }, - "node_modules/make-fetch-happen/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==", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/make-fetch-happen/node_modules/cacache": { - "version": "16.1.3", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-16.1.3.tgz", - "integrity": "sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==", - "dependencies": { - "@npmcli/fs": "^2.1.0", - "@npmcli/move-file": "^2.0.0", - "chownr": "^2.0.0", - "fs-minipass": "^2.1.0", - "glob": "^8.0.1", - "infer-owner": "^1.0.4", - "lru-cache": "^7.7.1", - "minipass": "^3.1.6", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "mkdirp": "^1.0.4", - "p-map": "^4.0.0", - "promise-inflight": "^1.0.1", - "rimraf": "^3.0.2", - "ssri": "^9.0.0", - "tar": "^6.1.11", - "unique-filename": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/make-fetch-happen/node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "engines": { - "node": ">=10" - } - }, - "node_modules/make-fetch-happen/node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/make-fetch-happen/node_modules/http-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", - "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", - "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/make-fetch-happen/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "engines": { - "node": ">=12" - } - }, - "node_modules/make-fetch-happen/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/make-fetch-happen/node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-fetch-happen/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/make-fetch-happen/node_modules/rimraf/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==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/make-fetch-happen/node_modules/rimraf/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "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/make-fetch-happen/node_modules/rimraf/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/make-fetch-happen/node_modules/ssri": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz", - "integrity": "sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==", - "dependencies": { - "minipass": "^3.1.1" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/make-fetch-happen/node_modules/unique-filename": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-2.0.1.tgz", - "integrity": "sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==", - "dependencies": { - "unique-slug": "^3.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/make-fetch-happen/node_modules/unique-slug": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-3.0.0.tgz", - "integrity": "sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==", - "dependencies": { - "imurmurhash": "^0.1.4" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dependencies": { - "tmpl": "1.0.5" - } - }, - "node_modules/mamacro": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/mamacro/-/mamacro-0.0.3.tgz", - "integrity": "sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA==" - }, - "node_modules/map-age-cleaner": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", - "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", - "dev": true, - "dependencies": { - "p-defer": "^1.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/map-obj": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", - "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==", - "dependencies": { - "object-visit": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/md5.js": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", - "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", - "dependencies": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "node_modules/mdn-data": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", - "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==" - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mem": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", - "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", - "dev": true, - "dependencies": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^2.0.0", - "p-is-promise": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/memfs": { - "version": "3.4.13", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.13.tgz", - "integrity": "sha512-omTM41g3Skpvx5dSYeZIbXKcXoAVc/AoMNwn9TKx++L/gaen/+4TTttmu8ZSch5vfVJ8uJvGbroTsIlslRg6lg==", - "dependencies": { - "fs-monkey": "^1.0.3" - }, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/memory-fs": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", - "integrity": "sha512-cda4JKCxReDXFXRqOHPQscuIYg1PvxbE2S2GP45rnwfEK+vZaXC8C1OFvdHIbgw0DLzowXGVoxLaAmlgRy14GQ==", - "dependencies": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" - } - }, - "node_modules/memory-fs/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, - "node_modules/memory-fs/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/memory-fs/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/memory-fs/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/memorystream": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", - "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", - "dev": true, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/meow": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", - "integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==", - "dependencies": { - "@types/minimist": "^1.2.0", - "camelcase-keys": "^6.2.2", - "decamelize": "^1.2.0", - "decamelize-keys": "^1.1.0", - "hard-rejection": "^2.1.0", - "minimist-options": "4.1.0", - "normalize-package-data": "^3.0.0", - "read-pkg-up": "^7.0.1", - "redent": "^3.0.0", - "trim-newlines": "^3.0.0", - "type-fest": "^0.18.0", - "yargs-parser": "^20.2.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/meow/node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/meow/node_modules/type-fest": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", - "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/meow/node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "engines": { - "node": ">=10" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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==" - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/miller-rabin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", - "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", - "dependencies": { - "bn.js": "^4.0.0", - "brorand": "^1.0.1" - }, - "bin": { - "miller-rabin": "bin/miller-rabin" - } - }, - "node_modules/miller-rabin/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "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==", - "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==", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "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==", - "engines": { - "node": ">=6" - } - }, - "node_modules/min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "engines": { - "node": ">=4" - } - }, - "node_modules/minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" - }, - "node_modules/minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==" - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/minimist-options": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", - "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", - "dependencies": { - "arrify": "^1.0.1", - "is-plain-obj": "^1.1.0", - "kind-of": "^6.0.3" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-collect": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", - "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minipass-fetch": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-2.1.2.tgz", - "integrity": "sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==", - "dependencies": { - "minipass": "^3.1.6", - "minipass-sized": "^1.0.3", - "minizlib": "^2.1.2" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - }, - "optionalDependencies": { - "encoding": "^0.1.13" - } - }, - "node_modules/minipass-flush": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", - "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minipass-pipeline": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", - "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-sized": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", - "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minizlib/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/mississippi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", - "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", - "dependencies": { - "concat-stream": "^1.5.0", - "duplexify": "^3.4.2", - "end-of-stream": "^1.1.0", - "flush-write-stream": "^1.0.0", - "from2": "^2.1.0", - "parallel-transform": "^1.1.0", - "pump": "^3.0.0", - "pumpify": "^1.3.3", - "stream-each": "^1.1.0", - "through2": "^2.0.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", - "dependencies": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/move-concurrently": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", - "integrity": "sha512-hdrFxZOycD/g6A6SoI2bB5NA/5NEqD0569+S47WZhPvm46sD50ZHdYaFmnua5lndde9rCHGjmfK7Z8BuCt/PcQ==", - "dependencies": { - "aproba": "^1.1.1", - "copy-concurrently": "^1.0.0", - "fs-write-stream-atomic": "^1.0.8", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.3" - } - }, - "node_modules/move-concurrently/node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "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/multicast-dns": { - "version": "7.2.5", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", - "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", - "dependencies": { - "dns-packet": "^5.2.2", - "thunky": "^1.0.2" - }, - "bin": { - "multicast-dns": "cli.js" - } - }, - "node_modules/mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" - }, - "node_modules/nan": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz", - "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==" - }, - "node_modules/nanoid": { - "version": "2.1.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-2.1.11.tgz", - "integrity": "sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA==", - "devOptional": true - }, - "node_modules/nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" - }, - "node_modules/natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==" - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" - }, - "node_modules/nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" - }, - "node_modules/no-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", - "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", - "dependencies": { - "lower-case": "^2.0.2", - "tslib": "^2.0.3" - } - }, - "node_modules/no-case/node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" - }, - "node_modules/node-dir": { - "version": "0.1.17", - "resolved": "https://registry.npmjs.org/node-dir/-/node-dir-0.1.17.tgz", - "integrity": "sha512-tmPX422rYgofd4epzrNoOXiE8XFZYOcCq1vD7MAXCDO+O+zndlA2ztdKKMa+EeuBG5tHETpr4ml4RGgpqDCCAg==", - "dev": true, - "dependencies": { - "minimatch": "^3.0.2" - }, - "engines": { - "node": ">= 0.10.5" - } - }, - "node_modules/node-forge": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", - "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", - "engines": { - "node": ">= 6.13.0" - } - }, - "node_modules/node-gyp": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", - "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", - "dependencies": { - "env-paths": "^2.2.0", - "glob": "^7.1.4", - "graceful-fs": "^4.2.6", - "make-fetch-happen": "^9.1.0", - "nopt": "^5.0.0", - "npmlog": "^6.0.0", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.2", - "which": "^2.0.2" - }, - "bin": { - "node-gyp": "bin/node-gyp.js" - }, - "engines": { - "node": ">= 10.12.0" - } - }, - "node_modules/node-gyp/node_modules/@npmcli/fs": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", - "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", - "dependencies": { - "@gar/promisify": "^1.0.1", - "semver": "^7.3.5" - } - }, - "node_modules/node-gyp/node_modules/@npmcli/move-file": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", - "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", - "deprecated": "This functionality has been moved to @npmcli/fs", - "dependencies": { - "mkdirp": "^1.0.4", - "rimraf": "^3.0.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-gyp/node_modules/cacache": { - "version": "15.3.0", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", - "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", - "dependencies": { - "@npmcli/fs": "^1.0.0", - "@npmcli/move-file": "^1.0.1", - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "glob": "^7.1.4", - "infer-owner": "^1.0.4", - "lru-cache": "^6.0.0", - "minipass": "^3.1.1", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.2", - "mkdirp": "^1.0.3", - "p-map": "^4.0.0", - "promise-inflight": "^1.0.1", - "rimraf": "^3.0.2", - "ssri": "^8.0.1", - "tar": "^6.0.2", - "unique-filename": "^1.1.1" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/node-gyp/node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "engines": { - "node": ">=10" - } - }, - "node_modules/node-gyp/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-gyp/node_modules/make-fetch-happen": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", - "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", - "dependencies": { - "agentkeepalive": "^4.1.3", - "cacache": "^15.2.0", - "http-cache-semantics": "^4.1.0", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", - "is-lambda": "^1.0.1", - "lru-cache": "^6.0.0", - "minipass": "^3.1.3", - "minipass-collect": "^1.0.2", - "minipass-fetch": "^1.3.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.2", - "promise-retry": "^2.0.1", - "socks-proxy-agent": "^6.0.0", - "ssri": "^8.0.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/node-gyp/node_modules/minipass-fetch": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", - "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", - "dependencies": { - "minipass": "^3.1.0", - "minipass-sized": "^1.0.3", - "minizlib": "^2.0.0" - }, - "engines": { - "node": ">=8" - }, - "optionalDependencies": { - "encoding": "^0.1.12" - } - }, - "node_modules/node-gyp/node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/node-gyp/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/node-gyp/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-gyp/node_modules/socks-proxy-agent": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz", - "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==", - "dependencies": { - "agent-base": "^6.0.2", - "debug": "^4.3.3", - "socks": "^2.6.2" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/node-gyp/node_modules/ssri": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", - "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", - "dependencies": { - "minipass": "^3.1.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/node-gyp/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/node-gyp/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "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==" - }, - "node_modules/node-libs-browser": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", - "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", - "dependencies": { - "assert": "^1.1.1", - "browserify-zlib": "^0.2.0", - "buffer": "^4.3.0", - "console-browserify": "^1.1.0", - "constants-browserify": "^1.0.0", - "crypto-browserify": "^3.11.0", - "domain-browser": "^1.1.1", - "events": "^3.0.0", - "https-browserify": "^1.0.0", - "os-browserify": "^0.3.0", - "path-browserify": "0.0.1", - "process": "^0.11.10", - "punycode": "^1.2.4", - "querystring-es3": "^0.2.0", - "readable-stream": "^2.3.3", - "stream-browserify": "^2.0.1", - "stream-http": "^2.7.2", - "string_decoder": "^1.0.0", - "timers-browserify": "^2.0.4", - "tty-browserify": "0.0.0", - "url": "^0.11.0", - "util": "^0.11.0", - "vm-browserify": "^1.0.1" - } - }, - "node_modules/node-libs-browser/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, - "node_modules/node-libs-browser/node_modules/punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==" - }, - "node_modules/node-libs-browser/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/node-libs-browser/node_modules/readable-stream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/node-libs-browser/node_modules/readable-stream/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/node-libs-browser/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/node-releases": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", - "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==" - }, - "node_modules/node-sass": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-9.0.0.tgz", - "integrity": "sha512-yltEuuLrfH6M7Pq2gAj5B6Zm7m+gdZoG66wTqG6mIZV/zijq3M2OO2HswtT6oBspPyFhHDcaxWpsBm0fRNDHPg==", - "hasInstallScript": true, - "dependencies": { - "async-foreach": "^0.1.3", - "chalk": "^4.1.2", - "cross-spawn": "^7.0.3", - "gaze": "^1.0.0", - "get-stdin": "^4.0.1", - "glob": "^7.0.3", - "lodash": "^4.17.15", - "make-fetch-happen": "^10.0.4", - "meow": "^9.0.0", - "nan": "^2.17.0", - "node-gyp": "^8.4.1", - "sass-graph": "^4.0.1", - "stdout-stream": "^1.4.0", - "true-case-path": "^2.2.1" - }, - "bin": { - "node-sass": "bin/node-sass" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/node-sass/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/node-sass/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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/node-sass/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/node-sass/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==" - }, - "node_modules/node-sass/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==", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/node-sass/node_modules/get-stdin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", - "integrity": "sha512-F5aQMywwJ2n85s4hJPTT9RPxGmubonuB10MNYo17/xph174n2MIR33HRguhzVag10O/npM7SPk73LMZNP+FaWw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/node-sass/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/node-sass/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/node-sass/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==", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/node-sass/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/node-sass/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/node-sass/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/nodemon": { - "version": "2.0.21", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.21.tgz", - "integrity": "sha512-djN/n2549DUtY33S7o1djRCd7dEm0kBnj9c7S9XVXqRUbuggN1MZH/Nqa+5RFQr63Fbefq37nFXAE9VU86yL1A==", - "dependencies": { - "chokidar": "^3.5.2", - "debug": "^3.2.7", - "ignore-by-default": "^1.0.1", - "minimatch": "^3.1.2", - "pstree.remy": "^1.1.8", - "semver": "^5.7.1", - "simple-update-notifier": "^1.0.7", - "supports-color": "^5.5.0", - "touch": "^3.1.0", - "undefsafe": "^2.0.5" - }, - "bin": { - "nodemon": "bin/nodemon.js" - }, - "engines": { - "node": ">=8.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/nodemon" - } - }, - "node_modules/nodemon/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/nodemon/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/noms": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/noms/-/noms-0.0.0.tgz", - "integrity": "sha512-lNDU9VJaOPxUmXcLb+HQFeUgQQPtMI24Gt6hgfuMHRJgMRHMF/qZ4HJD3GDru4sSw9IQl2jPjAYnQrdIeLbwow==", - "dev": true, - "dependencies": { - "inherits": "^2.0.1", - "readable-stream": "~1.0.31" - } - }, - "node_modules/nopt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", - "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/normalize-package-data": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", - "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", - "dependencies": { - "hosted-git-info": "^4.0.1", - "is-core-module": "^2.5.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/normalize-package-data/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/normalize-package-data/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/normalize-package-data/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "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==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm-run-all": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", - "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "chalk": "^2.4.1", - "cross-spawn": "^6.0.5", - "memorystream": "^0.3.1", - "minimatch": "^3.0.4", - "pidtree": "^0.3.0", - "read-pkg": "^3.0.0", - "shell-quote": "^1.6.1", - "string.prototype.padend": "^3.0.0" - }, - "bin": { - "npm-run-all": "bin/npm-run-all/index.js", - "run-p": "bin/run-p/index.js", - "run-s": "bin/run-s/index.js" - }, - "engines": { - "node": ">= 4" - } - }, - "node_modules/npm-run-all/node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, - "node_modules/npm-run-all/node_modules/load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/npm-run-all/node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "node_modules/npm-run-all/node_modules/path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "dependencies": { - "pify": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/npm-run-all/node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/npm-run-all/node_modules/read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", - "dev": true, - "dependencies": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/npm-run-all/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", - "dev": true, - "dependencies": { - "path-key": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/npmlog": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", - "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", - "dependencies": { - "are-we-there-yet": "^3.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^4.0.3", - "set-blocking": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/nth-check": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", - "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", - "dependencies": { - "boolbase": "~1.0.0" - } - }, - "node_modules/nwsapi": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.2.tgz", - "integrity": "sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw==" - }, - "node_modules/oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "optional": true, - "peer": true, - "engines": { - "node": "*" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==", - "dependencies": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy/node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy/node_modules/is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy/node_modules/is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy/node_modules/is-descriptor/node_modules/kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", - "engines": { - "node": ">= 6" - } - }, - "node_modules/object-inspect": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", - "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-is": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", - "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object-path": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/object-path/-/object-path-0.11.8.tgz", - "integrity": "sha512-YJjNZrlXJFM42wTBn6zgOJVar9KFJvzx6sTWDte8sWZF//cnjl0BxHNpfZx+ZffXX63A9q0b1zsFiBX4g4X5KA==", - "dev": true, - "engines": { - "node": ">= 10.12.0" - } - }, - "node_modules/object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==", - "dependencies": { - "isobject": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.entries": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.6.tgz", - "integrity": "sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.fromentries": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.6.tgz", - "integrity": "sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.getownpropertydescriptors": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.5.tgz", - "integrity": "sha512-yDNzckpM6ntyQiGTik1fKV1DcVDRS+w8bvpWNCBanvH5LfRX9O8WTHqQzG4RZwRAM4I0oU7TV11Lj5v0g20ibw==", - "dependencies": { - "array.prototype.reduce": "^1.0.5", - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "engines": { - "node": ">= 0.8" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.hasown": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.2.tgz", - "integrity": "sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==", - "dependencies": { - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==", - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object.values": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", - "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/obuf": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", - "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/only": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/only/-/only-0.0.2.tgz", - "integrity": "sha512-Fvw+Jemq5fjjyWz6CpKx6w9s7xxqo3+JCyM0WXWeCSOboZ8ABkyvP8ID4CZuChA/wxSx+XSJmdOm8rGVyJ1hdQ==", - "devOptional": true - }, - "node_modules/open": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", - "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", - "dependencies": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/open/node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/opn": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", - "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", - "devOptional": true, - "dependencies": { - "is-wsl": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dependencies": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/os-browserify": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", - "integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==" - }, - "node_modules/os-locale": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", - "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", - "dev": true, - "dependencies": { - "execa": "^1.0.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/p-defer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", - "integrity": "sha512-wB3wfAxZpk2AzOfUMJNL+d36xothRSyj8EXOa4f6GMqYDN9BJaaSISbsk+wS9abmnebVw95C2Kb5t85UmpCxuw==", - "devOptional": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/p-is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", - "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "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==", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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==", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-map": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", - "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", - "dev": true, - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-retry": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", - "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", - "dependencies": { - "@types/retry": "0.12.0", - "retry": "^0.13.1" - }, - "engines": { - "node": ">=8" - } - }, - "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==", - "engines": { - "node": ">=6" - } - }, - "node_modules/pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" - }, - "node_modules/parallel-transform": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz", - "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==", - "dependencies": { - "cyclist": "^1.0.1", - "inherits": "^2.0.3", - "readable-stream": "^2.1.5" - } - }, - "node_modules/parallel-transform/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, - "node_modules/parallel-transform/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/parallel-transform/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/parallel-transform/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/param-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", - "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", - "dependencies": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/param-case/node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" - }, - "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==", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-asn1": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", - "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", - "dependencies": { - "asn1.js": "^5.2.0", - "browserify-aes": "^1.0.0", - "evp_bytestokey": "^1.0.0", - "pbkdf2": "^3.0.3", - "safe-buffer": "^5.1.1" - } - }, - "node_modules/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", - "dev": true, - "dependencies": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/parse-passwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/pascal-case": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", - "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/pascal-case/node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" - }, - "node_modules/pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-browserify": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", - "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==" - }, - "node_modules/path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==", - "optional": true - }, - "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==", - "engines": { - "node": ">=8" - } - }, - "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==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", - "engines": { - "node": ">=4" - } - }, - "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==" - }, - "node_modules/path-to-regexp": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.9.0.tgz", - "integrity": "sha512-xIp7/apCFJuUHdDLWe8O1HIkb0kQrOMb/0u6FXQjemHn/ii5LrIzU6bdECnsiTF/GjZkMEKg1xdiZwNqDYlZ6g==", - "devOptional": true, - "dependencies": { - "isarray": "0.0.1" - } - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/pbkdf2": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", - "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", - "dependencies": { - "create-hash": "^1.1.2", - "create-hmac": "^1.1.4", - "ripemd160": "^2.0.1", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - }, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" - }, - "node_modules/picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pidtree": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", - "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", - "dev": true, - "bin": { - "pidtree": "bin/pidtree.js" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "engines": { - "node": ">=6" - } - }, - "node_modules/pirates": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", - "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-conf": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-3.1.0.tgz", - "integrity": "sha512-m0OTbR/5VPNPqO1ph6Fqbj7Hv6QU7gR/tQW40ZqrL1rjgCU85W6C1bJn0BItuJqnR98PWzw7Z8hHeChD1WrgdQ==", - "dev": true, - "dependencies": { - "find-up": "^3.0.0", - "load-json-file": "^5.2.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-conf/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-conf/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-conf/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-conf/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "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==", - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-up": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", - "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", - "dependencies": { - "find-up": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-up/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-up/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-up/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-up/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "engines": { - "node": ">=4" - } - }, - "node_modules/posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss": { - "version": "7.0.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", - "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", - "dependencies": { - "picocolors": "^0.2.1", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/postcss-modules-extract-imports": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz", - "integrity": "sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==", - "dev": true, - "dependencies": { - "postcss": "^7.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/postcss-modules-local-by-default": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.3.tgz", - "integrity": "sha512-e3xDq+LotiGesympRlKNgaJ0PCzoUIdpH0dj47iWAui/kyTgh3CiAr1qP54uodmJhl6p9rN6BoNcdEDVJx9RDw==", - "dev": true, - "dependencies": { - "icss-utils": "^4.1.1", - "postcss": "^7.0.32", - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/postcss-modules-scope": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz", - "integrity": "sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ==", - "dev": true, - "dependencies": { - "postcss": "^7.0.6", - "postcss-selector-parser": "^6.0.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/postcss-modules-values": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz", - "integrity": "sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg==", - "dev": true, - "dependencies": { - "icss-utils": "^4.0.0", - "postcss": "^7.0.6" - } - }, - "node_modules/postcss-selector-parser": { - "version": "6.0.11", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz", - "integrity": "sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" - }, - "node_modules/prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/pretty-bytes": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", - "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pretty-error": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", - "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", - "dependencies": { - "lodash": "^4.17.20", - "renderkid": "^3.0.0" - } - }, - "node_modules/pretty-format": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", - "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", - "dependencies": { - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^17.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/pretty-format/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==", - "engines": { - "node": ">=8" - } - }, - "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==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/pretty-format/node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" - }, - "node_modules/private": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", - "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/promise": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", - "integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==", - "dependencies": { - "asap": "~2.0.6" - } - }, - "node_modules/promise-inflight": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==" - }, - "node_modules/promise-retry": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", - "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", - "dependencies": { - "err-code": "^2.0.2", - "retry": "^0.12.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/promise-retry/node_modules/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", - "engines": { - "node": ">= 4" - } - }, - "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==", - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "node_modules/prop-types-extra": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.1.1.tgz", - "integrity": "sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==", - "dependencies": { - "react-is": "^16.3.2", - "warning": "^4.0.0" - }, - "peerDependencies": { - "react": ">=0.14.0" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/proxy-addr/node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/prr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==" - }, - "node_modules/psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" - }, - "node_modules/pstree.remy": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", - "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==" - }, - "node_modules/public-encrypt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", - "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", - "dependencies": { - "bn.js": "^4.1.0", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "parse-asn1": "^5.0.0", - "randombytes": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "node_modules/public-encrypt/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" - }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/pumpify": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", - "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", - "dependencies": { - "duplexify": "^3.6.0", - "inherits": "^2.0.3", - "pump": "^2.0.0" - } - }, - "node_modules/pumpify/node_modules/pump": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", - "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", - "engines": { - "node": ">=0.6.0", - "teleport": ">=0.2.0" - } - }, - "node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "dependencies": { - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", - "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", - "engines": { - "node": ">=0.4.x" - } - }, - "node_modules/querystring-es3": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", - "integrity": "sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==", - "engines": { - "node": ">=0.4.x" - } - }, - "node_modules/querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" - }, - "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==", - "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/quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/raf": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", - "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", - "dependencies": { - "performance-now": "^2.1.0" - } - }, - "node_modules/ramda": { - "version": "0.26.1", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.26.1.tgz", - "integrity": "sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ==" - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/randomfill": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", - "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", - "dependencies": { - "randombytes": "^2.0.5", - "safe-buffer": "^5.1.0" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/raw-body/node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/raw-body/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", - "dependencies": { - "loose-envify": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-app-polyfill": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/react-app-polyfill/-/react-app-polyfill-3.0.0.tgz", - "integrity": "sha512-sZ41cxiU5llIB003yxxQBYrARBqe0repqPTTYBTmMqTz9szeBbE37BehCE891NZsmdZqqP+xWKdT3eo3vOzN8w==", - "dependencies": { - "core-js": "^3.19.2", - "object-assign": "^4.1.1", - "promise": "^8.1.0", - "raf": "^3.4.1", - "regenerator-runtime": "^0.13.9", - "whatwg-fetch": "^3.6.2" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/react-bootstrap": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-2.7.2.tgz", - "integrity": "sha512-WDSln+mG4RLLFO01stkj2bEx/3MF4YihK9D/dWnHaSxOiQZLbhhlf95D2Jb20X3t2m7vMxRe888FVrfLJoGmmA==", - "dependencies": { - "@babel/runtime": "^7.17.2", - "@restart/hooks": "^0.4.6", - "@restart/ui": "^1.4.1", - "@types/react-transition-group": "^4.4.4", - "classnames": "^2.3.1", - "dom-helpers": "^5.2.1", - "invariant": "^2.2.4", - "prop-types": "^15.8.1", - "prop-types-extra": "^1.1.0", - "react-transition-group": "^4.4.2", - "uncontrollable": "^7.2.1", - "warning": "^4.0.3" - }, - "peerDependencies": { - "@types/react": ">=16.14.8", - "react": ">=16.14.0", - "react-dom": ">=16.14.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/react-dev-utils": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", - "integrity": "sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==", - "dependencies": { - "@babel/code-frame": "^7.16.0", - "address": "^1.1.2", - "browserslist": "^4.18.1", - "chalk": "^4.1.2", - "cross-spawn": "^7.0.3", - "detect-port-alt": "^1.1.6", - "escape-string-regexp": "^4.0.0", - "filesize": "^8.0.6", - "find-up": "^5.0.0", - "fork-ts-checker-webpack-plugin": "^6.5.0", - "global-modules": "^2.0.0", - "globby": "^11.0.4", - "gzip-size": "^6.0.0", - "immer": "^9.0.7", - "is-root": "^2.1.0", - "loader-utils": "^3.2.0", - "open": "^8.4.0", - "pkg-up": "^3.1.0", - "prompts": "^2.4.2", - "react-error-overlay": "^6.0.11", - "recursive-readdir": "^2.2.2", - "shell-quote": "^1.7.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "engines": { - "node": ">=14" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/react-dev-utils/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/jest-resolve-dependencies": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz", + "integrity": "sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg==", + "license": "MIT", + "dependencies": { + "@jest/types": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-snapshot": "^27.5.1" + }, "engines": { - "node": ">=8" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/react-dev-utils/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/jest-runner": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.5.1.tgz", + "integrity": "sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ==", + "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "@jest/console": "^27.5.1", + "@jest/environment": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.8.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^27.5.1", + "jest-environment-jsdom": "^27.5.1", + "jest-environment-node": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-leak-detector": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "source-map-support": "^0.5.6", + "throat": "^6.0.1" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/react-dev-utils/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/jest-runtime": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.5.1.tgz", + "integrity": "sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==", + "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/globals": "^27.5.1", + "@jest/source-map": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "execa": "^5.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-mock": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/react-dev-utils/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/jest-serializer": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.5.1.tgz", + "integrity": "sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==", + "license": "MIT", "dependencies": { - "color-name": "~1.1.4" + "@types/node": "*", + "graceful-fs": "^4.2.9" }, "engines": { - "node": ">=7.0.0" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/react-dev-utils/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==" - }, - "node_modules/react-dev-utils/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/jest-snapshot": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.5.1.tgz", + "integrity": "sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==", + "license": "MIT", "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "@babel/core": "^7.7.2", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.0.0", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/babel__traverse": "^7.0.4", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^27.5.1", + "graceful-fs": "^4.2.9", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-util": "^27.5.1", + "natural-compare": "^1.4.0", + "pretty-format": "^27.5.1", + "semver": "^7.3.2" }, "engines": { - "node": ">= 8" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/react-dev-utils/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/jest-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", + "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", + "license": "MIT", + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, "engines": { - "node": ">=10" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-validate": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.5.1.tgz", + "integrity": "sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==", + "license": "MIT", + "dependencies": { + "@jest/types": "^27.5.1", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^27.5.1", + "leven": "^3.1.0", + "pretty-format": "^27.5.1" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/react-dev-utils/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/jest-watch-typeahead": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jest-watch-typeahead/-/jest-watch-typeahead-1.1.0.tgz", + "integrity": "sha512-Va5nLSJTN7YFtC2jd+7wsoe1pNe5K4ShLux/E5iHEwlB9AxaxmggY7to9KUqKojhaJw3aXqt5WAb4jGPOolpEw==", + "license": "MIT", "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" + "ansi-escapes": "^4.3.1", + "chalk": "^4.0.0", + "jest-regex-util": "^28.0.0", + "jest-watcher": "^28.0.0", + "slash": "^4.0.0", + "string-length": "^5.0.1", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=10" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "jest": "^27.0.0 || ^28.0.0" } }, - "node_modules/react-dev-utils/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==", + "node_modules/jest-watch-typeahead/node_modules/@jest/console": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-28.1.3.tgz", + "integrity": "sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw==", + "license": "MIT", + "dependencies": { + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3", + "slash": "^3.0.0" + }, "engines": { - "node": ">=8" + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/react-dev-utils/node_modules/loader-utils": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz", - "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==", + "node_modules/jest-watch-typeahead/node_modules/@jest/console/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "license": "MIT", "engines": { - "node": ">= 12.13.0" + "node": ">=8" } }, - "node_modules/react-dev-utils/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/jest-watch-typeahead/node_modules/@jest/test-result": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-28.1.3.tgz", + "integrity": "sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg==", + "license": "MIT", "dependencies": { - "p-locate": "^5.0.0" + "@jest/console": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/react-dev-utils/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/jest-watch-typeahead/node_modules/@jest/types": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", + "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", + "license": "MIT", "dependencies": { - "yocto-queue": "^0.1.0" + "@jest/schemas": "^28.1.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": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-watch-typeahead/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==", + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" } }, - "node_modules/react-dev-utils/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==", - "dependencies": { - "p-limit": "^3.0.2" - }, + "node_modules/jest-watch-typeahead/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==", + "license": "MIT", "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/react-dev-utils/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/jest-watch-typeahead/node_modules/emittery": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz", + "integrity": "sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==", + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" } }, - "node_modules/react-dev-utils/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/jest-watch-typeahead/node_modules/jest-message-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz", + "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==", + "license": "MIT", "dependencies": { - "shebang-regex": "^3.0.0" + "@babel/code-frame": "^7.12.13", + "@jest/types": "^28.1.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^28.1.3", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" }, "engines": { - "node": ">=8" + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/react-dev-utils/node_modules/shebang-regex": { + "node_modules/jest-watch-typeahead/node_modules/jest-message-util/node_modules/slash": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/react-dev-utils/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==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, + "node_modules/jest-watch-typeahead/node_modules/jest-regex-util": { + "version": "28.0.2", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz", + "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==", + "license": "MIT", "engines": { - "node": ">=8" + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/react-dev-utils/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/jest-watch-typeahead/node_modules/jest-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", + "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", + "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" }, "engines": { - "node": ">=8" + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/react-dev-utils/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "node_modules/jest-watch-typeahead/node_modules/jest-watcher": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-28.1.3.tgz", + "integrity": "sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==", + "license": "MIT", "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" + "@jest/test-result": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.10.2", + "jest-util": "^28.1.3", + "string-length": "^4.0.1" }, "engines": { - "node": ">= 8" + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/react-docgen": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/react-docgen/-/react-docgen-4.1.1.tgz", - "integrity": "sha512-o1wdswIxbgJRI4pckskE7qumiFyqkbvCO++TylEDOo2RbMiueIOg8YzKU4X9++r0DjrbXePw/LHnh81GRBTWRw==", - "dev": true, + "node_modules/jest-watch-typeahead/node_modules/jest-watcher/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==", + "license": "MIT", "dependencies": { - "@babel/core": "^7.0.0", - "@babel/runtime": "^7.0.0", - "async": "^2.1.4", - "commander": "^2.19.0", - "doctrine": "^3.0.0", - "node-dir": "^0.1.10", - "recast": "^0.17.3" - }, - "bin": { - "react-docgen": "bin/react-docgen.js" + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">=6" + "node": ">=10" } }, - "node_modules/react-dom": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "node_modules/jest-watch-typeahead/node_modules/jest-watcher/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.0" + "ansi-regex": "^5.0.1" }, - "peerDependencies": { - "react": "^18.2.0" - } - }, - "node_modules/react-error-overlay": { - "version": "6.0.11", - "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", - "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" - }, - "node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - }, - "node_modules/react-lifecycles-compat": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", - "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" - }, - "node_modules/react-refresh": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", - "integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==", "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-resize-detector": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/react-resize-detector/-/react-resize-detector-7.1.2.tgz", - "integrity": "sha512-zXnPJ2m8+6oq9Nn8zsep/orts9vQv3elrpA+R8XTcW7DVVUJ9vwDwMXaBtykAYjMnkCIaOoK9vObyR7ZgFNlOw==", - "dependencies": { - "lodash": "^4.17.21" - }, - "peerDependencies": { - "react": "^16.0.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" + "node": ">=8" } }, - "node_modules/react-router": { - "version": "6.8.2", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.8.2.tgz", - "integrity": "sha512-lF7S0UmXI5Pd8bmHvMdPKI4u4S5McxmHnzJhrYi9ZQ6wE+DA8JN5BzVC5EEBuduWWDaiJ8u6YhVOCmThBli+rw==", + "node_modules/jest-watch-typeahead/node_modules/pretty-format": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "license": "MIT", "dependencies": { - "@remix-run/router": "1.3.3" + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" }, "engines": { - "node": ">=14" - }, - "peerDependencies": { - "react": ">=16.8" + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/react-router-dom": { - "version": "6.8.2", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.8.2.tgz", - "integrity": "sha512-N/oAF1Shd7g4tWy+75IIufCGsHBqT74tnzHQhbiUTYILYF0Blk65cg+HPZqwC+6SqEyx033nKqU7by38v3lBZg==", - "dependencies": { - "@remix-run/router": "1.3.3", - "react-router": "6.8.2" - }, + "node_modules/jest-watch-typeahead/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==", + "license": "MIT" + }, + "node_modules/jest-watch-typeahead/node_modules/slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "license": "MIT", "engines": { - "node": ">=14" + "node": ">=12" }, - "peerDependencies": { - "react": ">=16.8", - "react-dom": ">=16.8" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/react-scripts": { + "node_modules/jest-watch-typeahead/node_modules/string-length": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz", - "integrity": "sha512-8VAmEm/ZAwQzJ+GOMLbBsTdDKOpuZh7RPs0UymvBR2vRk4iZWCskjbFnxqjrzoIvlNNRZ3QJFx6/qDSi6zSnaQ==", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-5.0.1.tgz", + "integrity": "sha512-9Ep08KAMUn0OadnVaBuRdE2l615CQ508kr0XMadjClfYpdCyvrbFp6Taebo8yyxokQ4viUd/xPPUA4FGgUa0ow==", + "license": "MIT", "dependencies": { - "@babel/core": "^7.16.0", - "@pmmmwh/react-refresh-webpack-plugin": "^0.5.3", - "@svgr/webpack": "^5.5.0", - "babel-jest": "^27.4.2", - "babel-loader": "^8.2.3", - "babel-plugin-named-asset-import": "^0.3.8", - "babel-preset-react-app": "^10.0.1", - "bfj": "^7.0.2", - "browserslist": "^4.18.1", - "camelcase": "^6.2.1", - "case-sensitive-paths-webpack-plugin": "^2.4.0", - "css-loader": "^6.5.1", - "css-minimizer-webpack-plugin": "^3.2.0", - "dotenv": "^10.0.0", - "dotenv-expand": "^5.1.0", - "eslint": "^8.3.0", - "eslint-config-react-app": "^7.0.1", - "eslint-webpack-plugin": "^3.1.1", - "file-loader": "^6.2.0", - "fs-extra": "^10.0.0", - "html-webpack-plugin": "^5.5.0", - "identity-obj-proxy": "^3.0.0", - "jest": "^27.4.3", - "jest-resolve": "^27.4.2", - "jest-watch-typeahead": "^1.0.0", - "mini-css-extract-plugin": "^2.4.5", - "postcss": "^8.4.4", - "postcss-flexbugs-fixes": "^5.0.2", - "postcss-loader": "^6.2.1", - "postcss-normalize": "^10.0.1", - "postcss-preset-env": "^7.0.1", - "prompts": "^2.4.2", - "react-app-polyfill": "^3.0.0", - "react-dev-utils": "^12.0.1", - "react-refresh": "^0.11.0", - "resolve": "^1.20.0", - "resolve-url-loader": "^4.0.0", - "sass-loader": "^12.3.0", - "semver": "^7.3.5", - "source-map-loader": "^3.0.0", - "style-loader": "^3.3.1", - "tailwindcss": "^3.0.2", - "terser-webpack-plugin": "^5.2.5", - "webpack": "^5.64.4", - "webpack-dev-server": "^4.6.0", - "webpack-manifest-plugin": "^4.0.2", - "workbox-webpack-plugin": "^6.4.1" - }, - "bin": { - "react-scripts": "bin/react-scripts.js" + "char-regex": "^2.0.0", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=14.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - }, - "peerDependencies": { - "react": ">= 16", - "typescript": "^3.2.1 || ^4" + "node": ">=12.20" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watch-typeahead/node_modules/string-length/node_modules/char-regex": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-2.0.1.tgz", + "integrity": "sha512-oSvEeo6ZUD7NepqAat3RqoucZ5SeqLJgOvVIwkafu6IP3V0pO38s/ypdVUmDDK6qIIHNlYHJAKX9E7R7HoKElw==", + "license": "MIT", + "engines": { + "node": ">=12.20" } }, - "node_modules/react-scripts/node_modules/@babel/eslint-parser": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.19.1.tgz", - "integrity": "sha512-AqNf2QWt1rtu2/1rLswy6CDP7H9Oh3mMhk177Y67Rg8d7RD9WfOLLv8CGn6tisFvS2htm86yIe1yLF6I1UDaGQ==", + "node_modules/jest-watch-typeahead/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==", + "license": "MIT", "dependencies": { - "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", - "eslint-visitor-keys": "^2.1.0", - "semver": "^6.3.0" + "ansi-regex": "^6.0.1" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || >=14.0.0" + "node": ">=12" }, - "peerDependencies": { - "@babel/core": ">=7.11.0", - "eslint": "^7.5.0 || ^8.0.0" + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/react-scripts/node_modules/@babel/eslint-parser/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/jest-watch-typeahead/node_modules/strip-ansi/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/react-scripts/node_modules/@babel/eslint-parser/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" + "node_modules/jest-watcher": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.5.1.tgz", + "integrity": "sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw==", + "license": "MIT", + "dependencies": { + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "jest-util": "^27.5.1", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/react-scripts/node_modules/@csstools/postcss-cascade-layers": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-1.1.1.tgz", - "integrity": "sha512-+KdYrpKC5TgomQr2DlZF4lDEpHcoxnj5IGddYYfBWJAKfj1JtuHUIqMa+E1pJJ+z3kvDViWMqyqPlG4Ja7amQA==", + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "license": "MIT", "dependencies": { - "@csstools/selector-specificity": "^2.0.2", - "postcss-selector-parser": "^6.0.10" + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" }, "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" + "node": ">= 10.13.0" } }, - "node_modules/react-scripts/node_modules/@csstools/postcss-color-function": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-1.1.1.tgz", - "integrity": "sha512-Bc0f62WmHdtRDjf5f3e2STwRAl89N2CLb+9iAwzrv4L2hncrbDwnQD9PCq0gtAt7pOI2leIV08HIBUd4jxD8cw==", + "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==", + "license": "MIT", "dependencies": { - "@csstools/postcss-progressive-custom-properties": "^1.1.0", - "postcss-value-parser": "^4.2.0" + "has-flag": "^4.0.0" }, "engines": { - "node": "^12 || ^14 || >=16" + "node": ">=10" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jiti": { + "version": "1.21.6", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz", + "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==", + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "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==", + "license": "MIT" + }, + "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==", + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" }, - "peerDependencies": { - "postcss": "^8.2" + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/react-scripts/node_modules/@csstools/postcss-font-format-keywords": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-font-format-keywords/-/postcss-font-format-keywords-1.0.1.tgz", - "integrity": "sha512-ZgrlzuUAjXIOc2JueK0X5sZDjCtgimVp/O5CEqTcs5ShWBa6smhWYbS0x5cVc/+rycTDbjjzoP0KTDnUneZGOg==", + "node_modules/jsdom": { + "version": "16.7.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", + "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", + "license": "MIT", "dependencies": { - "postcss-value-parser": "^4.2.0" + "abab": "^2.0.5", + "acorn": "^8.2.4", + "acorn-globals": "^6.0.0", + "cssom": "^0.4.4", + "cssstyle": "^2.3.0", + "data-urls": "^2.0.0", + "decimal.js": "^10.2.1", + "domexception": "^2.0.1", + "escodegen": "^2.0.0", + "form-data": "^3.0.0", + "html-encoding-sniffer": "^2.0.1", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.0", + "parse5": "6.0.1", + "saxes": "^5.0.1", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.0.0", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^2.0.0", + "webidl-conversions": "^6.1.0", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.5.0", + "ws": "^7.4.6", + "xml-name-validator": "^3.0.0" }, "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" + "node": ">=10" }, "peerDependencies": { - "postcss": "^8.2" + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } } }, - "node_modules/react-scripts/node_modules/@csstools/postcss-hwb-function": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-1.0.2.tgz", - "integrity": "sha512-YHdEru4o3Rsbjmu6vHy4UKOXZD+Rn2zmkAmLRfPet6+Jz4Ojw8cbWxe1n42VaXQhD3CQUXXTooIy8OkVbUcL+w==", - "dependencies": { - "postcss-value-parser": "^4.2.0" + "node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" }, "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" + "node": ">=6" } }, - "node_modules/react-scripts/node_modules/@csstools/postcss-ic-unit": { + "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==", + "license": "MIT" + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true, + "license": "MIT" + }, + "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==", + "license": "MIT" + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "license": "(AFL-2.1 OR BSD-3-Clause)" + }, + "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==", + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-1.0.1.tgz", - "integrity": "sha512-Ot1rcwRAaRHNKC9tAqoqNZhjdYBzKk1POgWfhN4uCOE47ebGcLRqXjKkApVDpjifL6u2/55ekkpnFcp+s/OZUw==", - "dependencies": { - "@csstools/postcss-progressive-custom-properties": "^1.1.0", - "postcss-value-parser": "^4.2.0" + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "license": "MIT", + "bin": { + "json5": "lib/cli.js" }, "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" + "node": ">=6" } }, - "node_modules/react-scripts/node_modules/@csstools/postcss-is-pseudo-class": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-2.0.7.tgz", - "integrity": "sha512-7JPeVVZHd+jxYdULl87lvjgvWldYu+Bc62s9vD/ED6/QTGjy0jy0US/f6BG53sVMTBJ1lzKZFpYmofBN9eaRiA==", + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "license": "MIT", "dependencies": { - "@csstools/selector-specificity": "^2.0.0", - "postcss-selector-parser": "^6.0.10" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" + "universalify": "^2.0.0" }, - "peerDependencies": { - "postcss": "^8.2" + "optionalDependencies": { + "graceful-fs": "^4.1.6" } }, - "node_modules/react-scripts/node_modules/@csstools/postcss-nested-calc": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-nested-calc/-/postcss-nested-calc-1.0.0.tgz", - "integrity": "sha512-JCsQsw1wjYwv1bJmgjKSoZNvf7R6+wuHDAbi5f/7MbFhl2d/+v+TvBTU4BJH3G1X1H87dHl0mh6TfYogbT/dJQ==", + "node_modules/jsonpath": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/jsonpath/-/jsonpath-1.1.1.tgz", + "integrity": "sha512-l6Cg7jRpixfbgoWgkrl77dgEj8RPvND0wMH6TwQmi9Qs4TFfS9u5cUFnbeKTwj5ga5Y3BTGGNI28k117LJ009w==", + "license": "MIT", "dependencies": { - "postcss-value-parser": "^4.2.0" + "esprima": "1.2.2", + "static-eval": "2.0.2", + "underscore": "1.12.1" + } + }, + "node_modules/jsonpath/node_modules/esprima": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.2.2.tgz", + "integrity": "sha512-+JpPZam9w5DuJ3Q67SqsMGtiHKENSMRVoxvArfJZK01/BfLEObtZ6orJa/MtoGNR/rfMgp5837T41PAmTwAv/A==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" }, "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" + "node": ">=0.4.0" + } + }, + "node_modules/jsonpointer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" } }, - "node_modules/react-scripts/node_modules/@csstools/postcss-normalize-display-values": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-normalize-display-values/-/postcss-normalize-display-values-1.0.1.tgz", - "integrity": "sha512-jcOanIbv55OFKQ3sYeFD/T0Ti7AMXc9nM1hZWu8m/2722gOTxFg7xYu4RDLJLeZmPUVQlGzo4jhzvTUq3x4ZUw==", + "node_modules/jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "license": "MIT", "dependencies": { - "postcss-value-parser": "^4.2.0" + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" }, "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" + "node": ">=4.0" } }, - "node_modules/react-scripts/node_modules/@csstools/postcss-oklab-function": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-1.1.1.tgz", - "integrity": "sha512-nJpJgsdA3dA9y5pgyb/UfEzE7W5Ka7u0CX0/HIMVBNWzWemdcTH3XwANECU6anWv/ao4vVNLTMxhiPNZsTK6iA==", + "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==", + "license": "MIT", "dependencies": { - "@csstools/postcss-progressive-custom-properties": "^1.1.0", - "postcss-value-parser": "^4.2.0" - }, + "json-buffer": "3.0.1" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "license": "MIT", "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" + "node": ">=0.10.0" } }, - "node_modules/react-scripts/node_modules/@csstools/postcss-progressive-custom-properties": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-1.3.0.tgz", - "integrity": "sha512-ASA9W1aIy5ygskZYuWams4BzafD12ULvSypmaLJT2jvQ8G0M3I8PRQhC0h7mG0Z3LI05+agZjqSR9+K9yaQQjA==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "license": "MIT", "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.3" + "node": ">=6" } }, - "node_modules/react-scripts/node_modules/@csstools/postcss-stepped-value-functions": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-1.0.1.tgz", - "integrity": "sha512-dz0LNoo3ijpTOQqEJLY8nyaapl6umbmDcgj4AD0lgVQ572b2eqA1iGZYTTWhrcrHztWDDRAX2DGYyw2VBjvCvQ==", + "node_modules/klona": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", + "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/language-subtag-registry": { + "version": "0.3.23", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", + "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", + "license": "CC0-1.0" + }, + "node_modules/language-tags": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", + "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", + "license": "MIT", "dependencies": { - "postcss-value-parser": "^4.2.0" + "language-subtag-registry": "^0.3.20" }, "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" + "node": ">=0.10" } }, - "node_modules/react-scripts/node_modules/@csstools/postcss-text-decoration-shorthand": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-1.0.0.tgz", - "integrity": "sha512-c1XwKJ2eMIWrzQenN0XbcfzckOLLJiczqy+YvfGmzoVXd7pT9FfObiSEfzs84bpE/VqfpEuAZ9tCRbZkZxxbdw==", + "node_modules/launch-editor": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.9.1.tgz", + "integrity": "sha512-Gcnl4Bd+hRO9P9icCP/RVVT2o8SFlPXofuCxvA2SaZuH45whSvf5p8x5oih5ftLiVhEI4sp5xDY+R+b3zJBh5w==", + "license": "MIT", "dependencies": { - "postcss-value-parser": "^4.2.0" - }, + "picocolors": "^1.0.0", + "shell-quote": "^1.8.1" + } + }, + "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==", + "license": "MIT", "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" + "node": ">=6" } }, - "node_modules/react-scripts/node_modules/@csstools/postcss-trigonometric-functions": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-1.0.2.tgz", - "integrity": "sha512-woKaLO///4bb+zZC2s80l+7cm07M7268MsyG3M0ActXXEFi6SuhvriQYcb58iiKGbjwwIU7n45iRLEHypB47Og==", + "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==", + "license": "MIT", "dependencies": { - "postcss-value-parser": "^4.2.0" + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" }, "engines": { - "node": "^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" + "node": ">= 0.8.0" } }, - "node_modules/react-scripts/node_modules/@csstools/postcss-unset-value": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-1.0.2.tgz", - "integrity": "sha512-c8J4roPBILnelAsdLr4XOAR/GsTm0GJi4XpcfvoWk3U6KiTCqiFYc63KhRMQQX35jYMp4Ao8Ij9+IZRgMfJp1g==", + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "license": "MIT", "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" + "node": ">=10" } }, - "node_modules/react-scripts/node_modules/@csstools/selector-specificity": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.1.1.tgz", - "integrity": "sha512-jwx+WCqszn53YHOfvFMJJRd/B2GqkCBt+1MJSG6o5/s8+ytHMvDZXsJgUEWLk12UnLd7HYKac4BYU5i/Ron1Cw==", - "engines": { - "node": "^14 || ^16 || >=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" + "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==", + "license": "MIT" + }, + "node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" }, - "peerDependencies": { - "postcss": "^8.4", - "postcss-selector-parser": "^6.0.10" + "engines": { + "node": ">=4" } }, - "node_modules/react-scripts/node_modules/@pmmmwh/react-refresh-webpack-plugin": { - "version": "0.5.10", - "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.10.tgz", - "integrity": "sha512-j0Ya0hCFZPd4x40qLzbhGsh9TMtdb+CJQiso+WxLOPNasohq9cc5SNUcwsZaRH6++Xh91Xkm/xHCkuIiIu0LUA==", + "node_modules/load-json-file/node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, + "license": "MIT", "dependencies": { - "ansi-html-community": "^0.0.8", - "common-path-prefix": "^3.0.0", - "core-js-pure": "^3.23.3", - "error-stack-parser": "^2.0.6", - "find-up": "^5.0.0", - "html-entities": "^2.1.0", - "loader-utils": "^2.0.4", - "schema-utils": "^3.0.0", - "source-map": "^0.7.3" + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" }, "engines": { - "node": ">= 10.13" - }, - "peerDependencies": { - "@types/webpack": "4.x || 5.x", - "react-refresh": ">=0.10.0 <1.0.0", - "sockjs-client": "^1.4.0", - "type-fest": ">=0.17.0 <4.0.0", - "webpack": ">=4.43.0 <6.0.0", - "webpack-dev-server": "3.x || 4.x", - "webpack-hot-middleware": "2.x", - "webpack-plugin-serve": "0.x || 1.x" - }, - "peerDependenciesMeta": { - "@types/webpack": { - "optional": true - }, - "sockjs-client": { - "optional": true - }, - "type-fest": { - "optional": true - }, - "webpack-dev-server": { - "optional": true - }, - "webpack-hot-middleware": { - "optional": true - }, - "webpack-plugin-serve": { - "optional": true - } + "node": ">=4" } }, - "node_modules/react-scripts/node_modules/@pmmmwh/react-refresh-webpack-plugin/node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "node_modules/load-json-file/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "license": "MIT", "engines": { - "node": ">= 8" + "node": ">=4" } }, - "node_modules/react-scripts/node_modules/@types/estree": { - "version": "0.0.51", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", - "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==" - }, - "node_modules/react-scripts/node_modules/@webassemblyjs/ast": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", - "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", - "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + "node_modules/load-json-file/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, + "license": "MIT", + "engines": { + "node": ">=4" } }, - "node_modules/react-scripts/node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", - "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==" - }, - "node_modules/react-scripts/node_modules/@webassemblyjs/helper-buffer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", - "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==" + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "license": "MIT", + "engines": { + "node": ">=6.11.5" + } }, - "node_modules/react-scripts/node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", - "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==" + "node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "license": "MIT", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } }, - "node_modules/react-scripts/node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", - "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "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==", + "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1" + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/react-scripts/node_modules/@webassemblyjs/ieee754": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", - "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", - "dependencies": { - "@xtuc/ieee754": "^1.2.0" - } + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "license": "MIT" }, - "node_modules/react-scripts/node_modules/@webassemblyjs/leb128": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", - "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", - "dependencies": { - "@xtuc/long": "4.2.2" - } + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "license": "MIT" }, - "node_modules/react-scripts/node_modules/@webassemblyjs/utf8": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", - "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==" + "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==", + "license": "MIT" }, - "node_modules/react-scripts/node_modules/@webassemblyjs/wasm-edit": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", - "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", - "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/helper-wasm-section": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-opt": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "@webassemblyjs/wast-printer": "1.11.1" - } + "node_modules/lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", + "license": "MIT" }, - "node_modules/react-scripts/node_modules/@webassemblyjs/wasm-gen": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", - "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", - "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" - } + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "license": "MIT" }, - "node_modules/react-scripts/node_modules/@webassemblyjs/wasm-opt": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", - "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1" + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" } }, - "node_modules/react-scripts/node_modules/@webassemblyjs/wasm-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", - "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "tslib": "^2.0.3" } }, - "node_modules/react-scripts/node_modules/@webassemblyjs/wast-printer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", - "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "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==", + "license": "ISC", "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/react-scripts/node_modules/acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/react-scripts/node_modules/acorn-import-assertions": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", - "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", - "peerDependencies": { - "acorn": "^8" + "yallist": "^3.0.2" } }, - "node_modules/react-scripts/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==", - "engines": { - "node": ">=8" + "node_modules/magic-string": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", + "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", + "license": "MIT", + "dependencies": { + "sourcemap-codec": "^1.4.8" } }, - "node_modules/react-scripts/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/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "semver": "^6.0.0" }, "engines": { "node": ">=8" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/react-scripts/node_modules/are-we-there-yet": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", - "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", - "optional": true, - "peer": true, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "license": "BSD-3-Clause", "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">=10" + "tmpl": "1.0.5" } }, - "node_modules/react-scripts/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==" + "node_modules/mdn-data": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", + "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==", + "license": "CC0-1.0" }, - "node_modules/react-scripts/node_modules/autoprefixer": { - "version": "10.4.13", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz", - "integrity": "sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/autoprefixer" - } - ], - "dependencies": { - "browserslist": "^4.21.4", - "caniuse-lite": "^1.0.30001426", - "fraction.js": "^4.2.0", - "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", - "postcss-value-parser": "^4.2.0" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" - }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", "engines": { - "node": "^10 || ^12 || >=14" - }, - "peerDependencies": { - "postcss": "^8.1.0" + "node": ">= 0.6" } }, - "node_modules/react-scripts/node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "node_modules/memfs": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", + "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", + "license": "Unlicense", "dependencies": { - "fill-range": "^7.0.1" + "fs-monkey": "^1.0.4" }, "engines": { - "node": ">=8" + "node": ">= 4.0.0" } }, - "node_modules/react-scripts/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "dev": true, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.10.0" } }, - "node_modules/react-scripts/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/react-scripts/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/react-scripts/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==" + "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==", + "license": "MIT" }, - "node_modules/react-scripts/node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "license": "MIT", "engines": { - "node": ">= 10" + "node": ">= 8" } }, - "node_modules/react-scripts/node_modules/connect-history-api-fallback": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", - "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", "engines": { - "node": ">=0.8" + "node": ">= 0.6" } }, - "node_modules/react-scripts/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/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "license": "MIT", "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "braces": "^3.0.3", + "picomatch": "^2.3.1" }, "engines": { - "node": ">= 8" + "node": ">=8.6" } }, - "node_modules/react-scripts/node_modules/css-blank-pseudo": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-3.0.3.tgz", - "integrity": "sha512-VS90XWtsHGqoM0t4KpH053c4ehxZ2E6HtGI7x68YFV0pTo/QmkV/YFA+NnlvK8guxZVNWGQhVNJGC39Q8XF4OQ==", - "dependencies": { - "postcss-selector-parser": "^6.0.9" - }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", "bin": { - "css-blank-pseudo": "dist/cli.cjs" + "mime": "cli.js" }, "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" + "node": ">=4" } }, - "node_modules/react-scripts/node_modules/css-declaration-sorter": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.3.1.tgz", - "integrity": "sha512-fBffmak0bPAnyqc/HO8C3n2sHrp9wcqQz6ES9koRF2/mLOVAx9zIQ3Y7R29sYCteTPqMCwns4WYQoCX91Xl3+w==", + "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==", + "license": "MIT", "engines": { - "node": "^10 || ^12 || >=14" - }, - "peerDependencies": { - "postcss": "^8.0.9" + "node": ">= 0.6" } }, - "node_modules/react-scripts/node_modules/css-has-pseudo": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-3.0.4.tgz", - "integrity": "sha512-Vse0xpR1K9MNlp2j5w1pgWIJtm1a8qS0JwS9goFYcImjlHEmywP9VUF05aGBXzGpDJF86QXk4L0ypBmwPhGArw==", + "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==", + "license": "MIT", "dependencies": { - "postcss-selector-parser": "^6.0.9" - }, - "bin": { - "css-has-pseudo": "dist/cli.cjs" + "mime-db": "1.52.0" }, "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" + "node": ">= 0.6" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=6" } }, - "node_modules/react-scripts/node_modules/css-loader": { - "version": "6.7.3", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.3.tgz", - "integrity": "sha512-qhOH1KlBMnZP8FzRO6YCH9UHXQhVMcEGLyNdb7Hv2cpcmJbW0YrddO+tG1ab5nT41KpHIYGsbeHqxB9xPu1pKQ==", + "node_modules/mini-css-extract-plugin": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.2.tgz", + "integrity": "sha512-GJuACcS//jtq4kCtd5ii/M0SZf7OZRH+BxdqXZHaJfb8TJiVl+NgQRPwiYt2EuqeSkNydn/7vP+bcE27C5mb9w==", + "license": "MIT", "dependencies": { - "icss-utils": "^5.1.0", - "postcss": "^8.4.19", - "postcss-modules-extract-imports": "^3.0.0", - "postcss-modules-local-by-default": "^4.0.0", - "postcss-modules-scope": "^3.0.0", - "postcss-modules-values": "^4.0.0", - "postcss-value-parser": "^4.2.0", - "semver": "^7.3.8" + "schema-utils": "^4.0.0", + "tapable": "^2.2.1" }, "engines": { "node": ">= 12.13.0" @@ -21853,265 +12702,273 @@ "webpack": "^5.0.0" } }, - "node_modules/react-scripts/node_modules/css-minimizer-webpack-plugin": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.4.1.tgz", - "integrity": "sha512-1u6D71zeIfgngN2XNRJefc/hY7Ybsxd74Jm4qngIXyUEk7fss3VUzuHxLAq/R8NAba4QU9OUSaMZlbpRc7bM4Q==", + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "license": "ISC" + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", "dependencies": { - "cssnano": "^5.0.6", - "jest-worker": "^27.0.2", - "postcss": "^8.3.5", - "schema-utils": "^4.0.0", - "serialize-javascript": "^6.0.0", - "source-map": "^0.6.1" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">= 12.13.0" - }, + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - }, - "peerDependenciesMeta": { - "@parcel/css": { - "optional": true - }, - "clean-css": { - "optional": true - }, - "csso": { - "optional": true - }, - "esbuild": { - "optional": true - } + "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==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" } }, - "node_modules/react-scripts/node_modules/css-minimizer-webpack-plugin/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "minimist": "^1.2.6" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "bin": { + "mkdirp": "bin/cmd.js" } }, - "node_modules/react-scripts/node_modules/css-minimizer-webpack-plugin/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/multicast-dns": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", + "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.3" + "dns-packet": "^5.2.2", + "thunky": "^1.0.2" }, - "peerDependencies": { - "ajv": "^8.8.2" + "bin": { + "multicast-dns": "cli.js" } }, - "node_modules/react-scripts/node_modules/css-minimizer-webpack-plugin/node_modules/schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "license": "MIT", "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" } }, - "node_modules/react-scripts/node_modules/css-prefers-color-scheme": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz", - "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==", + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", "bin": { - "css-prefers-color-scheme": "dist/cli.cjs" + "nanoid": "bin/nanoid.cjs" }, "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/react-scripts/node_modules/css-select": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", - "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.0.1", - "domhandler": "^4.3.1", - "domutils": "^2.8.0", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "license": "MIT" + }, + "node_modules/natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" } }, - "node_modules/react-scripts/node_modules/css-tree": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", - "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "license": "MIT" + }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "license": "MIT", "dependencies": { - "mdn-data": "2.0.14", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=8.0.0" + "lower-case": "^2.0.2", + "tslib": "^2.0.3" } }, - "node_modules/react-scripts/node_modules/css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", - "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "license": "(BSD-3-Clause OR GPL-2.0)", "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" + "node": ">= 6.13.0" } }, - "node_modules/react-scripts/node_modules/cssnano": { - "version": "5.1.15", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.15.tgz", - "integrity": "sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw==", + "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==", + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", + "license": "MIT" + }, + "node_modules/nodemon": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.7.tgz", + "integrity": "sha512-hLj7fuMow6f0lbB0cD14Lz2xNjwsyruH251Pk4t/yIitCFJbmY1myuLlHm/q06aST4jg6EgAh74PIBBrRqpVAQ==", + "dev": true, + "license": "MIT", "dependencies": { - "cssnano-preset-default": "^5.2.14", - "lilconfig": "^2.0.3", - "yaml": "^1.10.2" + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": ">=10" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/cssnano" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/react-scripts/node_modules/cssnano-preset-default": { - "version": "5.2.14", - "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.14.tgz", - "integrity": "sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A==", - "dependencies": { - "css-declaration-sorter": "^6.3.1", - "cssnano-utils": "^3.1.0", - "postcss-calc": "^8.2.3", - "postcss-colormin": "^5.3.1", - "postcss-convert-values": "^5.1.3", - "postcss-discard-comments": "^5.1.2", - "postcss-discard-duplicates": "^5.1.0", - "postcss-discard-empty": "^5.1.1", - "postcss-discard-overridden": "^5.1.0", - "postcss-merge-longhand": "^5.1.7", - "postcss-merge-rules": "^5.1.4", - "postcss-minify-font-values": "^5.1.0", - "postcss-minify-gradients": "^5.1.1", - "postcss-minify-params": "^5.1.4", - "postcss-minify-selectors": "^5.2.1", - "postcss-normalize-charset": "^5.1.0", - "postcss-normalize-display-values": "^5.1.0", - "postcss-normalize-positions": "^5.1.1", - "postcss-normalize-repeat-style": "^5.1.1", - "postcss-normalize-string": "^5.1.0", - "postcss-normalize-timing-functions": "^5.1.0", - "postcss-normalize-unicode": "^5.1.1", - "postcss-normalize-url": "^5.1.0", - "postcss-normalize-whitespace": "^5.1.1", - "postcss-ordered-values": "^5.1.3", - "postcss-reduce-initial": "^5.1.2", - "postcss-reduce-transforms": "^5.1.0", - "postcss-svgo": "^5.1.0", - "postcss-unique-selectors": "^5.1.1" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" + "url": "https://opencollective.com/nodemon" } }, - "node_modules/react-scripts/node_modules/cssnano-utils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz", - "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==", + "node_modules/nodemon/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" + "node": ">=4" } }, - "node_modules/react-scripts/node_modules/dom-serializer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", - "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "node_modules/nodemon/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" + "has-flag": "^3.0.0" }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + "engines": { + "node": ">=4" } }, - "node_modules/react-scripts/node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ] - }, - "node_modules/react-scripts/node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" } }, - "node_modules/react-scripts/node_modules/enhanced-resolve": { - "version": "5.12.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz", - "integrity": "sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "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==", + "license": "MIT", "engines": { - "node": ">=10.13.0" + "node": ">=0.10.0" } }, - "node_modules/react-scripts/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/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "license": "MIT", "engines": { "node": ">=10" }, @@ -22119,782 +12976,772 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/react-scripts/node_modules/eslint": { - "version": "8.35.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.35.0.tgz", - "integrity": "sha512-BxAf1fVL7w+JLRQhWl2pzGeSiGqbWumV4WNvc9Rhp6tiCtm4oHnyPBSEtMGZwrQgudFQ+otqzWoPB7x+hxoWsw==", + "node_modules/npm-run-all": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", + "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", + "dev": true, + "license": "MIT", "dependencies": { - "@eslint/eslintrc": "^2.0.0", - "@eslint/js": "8.35.0", - "@humanwhocodes/config-array": "^0.11.8", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", - "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.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.4.0", - "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", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", - "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.1", - "regexpp": "^3.2.0", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0" + "ansi-styles": "^3.2.1", + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "memorystream": "^0.3.1", + "minimatch": "^3.0.4", + "pidtree": "^0.3.0", + "read-pkg": "^3.0.0", + "shell-quote": "^1.6.1", + "string.prototype.padend": "^3.0.0" }, "bin": { - "eslint": "bin/eslint.js" + "npm-run-all": "bin/npm-run-all/index.js", + "run-p": "bin/run-p/index.js", + "run-s": "bin/run-s/index.js" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">= 4" } }, - "node_modules/react-scripts/node_modules/eslint-config-react-app": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-7.0.1.tgz", - "integrity": "sha512-K6rNzvkIeHaTd8m/QEh1Zko0KI7BACWkkneSs6s9cKZC/J27X3eZR6Upt1jkmZ/4FK+XUOPPxMEN7+lbUXfSlA==", + "node_modules/npm-run-all/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==", + "dev": true, + "license": "MIT", "dependencies": { - "@babel/core": "^7.16.0", - "@babel/eslint-parser": "^7.16.3", - "@rushstack/eslint-patch": "^1.1.0", - "@typescript-eslint/eslint-plugin": "^5.5.0", - "@typescript-eslint/parser": "^5.5.0", - "babel-preset-react-app": "^10.0.1", - "confusing-browser-globals": "^1.0.11", - "eslint-plugin-flowtype": "^8.0.3", - "eslint-plugin-import": "^2.25.3", - "eslint-plugin-jest": "^25.3.0", - "eslint-plugin-jsx-a11y": "^6.5.1", - "eslint-plugin-react": "^7.27.1", - "eslint-plugin-react-hooks": "^4.3.0", - "eslint-plugin-testing-library": "^5.0.1" + "color-convert": "^1.9.0" }, "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "eslint": "^8.0.0" + "node": ">=4" } }, - "node_modules/react-scripts/node_modules/eslint-plugin-flowtype": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-8.0.3.tgz", - "integrity": "sha512-dX8l6qUL6O+fYPtpNRideCFSpmWOUVx5QcaGLVqe/vlDiBSe4vYljDWDETwnyFzpl7By/WVIu6rcrniCgH9BqQ==", + "node_modules/npm-run-all/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==", + "dev": true, + "license": "MIT", "dependencies": { - "lodash": "^4.17.21", - "string-natural-compare": "^3.0.1" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" }, "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "@babel/plugin-syntax-flow": "^7.14.5", - "@babel/plugin-transform-react-jsx": "^7.14.9", - "eslint": "^8.1.0" + "node": ">=4" + } + }, + "node_modules/npm-run-all/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" } }, - "node_modules/react-scripts/node_modules/eslint-plugin-testing-library": { - "version": "5.10.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-5.10.2.tgz", - "integrity": "sha512-f1DmDWcz5SDM+IpCkEX0lbFqrrTs8HRsEElzDEqN/EBI0hpRj8Cns5+IVANXswE8/LeybIJqPAOQIFu2j5Y5sw==", + "node_modules/npm-run-all/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==", + "dev": true, + "license": "MIT" + }, + "node_modules/npm-run-all/node_modules/cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/utils": "^5.43.0" + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0", - "npm": ">=6" - }, - "peerDependencies": { - "eslint": "^7.5.0 || ^8.0.0" + "node": ">=4.8" + } + }, + "node_modules/npm-run-all/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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" } }, - "node_modules/react-scripts/node_modules/eslint-utils": { + "node_modules/npm-run-all/node_modules/has-flag": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dependencies": { - "eslint-visitor-keys": "^2.0.0" - }, + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", "engines": { - "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=5" + "node": ">=4" } }, - "node_modules/react-scripts/node_modules/eslint-utils/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/npm-run-all/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/npm-run-all/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^1.0.0" + }, "engines": { - "node": ">=10" + "node": ">=0.10.0" } }, - "node_modules/react-scripts/node_modules/eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "node_modules/npm-run-all/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "dev": true, + "license": "MIT", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=0.10.0" } }, - "node_modules/react-scripts/node_modules/eslint-webpack-plugin": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/eslint-webpack-plugin/-/eslint-webpack-plugin-3.2.0.tgz", - "integrity": "sha512-avrKcGncpPbPSUHX6B3stNGzkKFto3eL+DKM4+VyMrVnhPc3vRczVlCq3uhuFOdRvDHTVXuzwk1ZKUrqDQHQ9w==", + "node_modules/npm-run-all/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", "dependencies": { - "@types/eslint": "^7.29.0 || ^8.4.1", - "jest-worker": "^28.0.2", - "micromatch": "^4.0.5", - "normalize-path": "^3.0.0", - "schema-utils": "^4.0.0" + "has-flag": "^3.0.0" }, "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0", - "webpack": "^5.0.0" + "node": ">=4" } }, - "node_modules/react-scripts/node_modules/eslint-webpack-plugin/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "node_modules/npm-run-all/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "license": "ISC", "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "isexe": "^2.0.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "bin": { + "which": "bin/which" } }, - "node_modules/react-scripts/node_modules/eslint-webpack-plugin/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "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==", + "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.3" + "path-key": "^3.0.0" }, - "peerDependencies": { - "ajv": "^8.8.2" + "engines": { + "node": ">=8" } }, - "node_modules/react-scripts/node_modules/eslint-webpack-plugin/node_modules/jest-worker": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz", - "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==", + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "license": "BSD-2-Clause", "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" + "boolbase": "^1.0.0" }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/nwsapi": { + "version": "2.2.13", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.13.tgz", + "integrity": "sha512-cTGB9ptp9dY9A5VbMSe7fQBcl/tt22Vcqdq8+eN93rblOuE0aCFu4aZ2vMwct/2t+lFnosm8RkQW1I0Omb1UtQ==", + "license": "MIT" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "node": ">=0.10.0" } }, - "node_modules/react-scripts/node_modules/eslint-webpack-plugin/node_modules/schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" - }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "license": "MIT", "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "node": ">= 6" } }, - "node_modules/react-scripts/node_modules/eslint-webpack-plugin/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, + "node_modules/object-inspect": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", + "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", + "license": "MIT", "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/react-scripts/node_modules/eslint/node_modules/eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, + "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==", + "license": "MIT", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">= 0.4" } }, - "node_modules/react-scripts/node_modules/espree": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", - "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", + "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==", + "license": "MIT", "dependencies": { - "acorn": "^8.8.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">= 0.4" }, "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/react-scripts/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==", - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/react-scripts/node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "node_modules/object.entries": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz", + "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==", + "license": "MIT", "dependencies": { - "to-regex-range": "^5.0.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" }, "engines": { - "node": ">=8" + "node": ">= 0.4" } }, - "node_modules/react-scripts/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/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "license": "MIT", "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" + "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/react-scripts/node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "node_modules/object.getownpropertydescriptors": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.8.tgz", + "integrity": "sha512-qkHIGe4q0lSYMv0XI4SsBTJz3WaURhLvd0lKSgtVuOsJ2krg4SgMw3PIRQFMp07yi++UR3se2mkcLqsBNpBb/A==", + "license": "MIT", "dependencies": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" + "array.prototype.reduce": "^1.0.6", + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "gopd": "^1.0.1", + "safe-array-concat": "^1.1.2" }, "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/react-scripts/node_modules/flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==" - }, - "node_modules/react-scripts/node_modules/gauge": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", - "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", - "optional": true, - "peer": true, - "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.2", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.1", - "object-assign": "^4.1.1", - "signal-exit": "^3.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.2" + "node": ">= 0.8" }, - "engines": { - "node": ">=10" - } - }, - "node_modules/react-scripts/node_modules/get-stdin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", - "integrity": "sha512-F5aQMywwJ2n85s4hJPTT9RPxGmubonuB10MNYo17/xph174n2MIR33HRguhzVag10O/npM7SPk73LMZNP+FaWw==", - "optional": true, - "peer": true, - "engines": { - "node": ">=0.10.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/react-scripts/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/object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "license": "MIT", "dependencies": { - "is-glob": "^4.0.3" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" }, "engines": { - "node": ">=10.13.0" + "node": ">= 0.4" } }, - "node_modules/react-scripts/node_modules/globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "node_modules/object.values": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", + "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", + "license": "MIT", "dependencies": { - "type-fest": "^0.20.2" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" }, "engines": { - "node": ">=8" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/react-scripts/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==", - "engines": { - "node": ">=8" - } + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "license": "MIT" }, - "node_modules/react-scripts/node_modules/html-webpack-plugin": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz", - "integrity": "sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw==", + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", "dependencies": { - "@types/html-minifier-terser": "^6.0.0", - "html-minifier-terser": "^6.0.2", - "lodash": "^4.17.21", - "pretty-error": "^4.0.0", - "tapable": "^2.0.0" + "ee-first": "1.1.1" }, "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/html-webpack-plugin" - }, - "peerDependencies": { - "webpack": "^5.20.0" + "node": ">= 0.8" } }, - "node_modules/react-scripts/node_modules/http-proxy-middleware": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", - "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", - "dependencies": { - "@types/http-proxy": "^1.17.8", - "http-proxy": "^1.18.1", - "is-glob": "^4.0.1", - "is-plain-obj": "^3.0.0", - "micromatch": "^4.0.2" - }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "license": "MIT", "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "@types/express": "^4.17.13" - }, - "peerDependenciesMeta": { - "@types/express": { - "optional": true - } + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" } }, - "node_modules/react-scripts/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==", + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "license": "MIT", "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" + "mimic-fn": "^2.1.0" }, "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-scripts/node_modules/icss-utils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", - "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "engines": { - "node": "^10 || ^12 || >= 14" + "node": ">=6" }, - "peerDependencies": { - "postcss": "^8.1.0" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/react-scripts/node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "license": "MIT", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, "engines": { - "node": ">= 4" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/react-scripts/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==", + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, "engines": { - "node": ">=0.12.0" + "node": ">= 0.8.0" } }, - "node_modules/react-scripts/node_modules/is-plain-obj": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", - "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "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==", + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, "engines": { - "node": ">=10" + "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/react-scripts/node_modules/jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "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==", + "license": "MIT", "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" + "p-limit": "^2.2.0" }, "engines": { - "node": ">= 10.13.0" + "node": ">=8" } }, - "node_modules/react-scripts/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==", + "node_modules/p-retry": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-6.2.0.tgz", + "integrity": "sha512-JA6nkq6hKyWLLasXQXUrO4z8BUZGUt/LjlJxx8Gb2+2ntodU/SS63YZ8b0LUTbQ8ZB9iwOfhEPhg4ykKnn2KsA==", + "devOptional": true, + "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "@types/retry": "0.12.2", + "is-network-error": "^1.0.0", + "retry": "^0.13.1" }, "engines": { - "node": ">=10" + "node": ">=16.17" }, "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/react-scripts/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==", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "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==", + "license": "MIT", + "engines": { + "node": ">=6" } }, - "node_modules/react-scripts/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + "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==", + "license": "BlueOak-1.0.0" }, - "node_modules/react-scripts/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/param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "license": "MIT", "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" + "dot-case": "^3.0.4", + "tslib": "^2.0.3" } }, - "node_modules/react-scripts/node_modules/loader-runner": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", - "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "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==", + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, "engines": { - "node": ">=6.11.5" + "node": ">=6" } }, - "node_modules/react-scripts/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/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==", + "license": "MIT", "dependencies": { - "p-locate": "^5.0.0" + "@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": ">=10" + "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/react-scripts/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "license": "MIT" + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "license": "MIT", "dependencies": { - "yallist": "^4.0.0" - }, + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "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==", + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=8" } }, - "node_modules/react-scripts/node_modules/mdn-data": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", - "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" + "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==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } }, - "node_modules/react-scripts/node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, + "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==", + "license": "MIT", "engines": { - "node": ">=8.6" + "node": ">=8" } }, - "node_modules/react-scripts/node_modules/mini-css-extract-plugin": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.2.tgz", - "integrity": "sha512-EdlUizq13o0Pd+uCp+WO/JpkLvHRVGt97RqfeGhXqAcorYo1ypJSpkV+WDT0vY/kmh/p7wRdJNJtuyK540PXDw==", + "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==", + "license": "MIT" + }, + "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==", + "license": "BlueOak-1.0.0", "dependencies": { - "schema-utils": "^4.0.0" + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { - "node": ">= 12.13.0" + "node": ">=16 || 14 >=14.18" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/react-scripts/node_modules/mini-css-extract-plugin/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } + "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==", + "license": "ISC" }, - "node_modules/react-scripts/node_modules/mini-css-extract-plugin/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } + "node_modules/path-to-regexp": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", + "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==", + "license": "MIT" }, - "node_modules/react-scripts/node_modules/mini-css-extract-plugin/node_modules/schema-utils": { + "node_modules/path-type": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" - }, + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "license": "MIT", "engines": { - "node": ">= 12.13.0" + "node": ">=8" + } + }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", + "engines": { + "node": ">=8.6" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/react-scripts/node_modules/nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "node_modules/pidtree": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", + "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", + "dev": true, + "license": "MIT", "bin": { - "nanoid": "bin/nanoid.cjs" + "pidtree": "bin/pidtree.js" }, "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + "node": ">=0.10" } }, - "node_modules/react-scripts/node_modules/node-sass": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-7.0.3.tgz", - "integrity": "sha512-8MIlsY/4dXUkJDYht9pIWBhMil3uHmE8b/AdJPjmFn1nBx9X9BASzfzmsCy0uCCb8eqI3SYYzVPDswWqSx7gjw==", - "hasInstallScript": true, - "optional": true, - "peer": true, - "dependencies": { - "async-foreach": "^0.1.3", - "chalk": "^4.1.2", - "cross-spawn": "^7.0.3", - "gaze": "^1.0.0", - "get-stdin": "^4.0.1", - "glob": "^7.0.3", - "lodash": "^4.17.15", - "meow": "^9.0.0", - "nan": "^2.13.2", - "node-gyp": "^8.4.1", - "npmlog": "^5.0.0", - "request": "^2.88.0", - "sass-graph": "^4.0.1", - "stdout-stream": "^1.4.0", - "true-case-path": "^1.0.2" - }, - "bin": { - "node-sass": "bin/node-sass" - }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "license": "MIT", "engines": { - "node": ">=12" + "node": ">=0.10.0" } }, - "node_modules/react-scripts/node_modules/npmlog": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", - "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", - "optional": true, - "peer": true, - "dependencies": { - "are-we-there-yet": "^2.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^3.0.0", - "set-blocking": "^2.0.0" + "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==", + "license": "MIT", + "engines": { + "node": ">= 6" } }, - "node_modules/react-scripts/node_modules/nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "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==", + "license": "MIT", "dependencies": { - "boolbase": "^1.0.0" + "find-up": "^4.0.0" }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" + "engines": { + "node": ">=8" } }, - "node_modules/react-scripts/node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "node_modules/pkg-up": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", + "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", + "license": "MIT", "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" + "find-up": "^3.0.0" }, "engines": { - "node": ">= 0.8.0" + "node": ">=8" } }, - "node_modules/react-scripts/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/pkg-up/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "license": "MIT", "dependencies": { - "yocto-queue": "^0.1.0" + "locate-path": "^3.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=6" } }, - "node_modules/react-scripts/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/pkg-up/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "license": "MIT", "dependencies": { - "p-limit": "^3.0.2" + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" }, "engines": { - "node": ">=10" + "node": ">=6" + } + }, + "node_modules/pkg-up/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "license": "MIT", + "dependencies": { + "p-limit": "^2.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=6" } }, - "node_modules/react-scripts/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/pkg-up/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/react-scripts/node_modules/picocolors": { + "node_modules/possible-typed-array-names": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } }, - "node_modules/react-scripts/node_modules/postcss": { - "version": "8.4.21", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", - "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==", + "node_modules/postcss": { + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", "funding": [ { "type": "opencollective", @@ -22903,21 +13750,27 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "nanoid": "^3.3.4", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" + "nanoid": "^3.3.7", + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" } }, - "node_modules/react-scripts/node_modules/postcss-attribute-case-insensitive": { + "node_modules/postcss-attribute-case-insensitive": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.2.tgz", "integrity": "sha512-XIidXV8fDr0kKt28vqki84fRK8VW8eTuIa4PChv2MqKuT6C9UjmSKzen6KaWhWEoYvwxFCa7n/tC1SZ3tyq4SQ==", + "license": "MIT", "dependencies": { "postcss-selector-parser": "^6.0.10" }, @@ -22932,10 +13785,11 @@ "postcss": "^8.2" } }, - "node_modules/react-scripts/node_modules/postcss-browser-comments": { + "node_modules/postcss-browser-comments": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/postcss-browser-comments/-/postcss-browser-comments-4.0.0.tgz", "integrity": "sha512-X9X9/WN3KIvY9+hNERUqX9gncsgBA25XaeR+jshHz2j8+sYyHktHw1JdKuMjeLpGktXidqDhA7b/qm1mrBDmgg==", + "license": "CC0-1.0", "engines": { "node": ">=8" }, @@ -22944,10 +13798,11 @@ "postcss": ">=8" } }, - "node_modules/react-scripts/node_modules/postcss-calc": { + "node_modules/postcss-calc": { "version": "8.2.4", "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz", "integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==", + "license": "MIT", "dependencies": { "postcss-selector-parser": "^6.0.9", "postcss-value-parser": "^4.2.0" @@ -22956,10 +13811,11 @@ "postcss": "^8.2.2" } }, - "node_modules/react-scripts/node_modules/postcss-clamp": { + "node_modules/postcss-clamp": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/postcss-clamp/-/postcss-clamp-4.1.0.tgz", "integrity": "sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==", + "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -22970,10 +13826,11 @@ "postcss": "^8.4.6" } }, - "node_modules/react-scripts/node_modules/postcss-color-functional-notation": { + "node_modules/postcss-color-functional-notation": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-4.2.4.tgz", "integrity": "sha512-2yrTAUZUab9s6CpxkxC4rVgFEVaR6/2Pipvi6qcgvnYiVqZcbDHEoBDhrXzyb7Efh2CCfHQNtcqWcIruDTIUeg==", + "license": "CC0-1.0", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -22988,10 +13845,11 @@ "postcss": "^8.2" } }, - "node_modules/react-scripts/node_modules/postcss-color-hex-alpha": { + "node_modules/postcss-color-hex-alpha": { "version": "8.0.4", "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-8.0.4.tgz", "integrity": "sha512-nLo2DCRC9eE4w2JmuKgVA3fGL3d01kGq752pVALF68qpGLmx2Qrk91QTKkdUqqp45T1K1XV8IhQpcu1hoAQflQ==", + "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -23006,10 +13864,11 @@ "postcss": "^8.4" } }, - "node_modules/react-scripts/node_modules/postcss-color-rebeccapurple": { + "node_modules/postcss-color-rebeccapurple": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-7.1.1.tgz", "integrity": "sha512-pGxkuVEInwLHgkNxUc4sdg4g3py7zUeCQ9sMfwyHAT+Ezk8a4OaaVZ8lIY5+oNqA/BXXgLyXv0+5wHP68R79hg==", + "license": "CC0-1.0", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -23024,10 +13883,11 @@ "postcss": "^8.2" } }, - "node_modules/react-scripts/node_modules/postcss-colormin": { + "node_modules/postcss-colormin": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.1.tgz", "integrity": "sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ==", + "license": "MIT", "dependencies": { "browserslist": "^4.21.4", "caniuse-api": "^3.0.0", @@ -23041,10 +13901,11 @@ "postcss": "^8.2.15" } }, - "node_modules/react-scripts/node_modules/postcss-convert-values": { + "node_modules/postcss-convert-values": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.3.tgz", "integrity": "sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==", + "license": "MIT", "dependencies": { "browserslist": "^4.21.4", "postcss-value-parser": "^4.2.0" @@ -23056,10 +13917,11 @@ "postcss": "^8.2.15" } }, - "node_modules/react-scripts/node_modules/postcss-custom-media": { + "node_modules/postcss-custom-media": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-8.0.2.tgz", "integrity": "sha512-7yi25vDAoHAkbhAzX9dHx2yc6ntS4jQvejrNcC+csQJAXjj15e7VcWfMgLqBNAbOvqi5uIa9huOVwdHbf+sKqg==", + "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -23074,10 +13936,11 @@ "postcss": "^8.3" } }, - "node_modules/react-scripts/node_modules/postcss-custom-properties": { + "node_modules/postcss-custom-properties": { "version": "12.1.11", "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.11.tgz", "integrity": "sha512-0IDJYhgU8xDv1KY6+VgUwuQkVtmYzRwu+dMjnmdMafXYv86SWqfxkc7qdDvWS38vsjaEtv8e0vGOUQrAiMBLpQ==", + "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -23092,10 +13955,11 @@ "postcss": "^8.2" } }, - "node_modules/react-scripts/node_modules/postcss-custom-selectors": { + "node_modules/postcss-custom-selectors": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-6.0.3.tgz", "integrity": "sha512-fgVkmyiWDwmD3JbpCmB45SvvlCD6z9CG6Ie6Iere22W5aHea6oWa7EM2bpnv2Fj3I94L3VbtvX9KqwSi5aFzSg==", + "license": "MIT", "dependencies": { "postcss-selector-parser": "^6.0.4" }, @@ -23110,10 +13974,11 @@ "postcss": "^8.3" } }, - "node_modules/react-scripts/node_modules/postcss-dir-pseudo-class": { + "node_modules/postcss-dir-pseudo-class": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-6.0.5.tgz", "integrity": "sha512-eqn4m70P031PF7ZQIvSgy9RSJ5uI2171O/OO/zcRNYpJbvaeKFUlar1aJ7rmgiQtbm0FSPsRewjpdS0Oew7MPA==", + "license": "CC0-1.0", "dependencies": { "postcss-selector-parser": "^6.0.10" }, @@ -23128,10 +13993,11 @@ "postcss": "^8.2" } }, - "node_modules/react-scripts/node_modules/postcss-discard-comments": { + "node_modules/postcss-discard-comments": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz", "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==", + "license": "MIT", "engines": { "node": "^10 || ^12 || >=14.0" }, @@ -23139,10 +14005,11 @@ "postcss": "^8.2.15" } }, - "node_modules/react-scripts/node_modules/postcss-discard-duplicates": { + "node_modules/postcss-discard-duplicates": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz", "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==", + "license": "MIT", "engines": { "node": "^10 || ^12 || >=14.0" }, @@ -23150,10 +14017,11 @@ "postcss": "^8.2.15" } }, - "node_modules/react-scripts/node_modules/postcss-discard-empty": { + "node_modules/postcss-discard-empty": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz", "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==", + "license": "MIT", "engines": { "node": "^10 || ^12 || >=14.0" }, @@ -23161,10 +14029,11 @@ "postcss": "^8.2.15" } }, - "node_modules/react-scripts/node_modules/postcss-discard-overridden": { + "node_modules/postcss-discard-overridden": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz", "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==", + "license": "MIT", "engines": { "node": "^10 || ^12 || >=14.0" }, @@ -23172,10 +14041,11 @@ "postcss": "^8.2.15" } }, - "node_modules/react-scripts/node_modules/postcss-double-position-gradients": { + "node_modules/postcss-double-position-gradients": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-3.1.2.tgz", "integrity": "sha512-GX+FuE/uBR6eskOK+4vkXgT6pDkexLokPaz/AbJna9s5Kzp/yl488pKPjhy0obB475ovfT1Wv8ho7U/cHNaRgQ==", + "license": "CC0-1.0", "dependencies": { "@csstools/postcss-progressive-custom-properties": "^1.1.0", "postcss-value-parser": "^4.2.0" @@ -23191,10 +14061,11 @@ "postcss": "^8.2" } }, - "node_modules/react-scripts/node_modules/postcss-env-function": { + "node_modules/postcss-env-function": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-4.0.6.tgz", "integrity": "sha512-kpA6FsLra+NqcFnL81TnsU+Z7orGtDTxcOhl6pwXeEq1yFPpRMkCDpHhrz8CFQDr/Wfm0jLiNQ1OsGGPjlqPwA==", + "license": "CC0-1.0", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -23205,18 +14076,20 @@ "postcss": "^8.4" } }, - "node_modules/react-scripts/node_modules/postcss-flexbugs-fixes": { + "node_modules/postcss-flexbugs-fixes": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-5.0.2.tgz", "integrity": "sha512-18f9voByak7bTktR2QgDveglpn9DTbBWPUzSOe9g0N4WR/2eSt6Vrcbf0hmspvMI6YWGywz6B9f7jzpFNJJgnQ==", + "license": "MIT", "peerDependencies": { "postcss": "^8.1.4" } }, - "node_modules/react-scripts/node_modules/postcss-focus-visible": { + "node_modules/postcss-focus-visible": { "version": "6.0.4", "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-6.0.4.tgz", "integrity": "sha512-QcKuUU/dgNsstIK6HELFRT5Y3lbrMLEOwG+A4s5cA+fx3A3y/JTq3X9LaOj3OC3ALH0XqyrgQIgey/MIZ8Wczw==", + "license": "CC0-1.0", "dependencies": { "postcss-selector-parser": "^6.0.9" }, @@ -23227,10 +14100,11 @@ "postcss": "^8.4" } }, - "node_modules/react-scripts/node_modules/postcss-focus-within": { + "node_modules/postcss-focus-within": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-5.0.4.tgz", "integrity": "sha512-vvjDN++C0mu8jz4af5d52CB184ogg/sSxAFS+oUJQq2SuCe7T5U2iIsVJtsCp2d6R4j0jr5+q3rPkBVZkXD9fQ==", + "license": "CC0-1.0", "dependencies": { "postcss-selector-parser": "^6.0.9" }, @@ -23241,18 +14115,20 @@ "postcss": "^8.4" } }, - "node_modules/react-scripts/node_modules/postcss-font-variant": { + "node_modules/postcss-font-variant": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", + "license": "MIT", "peerDependencies": { "postcss": "^8.1.0" } }, - "node_modules/react-scripts/node_modules/postcss-gap-properties": { + "node_modules/postcss-gap-properties": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.5.tgz", "integrity": "sha512-IuE6gKSdoUNcvkGIqdtjtcMtZIFyXZhmFd5RUlg97iVEvp1BZKV5ngsAjCjrVy+14uhGBQl9tzmi1Qwq4kqVOg==", + "license": "CC0-1.0", "engines": { "node": "^12 || ^14 || >=16" }, @@ -23264,10 +14140,11 @@ "postcss": "^8.2" } }, - "node_modules/react-scripts/node_modules/postcss-image-set-function": { + "node_modules/postcss-image-set-function": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-4.0.7.tgz", "integrity": "sha512-9T2r9rsvYzm5ndsBE8WgtrMlIT7VbtTfE7b3BQnudUqnBcBo7L758oc+o+pdj/dUV0l5wjwSdjeOH2DZtfv8qw==", + "license": "CC0-1.0", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -23282,18 +14159,56 @@ "postcss": "^8.2" } }, - "node_modules/react-scripts/node_modules/postcss-initial": { + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-initial": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz", "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==", + "license": "MIT", "peerDependencies": { "postcss": "^8.0.0" } }, - "node_modules/react-scripts/node_modules/postcss-lab-function": { + "node_modules/postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-lab-function": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-4.2.1.tgz", "integrity": "sha512-xuXll4isR03CrQsmxyz92LJB2xX9n+pZJ5jE9JgcnmsCammLyKdlzrBin+25dy6wIjfhJpKBAN80gsTlCgRk2w==", + "license": "CC0-1.0", "dependencies": { "@csstools/postcss-progressive-custom-properties": "^1.1.0", "postcss-value-parser": "^4.2.0" @@ -23309,31 +14224,149 @@ "postcss": "^8.2" } }, - "node_modules/react-scripts/node_modules/postcss-loader": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz", - "integrity": "sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==", + "node_modules/postcss-load-config": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", "dependencies": { - "cosmiconfig": "^7.0.0", - "klona": "^2.0.5", - "semver": "^7.3.5" + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" }, "engines": { - "node": ">= 12.13.0" + "node": ">= 14" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-load-config/node_modules/lilconfig": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", + "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/postcss-load-config/node_modules/yaml": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.0.tgz", + "integrity": "sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/postcss-loader": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-8.1.1.tgz", + "integrity": "sha512-0IeqyAsG6tYiDRCYKQJLAmgQr47DX6N7sFSWvQxt6AcupX8DIdmykuk/o/tx0Lze3ErGHJEp5OSRxrelC6+NdQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "cosmiconfig": "^9.0.0", + "jiti": "^1.20.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">= 18.12.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/webpack" }, "peerDependencies": { + "@rspack/core": "0.x || 1.x", "postcss": "^7.0.0 || ^8.0.1", "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/postcss-loader/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, + "license": "Python-2.0" + }, + "node_modules/postcss-loader/node_modules/cosmiconfig": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", + "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/postcss-loader/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, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/react-scripts/node_modules/postcss-logical": { + "node_modules/postcss-logical": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz", "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==", + "license": "CC0-1.0", "engines": { "node": "^12 || ^14 || >=16" }, @@ -23341,10 +14374,11 @@ "postcss": "^8.4" } }, - "node_modules/react-scripts/node_modules/postcss-media-minmax": { + "node_modules/postcss-media-minmax": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz", "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==", + "license": "MIT", "engines": { "node": ">=10.0.0" }, @@ -23352,10 +14386,11 @@ "postcss": "^8.1.0" } }, - "node_modules/react-scripts/node_modules/postcss-merge-longhand": { + "node_modules/postcss-merge-longhand": { "version": "5.1.7", "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.7.tgz", "integrity": "sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==", + "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0", "stylehacks": "^5.1.1" @@ -23367,10 +14402,11 @@ "postcss": "^8.2.15" } }, - "node_modules/react-scripts/node_modules/postcss-merge-rules": { + "node_modules/postcss-merge-rules": { "version": "5.1.4", "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.4.tgz", "integrity": "sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g==", + "license": "MIT", "dependencies": { "browserslist": "^4.21.4", "caniuse-api": "^3.0.0", @@ -23384,10 +14420,11 @@ "postcss": "^8.2.15" } }, - "node_modules/react-scripts/node_modules/postcss-minify-font-values": { + "node_modules/postcss-minify-font-values": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz", "integrity": "sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==", + "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -23398,10 +14435,11 @@ "postcss": "^8.2.15" } }, - "node_modules/react-scripts/node_modules/postcss-minify-gradients": { + "node_modules/postcss-minify-gradients": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz", "integrity": "sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==", + "license": "MIT", "dependencies": { "colord": "^2.9.1", "cssnano-utils": "^3.1.0", @@ -23414,10 +14452,11 @@ "postcss": "^8.2.15" } }, - "node_modules/react-scripts/node_modules/postcss-minify-params": { + "node_modules/postcss-minify-params": { "version": "5.1.4", "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.4.tgz", "integrity": "sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==", + "license": "MIT", "dependencies": { "browserslist": "^4.21.4", "cssnano-utils": "^3.1.0", @@ -23430,10 +14469,11 @@ "postcss": "^8.2.15" } }, - "node_modules/react-scripts/node_modules/postcss-minify-selectors": { + "node_modules/postcss-minify-selectors": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz", "integrity": "sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==", + "license": "MIT", "dependencies": { "postcss-selector-parser": "^6.0.5" }, @@ -23444,10 +14484,11 @@ "postcss": "^8.2.15" } }, - "node_modules/react-scripts/node_modules/postcss-modules-extract-imports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", - "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "node_modules/postcss-modules-extract-imports": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", + "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", + "license": "ISC", "engines": { "node": "^10 || ^12 || >= 14" }, @@ -23455,10 +14496,11 @@ "postcss": "^8.1.0" } }, - "node_modules/react-scripts/node_modules/postcss-modules-local-by-default": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", - "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", + "node_modules/postcss-modules-local-by-default": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.5.tgz", + "integrity": "sha512-6MieY7sIfTK0hYfafw1OMEG+2bg8Q1ocHCpoWLqOKj3JXlKu4G7btkmM/B7lFubYkYWmRSPLZi5chid63ZaZYw==", + "license": "MIT", "dependencies": { "icss-utils": "^5.0.0", "postcss-selector-parser": "^6.0.2", @@ -23471,10 +14513,11 @@ "postcss": "^8.1.0" } }, - "node_modules/react-scripts/node_modules/postcss-modules-scope": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", - "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "node_modules/postcss-modules-scope": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.0.tgz", + "integrity": "sha512-oq+g1ssrsZOsx9M96c5w8laRmvEu9C3adDSjI8oTcbfkrTE8hx/zfyobUoWIxaKPO8bt6S62kxpw5GqypEw1QQ==", + "license": "ISC", "dependencies": { "postcss-selector-parser": "^6.0.4" }, @@ -23485,24 +14528,51 @@ "postcss": "^8.1.0" } }, - "node_modules/react-scripts/node_modules/postcss-modules-values": { + "node_modules/postcss-modules-values": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "license": "ISC", + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", "dependencies": { - "icss-utils": "^5.0.0" + "postcss-selector-parser": "^6.1.1" }, "engines": { - "node": "^10 || ^12 || >= 14" + "node": ">=12.0" }, "peerDependencies": { - "postcss": "^8.1.0" + "postcss": "^8.2.14" } }, - "node_modules/react-scripts/node_modules/postcss-nesting": { + "node_modules/postcss-nesting": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-10.2.0.tgz", "integrity": "sha512-EwMkYchxiDiKUhlJGzWsD9b2zvq/r2SSubcRrgP+jujMXFzqvANLt16lJANC+5uZ6hjI7lpRmI6O8JIl+8l1KA==", + "license": "CC0-1.0", "dependencies": { "@csstools/selector-specificity": "^2.0.0", "postcss-selector-parser": "^6.0.10" @@ -23518,10 +14588,11 @@ "postcss": "^8.2" } }, - "node_modules/react-scripts/node_modules/postcss-normalize": { + "node_modules/postcss-normalize": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/postcss-normalize/-/postcss-normalize-10.0.1.tgz", "integrity": "sha512-+5w18/rDev5mqERcG3W5GZNMJa1eoYYNGo8gB7tEwaos0ajk3ZXAI4mHGcNT47NE+ZnZD1pEpUOFLvltIwmeJA==", + "license": "CC0-1.0", "dependencies": { "@csstools/normalize.css": "*", "postcss-browser-comments": "^4", @@ -23535,10 +14606,11 @@ "postcss": ">= 8" } }, - "node_modules/react-scripts/node_modules/postcss-normalize-charset": { + "node_modules/postcss-normalize-charset": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz", "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==", + "license": "MIT", "engines": { "node": "^10 || ^12 || >=14.0" }, @@ -23546,10 +14618,11 @@ "postcss": "^8.2.15" } }, - "node_modules/react-scripts/node_modules/postcss-normalize-display-values": { + "node_modules/postcss-normalize-display-values": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz", "integrity": "sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==", + "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -23560,10 +14633,11 @@ "postcss": "^8.2.15" } }, - "node_modules/react-scripts/node_modules/postcss-normalize-positions": { + "node_modules/postcss-normalize-positions": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz", "integrity": "sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==", + "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -23574,10 +14648,11 @@ "postcss": "^8.2.15" } }, - "node_modules/react-scripts/node_modules/postcss-normalize-repeat-style": { + "node_modules/postcss-normalize-repeat-style": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz", "integrity": "sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==", + "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -23588,10 +14663,11 @@ "postcss": "^8.2.15" } }, - "node_modules/react-scripts/node_modules/postcss-normalize-string": { + "node_modules/postcss-normalize-string": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz", "integrity": "sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==", + "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -23602,10 +14678,11 @@ "postcss": "^8.2.15" } }, - "node_modules/react-scripts/node_modules/postcss-normalize-timing-functions": { + "node_modules/postcss-normalize-timing-functions": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz", "integrity": "sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==", + "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -23616,10 +14693,11 @@ "postcss": "^8.2.15" } }, - "node_modules/react-scripts/node_modules/postcss-normalize-unicode": { + "node_modules/postcss-normalize-unicode": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.1.tgz", "integrity": "sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==", + "license": "MIT", "dependencies": { "browserslist": "^4.21.4", "postcss-value-parser": "^4.2.0" @@ -23631,10 +14709,11 @@ "postcss": "^8.2.15" } }, - "node_modules/react-scripts/node_modules/postcss-normalize-url": { + "node_modules/postcss-normalize-url": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz", "integrity": "sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==", + "license": "MIT", "dependencies": { "normalize-url": "^6.0.1", "postcss-value-parser": "^4.2.0" @@ -23646,10 +14725,11 @@ "postcss": "^8.2.15" } }, - "node_modules/react-scripts/node_modules/postcss-normalize-whitespace": { + "node_modules/postcss-normalize-whitespace": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz", "integrity": "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==", + "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -23660,7 +14740,7 @@ "postcss": "^8.2.15" } }, - "node_modules/react-scripts/node_modules/postcss-opacity-percentage": { + "node_modules/postcss-opacity-percentage": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/postcss-opacity-percentage/-/postcss-opacity-percentage-1.1.3.tgz", "integrity": "sha512-An6Ba4pHBiDtyVpSLymUUERMo2cU7s+Obz6BTrS+gxkbnSBNKSuD0AVUc+CpBMrpVPKKfoVz0WQCX+Tnst0i4A==", @@ -23674,6 +14754,7 @@ "url": "https://liberapay.com/mrcgrtz" } ], + "license": "MIT", "engines": { "node": "^12 || ^14 || >=16" }, @@ -23681,646 +14762,1105 @@ "postcss": "^8.2" } }, - "node_modules/react-scripts/node_modules/postcss-ordered-values": { + "node_modules/postcss-ordered-values": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz", "integrity": "sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==", + "license": "MIT", "dependencies": { "cssnano-utils": "^3.1.0", "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-overflow-shorthand": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.4.tgz", + "integrity": "sha512-otYl/ylHK8Y9bcBnPLo3foYFLL6a6Ak+3EQBPOTR7luMYCOsiVTUk1iLvNf6tVPNGXcoL9Hoz37kpfriRIFb4A==", + "license": "CC0-1.0", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-page-break": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", + "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", + "license": "MIT", + "peerDependencies": { + "postcss": "^8" + } + }, + "node_modules/postcss-place": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-7.0.5.tgz", + "integrity": "sha512-wR8igaZROA6Z4pv0d+bvVrvGY4GVHihBCBQieXFY3kuSuMyOmEnnfFzHl/tQuqHZkfkIVBEbDvYcFfHmpSet9g==", + "license": "CC0-1.0", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-preset-env": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-7.8.3.tgz", + "integrity": "sha512-T1LgRm5uEVFSEF83vHZJV2z19lHg4yJuZ6gXZZkqVsqv63nlr6zabMH3l4Pc01FQCyfWVrh2GaUeCVy9Po+Aag==", + "license": "CC0-1.0", + "dependencies": { + "@csstools/postcss-cascade-layers": "^1.1.1", + "@csstools/postcss-color-function": "^1.1.1", + "@csstools/postcss-font-format-keywords": "^1.0.1", + "@csstools/postcss-hwb-function": "^1.0.2", + "@csstools/postcss-ic-unit": "^1.0.1", + "@csstools/postcss-is-pseudo-class": "^2.0.7", + "@csstools/postcss-nested-calc": "^1.0.0", + "@csstools/postcss-normalize-display-values": "^1.0.1", + "@csstools/postcss-oklab-function": "^1.1.1", + "@csstools/postcss-progressive-custom-properties": "^1.3.0", + "@csstools/postcss-stepped-value-functions": "^1.0.1", + "@csstools/postcss-text-decoration-shorthand": "^1.0.0", + "@csstools/postcss-trigonometric-functions": "^1.0.2", + "@csstools/postcss-unset-value": "^1.0.2", + "autoprefixer": "^10.4.13", + "browserslist": "^4.21.4", + "css-blank-pseudo": "^3.0.3", + "css-has-pseudo": "^3.0.4", + "css-prefers-color-scheme": "^6.0.3", + "cssdb": "^7.1.0", + "postcss-attribute-case-insensitive": "^5.0.2", + "postcss-clamp": "^4.1.0", + "postcss-color-functional-notation": "^4.2.4", + "postcss-color-hex-alpha": "^8.0.4", + "postcss-color-rebeccapurple": "^7.1.1", + "postcss-custom-media": "^8.0.2", + "postcss-custom-properties": "^12.1.10", + "postcss-custom-selectors": "^6.0.3", + "postcss-dir-pseudo-class": "^6.0.5", + "postcss-double-position-gradients": "^3.1.2", + "postcss-env-function": "^4.0.6", + "postcss-focus-visible": "^6.0.4", + "postcss-focus-within": "^5.0.4", + "postcss-font-variant": "^5.0.0", + "postcss-gap-properties": "^3.0.5", + "postcss-image-set-function": "^4.0.7", + "postcss-initial": "^4.0.1", + "postcss-lab-function": "^4.2.1", + "postcss-logical": "^5.0.4", + "postcss-media-minmax": "^5.0.0", + "postcss-nesting": "^10.2.0", + "postcss-opacity-percentage": "^1.1.2", + "postcss-overflow-shorthand": "^3.0.4", + "postcss-page-break": "^3.0.4", + "postcss-place": "^7.0.5", + "postcss-pseudo-class-any-link": "^7.1.6", + "postcss-replace-overflow-wrap": "^4.0.0", + "postcss-selector-not": "^6.0.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-pseudo-class-any-link": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-7.1.6.tgz", + "integrity": "sha512-9sCtZkO6f/5ML9WcTLcIyV1yz9D1rf0tWc+ulKcvV30s0iZKS/ONyETvoWsr6vnrmW+X+KmuK3gV/w5EWnT37w==", + "license": "CC0-1.0", + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-reduce-initial": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.2.tgz", + "integrity": "sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg==", + "license": "MIT", + "dependencies": { + "browserslist": "^4.21.4", + "caniuse-api": "^3.0.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-reduce-transforms": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz", + "integrity": "sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-replace-overflow-wrap": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", + "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", + "license": "MIT", + "peerDependencies": { + "postcss": "^8.0.3" + } + }, + "node_modules/postcss-selector-not": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-6.0.1.tgz", + "integrity": "sha512-1i9affjAe9xu/y9uqWH+tD4r6/hDaXJruk8xn2x1vzxC2U3J3LKO3zJW4CyxlNhA56pADJ/djpEwpH1RClI2rQ==", + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-svgo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.1.0.tgz", + "integrity": "sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0", + "svgo": "^2.7.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-svgo/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/postcss-svgo/node_modules/css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/postcss-svgo/node_modules/mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", + "license": "CC0-1.0" + }, + "node_modules/postcss-svgo/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==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-svgo/node_modules/svgo": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", + "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "license": "MIT", + "dependencies": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^4.1.3", + "css-tree": "^1.1.3", + "csso": "^4.2.0", + "picocolors": "^1.0.0", + "stable": "^0.1.8" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=10.13.0" } }, - "node_modules/react-scripts/node_modules/postcss-overflow-shorthand": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.4.tgz", - "integrity": "sha512-otYl/ylHK8Y9bcBnPLo3foYFLL6a6Ak+3EQBPOTR7luMYCOsiVTUk1iLvNf6tVPNGXcoL9Hoz37kpfriRIFb4A==", + "node_modules/postcss-unique-selectors": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz", + "integrity": "sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==", + "license": "MIT", "dependencies": { - "postcss-value-parser": "^4.2.0" + "postcss-selector-parser": "^6.0.5" }, "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" + "node": "^10 || ^12 || >=14.0" }, "peerDependencies": { - "postcss": "^8.2" + "postcss": "^8.2.15" } }, - "node_modules/react-scripts/node_modules/postcss-page-break": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", - "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", - "peerDependencies": { - "postcss": "^8" + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "license": "MIT" + }, + "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==", + "license": "MIT", + "engines": { + "node": ">= 0.8.0" } }, - "node_modules/react-scripts/node_modules/postcss-place": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-7.0.5.tgz", - "integrity": "sha512-wR8igaZROA6Z4pv0d+bvVrvGY4GVHihBCBQieXFY3kuSuMyOmEnnfFzHl/tQuqHZkfkIVBEbDvYcFfHmpSet9g==", - "dependencies": { - "postcss-value-parser": "^4.2.0" + "node_modules/prettier": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" }, "engines": { - "node": "^12 || ^14 || >=16" + "node": ">=14" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" + "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/react-scripts/node_modules/postcss-preset-env": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-7.8.3.tgz", - "integrity": "sha512-T1LgRm5uEVFSEF83vHZJV2z19lHg4yJuZ6gXZZkqVsqv63nlr6zabMH3l4Pc01FQCyfWVrh2GaUeCVy9Po+Aag==", - "dependencies": { - "@csstools/postcss-cascade-layers": "^1.1.1", - "@csstools/postcss-color-function": "^1.1.1", - "@csstools/postcss-font-format-keywords": "^1.0.1", - "@csstools/postcss-hwb-function": "^1.0.2", - "@csstools/postcss-ic-unit": "^1.0.1", - "@csstools/postcss-is-pseudo-class": "^2.0.7", - "@csstools/postcss-nested-calc": "^1.0.0", - "@csstools/postcss-normalize-display-values": "^1.0.1", - "@csstools/postcss-oklab-function": "^1.1.1", - "@csstools/postcss-progressive-custom-properties": "^1.3.0", - "@csstools/postcss-stepped-value-functions": "^1.0.1", - "@csstools/postcss-text-decoration-shorthand": "^1.0.0", - "@csstools/postcss-trigonometric-functions": "^1.0.2", - "@csstools/postcss-unset-value": "^1.0.2", - "autoprefixer": "^10.4.13", - "browserslist": "^4.21.4", - "css-blank-pseudo": "^3.0.3", - "css-has-pseudo": "^3.0.4", - "css-prefers-color-scheme": "^6.0.3", - "cssdb": "^7.1.0", - "postcss-attribute-case-insensitive": "^5.0.2", - "postcss-clamp": "^4.1.0", - "postcss-color-functional-notation": "^4.2.4", - "postcss-color-hex-alpha": "^8.0.4", - "postcss-color-rebeccapurple": "^7.1.1", - "postcss-custom-media": "^8.0.2", - "postcss-custom-properties": "^12.1.10", - "postcss-custom-selectors": "^6.0.3", - "postcss-dir-pseudo-class": "^6.0.5", - "postcss-double-position-gradients": "^3.1.2", - "postcss-env-function": "^4.0.6", - "postcss-focus-visible": "^6.0.4", - "postcss-focus-within": "^5.0.4", - "postcss-font-variant": "^5.0.0", - "postcss-gap-properties": "^3.0.5", - "postcss-image-set-function": "^4.0.7", - "postcss-initial": "^4.0.1", - "postcss-lab-function": "^4.2.1", - "postcss-logical": "^5.0.4", - "postcss-media-minmax": "^5.0.0", - "postcss-nesting": "^10.2.0", - "postcss-opacity-percentage": "^1.1.2", - "postcss-overflow-shorthand": "^3.0.4", - "postcss-page-break": "^3.0.4", - "postcss-place": "^7.0.5", - "postcss-pseudo-class-any-link": "^7.1.6", - "postcss-replace-overflow-wrap": "^4.0.0", - "postcss-selector-not": "^6.0.1", - "postcss-value-parser": "^4.2.0" - }, + "node_modules/pretty-bytes": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", + "license": "MIT", "engines": { - "node": "^12 || ^14 || >=16" + "node": ">=6" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/react-scripts/node_modules/postcss-pseudo-class-any-link": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-7.1.6.tgz", - "integrity": "sha512-9sCtZkO6f/5ML9WcTLcIyV1yz9D1rf0tWc+ulKcvV30s0iZKS/ONyETvoWsr6vnrmW+X+KmuK3gV/w5EWnT37w==", + "node_modules/pretty-error": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", + "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", + "license": "MIT", "dependencies": { - "postcss-selector-parser": "^6.0.10" + "lodash": "^4.17.20", + "renderkid": "^3.0.0" + } + }, + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" }, "engines": { - "node": "^12 || ^14 || >=16" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=10" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/react-scripts/node_modules/postcss-reduce-initial": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.2.tgz", - "integrity": "sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg==", + "node_modules/pretty-format/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "license": "MIT" + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, + "node_modules/promise": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", + "integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==", + "license": "MIT", "dependencies": { - "browserslist": "^4.21.4", - "caniuse-api": "^3.0.0" + "asap": "~2.0.6" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": ">= 6" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types-extra": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.1.1.tgz", + "integrity": "sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==", + "license": "MIT", + "dependencies": { + "react-is": "^16.3.2", + "warning": "^4.0.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "react": ">=0.14.0" } }, - "node_modules/react-scripts/node_modules/postcss-reduce-transforms": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz", - "integrity": "sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==", + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", "dependencies": { - "postcss-value-parser": "^4.2.0" + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" }, "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" + "node": ">= 0.10" } }, - "node_modules/react-scripts/node_modules/postcss-replace-overflow-wrap": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", - "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", - "peerDependencies": { - "postcss": "^8.0.3" + "node_modules/proxy-addr/node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" } }, - "node_modules/react-scripts/node_modules/postcss-selector-not": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-6.0.1.tgz", - "integrity": "sha512-1i9affjAe9xu/y9uqWH+tD4r6/hDaXJruk8xn2x1vzxC2U3J3LKO3zJW4CyxlNhA56pADJ/djpEwpH1RClI2rQ==", + "node_modules/psl": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.10.0.tgz", + "integrity": "sha512-KSKHEbjAnpUuAUserOq0FxGXCUrzC3WniuSJhvdbs102rL55266ZcHBqLWOsG30spQMlPdpy7icATiAQehg/iA==", + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + } + }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true, + "license": "MIT" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", + "deprecated": "You or someone you depend on is using Q, the JavaScript Promise library that gave JavaScript developers strong feelings about promises. They can almost certainly migrate to the native JavaScript promise now. Thank you literally everyone for joining me in this bet against the odds. Be excellent to each other.\n\n(For a CapTP with native promises, see @endo/eventual-send and @endo/captp)", + "license": "MIT", + "engines": { + "node": ">=0.6.0", + "teleport": ">=0.2.0" + } + }, + "node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "license": "BSD-3-Clause", "dependencies": { - "postcss-selector-parser": "^6.0.10" + "side-channel": "^1.0.6" }, "engines": { - "node": "^12 || ^14 || >=16" + "node": ">=0.6" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/react-scripts/node_modules/postcss-svgo": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.1.0.tgz", - "integrity": "sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==", + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "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==", + "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/raf": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "license": "MIT", "dependencies": { - "postcss-value-parser": "^4.2.0", - "svgo": "^2.7.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" + "performance-now": "^2.1.0" } }, - "node_modules/react-scripts/node_modules/postcss-unique-selectors": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz", - "integrity": "sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==", + "node_modules/ramda": { + "version": "0.30.1", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.30.1.tgz", + "integrity": "sha512-tEF5I22zJnuclswcZMc8bDIrwRHRzf+NqVEmqg50ShAZMP7MWeR/RGDthfM/p+BlqvF2fXAzpn8i+SJcYD3alw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ramda" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "license": "MIT", "dependencies": { - "postcss-selector-parser": "^6.0.5" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" + "safe-buffer": "^5.1.0" } }, - "node_modules/react-scripts/node_modules/prelude-ls": { + "node_modules/range-parser": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", "engines": { - "node": ">= 0.8.0" + "node": ">= 0.6" } }, - "node_modules/react-scripts/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "optional": true, - "peer": true, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "license": "MIT", "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" }, "engines": { - "node": ">= 6" + "node": ">= 0.8" } }, - "node_modules/react-scripts/node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "engines": { - "node": ">=8" + "node_modules/raw-body/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/react-scripts/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/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" + "loose-envify": "^1.1.0" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/react-scripts/node_modules/sass-loader": { - "version": "12.6.0", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz", - "integrity": "sha512-oLTaH0YCtX4cfnJZxKSLAyglED0naiYfNG1iXfU5w1LNZ+ukoA5DtyDIN5zmKVZwYNJP4KRc5Y3hkWga+7tYfA==", + "node_modules/react-app-polyfill": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/react-app-polyfill/-/react-app-polyfill-3.0.0.tgz", + "integrity": "sha512-sZ41cxiU5llIB003yxxQBYrARBqe0repqPTTYBTmMqTz9szeBbE37BehCE891NZsmdZqqP+xWKdT3eo3vOzN8w==", + "license": "MIT", "dependencies": { - "klona": "^2.0.4", - "neo-async": "^2.6.2" + "core-js": "^3.19.2", + "object-assign": "^4.1.1", + "promise": "^8.1.0", + "raf": "^3.4.1", + "regenerator-runtime": "^0.13.9", + "whatwg-fetch": "^3.6.2" }, "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "node": ">=14" + } + }, + "node_modules/react-app-polyfill/node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "license": "MIT" + }, + "node_modules/react-bootstrap": { + "version": "2.10.5", + "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-2.10.5.tgz", + "integrity": "sha512-XueAOEn64RRkZ0s6yzUTdpFtdUXs5L5491QU//8ZcODKJNDLt/r01tNyriZccjgRImH1REynUc9pqjiRMpDLWQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.24.7", + "@restart/hooks": "^0.4.9", + "@restart/ui": "^1.6.9", + "@types/react-transition-group": "^4.4.6", + "classnames": "^2.3.2", + "dom-helpers": "^5.2.1", + "invariant": "^2.2.4", + "prop-types": "^15.8.1", + "prop-types-extra": "^1.1.0", + "react-transition-group": "^4.4.5", + "uncontrollable": "^7.2.1", + "warning": "^4.0.3" }, "peerDependencies": { - "fibers": ">= 3.1.0", - "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", - "sass": "^1.3.0", - "sass-embedded": "*", - "webpack": "^5.0.0" + "@types/react": ">=16.14.8", + "react": ">=16.14.0", + "react-dom": ">=16.14.0" }, "peerDependenciesMeta": { - "fibers": { - "optional": true - }, - "node-sass": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { + "@types/react": { "optional": true } } }, - "node_modules/react-scripts/node_modules/schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "node_modules/react-dev-utils": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", + "integrity": "sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==", + "license": "MIT", "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" + "@babel/code-frame": "^7.16.0", + "address": "^1.1.2", + "browserslist": "^4.18.1", + "chalk": "^4.1.2", + "cross-spawn": "^7.0.3", + "detect-port-alt": "^1.1.6", + "escape-string-regexp": "^4.0.0", + "filesize": "^8.0.6", + "find-up": "^5.0.0", + "fork-ts-checker-webpack-plugin": "^6.5.0", + "global-modules": "^2.0.0", + "globby": "^11.0.4", + "gzip-size": "^6.0.0", + "immer": "^9.0.7", + "is-root": "^2.1.0", + "loader-utils": "^3.2.0", + "open": "^8.4.0", + "pkg-up": "^3.1.0", + "prompts": "^2.4.2", + "react-error-overlay": "^6.0.11", + "recursive-readdir": "^2.2.2", + "shell-quote": "^1.7.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" }, "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "node": ">=14" } }, - "node_modules/react-scripts/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "node_modules/react-dev-utils/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==", + "license": "MIT", "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/react-scripts/node_modules/serialize-javascript": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", - "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", - "dependencies": { - "randombytes": "^2.1.0" + "node_modules/react-dev-utils/node_modules/loader-utils": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.3.1.tgz", + "integrity": "sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg==", + "license": "MIT", + "engines": { + "node": ">= 12.13.0" } }, - "node_modules/react-scripts/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/react-dev-utils/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==", + "license": "MIT", "dependencies": { - "shebang-regex": "^3.0.0" + "p-locate": "^5.0.0" }, "engines": { - "node": ">=8" - } - }, - "node_modules/react-scripts/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==", - "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/react-scripts/node_modules/source-map-loader": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-3.0.2.tgz", - "integrity": "sha512-BokxPoLjyl3iOrgkWaakaxqnelAJSS+0V+De0kKIq6lyWrXuiPgYTGp6z3iHmqljKAaLXwZa+ctD8GccRJeVvg==", + "node_modules/react-dev-utils/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==", + "license": "MIT", "dependencies": { - "abab": "^2.0.5", - "iconv-lite": "^0.6.3", - "source-map-js": "^1.0.1" + "yocto-queue": "^0.1.0" }, "engines": { - "node": ">= 12.13.0" + "node": ">=10" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/react-scripts/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "optional": true, - "peer": true, + "node_modules/react-dev-utils/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==", + "license": "MIT", "dependencies": { - "safe-buffer": "~5.2.0" + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/react-scripts/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==", + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1" + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" }, - "engines": { - "node": ">=8" + "peerDependencies": { + "react": "^18.3.1" } }, - "node_modules/react-scripts/node_modules/style-loader": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.1.tgz", - "integrity": "sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==", + "node_modules/react-error-overlay": { + "version": "6.0.11", + "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", + "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==", + "license": "MIT" + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==", + "license": "MIT" + }, + "node_modules/react-refresh": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", + "integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==", + "license": "MIT", "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" + "node": ">=0.10.0" } }, - "node_modules/react-scripts/node_modules/stylehacks": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", - "integrity": "sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==", + "node_modules/react-router": { + "version": "6.28.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.28.0.tgz", + "integrity": "sha512-HrYdIFqdrnhDw0PqG/AKjAqEqM7AvxCz0DQ4h2W8k6nqmc5uRBYDag0SBxx9iYz5G8gnuNVLzUe13wl9eAsXXg==", + "license": "MIT", "dependencies": { - "browserslist": "^4.21.4", - "postcss-selector-parser": "^6.0.4" + "@remix-run/router": "1.21.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": ">=14.0.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "react": ">=16.8" } }, - "node_modules/react-scripts/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/react-router-dom": { + "version": "6.28.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.28.0.tgz", + "integrity": "sha512-kQ7Unsl5YdyOltsPGl31zOjLrDv+m2VcIEcIHqYYD3Lp0UppLjrzcfJqDJwXxFw3TH/yvapbnUvPlAj7Kx5nbg==", + "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "@remix-run/router": "1.21.0", + "react-router": "6.28.0" }, "engines": { - "node": ">=8" + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" } }, - "node_modules/react-scripts/node_modules/svgo": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", - "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "node_modules/react-scripts": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz", + "integrity": "sha512-8VAmEm/ZAwQzJ+GOMLbBsTdDKOpuZh7RPs0UymvBR2vRk4iZWCskjbFnxqjrzoIvlNNRZ3QJFx6/qDSi6zSnaQ==", + "license": "MIT", "dependencies": { - "@trysound/sax": "0.2.0", - "commander": "^7.2.0", - "css-select": "^4.1.3", - "css-tree": "^1.1.3", - "csso": "^4.2.0", - "picocolors": "^1.0.0", - "stable": "^0.1.8" + "@babel/core": "^7.16.0", + "@pmmmwh/react-refresh-webpack-plugin": "^0.5.3", + "@svgr/webpack": "^5.5.0", + "babel-jest": "^27.4.2", + "babel-loader": "^8.2.3", + "babel-plugin-named-asset-import": "^0.3.8", + "babel-preset-react-app": "^10.0.1", + "bfj": "^7.0.2", + "browserslist": "^4.18.1", + "camelcase": "^6.2.1", + "case-sensitive-paths-webpack-plugin": "^2.4.0", + "css-loader": "^6.5.1", + "css-minimizer-webpack-plugin": "^3.2.0", + "dotenv": "^10.0.0", + "dotenv-expand": "^5.1.0", + "eslint": "^8.3.0", + "eslint-config-react-app": "^7.0.1", + "eslint-webpack-plugin": "^3.1.1", + "file-loader": "^6.2.0", + "fs-extra": "^10.0.0", + "html-webpack-plugin": "^5.5.0", + "identity-obj-proxy": "^3.0.0", + "jest": "^27.4.3", + "jest-resolve": "^27.4.2", + "jest-watch-typeahead": "^1.0.0", + "mini-css-extract-plugin": "^2.4.5", + "postcss": "^8.4.4", + "postcss-flexbugs-fixes": "^5.0.2", + "postcss-loader": "^6.2.1", + "postcss-normalize": "^10.0.1", + "postcss-preset-env": "^7.0.1", + "prompts": "^2.4.2", + "react-app-polyfill": "^3.0.0", + "react-dev-utils": "^12.0.1", + "react-refresh": "^0.11.0", + "resolve": "^1.20.0", + "resolve-url-loader": "^4.0.0", + "sass-loader": "^12.3.0", + "semver": "^7.3.5", + "source-map-loader": "^3.0.0", + "style-loader": "^3.3.1", + "tailwindcss": "^3.0.2", + "terser-webpack-plugin": "^5.2.5", + "webpack": "^5.64.4", + "webpack-dev-server": "^4.6.0", + "webpack-manifest-plugin": "^4.0.2", + "workbox-webpack-plugin": "^6.4.1" }, "bin": { - "svgo": "bin/svgo" + "react-scripts": "bin/react-scripts.js" }, "engines": { - "node": ">=10.13.0" + "node": ">=14.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + }, + "peerDependencies": { + "react": ">= 16", + "typescript": "^3.2.1 || ^4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/react-scripts/node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "node_modules/react-scripts/node_modules/@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "license": "MIT" + }, + "node_modules/react-scripts/node_modules/babel-loader": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.4.1.tgz", + "integrity": "sha512-nXzRChX+Z1GoE6yWavBQg6jDslyFF3SDjl2paADuoQtQW10JqShJt62R6eJQ5m/pjJFDT8xgKIWSP85OY8eXeA==", + "license": "MIT", + "dependencies": { + "find-cache-dir": "^3.3.1", + "loader-utils": "^2.0.4", + "make-dir": "^3.1.0", + "schema-utils": "^2.6.5" + }, "engines": { - "node": ">=6" + "node": ">= 8.9" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "webpack": ">=2" } }, - "node_modules/react-scripts/node_modules/terser": { - "version": "5.16.5", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.5.tgz", - "integrity": "sha512-qcwfg4+RZa3YvlFh0qjifnzBHjKGNbtDo9yivMqMFDy9Q6FSaQWSB/j1xKhsoUFJIqDOM3TsN6D5xbrMrFcHbg==", + "node_modules/react-scripts/node_modules/babel-loader/node_modules/schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "license": "MIT", "dependencies": { - "@jridgewell/source-map": "^0.3.2", - "acorn": "^8.5.0", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" }, "engines": { - "node": ">=10" + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, - "node_modules/react-scripts/node_modules/terser-webpack-plugin": { - "version": "5.3.6", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz", - "integrity": "sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==", + "node_modules/react-scripts/node_modules/css-loader": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.11.0.tgz", + "integrity": "sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==", + "license": "MIT", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.14", - "jest-worker": "^27.4.5", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.0", - "terser": "^5.14.1" + "icss-utils": "^5.1.0", + "postcss": "^8.4.33", + "postcss-modules-extract-imports": "^3.1.0", + "postcss-modules-local-by-default": "^4.0.5", + "postcss-modules-scope": "^3.2.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.5.4" }, "engines": { - "node": ">= 10.13.0" + "node": ">= 12.13.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/webpack" }, "peerDependencies": { - "webpack": "^5.1.0" + "@rspack/core": "0.x || 1.x", + "webpack": "^5.0.0" }, "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "esbuild": { + "@rspack/core": { "optional": true }, - "uglify-js": { + "webpack": { "optional": true } } }, - "node_modules/react-scripts/node_modules/terser/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" - }, - "node_modules/react-scripts/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==", + "node_modules/react-scripts/node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "license": "MIT", "dependencies": { - "is-number": "^7.0.0" + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" }, "engines": { - "node": ">=8.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" } }, - "node_modules/react-scripts/node_modules/true-case-path": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz", - "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==", - "optional": true, - "peer": true, + "node_modules/react-scripts/node_modules/p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "license": "MIT", "dependencies": { - "glob": "^7.1.2" + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" } }, - "node_modules/react-scripts/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==", + "node_modules/react-scripts/node_modules/postcss-loader": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz", + "integrity": "sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==", + "license": "MIT", "dependencies": { - "prelude-ls": "^1.2.1" + "cosmiconfig": "^7.0.0", + "klona": "^2.0.5", + "semver": "^7.3.5" }, "engines": { - "node": ">= 0.8.0" + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "postcss": "^7.0.0 || ^8.0.1", + "webpack": "^5.0.0" } }, - "node_modules/react-scripts/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==", + "node_modules/react-scripts/node_modules/style-loader": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.4.tgz", + "integrity": "sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w==", + "license": "MIT", "engines": { - "node": ">=10" + "node": ">= 12.13.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-scripts/node_modules/watchpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" + "type": "opencollective", + "url": "https://opencollective.com/webpack" }, - "engines": { - "node": ">=10.13.0" + "peerDependencies": { + "webpack": "^5.0.0" } }, - "node_modules/react-scripts/node_modules/webpack": { - "version": "5.75.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.75.0.tgz", - "integrity": "sha512-piaIaoVJlqMsPtX/+3KTTO6jfvrSYgauFVdt8cr9LTHKmcq/AMd4mhzsiP7ZF/PGRNPGA8336jldh9l2Kt2ogQ==", + "node_modules/react-scripts/node_modules/webpack-dev-middleware": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz", + "integrity": "sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==", + "license": "MIT", "dependencies": { - "@types/eslint-scope": "^3.7.3", - "@types/estree": "^0.0.51", - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/wasm-edit": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "acorn": "^8.7.1", - "acorn-import-assertions": "^1.7.6", - "browserslist": "^4.14.5", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.10.0", - "es-module-lexer": "^0.9.0", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.9", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^3.1.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.1.3", - "watchpack": "^2.4.0", - "webpack-sources": "^3.2.3" - }, - "bin": { - "webpack": "bin/webpack.js" + "colorette": "^2.0.10", + "memfs": "^3.4.3", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" }, "engines": { - "node": ">=10.13.0" + "node": ">= 12.13.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/webpack" }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" } }, "node_modules/react-scripts/node_modules/webpack-dev-server": { - "version": "4.11.1", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.11.1.tgz", - "integrity": "sha512-lILVz9tAUy1zGFwieuaQtYiadImb5M3d+H+L1zDYalYoDl0cksAB1UNyuE5MMWJrG6zR1tXkCP2fitl7yoUJiw==", + "version": "4.15.2", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.2.tgz", + "integrity": "sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g==", + "license": "MIT", "dependencies": { "@types/bonjour": "^3.5.9", "@types/connect-history-api-fallback": "^1.3.5", @@ -24328,7 +15868,7 @@ "@types/serve-index": "^1.9.1", "@types/serve-static": "^1.13.10", "@types/sockjs": "^0.3.33", - "@types/ws": "^8.5.1", + "@types/ws": "^8.5.5", "ansi-html-community": "^0.0.8", "bonjour-service": "^1.0.11", "chokidar": "^3.5.3", @@ -24341,6 +15881,7 @@ "html-entities": "^2.3.2", "http-proxy-middleware": "^2.0.3", "ipaddr.js": "^2.0.1", + "launch-editor": "^2.6.0", "open": "^8.0.9", "p-retry": "^4.5.0", "rimraf": "^3.0.2", @@ -24349,8 +15890,8 @@ "serve-index": "^1.9.1", "sockjs": "^0.3.24", "spdy": "^4.0.2", - "webpack-dev-middleware": "^5.3.1", - "ws": "^8.4.2" + "webpack-dev-middleware": "^5.3.4", + "ws": "^8.13.0" }, "bin": { "webpack-dev-server": "bin/webpack-dev-server.js" @@ -24366,108 +15907,19 @@ "webpack": "^4.37.0 || ^5.0.0" }, "peerDependenciesMeta": { + "webpack": { + "optional": true + }, "webpack-cli": { "optional": true } } }, - "node_modules/react-scripts/node_modules/webpack-dev-server/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/react-scripts/node_modules/webpack-dev-server/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/react-scripts/node_modules/webpack-dev-server/node_modules/schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/react-scripts/node_modules/webpack-manifest-plugin": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/webpack-manifest-plugin/-/webpack-manifest-plugin-4.1.1.tgz", - "integrity": "sha512-YXUAwxtfKIJIKkhg03MKuiFAD72PlrqCiwdwO4VEXdRO5V0ORCNwaOwAZawPZalCbmH9kBDmXnNeQOw+BIEiow==", - "dependencies": { - "tapable": "^2.0.0", - "webpack-sources": "^2.2.0" - }, - "engines": { - "node": ">=12.22.0" - }, - "peerDependencies": { - "webpack": "^4.44.2 || ^5.47.0" - } - }, - "node_modules/react-scripts/node_modules/webpack-manifest-plugin/node_modules/webpack-sources": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.3.1.tgz", - "integrity": "sha512-y9EI9AO42JjEcrTJFOYmVywVZdKVUfOvDUPsJea5GIr1JOEGFVqwlY2K098fFoIjOkDzHn2AjRvM8dsBZu+gCA==", - "dependencies": { - "source-list-map": "^2.0.1", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/react-scripts/node_modules/webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/react-scripts/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/react-scripts/node_modules/ws": { - "version": "8.12.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.12.1.tgz", - "integrity": "sha512-1qo+M9Ba+xNhPB+YTWUlK6M17brTut5EXbcBaMRN5pH5dFrXz7lzz1ChFSUq3bOUl8yEvSenhHmYUNJxFzdJew==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "license": "MIT", "engines": { "node": ">=10.0.0" }, @@ -24484,54 +15936,28 @@ } } }, - "node_modules/react-scripts/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, "node_modules/react-smooth": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-2.0.2.tgz", - "integrity": "sha512-pgqSp1q8rAGtF1bXQE0m3CHGLNfZZh5oA5o1tsPLXRHnKtkujMIJ8Ws5nO1mTySZf1c4vgwlEk+pHi3Ln6eYLw==", - "dependencies": { - "fast-equals": "^4.0.3", - "react-transition-group": "2.9.0" - }, - "peerDependencies": { - "prop-types": "^15.6.0", - "react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/react-smooth/node_modules/dom-helpers": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", - "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==", - "dependencies": { - "@babel/runtime": "^7.1.2" - } - }, - "node_modules/react-smooth/node_modules/react-transition-group": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz", - "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.1.tgz", + "integrity": "sha512-OE4hm7XqR0jNOq3Qmk9mFLyd6p2+j6bvbPJ7qlB7+oo0eNcL2l7WQzG6MBnT3EXY6xzkLMUBec3AfewJdA0J8w==", + "license": "MIT", "dependencies": { - "dom-helpers": "^3.4.0", - "loose-envify": "^1.4.0", - "prop-types": "^15.6.2", - "react-lifecycles-compat": "^3.0.4" + "fast-equals": "^5.0.1", + "prop-types": "^15.8.1", + "react-transition-group": "^4.4.5" }, "peerDependencies": { - "react": ">=15.0.0", - "react-dom": ">=15.0.0" + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, "node_modules/react-tooltip": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-5.20.0.tgz", - "integrity": "sha512-LWBIHEZjwDW9ZJ/Dn2xeZrsz+WKMii61CIsx2XPfs1IiIRnWyvKJXrgy6uEGOXYvrnCd4jiEvurn8Y+zJ1bw5Q==", + "version": "5.28.0", + "resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-5.28.0.tgz", + "integrity": "sha512-R5cO3JPPXk6FRbBHMO0rI9nkUG/JKfalBSQfZedZYzmqaZQgq7GLzF8vcCWx6IhUCKg0yPqJhXIzmIO5ff15xg==", + "license": "MIT", "dependencies": { - "@floating-ui/dom": "^1.0.0", + "@floating-ui/dom": "^1.6.1", "classnames": "^2.3.0" }, "peerDependencies": { @@ -24543,6 +15969,7 @@ "version": "4.4.5", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", "dependencies": { "@babel/runtime": "^7.5.5", "dom-helpers": "^5.0.1", @@ -24558,162 +15985,94 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "license": "MIT", "dependencies": { "pify": "^2.3.0" } }, - "node_modules/read-cache/node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", + "dev": true, + "license": "MIT", "dependencies": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" }, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "node_modules/read-pkg/node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "license": "MIT", "dependencies": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" + "pify": "^3.0.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=4" } }, - "node_modules/read-pkg-up/node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "node_modules/read-pkg/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg/node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" - }, - "node_modules/read-pkg/node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" + "node": ">=4" } }, - "node_modules/read-pkg/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==", + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", "dependencies": { - "@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": ">=8" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/read-pkg/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/read-pkg/node_modules/type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", "engines": { - "node": ">=8" - } - }, - "node_modules/readable-stream": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" + "node": ">= 6" } }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "license": "MIT", "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/recast": { - "version": "0.17.6", - "resolved": "https://registry.npmjs.org/recast/-/recast-0.17.6.tgz", - "integrity": "sha512-yoQRMRrK1lszNtbkGyM4kN45AwylV5hMiuEveUBlxytUViWevjvX6w+tzJt1LH4cfUhWt4NZvy3ThIhu6+m5wQ==", - "dev": true, - "dependencies": { - "ast-types": "0.12.4", - "esprima": "~4.0.0", - "private": "^0.1.8", - "source-map": "~0.6.1" + "picomatch": "^2.2.1" }, "engines": { - "node": ">= 4" + "node": ">=8.10.0" } }, "node_modules/recharts": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.4.3.tgz", - "integrity": "sha512-/hkRHTQShEOKDYd2OlKLIvGA0X9v/XVO/mNeRoDHg0lgFRL2KbGzeqVnStI3mMfORUZ6Hak4JbQ+uDiin1Foqg==", + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.13.3.tgz", + "integrity": "sha512-YDZ9dOfK9t3ycwxgKbrnDlRC4BHdjlY73fet3a0C1+qGMjXVZe6+VXmpOIIhzkje5MMEL8AN4hLIe4AMskBzlA==", + "license": "MIT", "dependencies": { - "classnames": "^2.2.5", + "clsx": "^2.0.0", "eventemitter3": "^4.0.1", - "lodash": "^4.17.19", - "react-is": "^16.10.2", - "react-resize-detector": "^7.1.2", - "react-smooth": "^2.0.1", + "lodash": "^4.17.21", + "react-is": "^18.3.1", + "react-smooth": "^4.0.0", "recharts-scale": "^0.4.4", - "reduce-css-calc": "^2.1.8", + "tiny-invariant": "^1.3.1", "victory-vendor": "^36.6.8" }, "engines": { - "node": ">=12" + "node": ">=14" }, "peerDependencies": { - "prop-types": "^15.6.0", "react": "^16.0.0 || ^17.0.0 || ^18.0.0", "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" } @@ -24722,26 +16081,35 @@ "version": "0.4.5", "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz", "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==", + "license": "MIT", "dependencies": { "decimal.js-light": "^2.4.1" } }, + "node_modules/recharts/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==", + "license": "MIT" + }, "node_modules/rechoir": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", - "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", "dev": true, + "license": "MIT", "dependencies": { - "resolve": "^1.9.0" + "resolve": "^1.20.0" }, "engines": { - "node": ">= 0.10" + "node": ">= 10.13.0" } }, "node_modules/recursive-readdir": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", + "license": "MIT", "dependencies": { "minimatch": "^3.0.5" }, @@ -24749,41 +16117,38 @@ "node": ">=6.0.0" } }, - "node_modules/redent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "node_modules/reflect.getprototypeof": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz", + "integrity": "sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==", + "license": "MIT", "dependencies": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.1", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "globalthis": "^1.0.3", + "which-builtin-type": "^1.1.3" }, "engines": { - "node": ">=8" - } - }, - "node_modules/reduce-css-calc": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-2.1.8.tgz", - "integrity": "sha512-8liAVezDmUcH+tdzoEGrhfbGcP7nOV4NkGE3a74+qqvE7nt9i4sKLGBuZNOnpI4WiGksiNPklZxva80061QiPg==", - "dependencies": { - "css-unit-converter": "^1.1.1", - "postcss-value-parser": "^3.3.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/reduce-css-calc/node_modules/postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" - }, "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "license": "MIT" }, "node_modules/regenerate-unicode-properties": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz", - "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", + "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", + "license": "MIT", "dependencies": { "regenerate": "^1.4.2" }, @@ -24792,43 +16157,36 @@ } }, "node_modules/regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "license": "MIT" }, "node_modules/regenerator-transform": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz", - "integrity": "sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==", + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", + "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.8.4" } }, - "node_modules/regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "dependencies": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/regex-parser": { - "version": "2.2.11", - "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz", - "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==" + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.3.0.tgz", + "integrity": "sha512-TVILVSz2jY5D47F4mA4MppkBrafEaiUWJO/TcZHEIuI13AqoZMkK1WMA4Om1YkYbTx+9Ki1/tSUXbceyr9saRg==", + "license": "MIT" }, "node_modules/regexp.prototype.flags": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", - "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz", + "integrity": "sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "functions-have-names": "^1.2.2" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "set-function-name": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -24838,22 +16196,28 @@ } }, "node_modules/regexpp": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", - "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=6.5.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" } }, "node_modules/regexpu-core": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.1.tgz", - "integrity": "sha512-nCOzW2V/X15XpLsK2rlgdwrysrBq+AauCn+omItIz4R1pIcmeot5zvjdmOBRLzEH/CkC6IxMJVmxDe3QcMuNVQ==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.1.1.tgz", + "integrity": "sha512-k67Nb9jvwJcJmVpw0jPttR1/zVfnKf8Km0IPatrU/zJ5XeG3+Slx0xLXs9HByJSzXzrlz5EDvN6yLNMDc2qdnw==", + "license": "MIT", "dependencies": { - "@babel/regjsgen": "^0.8.0", "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.1.0", - "regjsparser": "^0.9.1", + "regenerate-unicode-properties": "^10.2.0", + "regjsgen": "^0.8.0", + "regjsparser": "^0.11.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.1.0" }, @@ -24861,43 +16225,38 @@ "node": ">=4" } }, + "node_modules/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", + "license": "MIT" + }, "node_modules/regjsparser": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", - "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.11.2.tgz", + "integrity": "sha512-3OGZZ4HoLJkkAZx/48mTXJNlmqTGOzc0o9OWQPuWpkOlXXPbyN6OafCcoXUnBqE2D3f/T5L+pWc1kdEmnfnRsA==", + "license": "BSD-2-Clause", "dependencies": { - "jsesc": "~0.5.0" + "jsesc": "~3.0.2" }, "bin": { "regjsparser": "bin/parser" } }, - "node_modules/regjsparser/node_modules/jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", - "bin": { - "jsesc": "bin/jsesc" - } - }, "node_modules/relateurl": { "version": "0.2.7", "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", + "license": "MIT", "engines": { "node": ">= 0.10" } }, - "node_modules/remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", - "optional": true - }, "node_modules/renderkid": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", + "license": "MIT", "dependencies": { "css-select": "^4.1.3", "dom-converter": "^0.2.0", @@ -24906,202 +16265,11 @@ "strip-ansi": "^6.0.1" } }, - "node_modules/renderkid/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/renderkid/node_modules/css-select": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", - "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.0.1", - "domhandler": "^4.3.1", - "domutils": "^2.8.0", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/renderkid/node_modules/css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", - "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/renderkid/node_modules/dom-serializer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", - "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/renderkid/node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ] - }, - "node_modules/renderkid/node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/renderkid/node_modules/nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } - }, - "node_modules/renderkid/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==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/repeat-element": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", - "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", - "optional": true, - "peer": true, - "dependencies": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/request/node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "optional": true, - "peer": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 0.12" - } - }, - "node_modules/request/node_modules/qs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", - "optional": true, - "peer": true, - "engines": { - "node": ">=0.6" - } - }, - "node_modules/request/node_modules/tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "optional": true, - "peer": true, - "dependencies": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/request/node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", - "optional": true, - "peer": true, - "bin": { - "uuid": "bin/uuid" - } - }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -25110,168 +16278,71 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, "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==" + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "license": "MIT" }, "node_modules/resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", - "dependencies": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-cwd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", - "integrity": "sha512-ccu8zQTrzVr954472aUVPLEcB3YpKSYR3cg/3lo1okzobPBM+1INXBbBZlDbnI/hbEocnf8j0QVo43hQKrbchg==", - "dev": true, - "dependencies": { - "resolve-from": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve-cwd/node_modules/resolve-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve-dir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", - "integrity": "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==", - "dev": true, - "dependencies": { - "expand-tilde": "^2.0.0", - "global-modules": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve-dir/node_modules/global-modules": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", - "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", - "dev": true, - "dependencies": { - "global-prefix": "^1.0.1", - "is-windows": "^1.0.1", - "resolve-dir": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve-dir/node_modules/global-prefix": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", - "integrity": "sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==", - "dev": true, + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "license": "MIT", "dependencies": { - "expand-tilde": "^2.0.2", - "homedir-polyfill": "^1.0.1", - "ini": "^1.3.4", - "is-windows": "^1.0.1", - "which": "^1.2.14" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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==", - "engines": { - "node": ">=4" + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/resolve-path": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/resolve-path/-/resolve-path-1.4.0.tgz", - "integrity": "sha512-i1xevIst/Qa+nA9olDxLWnLk8YZbi8R/7JPbCMcgyWaFR6bKWaexgJgEB5oc2PKMjYdrHynyz0NY+if+H98t1w==", - "devOptional": true, + "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==", + "license": "MIT", "dependencies": { - "http-errors": "~1.6.2", - "path-is-absolute": "1.0.1" + "resolve-from": "^5.0.0" }, "engines": { - "node": ">= 0.8" + "node": ">=8" } }, - "node_modules/resolve-path/node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", - "devOptional": true, + "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==", + "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">=8" } }, - "node_modules/resolve-path/node_modules/http-errors": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", - "devOptional": true, - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" - }, - "engines": { - "node": ">= 0.6" + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, - "node_modules/resolve-path/node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", - "devOptional": true - }, - "node_modules/resolve-path/node_modules/setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", - "devOptional": true - }, - "node_modules/resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==", - "deprecated": "https://github.com/lydell/resolve-url#deprecated" - }, "node_modules/resolve-url-loader": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-4.0.0.tgz", "integrity": "sha512-05VEMczVREcbtT7Bz+C+96eUO5HDNvdthIiMB34t7FcF8ehcu4wC0sSgPUubs3XW2Q3CNLJk/BJrCU9wVRymiA==", + "license": "MIT", "dependencies": { "adjust-sourcemap-loader": "^4.0.0", "convert-source-map": "^1.7.0", @@ -25295,38 +16366,58 @@ } } }, - "node_modules/resolve.exports": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.1.tgz", - "integrity": "sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ==", - "engines": { - "node": ">=10" - } + "node_modules/resolve-url-loader/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "license": "MIT" }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "node_modules/resolve-url-loader/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "license": "ISC" + }, + "node_modules/resolve-url-loader/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "license": "MIT", "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" + "picocolors": "^0.2.1", + "source-map": "^0.6.1" }, "engines": { - "node": ">=8" + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/resolve-url-loader/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==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" } }, - "node_modules/ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "node_modules/resolve.exports": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.1.tgz", + "integrity": "sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ==", + "license": "MIT", "engines": { - "node": ">=0.12" + "node": ">=10" } }, "node_modules/retry": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "license": "MIT", "engines": { "node": ">= 4" } @@ -25335,35 +16426,33 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "license": "MIT", "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" } }, "node_modules/rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "license": "ISC", "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" - } - }, - "node_modules/ripemd160": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", - "dependencies": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/rollup": { "version": "2.79.2", "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz", "integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==", + "license": "MIT", "bin": { "rollup": "dist/bin/rollup" }, @@ -25379,6 +16468,7 @@ "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", "integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==", "deprecated": "This package has been deprecated and is no longer maintained. Please use @rollup/plugin-terser", + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.10.4", "jest-worker": "^26.2.1", @@ -25389,29 +16479,11 @@ "rollup": "^2.0.0" } }, - "node_modules/rollup-plugin-terser/node_modules/acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/rollup-plugin-terser/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==", - "engines": { - "node": ">=8" - } - }, "node_modules/rollup-plugin-terser/node_modules/jest-worker": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", + "license": "MIT", "dependencies": { "@types/node": "*", "merge-stream": "^2.0.0", @@ -25421,40 +16493,26 @@ "node": ">= 10.13.0" } }, - "node_modules/rollup-plugin-terser/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/rollup-plugin-terser/node_modules/terser": { - "version": "5.16.5", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.5.tgz", - "integrity": "sha512-qcwfg4+RZa3YvlFh0qjifnzBHjKGNbtDo9yivMqMFDy9Q6FSaQWSB/j1xKhsoUFJIqDOM3TsN6D5xbrMrFcHbg==", + "node_modules/rollup-plugin-terser/node_modules/serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "license": "BSD-3-Clause", "dependencies": { - "@jridgewell/source-map": "^0.3.2", - "acorn": "^8.5.0", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" + "randombytes": "^2.1.0" } }, - "node_modules/run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "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==", + "devOptional": true, + "license": "MIT", "engines": { - "node": ">=0.12.0" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/run-parallel": { @@ -25475,27 +16533,27 @@ "url": "https://feross.org/support" } ], + "license": "MIT", "dependencies": { "queue-microtask": "^1.2.2" } }, - "node_modules/run-queue": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", - "integrity": "sha512-ntymy489o0/QQplUDnpYAYUsO50K9SBrIVaKCWDOJzYJts0f9WH9RFJkyagebkw5+y1oi00R7ynNW/d12GBumg==", - "dependencies": { - "aproba": "^1.1.1" - } - }, - "node_modules/rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "node_modules/safe-array-concat": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", + "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", + "license": "MIT", "dependencies": { - "tslib": "^1.9.0" + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" }, "engines": { - "npm": ">=2.0.0" + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/safe-buffer": { @@ -25515,25 +16573,22 @@ "type": "consulting", "url": "https://feross.org/support" } - ] - }, - "node_modules/safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==", - "dependencies": { - "ret": "~0.1.10" - } + ], + "license": "MIT" }, "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==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", + "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", "is-regex": "^1.1.4" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -25541,96 +16596,115 @@ "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" }, "node_modules/sanitize.css": { "version": "13.0.0", "resolved": "https://registry.npmjs.org/sanitize.css/-/sanitize.css-13.0.0.tgz", - "integrity": "sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA==" + "integrity": "sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA==", + "license": "CC0-1.0" }, - "node_modules/sass-graph": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-4.0.1.tgz", - "integrity": "sha512-5YCfmGBmxoIRYHnKK2AKzrAkCoQ8ozO+iumT8K4tXJXRVCPf+7s1/9KxTSW3Rbvf+7Y7b4FR3mWyLnQr3PHocA==", + "node_modules/sass": { + "version": "1.80.6", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.80.6.tgz", + "integrity": "sha512-ccZgdHNiBF1NHBsWvacvT5rju3y1d/Eu+8Ex6c21nHp2lZGLBEtuwc415QfiI1PJa1TpCo3iXwwSRjRpn2Ckjg==", + "devOptional": true, + "license": "MIT", "dependencies": { - "glob": "^7.0.0", - "lodash": "^4.17.11", - "scss-tokenizer": "^0.4.3", - "yargs": "^17.2.1" + "chokidar": "^4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" }, "bin": { - "sassgraph": "bin/sassgraph" + "sass": "sass.js" }, "engines": { - "node": ">=12" - } - }, - "node_modules/sass-graph/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/sass-graph/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==", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" + "node": ">=14.0.0" }, - "engines": { - "node": ">=12" + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" } }, - "node_modules/sass-graph/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==", + "node_modules/sass-loader": { + "version": "12.6.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz", + "integrity": "sha512-oLTaH0YCtX4cfnJZxKSLAyglED0naiYfNG1iXfU5w1LNZ+ukoA5DtyDIN5zmKVZwYNJP4KRc5Y3hkWga+7tYfA==", + "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1" + "klona": "^2.0.4", + "neo-async": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "fibers": ">= 3.1.0", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", + "sass": "^1.3.0", + "sass-embedded": "*", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "fibers": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + } } }, - "node_modules/sass-graph/node_modules/yargs": { - "version": "17.7.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", - "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", + "node_modules/sass/node_modules/chokidar": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz", + "integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==", + "devOptional": true, + "license": "MIT", "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" + "readdirp": "^4.0.1" }, "engines": { - "node": ">=12" + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" } }, - "node_modules/sass-graph/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==", + "node_modules/sass/node_modules/readdirp": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz", + "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==", + "devOptional": true, + "license": "MIT", "engines": { - "node": ">=12" + "node": ">= 14.16.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" } }, "node_modules/sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "license": "ISC" }, "node_modules/saxes": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "license": "ISC", "dependencies": { "xmlchars": "^2.2.0" }, @@ -25639,57 +16713,80 @@ } }, "node_modules/scheduler": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", "dependencies": { "loose-envify": "^1.1.0" } }, "node_modules/schema-utils": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", - "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "license": "MIT", "dependencies": { - "@types/json-schema": "^7.0.5", - "ajv": "^6.12.4", - "ajv-keywords": "^3.5.2" + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" }, "engines": { - "node": ">= 8.9.0" + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/schema-utils/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/scss-tokenizer": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.4.3.tgz", - "integrity": "sha512-raKLgf1LI5QMQnG+RxHz6oK0sL3x3I4FN2UDLqgLOGO8hodECNnNh5BXn7fAyBxrA8zVzdQizQ6XjNJQ+uBwMw==", + "node_modules/schema-utils/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "license": "MIT", "dependencies": { - "js-base64": "^2.4.9", - "source-map": "^0.7.3" + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" } }, - "node_modules/scss-tokenizer/node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "engines": { - "node": ">= 8" - } + "node_modules/schema-utils/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" }, "node_modules/select-hose": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", - "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==" + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", + "license": "MIT" }, "node_modules/selfsigned": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.1.1.tgz", - "integrity": "sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", + "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", + "license": "MIT", "dependencies": { + "@types/node-forge": "^1.3.0", "node-forge": "^1" }, "engines": { @@ -25697,17 +16794,22 @@ } }, "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "license": "ISC", "bin": { "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/send": { "version": "0.19.0", "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "license": "MIT", "dependencies": { "debug": "2.6.9", "depd": "2.0.0", @@ -25731,6 +16833,7 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", "dependencies": { "ms": "2.0.0" } @@ -25738,40 +16841,23 @@ "node_modules/send/node_modules/debug/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/send/node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" }, - "node_modules/send/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "license": "MIT", "engines": { "node": ">= 0.8" } }, "node_modules/serialize-javascript": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", - "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "license": "BSD-3-Clause", "dependencies": { "randombytes": "^2.1.0" } @@ -25780,6 +16866,7 @@ "version": "1.9.1", "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", + "license": "MIT", "dependencies": { "accepts": "~1.3.4", "batch": "0.6.1", @@ -25797,6 +16884,7 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", "dependencies": { "ms": "2.0.0" } @@ -25805,6 +16893,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -25813,6 +16902,7 @@ "version": "1.6.3", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "license": "MIT", "dependencies": { "depd": "~1.1.2", "inherits": "2.0.3", @@ -25826,22 +16916,35 @@ "node_modules/serve-index/node_modules/inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "license": "ISC" }, "node_modules/serve-index/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" }, "node_modules/serve-index/node_modules/setprototypeof": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "license": "ISC" + }, + "node_modules/serve-index/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } }, "node_modules/serve-static": { "version": "1.16.2", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "license": "MIT", "dependencies": { "encodeurl": "~2.0.0", "escape-html": "~1.0.3", @@ -25852,23 +16955,11 @@ "node": ">= 0.8.0" } }, - "node_modules/serve-static/node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" - }, "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==", + "license": "MIT", "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -25881,475 +16972,245 @@ "node": ">= 0.4" } }, - "node_modules/set-value": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", - "dependencies": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/set-value/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "license": "MIT", "dependencies": { - "is-extendable": "^0.1.0" + "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.10.0" - } - }, - "node_modules/set-value/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" } }, - "node_modules/setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" - }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, - "node_modules/sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "dependencies": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - }, - "bin": { - "sha.js": "bin.js" - } - }, - "node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", - "dependencies": { - "shebang-regex": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/shell-quote": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.0.tgz", - "integrity": "sha512-QHsz8GgQIGKlRi24yFc6a6lN69Idnx634w49ay6+jA5yFh7a1UY+4Rp6HPx/L/1zcEDPEij8cIsiqR6bQsE5VQ==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", - "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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==" + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" }, - "node_modules/simple-update-notifier": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz", - "integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==", + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "license": "MIT", "dependencies": { - "semver": "~7.0.0" + "kind-of": "^6.0.2" }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/simple-update-notifier/node_modules/semver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "engines": { "node": ">=8" } }, - "node_modules/slice-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", - "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", - "dependencies": { - "ansi-styles": "^3.2.0", - "astral-regex": "^1.0.0", - "is-fullwidth-code-point": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": { + "node_modules/shebang-command": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", - "engines": { - "node": ">=4" - } - }, - "node_modules/smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "dependencies": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "dependencies": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-node/node_modules/define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", - "dependencies": { - "is-descriptor": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "dependencies": { - "kind-of": "^3.2.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-util/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/snapdragon/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon/node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon/node_modules/is-accessor-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", "dependencies": { - "is-buffer": "^1.1.5" + "shebang-regex": "^3.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/snapdragon/node_modules/is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", - "dependencies": { - "kind-of": "^3.0.2" - }, + "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==", + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/snapdragon/node_modules/is-data-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" + "node_modules/shell-quote": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", + "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/snapdragon/node_modules/is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "node_modules/side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "license": "MIT", "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/snapdragon/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "engines": { - "node": ">=0.10.0" - } + "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==", + "license": "ISC" }, - "node_modules/snapdragon/node_modules/kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, "engines": { - "node": ">=0.10.0" + "node": ">=10" } }, - "node_modules/snapdragon/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "license": "MIT" }, - "node_modules/snapdragon/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, "node_modules/sockjs": { "version": "0.3.24", "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "license": "MIT", "dependencies": { "faye-websocket": "^0.11.3", "uuid": "^8.3.2", "websocket-driver": "^0.7.4" } }, - "node_modules/socks": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", - "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==", - "dependencies": { - "ip": "^2.0.0", - "smart-buffer": "^4.2.0" - }, - "engines": { - "node": ">= 10.13.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks-proxy-agent": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", - "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", - "dependencies": { - "agent-base": "^6.0.2", - "debug": "^4.3.3", - "socks": "^2.6.2" - }, - "engines": { - "node": ">= 10" - } - }, "node_modules/source-list-map": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", - "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==" + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", + "license": "MIT" }, "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==", + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "license": "BSD-3-Clause", "engines": { - "node": ">=0.10.0" + "node": ">= 8" } }, "node_modules/source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "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==", + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, - "node_modules/source-map-resolve": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", - "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", - "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", + "node_modules/source-map-loader": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-3.0.2.tgz", + "integrity": "sha512-BokxPoLjyl3iOrgkWaakaxqnelAJSS+0V+De0kKIq6lyWrXuiPgYTGp6z3iHmqljKAaLXwZa+ctD8GccRJeVvg==", + "license": "MIT", "dependencies": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" + "abab": "^2.0.5", + "iconv-lite": "^0.6.3", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" } }, "node_modules/source-map-support": { "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "license": "MIT", "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, - "node_modules/source-map-url": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", - "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", - "deprecated": "See https://github.com/lydell/source-map-url#deprecated" + "node_modules/source-map-support/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==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } }, "node_modules/sourcemap-codec": { "version": "1.4.8", "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", - "deprecated": "Please use @jridgewell/sourcemap-codec instead" + "deprecated": "Please use @jridgewell/sourcemap-codec instead", + "license": "MIT" }, "node_modules/spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "license": "Apache-2.0", "dependencies": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" } }, "node_modules/spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, + "license": "CC-BY-3.0" }, "node_modules/spdx-expression-parse": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "license": "MIT", "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" } }, "node_modules/spdx-license-ids": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", - "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==" + "version": "3.0.20", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.20.tgz", + "integrity": "sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==", + "dev": true, + "license": "CC0-1.0" }, "node_modules/spdy": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "license": "MIT", "dependencies": { "debug": "^4.1.0", "handle-thing": "^2.0.0", @@ -26365,6 +17226,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "license": "MIT", "dependencies": { "debug": "^4.1.0", "detect-node": "^2.0.4", @@ -26374,367 +17236,169 @@ "wbuf": "^1.7.3" } }, - "node_modules/spdy-transport/node_modules/readable-stream": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.1.tgz", - "integrity": "sha512-+rQmrWMYGA90yenhTYsLWAsLsqVC8osOw6PKE1HDYiO0gdPeKe/xDHNzIAIn4C91YQ6oenEhfYqqc1883qHbjQ==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/spdy-transport/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "dependencies": { - "extend-shallow": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" - }, - "node_modules/sshpk": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", - "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", - "optional": true, - "peer": true, - "dependencies": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - }, - "bin": { - "sshpk-conv": "bin/sshpk-conv", - "sshpk-sign": "bin/sshpk-sign", - "sshpk-verify": "bin/sshpk-verify" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ssri": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-7.1.1.tgz", - "integrity": "sha512-w+daCzXN89PseTL99MkA+fxJEcU3wfaE/ah0i0lnOlpG1CYLJ2ZjzEry68YBKfLs4JfoTShrTEsJkAZuNZ/stw==", - "dev": true, - "dependencies": { - "figgy-pudding": "^3.5.1", - "minipass": "^3.1.1" - }, - "engines": { - "node": ">= 8" - } + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "license": "BSD-3-Clause" }, "node_modules/stable": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", - "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", - "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility" - }, - "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==", - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/stack-utils/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/stackframe": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", - "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==" - }, - "node_modules/static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==", - "dependencies": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/static-extend/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/static-extend/node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/static-extend/node_modules/is-accessor-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/static-extend/node_modules/is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/static-extend/node_modules/is-data-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/static-extend/node_modules/is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/static-extend/node_modules/kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/stdout-stream": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz", - "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==", - "dependencies": { - "readable-stream": "^2.0.1" - } - }, - "node_modules/stdout-stream/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, - "node_modules/stdout-stream/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/stdout-stream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility", + "license": "MIT" }, - "node_modules/stdout-stream/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "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==", + "license": "MIT", "dependencies": { - "safe-buffer": "~5.1.0" + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" } }, - "node_modules/stop-iteration-iterator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", - "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", - "dependencies": { - "internal-slot": "^1.0.4" - }, + "node_modules/stack-utils/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==", + "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">=8" } }, - "node_modules/stream-browserify": { + "node_modules/stackframe": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", + "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==", + "license": "MIT" + }, + "node_modules/static-eval": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", - "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "resolved": "https://registry.npmjs.org/static-eval/-/static-eval-2.0.2.tgz", + "integrity": "sha512-N/D219Hcr2bPjLxPiV+TQE++Tsmrady7TqAJugLy7Xk1EumfDWS/f5dtBbkRCGE7wKKXuYockQoj8Rm2/pVKyg==", + "license": "MIT", "dependencies": { - "inherits": "~2.0.1", - "readable-stream": "^2.0.2" + "escodegen": "^1.8.1" } }, - "node_modules/stream-browserify/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, - "node_modules/stream-browserify/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "node_modules/static-eval/node_modules/escodegen": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "license": "BSD-2-Clause", "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=4.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" } }, - "node_modules/stream-browserify/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/stream-browserify/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" + "node_modules/static-eval/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" } }, - "node_modules/stream-each": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", - "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", + "node_modules/static-eval/node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "license": "MIT", "dependencies": { - "end-of-stream": "^1.1.0", - "stream-shift": "^1.0.0" + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" } }, - "node_modules/stream-http": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", - "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "node_modules/static-eval/node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "license": "MIT", "dependencies": { - "builtin-status-codes": "^3.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.3.6", - "to-arraybuffer": "^1.0.0", - "xtend": "^4.0.0" + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, + "engines": { + "node": ">= 0.8.0" } }, - "node_modules/stream-http/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, - "node_modules/stream-http/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "node_modules/static-eval/node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "engines": { + "node": ">= 0.8.0" } }, - "node_modules/stream-http/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "node_modules/static-eval/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==", + "license": "BSD-3-Clause", + "optional": true, + "engines": { + "node": ">=0.10.0" + } }, - "node_modules/stream-http/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "node_modules/static-eval/node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "license": "MIT", "dependencies": { - "safe-buffer": "~5.1.0" + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" } }, - "node_modules/stream-shift": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", - "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } }, "node_modules/string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==" + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } }, "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "license": "MIT", "dependencies": { "char-regex": "^1.0.2", "strip-ansi": "^6.0.0" @@ -26743,34 +17407,17 @@ "node": ">=10" } }, - "node_modules/string-length/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/string-length/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==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/string-natural-compare": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/string-natural-compare/-/string-natural-compare-3.0.1.tgz", - "integrity": "sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==" + "integrity": "sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==", + "license": "MIT" }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -26780,52 +17427,112 @@ "node": ">=8" } }, - "node_modules/string-width/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/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==", + "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-width/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==", + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/string-width/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/string.prototype.includes": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", + "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==", + "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3" }, "engines": { - "node": ">=8" + "node": ">= 0.4" } }, "node_modules/string.prototype.matchall": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz", - "integrity": "sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==", + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz", + "integrity": "sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "get-intrinsic": "^1.1.3", + "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", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", - "regexp.prototype.flags": "^1.4.3", - "side-channel": "^1.0.4" + "internal-slot": "^1.0.7", + "regexp.prototype.flags": "^1.5.2", + "set-function-name": "^2.0.2", + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/string.prototype.padend": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.4.tgz", - "integrity": "sha512-67otBXoksdjsnXXRUq+KMVTdlVRZ2af422Y0aTyTjVaoQkGr3mxl2Bc5emi7dOQ3OGVVQQskmLEWwFXwommpNw==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.6.tgz", + "integrity": "sha512-XZpspuSB7vJWhvJc9DLSlrXl1mcA2BdoY5jjnS135ydXqLoqhs96JjDtCkjJEQHvfqZIp9hBuBMgI589peyx9Q==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.repeat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", + "license": "MIT", + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", + "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.0", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -26835,26 +17542,31 @@ } }, "node_modules/string.prototype.trimend": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", - "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", + "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/string.prototype.trimstart": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", - "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "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" @@ -26864,6 +17576,7 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "license": "BSD-2-Clause", "dependencies": { "get-own-enumerable-property-symbols": "^3.0.0", "is-obj": "^1.0.1", @@ -26874,64 +17587,62 @@ } }, "node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", "dependencies": { - "ansi-regex": "^4.1.0" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">=6" + "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==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "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==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "license": "MIT", "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/strip-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-comments/-/strip-comments-2.0.1.tgz", - "integrity": "sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==", - "engines": { - "node": ">=10" - } - }, - "node_modules/strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", - "dev": true, + "integrity": "sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==", + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=10" } }, "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==", + "license": "MIT", "engines": { "node": ">=6" } }, - "node_modules/strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "dependencies": { - "min-indent": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, "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==", + "license": "MIT", "engines": { "node": ">=8" }, @@ -26940,96 +17651,130 @@ } }, "node_modules/style-loader": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.23.1.tgz", - "integrity": "sha512-XK+uv9kWwhZMZ1y7mysB+zoihsEj4wneFWAS5qoiLwzW0WzSqMrrsIy+a3zkQJq0ipFtBpX5W3MqyRIBF/WFGg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-4.0.0.tgz", + "integrity": "sha512-1V4WqhhZZgjVAVJyt7TdDPZoPBPNHbekX4fWnCJL1yQukhCeZhJySUL+gL9y6sNdN95uEOS83Y55SqHcP7MzLA==", "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.27.0" + } + }, + "node_modules/stylehacks": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", + "integrity": "sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==", + "license": "MIT", "dependencies": { - "loader-utils": "^1.1.0", - "schema-utils": "^1.0.0" + "browserslist": "^4.21.4", + "postcss-selector-parser": "^6.0.4" }, "engines": { - "node": ">= 0.12.0" + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" } }, - "node_modules/style-loader/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, + "node_modules/sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "license": "MIT", "dependencies": { - "minimist": "^1.2.0" + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" }, "bin": { - "json5": "lib/cli.js" + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" } }, - "node_modules/style-loader/node_modules/loader-utils": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", - "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", - "dev": true, + "node_modules/sucrase/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==", + "license": "MIT", "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - }, + "balanced-match": "^1.0.0" + } + }, + "node_modules/sucrase/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "license": "MIT", "engines": { - "node": ">=4.0.0" + "node": ">= 6" } }, - "node_modules/style-loader/node_modules/schema-utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", - "dev": true, + "node_modules/sucrase/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==", + "license": "ISC", "dependencies": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" + "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" }, - "engines": { - "node": ">= 4" + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/styled-jsx": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.2.tgz", - "integrity": "sha512-FI5r0a5ED2/+DSdG2ZRz3a4FtNQnKPLadauU5v76a9QsscwZrWggQKOmyxGGP5EWKbyY3bsuWAJYzyKaDAVAcw==", - "dev": true, + "node_modules/sucrase/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", "dependencies": { - "client-only": "0.0.1" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">= 12.0.0" + "node": ">=16 || 14 >=14.17" }, - "peerDependencies": { - "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "babel-plugin-macros": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "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==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", "dependencies": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/supports-hyperlinks": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", + "license": "MIT", "dependencies": { "has-flag": "^4.0.0", "supports-color": "^7.0.0" @@ -27038,29 +17783,11 @@ "node": ">=8" } }, - "node_modules/supports-hyperlinks/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-hyperlinks/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "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==", + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -27071,13 +17798,15 @@ "node_modules/svg-parser": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", - "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==" + "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==", + "license": "MIT" }, "node_modules/svgo": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", "deprecated": "This SVGO version is no longer supported. Upgrade to v2.x.x.", + "license": "MIT", "dependencies": { "chalk": "^2.4.1", "coa": "^2.0.2", @@ -27100,339 +17829,193 @@ "node": ">=4.0.0" } }, - "node_modules/svgo/node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" - }, - "node_modules/table": { - "version": "5.4.6", - "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", - "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", - "dependencies": { - "ajv": "^6.10.2", - "lodash": "^4.17.14", - "slice-ansi": "^2.1.0", - "string-width": "^3.0.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/table/node_modules/emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" - }, - "node_modules/table/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", - "engines": { - "node": ">=4" - } - }, - "node_modules/table/node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tailwindcss": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.2.7.tgz", - "integrity": "sha512-B6DLqJzc21x7wntlH/GsZwEXTBttVSl1FtCzC8WP4oBc/NKef7kaax5jeihkkCEWc831/5NDJ9gRNDK6NEioQQ==", - "dependencies": { - "arg": "^5.0.2", - "chokidar": "^3.5.3", - "color-name": "^1.1.4", - "detective": "^5.2.1", - "didyoumean": "^1.2.2", - "dlv": "^1.1.3", - "fast-glob": "^3.2.12", - "glob-parent": "^6.0.2", - "is-glob": "^4.0.3", - "lilconfig": "^2.0.6", - "micromatch": "^4.0.5", - "normalize-path": "^3.0.0", - "object-hash": "^3.0.0", - "picocolors": "^1.0.0", - "postcss": "^8.0.9", - "postcss-import": "^14.1.0", - "postcss-js": "^4.0.0", - "postcss-load-config": "^3.1.4", - "postcss-nested": "6.0.0", - "postcss-selector-parser": "^6.0.11", - "postcss-value-parser": "^4.2.0", - "quick-lru": "^5.1.1", - "resolve": "^1.22.1" - }, - "bin": { - "tailwind": "lib/cli.js", - "tailwindcss": "lib/cli.js" - }, - "engines": { - "node": ">=12.13.0" - }, - "peerDependencies": { - "postcss": "^8.0.9" - } - }, - "node_modules/tailwindcss/node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tailwindcss/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==" - }, - "node_modules/tailwindcss/node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tailwindcss/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==", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/tailwindcss/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==", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/tailwindcss/node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/tailwindcss/node_modules/nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/tailwindcss/node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" - }, - "node_modules/tailwindcss/node_modules/postcss": { - "version": "8.4.21", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", - "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - } - ], - "dependencies": { - "nanoid": "^3.3.4", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/tailwindcss/node_modules/postcss-import": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.1.0.tgz", - "integrity": "sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==", - "dependencies": { - "postcss-value-parser": "^4.0.0", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" - }, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "postcss": "^8.0.0" - } - }, - "node_modules/tailwindcss/node_modules/postcss-js": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", - "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "node_modules/svgo/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==", + "license": "MIT", "dependencies": { - "camelcase-css": "^2.0.1" - }, - "engines": { - "node": "^12 || ^14 || >= 16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" + "color-convert": "^1.9.0" }, - "peerDependencies": { - "postcss": "^8.4.21" + "engines": { + "node": ">=4" } }, - "node_modules/tailwindcss/node_modules/postcss-load-config": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", - "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", + "node_modules/svgo/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==", + "license": "MIT", "dependencies": { - "lilconfig": "^2.0.5", - "yaml": "^1.10.2" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" }, "engines": { - "node": ">= 10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": ">=8.0.9", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "postcss": { - "optional": true - }, - "ts-node": { - "optional": true - } + "node": ">=4" } }, - "node_modules/tailwindcss/node_modules/postcss-nested": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.0.tgz", - "integrity": "sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w==", + "node_modules/svgo/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==", + "license": "MIT", "dependencies": { - "postcss-selector-parser": "^6.0.10" - }, + "color-name": "1.1.3" + } + }, + "node_modules/svgo/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==", + "license": "MIT" + }, + "node_modules/svgo/node_modules/css-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", + "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^3.2.1", + "domutils": "^1.7.0", + "nth-check": "^1.0.2" + } + }, + "node_modules/svgo/node_modules/css-what": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz", + "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==", + "license": "BSD-2-Clause", "engines": { - "node": ">=12.0" + "node": ">= 6" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.2.14" + "url": "https://github.com/sponsors/fb55" } }, - "node_modules/tailwindcss/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==", + "node_modules/svgo/node_modules/dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "license": "MIT", "dependencies": { - "is-number": "^7.0.0" - }, + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + } + }, + "node_modules/svgo/node_modules/domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "node_modules/svgo/node_modules/domutils/node_modules/domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", + "license": "BSD-2-Clause" + }, + "node_modules/svgo/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==", + "license": "MIT", "engines": { - "node": ">=8.0" + "node": ">=0.8.0" } }, - "node_modules/tapable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", - "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "node_modules/svgo/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==", + "license": "MIT", "engines": { - "node": ">=6" + "node": ">=4" } }, - "node_modules/tar": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", - "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "node_modules/svgo/node_modules/nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "~1.0.0" + } + }, + "node_modules/svgo/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==", + "license": "MIT", "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" + "has-flag": "^3.0.0" }, "engines": { - "node": ">=10" + "node": ">=4" } }, - "node_modules/tar/node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "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==", + "license": "MIT" + }, + "node_modules/tailwindcss": { + "version": "3.4.14", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.14.tgz", + "integrity": "sha512-IcSvOcTRcUtQQ7ILQL5quRDg7Xs93PdJEk1ZLbhhvJc7uj/OAhYOnruEiwnGgBvUtaUAJ8/mhSw1o8L2jCiENA==", + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.5.3", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.0", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.0", + "lilconfig": "^2.1.0", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.23", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.1", + "postcss-nested": "^6.0.1", + "postcss-selector-parser": "^6.0.11", + "resolve": "^1.22.2", + "sucrase": "^3.32.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, "engines": { - "node": ">=10" + "node": ">=14.0.0" } }, - "node_modules/tar/node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=6" } }, - "node_modules/tar/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, "node_modules/temp-dir": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", + "license": "MIT", "engines": { "node": ">=8" } @@ -27441,6 +18024,7 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/tempy/-/tempy-0.6.0.tgz", "integrity": "sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw==", + "license": "MIT", "dependencies": { "is-stream": "^2.0.0", "temp-dir": "^2.0.0", @@ -27454,21 +18038,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/tempy/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==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/tempy/node_modules/type-fest": { "version": "0.16.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz", "integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==", + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" }, @@ -27480,6 +18054,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "license": "MIT", "dependencies": { "ansi-escapes": "^4.2.1", "supports-hyperlinks": "^2.0.0" @@ -27492,257 +18067,195 @@ } }, "node_modules/terser": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.1.tgz", - "integrity": "sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw==", + "version": "5.36.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.36.0.tgz", + "integrity": "sha512-IYV9eNMuFAV4THUspIRXkLakHnV6XO7FEdtKjf/mDyrnqUg9LnlOn6/RwRvM9SZjR4GUq8Nk8zj67FzVARr74w==", + "license": "BSD-2-Clause", "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", "commander": "^2.20.0", - "source-map": "~0.6.1", - "source-map-support": "~0.5.12" + "source-map-support": "~0.5.20" }, "bin": { "terser": "bin/terser" }, "engines": { - "node": ">=6.0.0" + "node": ">=10" } }, "node_modules/terser-webpack-plugin": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-2.3.8.tgz", - "integrity": "sha512-/fKw3R+hWyHfYx7Bv6oPqmk4HGQcrWLtV3X6ggvPuwPNHSnzvVV51z6OaaCOus4YLjutYGOz3pEpbhe6Up2s1w==", - "dev": true, + "version": "5.3.10", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", + "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", + "license": "MIT", "dependencies": { - "cacache": "^13.0.1", - "find-cache-dir": "^3.3.1", - "jest-worker": "^25.4.0", - "p-limit": "^2.3.0", - "schema-utils": "^2.6.6", - "serialize-javascript": "^4.0.0", - "source-map": "^0.6.1", - "terser": "^4.6.12", - "webpack-sources": "^1.4.3" + "@jridgewell/trace-mapping": "^0.3.20", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.1", + "terser": "^5.26.0" }, "engines": { - "node": ">= 8.9.0" + "node": ">= 10.13.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/webpack" }, "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" - } - }, - "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==", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" + "webpack": "^5.1.0" }, - "engines": { - "node": ">=8" - } - }, - "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==" - }, - "node_modules/throat": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.2.tgz", - "integrity": "sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==" - }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" - }, - "node_modules/through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dependencies": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, - "node_modules/through2/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, - "node_modules/through2/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/through2/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/through2/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } } }, - "node_modules/thunky": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", - "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" - }, - "node_modules/timers-browserify": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", - "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==", + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "license": "MIT", "dependencies": { - "setimmediate": "^1.0.4" + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" }, "engines": { - "node": ">=0.6.0" + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, - "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "license": "MIT" + }, + "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==", + "license": "ISC", "dependencies": { - "os-tmpdir": "~1.0.2" + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" }, "engines": { - "node": ">=0.6.0" + "node": ">=8" } }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==" - }, - "node_modules/to-arraybuffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", - "integrity": "sha512-okFlQcoGTi4LQBG/PgSYblw9VOyptsz2KJZqc6qtgGdes8VktzUQkj4BI2blit072iS8VODNcMA+tvnS9dnuMA==" - }, - "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==", - "engines": { - "node": ">=4" - } + "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==", + "license": "MIT" }, - "node_modules/to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==", + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "license": "MIT", "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" + "any-promise": "^1.0.0" } }, - "node_modules/to-object-path/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "license": "MIT", "dependencies": { - "is-buffer": "^1.1.5" + "thenify": ">= 3.1.0 < 4" }, "engines": { - "node": ">=0.10.0" + "node": ">=0.8" } }, - "node_modules/to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "dependencies": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" - }, + "node_modules/thingies": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/thingies/-/thingies-1.21.0.tgz", + "integrity": "sha512-hsqsJsFMsV+aD4s3CWKk85ep/3I9XzYV/IXaSouJMYIoDlgyi11cBhsqYe9/geRfB0YIikBQg6raRaM+nIMP9g==", + "devOptional": true, + "license": "Unlicense", "engines": { - "node": ">=0.10.0" + "node": ">=10.18" + }, + "peerDependencies": { + "tslib": "^2" } }, + "node_modules/throat": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.2.tgz", + "integrity": "sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==", + "license": "MIT" + }, + "node_modules/thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "license": "MIT" + }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", + "license": "MIT" + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "license": "BSD-3-Clause" + }, "node_modules/to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "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==", + "license": "MIT", "dependencies": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" + "is-number": "^7.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=8.0" } }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", "engines": { "node": ">=0.6" } }, - "node_modules/topo": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/topo/-/topo-3.0.3.tgz", - "integrity": "sha512-IgpPtvD4kjrJ7CRA3ov2FhWQADwv+Tdqbsf1ZnPUSAtCJ9e1Z44MmoSGDXGk4IppoZA7jd/QRkNddlLJWlUZsQ==", - "deprecated": "This module has moved and is now available at @hapi/topo. Please update your dependencies as this version is no longer maintained an may contain bugs and security issues.", - "devOptional": true, - "dependencies": { - "hoek": "6.x.x" - } - }, "node_modules/touch": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", - "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", - "dependencies": { - "nopt": "~1.0.10" - }, + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "dev": true, + "license": "ISC", "bin": { "nodetouch": "bin/nodetouch.js" } }, - "node_modules/touch/node_modules/nopt": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", - "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": "*" - } - }, "node_modules/tough-cookie": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", - "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "license": "BSD-3-Clause", "dependencies": { "psl": "^1.1.33", "punycode": "^2.1.1", @@ -27757,6 +18270,7 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "license": "MIT", "engines": { "node": ">= 4.0.0" } @@ -27765,6 +18279,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", + "license": "MIT", "dependencies": { "punycode": "^2.1.1" }, @@ -27772,28 +18287,40 @@ "node": ">=8" } }, - "node_modules/trim-newlines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", - "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", + "node_modules/tree-dump": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tree-dump/-/tree-dump-1.0.2.tgz", + "integrity": "sha512-dpev9ABuLWdEubk+cIaI9cHwRNNDjkBBLXTwI4UCUFdQ5xXKqNXoK4FEciw/vxf+NQ7Cb7sGUyeUtORvHIdRXQ==", + "devOptional": true, + "license": "Apache-2.0", "engines": { - "node": ">=8" + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" } }, - "node_modules/true-case-path": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-2.2.1.tgz", - "integrity": "sha512-0z3j8R7MCjy10kc/g+qg7Ln3alJTodw9aDuVWZa3uiWqfuBMKeAeP2ocWcxoyM3D73yz3Jt/Pu4qPr4wHSdB/Q==" - }, "node_modules/tryer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", - "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==" + "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==", + "license": "MIT" + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "license": "Apache-2.0" }, "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==", + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "license": "MIT", "dependencies": { "@types/json5": "^0.0.29", "json5": "^1.0.2", @@ -27805,6 +18332,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "license": "MIT", "dependencies": { "minimist": "^1.2.0" }, @@ -27812,24 +18340,26 @@ "json5": "lib/cli.js" } }, - "node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/tsscmp": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz", - "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==", - "devOptional": true, + "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==", + "license": "MIT", "engines": { - "node": ">=0.6.x" + "node": ">=4" } }, + "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/tsutils": { "version": "3.21.0", "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "license": "MIT", "dependencies": { "tslib": "^1.8.1" }, @@ -27840,37 +18370,19 @@ "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" } }, - "node_modules/tty-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", - "integrity": "sha512-JVa5ijo+j/sOoHGjw0sxw734b1LhBkQ3bvUGNdxnVXDCX81Yx7TFgnZygxrIIWn23hbfTaMYLwRmAxFyDuFmIw==" - }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "optional": true, - "peer": true, - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", - "optional": true, - "peer": true + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "license": "0BSD" }, "node_modules/type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "license": "MIT", "dependencies": { - "prelude-ls": "~1.1.2" + "prelude-ls": "^1.2.1" }, "engines": { "node": ">= 0.8.0" @@ -27880,6 +18392,7 @@ "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==", + "license": "MIT", "engines": { "node": ">=4" } @@ -27888,6 +18401,7 @@ "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==", + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" }, @@ -27899,6 +18413,7 @@ "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" @@ -27907,28 +18422,84 @@ "node": ">= 0.6" } }, - "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==", + "node_modules/typed-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", + "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", + "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", + "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" + "node_modules/typed-array-length": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", + "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/typedarray-to-buffer": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "license": "MIT", "dependencies": { "is-typedarray": "^1.0.0" } @@ -27937,6 +18508,7 @@ "version": "4.9.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "license": "Apache-2.0", "peer": true, "bin": { "tsc": "bin/tsc", @@ -27950,6 +18522,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "license": "MIT", "dependencies": { "call-bind": "^1.0.2", "has-bigints": "^1.0.2", @@ -27964,6 +18537,7 @@ "version": "7.2.1", "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.2.1.tgz", "integrity": "sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.6.3", "@types/react": ">=16.9.11", @@ -27977,12 +18551,27 @@ "node_modules/undefsafe": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", - "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==" + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true, + "license": "MIT" + }, + "node_modules/underscore": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz", + "integrity": "sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==", + "license": "MIT" + }, + "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==", + "license": "MIT" }, "node_modules/unicode-canonical-property-names-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", - "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", + "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", + "license": "MIT", "engines": { "node": ">=4" } @@ -27991,6 +18580,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "license": "MIT", "dependencies": { "unicode-canonical-property-names-ecmascript": "^2.0.0", "unicode-property-aliases-ecmascript": "^2.0.0" @@ -28000,9 +18590,10 @@ } }, "node_modules/unicode-match-property-value-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", - "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", + "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", + "license": "MIT", "engines": { "node": ">=4" } @@ -28011,52 +18602,16 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "license": "MIT", "engines": { "node": ">=4" } }, - "node_modules/union-value": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", - "dependencies": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^2.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/union-value/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/unique-filename": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", - "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", - "dependencies": { - "unique-slug": "^2.0.0" - } - }, - "node_modules/unique-slug": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", - "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", - "dependencies": { - "imurmurhash": "^0.1.4" - } - }, "node_modules/unique-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "license": "MIT", "dependencies": { "crypto-random-string": "^2.0.0" }, @@ -28065,9 +18620,10 @@ } }, "node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "license": "MIT", "engines": { "node": ">= 10.0.0" } @@ -28076,6 +18632,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -28083,79 +18640,23 @@ "node_modules/unquote": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", - "integrity": "sha512-vRCqFv6UhXpWxZPyGDh/F3ZpNv8/qo7w6iufLpQg9aKnQ71qM4B5KiI7Mia9COcjEhrO9LueHpMYjYzsWH3OIg==" - }, - "node_modules/unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==", - "dependencies": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/unset-value/node_modules/has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==", - "dependencies": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/unset-value/node_modules/has-value/node_modules/isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==", - "dependencies": { - "isarray": "1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/unset-value/node_modules/has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/unset-value/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, - "node_modules/untildify": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", - "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", - "dev": true, - "engines": { - "node": ">=8" - } + "integrity": "sha512-vRCqFv6UhXpWxZPyGDh/F3ZpNv8/qo7w6iufLpQg9aKnQ71qM4B5KiI7Mia9COcjEhrO9LueHpMYjYzsWH3OIg==", + "license": "MIT" }, "node_modules/upath": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "license": "MIT", "engines": { "node": ">=4", "yarn": "*" } }, "node_modules/update-browserslist-db": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", - "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", + "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", "funding": [ { "type": "opencollective", @@ -28164,86 +18665,54 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" + "escalade": "^3.2.0", + "picocolors": "^1.1.0" }, "bin": { - "browserslist-lint": "cli.js" + "update-browserslist-db": "cli.js" }, "peerDependencies": { "browserslist": ">= 4.21.0" } }, - "node_modules/update-browserslist-db/node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" - }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" } }, - "node_modules/urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==", - "deprecated": "Please see https://github.com/lydell/urix#deprecated" - }, - "node_modules/url": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha512-kbailJa29QrtXnxgq+DdCEGlbTeYM2eJUxsz6vjZavrCYPMIFHMKQmSKYAIuUK2i7hgPm28a8piX5NTUtM/LKQ==", - "dependencies": { - "punycode": "1.3.2", - "querystring": "0.2.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==", + "license": "MIT", "dependencies": { "querystringify": "^2.1.1", "requires-port": "^1.0.0" } }, - "node_modules/url/node_modules/punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==" - }, - "node_modules/use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/util": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", - "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", - "dependencies": { - "inherits": "2.0.3" - } - }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" }, "node_modules/util.promisify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", + "license": "MIT", "dependencies": { "define-properties": "^1.1.3", "es-abstract": "^1.17.2", @@ -28254,20 +18723,17 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/util/node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" - }, "node_modules/utila": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", - "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==" + "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==", + "license": "MIT" }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", "engines": { "node": ">= 0.4.0" } @@ -28276,19 +18742,16 @@ "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/v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==" - }, "node_modules/v8-to-istanbul": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz", "integrity": "sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==", + "license": "ISC", "dependencies": { "@types/istanbul-lib-coverage": "^2.0.1", "convert-source-map": "^1.6.0", @@ -28298,18 +18761,18 @@ "node": ">=10.12.0" } }, - "node_modules/v8-to-istanbul/node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "engines": { - "node": ">= 8" - } + "node_modules/v8-to-istanbul/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "license": "MIT" }, "node_modules/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "license": "Apache-2.0", "dependencies": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" @@ -28319,36 +18782,16 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", "engines": { "node": ">= 0.8" } }, - "node_modules/verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", - "engines": [ - "node >=0.6.0" - ], - "optional": true, - "peer": true, - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "node_modules/verror/node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", - "optional": true, - "peer": true - }, "node_modules/victory-vendor": { - "version": "36.6.8", - "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.6.8.tgz", - "integrity": "sha512-H3kyQ+2zgjMPvbPqAl7Vwm2FD5dU7/4bCTQakFQnpIsfDljeOMDojRsrmJfwh4oAlNnWhpAf+mbAoLh8u7dwyQ==", + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz", + "integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==", + "license": "MIT AND ISC", "dependencies": { "@types/d3-array": "^3.0.3", "@types/d3-ease": "^3.0.0", @@ -28366,16 +18809,12 @@ "d3-timer": "^3.0.1" } }, - "node_modules/vm-browserify": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", - "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==" - }, "node_modules/w3c-hr-time": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", "deprecated": "Use your platform's native performance.now() and performance.timeOrigin.", + "license": "MIT", "dependencies": { "browser-process-hrtime": "^1.0.0" } @@ -28384,6 +18823,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", + "license": "MIT", "dependencies": { "xml-name-validator": "^3.0.0" }, @@ -28395,6 +18835,7 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "license": "Apache-2.0", "dependencies": { "makeerror": "1.0.12" } @@ -28403,850 +18844,435 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "license": "MIT", "dependencies": { "loose-envify": "^1.0.0" } }, "node_modules/watchpack": { - "version": "1.7.5", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz", - "integrity": "sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==", - "dependencies": { - "graceful-fs": "^4.1.2", - "neo-async": "^2.5.0" - }, - "optionalDependencies": { - "chokidar": "^3.4.1", - "watchpack-chokidar2": "^2.0.1" - } - }, - "node_modules/watchpack-chokidar2": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz", - "integrity": "sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==", - "optional": true, - "dependencies": { - "chokidar": "^2.1.8" - } - }, - "node_modules/watchpack-chokidar2/node_modules/anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "optional": true, - "dependencies": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - } - }, - "node_modules/watchpack-chokidar2/node_modules/anymatch/node_modules/normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", - "optional": true, - "dependencies": { - "remove-trailing-separator": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/watchpack-chokidar2/node_modules/binary-extensions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/watchpack-chokidar2/node_modules/chokidar": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", - "deprecated": "Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies", - "optional": true, - "dependencies": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" - }, - "optionalDependencies": { - "fsevents": "^1.2.7" - } - }, - "node_modules/watchpack-chokidar2/node_modules/fsevents": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", - "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", - "deprecated": "fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.", - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "dependencies": { - "bindings": "^1.5.0", - "nan": "^2.12.1" - }, - "engines": { - "node": ">= 4.0" - } - }, - "node_modules/watchpack-chokidar2/node_modules/glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==", - "optional": true, - "dependencies": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - } - }, - "node_modules/watchpack-chokidar2/node_modules/glob-parent/node_modules/is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", - "optional": true, - "dependencies": { - "is-extglob": "^2.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/watchpack-chokidar2/node_modules/is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==", - "optional": true, - "dependencies": { - "binary-extensions": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/watchpack-chokidar2/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "optional": true - }, - "node_modules/watchpack-chokidar2/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "optional": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/watchpack-chokidar2/node_modules/readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", - "optional": true, + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", + "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", + "license": "MIT", "dependencies": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" }, "engines": { - "node": ">=0.10" - } - }, - "node_modules/watchpack-chokidar2/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "optional": true - }, - "node_modules/watchpack-chokidar2/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "optional": true, - "dependencies": { - "safe-buffer": "~5.1.0" + "node": ">=10.13.0" } }, "node_modules/wbuf": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "license": "MIT", "dependencies": { "minimalistic-assert": "^1.0.0" } }, "node_modules/web-vitals": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-2.1.4.tgz", - "integrity": "sha512-sVWcwhU5mX6crfI5Vd2dC4qchyTqxV8URinzt25XqVh+bHEPGH4C3NPrNionCP7Obx59wrYEbNlw4Z8sjALzZg==" + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-4.2.4.tgz", + "integrity": "sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==", + "license": "Apache-2.0" }, "node_modules/webidl-conversions": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", + "license": "BSD-2-Clause", "engines": { "node": ">=10.4" } }, "node_modules/webpack": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.37.0.tgz", - "integrity": "sha512-iJPPvL7XpbcbwOthbzpa2BSPlmGp8lGDokAj/LdWtK80rsPoPOdANSbDBf2GAVLKZD3GhCuQ/gGkgN9HWs0Keg==", - "dependencies": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-module-context": "1.8.5", - "@webassemblyjs/wasm-edit": "1.8.5", - "@webassemblyjs/wasm-parser": "1.8.5", - "acorn": "^6.2.0", - "ajv": "^6.1.0", - "ajv-keywords": "^3.1.0", - "chrome-trace-event": "^1.0.0", - "enhanced-resolve": "^4.1.0", - "eslint-scope": "^4.0.0", - "json-parse-better-errors": "^1.0.2", - "loader-runner": "^2.3.0", - "loader-utils": "^1.1.0", - "memory-fs": "~0.4.1", - "micromatch": "^3.1.8", - "mkdirp": "~0.5.0", - "neo-async": "^2.5.0", - "node-libs-browser": "^2.0.0", - "schema-utils": "^1.0.0", - "tapable": "^1.1.0", - "terser-webpack-plugin": "^1.1.0", - "watchpack": "^1.5.0", - "webpack-sources": "^1.3.0" + "version": "5.96.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.96.1.tgz", + "integrity": "sha512-l2LlBSvVZGhL4ZrPwyr8+37AunkcYj5qh8o6u2/2rzoPc8gxFJkLj1WxNgooi9pnoc06jh0BjuXnamM4qlujZA==", + "license": "MIT", + "dependencies": { + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.6", + "@webassemblyjs/ast": "^1.12.1", + "@webassemblyjs/wasm-edit": "^1.12.1", + "@webassemblyjs/wasm-parser": "^1.12.1", + "acorn": "^8.14.0", + "browserslist": "^4.24.0", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.17.1", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.2.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.10", + "watchpack": "^2.4.1", + "webpack-sources": "^3.2.3" }, "bin": { "webpack": "bin/webpack.js" }, "engines": { - "node": ">=6.11.5" - } - }, - "node_modules/webpack-cli": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.6.tgz", - "integrity": "sha512-0vEa83M7kJtxK/jUhlpZ27WHIOndz5mghWL2O53kiDoA9DIxSKnfqB92LoqEn77cT4f3H2cZm1BMEat/6AZz3A==", - "dev": true, - "dependencies": { - "chalk": "2.4.2", - "cross-spawn": "6.0.5", - "enhanced-resolve": "4.1.0", - "findup-sync": "3.0.0", - "global-modules": "2.0.0", - "import-local": "2.0.0", - "interpret": "1.2.0", - "loader-utils": "1.2.3", - "supports-color": "6.1.0", - "v8-compile-cache": "2.0.3", - "yargs": "13.2.4" - }, - "bin": { - "webpack-cli": "bin/cli.js" - }, - "engines": { - "node": ">=6.11.5" - }, - "peerDependencies": { - "webpack": "4.x.x" - } - }, - "node_modules/webpack-cli/node_modules/cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "dev": true, - "dependencies": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - } - }, - "node_modules/webpack-cli/node_modules/emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "node_modules/webpack-cli/node_modules/emojis-list": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", - "integrity": "sha512-knHEZMgs8BB+MInokmNTg/OyPlAddghe1YBgNwJBc5zsJi/uyIcXoSDsL/W9ymOsBoBGdPIHXYJ9+qKFwRwDng==", - "dev": true, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/webpack-cli/node_modules/enhanced-resolve": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz", - "integrity": "sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "memory-fs": "^0.4.0", - "tapable": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/webpack-cli/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/webpack-cli/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/webpack-cli/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, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/webpack-cli/node_modules/loader-utils": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", - "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", - "dev": true, - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^2.0.0", - "json5": "^1.0.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/webpack-cli/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/webpack-cli/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/webpack-cli/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/webpack-cli/node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/webpack-cli/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" + "node": ">=10.13.0" }, - "engines": { - "node": ">=6" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } } }, - "node_modules/webpack-cli/node_modules/v8-compile-cache": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.0.3.tgz", - "integrity": "sha512-CNmdbwQMBjwr9Gsmohvm0pbL954tJrNzf6gWL3K+QMQf00PF7ERGrEiLgjuU3mKreLC2MeGhUsNV9ybTbLgd3w==", - "dev": true - }, - "node_modules/webpack-cli/node_modules/wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "node_modules/webpack-cli": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz", + "integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==", "dev": true, - "dependencies": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" + "license": "MIT", + "dependencies": { + "@discoveryjs/json-ext": "^0.5.0", + "@webpack-cli/configtest": "^2.1.1", + "@webpack-cli/info": "^2.0.2", + "@webpack-cli/serve": "^2.0.5", + "colorette": "^2.0.14", + "commander": "^10.0.1", + "cross-spawn": "^7.0.3", + "envinfo": "^7.7.3", + "fastest-levenshtein": "^1.0.12", + "import-local": "^3.0.2", + "interpret": "^3.1.1", + "rechoir": "^0.8.0", + "webpack-merge": "^5.7.3" + }, + "bin": { + "webpack-cli": "bin/cli.js" }, "engines": { - "node": ">=6" + "node": ">=14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "5.x.x" + }, + "peerDependenciesMeta": { + "@webpack-cli/generators": { + "optional": true + }, + "webpack-bundle-analyzer": { + "optional": true + }, + "webpack-dev-server": { + "optional": true + } } }, - "node_modules/webpack-cli/node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true - }, - "node_modules/webpack-cli/node_modules/yargs": { - "version": "13.2.4", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.4.tgz", - "integrity": "sha512-HG/DWAJa1PAnHT9JAhNa8AbAv3FPaiLzioSjCcmuXXhP8MlpHO5vwls4g4j6n30Z74GVQj8Xa62dWVx1QCGklg==", + "node_modules/webpack-cli/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", "dev": true, - "dependencies": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "os-locale": "^3.1.0", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.0" + "license": "MIT", + "engines": { + "node": ">=14" } }, "node_modules/webpack-dev-middleware": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz", - "integrity": "sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==", + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-7.4.2.tgz", + "integrity": "sha512-xOO8n6eggxnwYpy1NlzUKpvrjfJTvae5/D6WOK0S2LSo7vjmo5gCM1DbLUmFqrMTJP+W/0YZNctm7jasWvLuBA==", + "devOptional": true, + "license": "MIT", "dependencies": { "colorette": "^2.0.10", - "memfs": "^3.4.3", + "memfs": "^4.6.0", "mime-types": "^2.1.31", + "on-finished": "^2.4.1", "range-parser": "^1.2.1", "schema-utils": "^4.0.0" }, "engines": { - "node": ">= 12.13.0" + "node": ">= 18.12.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/webpack" }, "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + } } }, - "node_modules/webpack-dev-middleware/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "node_modules/webpack-dev-middleware/node_modules/memfs": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.14.0.tgz", + "integrity": "sha512-JUeY0F/fQZgIod31Ja1eJgiSxLn7BfQlCnqhwXFBzFHEw63OdLK7VJUJ7bnzNsWgCyoUP5tEp1VRY8rDaYzqOA==", + "devOptional": true, + "license": "Apache-2.0", "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "@jsonjoy.com/json-pack": "^1.0.3", + "@jsonjoy.com/util": "^1.3.0", + "tree-dump": "^1.0.1", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">= 4.0.0" }, "funding": { "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "url": "https://github.com/sponsors/streamich" } }, - "node_modules/webpack-dev-middleware/node_modules/ajv-keywords": { + "node_modules/webpack-dev-server": { "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dependencies": { - "fast-deep-equal": "^3.1.3" + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.1.0.tgz", + "integrity": "sha512-aQpaN81X6tXie1FoOB7xlMfCsN19pSvRAeYUHOdFWOlhpQ/LlbfTqYwwmEDFV0h8GGuqmCmKmT+pxcUV/Nt2gQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@types/bonjour": "^3.5.13", + "@types/connect-history-api-fallback": "^1.5.4", + "@types/express": "^4.17.21", + "@types/serve-index": "^1.9.4", + "@types/serve-static": "^1.15.5", + "@types/sockjs": "^0.3.36", + "@types/ws": "^8.5.10", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.2.1", + "chokidar": "^3.6.0", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^2.0.0", + "express": "^4.19.2", + "graceful-fs": "^4.2.6", + "html-entities": "^2.4.0", + "http-proxy-middleware": "^2.0.3", + "ipaddr.js": "^2.1.0", + "launch-editor": "^2.6.1", + "open": "^10.0.3", + "p-retry": "^6.2.0", + "schema-utils": "^4.2.0", + "selfsigned": "^2.4.1", + "serve-index": "^1.9.1", + "sockjs": "^0.3.24", + "spdy": "^4.0.2", + "webpack-dev-middleware": "^7.4.2", + "ws": "^8.18.0" }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/webpack-dev-middleware/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "node_modules/webpack-dev-middleware/node_modules/schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" }, "engines": { - "node": ">= 12.13.0" + "node": ">= 18.12.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/webpack" - } - }, - "node_modules/webpack-plugin-serve": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/webpack-plugin-serve/-/webpack-plugin-serve-0.8.0.tgz", - "integrity": "sha512-aQxQWqdnl+3v6Uf8krRB+cJ9faOPDWfrbHFmf5/oVFiqFeulL5q38V6SWdLlqlA7Biq6VKvXd7odAjd7XPIgDQ==", - "devOptional": true, - "dependencies": { - "chalk": "^2.4.1", - "connect-history-api-fallback": "^1.5.0", - "http-proxy-middleware": "^0.19.0", - "is-promise": "^2.1.0", - "joi": "^14.3.0", - "koa": "^2.5.3", - "koa-compress": "^3.0.0", - "koa-connect": "^2.0.1", - "koa-route": "^3.2.0", - "koa-static": "^5.0.0", - "loglevelnext": "^3.0.0", - "nanoid": "^2.0.0", - "onetime": "^3.0.0", - "opn": "^5.4.0", - "p-defer": "^1.0.0", - "strip-ansi": "^5.0.0", - "ws": "^6.0.0" - }, - "engines": { - "node": ">= 8.0.0 < 9.0.0 || >= 10.0.0 < 10.14.0 || >= 10.15.0" }, "peerDependencies": { - "webpack": "^4.20.2" - } - }, - "node_modules/webpack-plugin-serve/node_modules/mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", - "devOptional": true, - "engines": { - "node": ">=4" + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + }, + "webpack-cli": { + "optional": true + } } }, - "node_modules/webpack-plugin-serve/node_modules/onetime": { + "node_modules/webpack-dev-server/node_modules/define-lazy-prop": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-3.0.0.tgz", - "integrity": "sha512-t2j1nTo7vb2m/ZQAq5rcWjQgAglF/2rnvlO0cxkZ1GFOSEt0sQBHaytm5tC1ZNUlmKZAp5XF44kolGL9W/XJ2w==", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", "devOptional": true, - "dependencies": { - "mimic-fn": "^1.0.0" - }, + "license": "MIT", "engines": { - "node": ">=6" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/webpack-serve": { + "node_modules/webpack-dev-server/node_modules/is-wsl": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/webpack-serve/-/webpack-serve-3.1.0.tgz", - "integrity": "sha512-pSt5ryMSLNELP2nuzdeBWuiS/ZH9CEcgQ2QTHNUWZAJzWTlq28O5tnVR4p7VdATCO4E6PquswQDntk1glEsMYA==", - "dev": true, + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", + "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", + "devOptional": true, + "license": "MIT", "dependencies": { - "chalk": "^2.4.2", - "decamelize": "^3.0.0", - "import-local": "^2.0.0", - "is-plain-obj": "^1.1.0", - "object-path": "^0.11.4", - "pkg-conf": "^3.0.0", - "rechoir": "^0.7.0", - "v8-compile-cache": "^2.0.2", - "webpack-plugin-serve": "^0.8.0", - "yargs-parser": "^13.0.0" - }, - "bin": { - "webpack-serve": "bin/webpack-serve" + "is-inside-container": "^1.0.0" }, "engines": { - "node": ">= 8.0.0 < 9.0.0 || >= 10.0.0 < 10.14.0 || >= 10.15.0" - }, - "peerDependencies": { - "webpack": "^4.29.0" - } - }, - "node_modules/webpack-sources": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", - "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", - "dependencies": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" - } - }, - "node_modules/webpack/node_modules/acorn": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", - "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", - "bin": { - "acorn": "bin/acorn" + "node": ">=16" }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/webpack/node_modules/cacache": { - "version": "12.0.4", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", - "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==", - "dependencies": { - "bluebird": "^3.5.5", - "chownr": "^1.1.1", - "figgy-pudding": "^3.5.1", - "glob": "^7.1.4", - "graceful-fs": "^4.1.15", - "infer-owner": "^1.0.3", - "lru-cache": "^5.1.1", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.3", - "ssri": "^6.0.1", - "unique-filename": "^1.1.1", - "y18n": "^4.0.0" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/webpack/node_modules/eslint-scope": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", - "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "node_modules/webpack-dev-server/node_modules/open": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.1.0.tgz", + "integrity": "sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==", + "devOptional": true, + "license": "MIT", "dependencies": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^3.1.0" }, "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/webpack/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/webpack/node_modules/find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", - "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" + "node": ">=18" }, - "engines": { - "node": ">=6" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/webpack/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dependencies": { - "locate-path": "^3.0.0" - }, + "node_modules/webpack-dev-server/node_modules/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "devOptional": true, + "license": "MIT", "engines": { - "node": ">=6" - } - }, - "node_modules/webpack/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==", - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/webpack/node_modules/loader-utils": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", - "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" + "node": ">=10.0.0" }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/webpack/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" }, - "engines": { - "node": ">=6" + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } } }, - "node_modules/webpack/node_modules/make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "node_modules/webpack-manifest-plugin": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/webpack-manifest-plugin/-/webpack-manifest-plugin-4.1.1.tgz", + "integrity": "sha512-YXUAwxtfKIJIKkhg03MKuiFAD72PlrqCiwdwO4VEXdRO5V0ORCNwaOwAZawPZalCbmH9kBDmXnNeQOw+BIEiow==", + "license": "MIT", "dependencies": { - "pify": "^4.0.1", - "semver": "^5.6.0" + "tapable": "^2.0.0", + "webpack-sources": "^2.2.0" }, "engines": { - "node": ">=6" - } - }, - "node_modules/webpack/node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/webpack/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dependencies": { - "p-limit": "^2.0.0" + "node": ">=12.22.0" }, - "engines": { - "node": ">=6" + "peerDependencies": { + "webpack": "^4.44.2 || ^5.47.0" } }, - "node_modules/webpack/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "node_modules/webpack-manifest-plugin/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==", + "license": "BSD-3-Clause", "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, - "node_modules/webpack/node_modules/pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "node_modules/webpack-manifest-plugin/node_modules/webpack-sources": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.3.1.tgz", + "integrity": "sha512-y9EI9AO42JjEcrTJFOYmVywVZdKVUfOvDUPsJea5GIr1JOEGFVqwlY2K098fFoIjOkDzHn2AjRvM8dsBZu+gCA==", + "license": "MIT", "dependencies": { - "find-up": "^3.0.0" + "source-list-map": "^2.0.1", + "source-map": "^0.6.1" }, "engines": { - "node": ">=6" + "node": ">=10.13.0" } }, - "node_modules/webpack/node_modules/schema-utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "node_modules/webpack-merge": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz", + "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", + "dev": true, + "license": "MIT", "dependencies": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" + "clone-deep": "^4.0.1", + "flat": "^5.0.2", + "wildcard": "^2.0.0" }, "engines": { - "node": ">= 4" + "node": ">=10.0.0" } }, - "node_modules/webpack/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "bin": { - "semver": "bin/semver" + "node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "license": "MIT", + "engines": { + "node": ">=10.13.0" } }, - "node_modules/webpack/node_modules/ssri": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz", - "integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==", + "node_modules/webpack/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==", + "license": "BSD-2-Clause", "dependencies": { - "figgy-pudding": "^3.5.1" + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/webpack/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" } }, - "node_modules/webpack/node_modules/terser-webpack-plugin": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz", - "integrity": "sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw==", + "node_modules/webpack/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "license": "MIT", "dependencies": { - "cacache": "^12.0.2", - "find-cache-dir": "^2.1.0", - "is-wsl": "^1.1.0", - "schema-utils": "^1.0.0", - "serialize-javascript": "^4.0.0", - "source-map": "^0.6.1", - "terser": "^4.1.2", - "webpack-sources": "^1.4.0", - "worker-farm": "^1.7.0" + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" }, "engines": { - "node": ">= 6.9.0" + "node": ">= 10.13.0" }, - "peerDependencies": { - "webpack": "^4.0.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, - "node_modules/webpack/node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" - }, "node_modules/websocket-driver": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "license": "Apache-2.0", "dependencies": { "http-parser-js": ">=0.5.1", "safe-buffer": ">=5.1.0", @@ -29260,6 +19286,7 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "license": "Apache-2.0", "engines": { "node": ">=0.8.0" } @@ -29268,24 +19295,40 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "license": "MIT", "dependencies": { "iconv-lite": "0.4.24" } }, + "node_modules/whatwg-encoding/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/whatwg-fetch": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz", - "integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==" + "version": "3.6.20", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", + "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==", + "license": "MIT" }, "node_modules/whatwg-mimetype": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", - "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==" + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", + "license": "MIT" }, "node_modules/whatwg-url": { "version": "8.7.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", + "license": "MIT", "dependencies": { "lodash": "^4.7.0", "tr46": "^2.1.0", @@ -29296,20 +19339,25 @@ } }, "node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, "bin": { - "which": "bin/which" + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" } }, "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==", + "license": "MIT", "dependencies": { "is-bigint": "^1.0.1", "is-boolean-object": "^1.1.0", @@ -29321,37 +19369,61 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/which-collection": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", - "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", + "node_modules/which-builtin-type": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.4.tgz", + "integrity": "sha512-bppkmBSsHFmIMSl8BO9TbsyzsvGjVoppt8xUiGzwiu/bhDCGxnpOKCxgqj6GuyHE0mINMDecBFPlOm2hzY084w==", + "license": "MIT", "dependencies": { - "is-map": "^2.0.1", - "is-set": "^2.0.1", - "is-weakmap": "^2.0.1", - "is-weakset": "^2.0.1" + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.0.5", + "is-finalizationregistry": "^1.0.2", + "is-generator-function": "^1.0.10", + "is-regex": "^1.1.4", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.15" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", - "dev": true + "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==", + "license": "MIT", + "dependencies": { + "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/which-typed-array": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", - "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", + "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", + "license": "MIT", "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", "for-each": "^0.3.3", "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.10" + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -29360,43 +19432,46 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } + "node_modules/wildcard": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", + "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", + "dev": true, + "license": "MIT" }, "node_modules/word-wrap": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz", - "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/workbox-background-sync": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-6.5.4.tgz", - "integrity": "sha512-0r4INQZMyPky/lj4Ou98qxcThrETucOde+7mRGJl13MPJugQNKeZQOdIJe/1AchOP23cTqHcN/YVpD6r8E6I8g==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-6.6.0.tgz", + "integrity": "sha512-jkf4ZdgOJxC9u2vztxLuPT/UjlH7m/nWRQ/MgGL0v8BJHoZdVGJd18Kck+a0e55wGXdqyHO+4IQTk0685g4MUw==", + "license": "MIT", "dependencies": { "idb": "^7.0.1", - "workbox-core": "6.5.4" + "workbox-core": "6.6.0" } }, "node_modules/workbox-broadcast-update": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-broadcast-update/-/workbox-broadcast-update-6.5.4.tgz", - "integrity": "sha512-I/lBERoH1u3zyBosnpPEtcAVe5lwykx9Yg1k6f8/BGEPGaMMgZrwVrqL1uA9QZ1NGGFoyE6t9i7lBjOlDhFEEw==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-broadcast-update/-/workbox-broadcast-update-6.6.0.tgz", + "integrity": "sha512-nm+v6QmrIFaB/yokJmQ/93qIJ7n72NICxIwQwe5xsZiV2aI93MGGyEyzOzDPVz5THEr5rC3FJSsO3346cId64Q==", + "license": "MIT", "dependencies": { - "workbox-core": "6.5.4" + "workbox-core": "6.6.0" } }, "node_modules/workbox-build": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-build/-/workbox-build-6.5.4.tgz", - "integrity": "sha512-kgRevLXEYvUW9WS4XoziYqZ8Q9j/2ziJYEtTrjdz5/L/cTUa2XfyMP2i7c3p34lgqJ03+mTiz13SdFef2POwbA==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-build/-/workbox-build-6.6.0.tgz", + "integrity": "sha512-Tjf+gBwOTuGyZwMz2Nk/B13Fuyeo0Q84W++bebbVsfr9iLkDSo6j6PST8tET9HYA58mlRXwlMGpyWO8ETJiXdQ==", + "license": "MIT", "dependencies": { "@apideck/better-ajv-errors": "^0.3.1", "@babel/core": "^7.11.1", @@ -29420,21 +19495,21 @@ "strip-comments": "^2.0.1", "tempy": "^0.6.0", "upath": "^1.2.0", - "workbox-background-sync": "6.5.4", - "workbox-broadcast-update": "6.5.4", - "workbox-cacheable-response": "6.5.4", - "workbox-core": "6.5.4", - "workbox-expiration": "6.5.4", - "workbox-google-analytics": "6.5.4", - "workbox-navigation-preload": "6.5.4", - "workbox-precaching": "6.5.4", - "workbox-range-requests": "6.5.4", - "workbox-recipes": "6.5.4", - "workbox-routing": "6.5.4", - "workbox-strategies": "6.5.4", - "workbox-streams": "6.5.4", - "workbox-sw": "6.5.4", - "workbox-window": "6.5.4" + "workbox-background-sync": "6.6.0", + "workbox-broadcast-update": "6.6.0", + "workbox-cacheable-response": "6.6.0", + "workbox-core": "6.6.0", + "workbox-expiration": "6.6.0", + "workbox-google-analytics": "6.6.0", + "workbox-navigation-preload": "6.6.0", + "workbox-precaching": "6.6.0", + "workbox-range-requests": "6.6.0", + "workbox-recipes": "6.6.0", + "workbox-routing": "6.6.0", + "workbox-strategies": "6.6.0", + "workbox-streams": "6.6.0", + "workbox-sw": "6.6.0", + "workbox-window": "6.6.0" }, "engines": { "node": ">=10.0.0" @@ -29444,6 +19519,7 @@ "version": "0.3.6", "resolved": "https://registry.npmjs.org/@apideck/better-ajv-errors/-/better-ajv-errors-0.3.6.tgz", "integrity": "sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA==", + "license": "MIT", "dependencies": { "json-schema": "^0.4.0", "jsonpointer": "^5.0.0", @@ -29457,14 +19533,15 @@ } }, "node_modules/workbox-build/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.1", + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "require-from-string": "^2.0.2" }, "funding": { "type": "github", @@ -29475,6 +19552,7 @@ "version": "9.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "license": "MIT", "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", @@ -29488,12 +19566,14 @@ "node_modules/workbox-build/node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" }, "node_modules/workbox-build/node_modules/source-map": { "version": "0.8.0-beta.0", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", + "license": "BSD-3-Clause", "dependencies": { "whatwg-url": "^7.0.0" }, @@ -29505,6 +19585,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", + "license": "MIT", "dependencies": { "punycode": "^2.1.0" } @@ -29512,12 +19593,14 @@ "node_modules/workbox-build/node_modules/webidl-conversions": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==" + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", + "license": "BSD-2-Clause" }, "node_modules/workbox-build/node_modules/whatwg-url": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "license": "MIT", "dependencies": { "lodash.sortby": "^4.7.0", "tr46": "^1.0.1", @@ -29525,117 +19608,132 @@ } }, "node_modules/workbox-cacheable-response": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-6.5.4.tgz", - "integrity": "sha512-DCR9uD0Fqj8oB2TSWQEm1hbFs/85hXXoayVwFKLVuIuxwJaihBsLsp4y7J9bvZbqtPJ1KlCkmYVGQKrBU4KAug==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-6.6.0.tgz", + "integrity": "sha512-JfhJUSQDwsF1Xv3EV1vWzSsCOZn4mQ38bWEBR3LdvOxSPgB65gAM6cS2CX8rkkKHRgiLrN7Wxoyu+TuH67kHrw==", + "deprecated": "workbox-background-sync@6.6.0", + "license": "MIT", "dependencies": { - "workbox-core": "6.5.4" + "workbox-core": "6.6.0" } }, "node_modules/workbox-core": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-6.5.4.tgz", - "integrity": "sha512-OXYb+m9wZm8GrORlV2vBbE5EC1FKu71GGp0H4rjmxmF4/HLbMCoTFws87M3dFwgpmg0v00K++PImpNQ6J5NQ6Q==" + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-6.6.0.tgz", + "integrity": "sha512-GDtFRF7Yg3DD859PMbPAYPeJyg5gJYXuBQAC+wyrWuuXgpfoOrIQIvFRZnQ7+czTIQjIr1DhLEGFzZanAT/3bQ==", + "license": "MIT" }, "node_modules/workbox-expiration": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-expiration/-/workbox-expiration-6.5.4.tgz", - "integrity": "sha512-jUP5qPOpH1nXtjGGh1fRBa1wJL2QlIb5mGpct3NzepjGG2uFFBn4iiEBiI9GUmfAFR2ApuRhDydjcRmYXddiEQ==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-expiration/-/workbox-expiration-6.6.0.tgz", + "integrity": "sha512-baplYXcDHbe8vAo7GYvyAmlS4f6998Jff513L4XvlzAOxcl8F620O91guoJ5EOf5qeXG4cGdNZHkkVAPouFCpw==", + "license": "MIT", "dependencies": { "idb": "^7.0.1", - "workbox-core": "6.5.4" + "workbox-core": "6.6.0" } }, "node_modules/workbox-google-analytics": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-google-analytics/-/workbox-google-analytics-6.5.4.tgz", - "integrity": "sha512-8AU1WuaXsD49249Wq0B2zn4a/vvFfHkpcFfqAFHNHwln3jK9QUYmzdkKXGIZl9wyKNP+RRX30vcgcyWMcZ9VAg==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-google-analytics/-/workbox-google-analytics-6.6.0.tgz", + "integrity": "sha512-p4DJa6OldXWd6M9zRl0H6vB9lkrmqYFkRQ2xEiNdBFp9U0LhsGO7hsBscVEyH9H2/3eZZt8c97NB2FD9U2NJ+Q==", + "deprecated": "It is not compatible with newer versions of GA starting with v4, as long as you are using GAv3 it should be ok, but the package is not longer being maintained", + "license": "MIT", "dependencies": { - "workbox-background-sync": "6.5.4", - "workbox-core": "6.5.4", - "workbox-routing": "6.5.4", - "workbox-strategies": "6.5.4" + "workbox-background-sync": "6.6.0", + "workbox-core": "6.6.0", + "workbox-routing": "6.6.0", + "workbox-strategies": "6.6.0" } }, "node_modules/workbox-navigation-preload": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-navigation-preload/-/workbox-navigation-preload-6.5.4.tgz", - "integrity": "sha512-IIwf80eO3cr8h6XSQJF+Hxj26rg2RPFVUmJLUlM0+A2GzB4HFbQyKkrgD5y2d84g2IbJzP4B4j5dPBRzamHrng==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-navigation-preload/-/workbox-navigation-preload-6.6.0.tgz", + "integrity": "sha512-utNEWG+uOfXdaZmvhshrh7KzhDu/1iMHyQOV6Aqup8Mm78D286ugu5k9MFD9SzBT5TcwgwSORVvInaXWbvKz9Q==", + "license": "MIT", "dependencies": { - "workbox-core": "6.5.4" + "workbox-core": "6.6.0" } }, "node_modules/workbox-precaching": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-precaching/-/workbox-precaching-6.5.4.tgz", - "integrity": "sha512-hSMezMsW6btKnxHB4bFy2Qfwey/8SYdGWvVIKFaUm8vJ4E53JAY+U2JwLTRD8wbLWoP6OVUdFlXsTdKu9yoLTg==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-precaching/-/workbox-precaching-6.6.0.tgz", + "integrity": "sha512-eYu/7MqtRZN1IDttl/UQcSZFkHP7dnvr/X3Vn6Iw6OsPMruQHiVjjomDFCNtd8k2RdjLs0xiz9nq+t3YVBcWPw==", + "license": "MIT", "dependencies": { - "workbox-core": "6.5.4", - "workbox-routing": "6.5.4", - "workbox-strategies": "6.5.4" + "workbox-core": "6.6.0", + "workbox-routing": "6.6.0", + "workbox-strategies": "6.6.0" } }, "node_modules/workbox-range-requests": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-range-requests/-/workbox-range-requests-6.5.4.tgz", - "integrity": "sha512-Je2qR1NXCFC8xVJ/Lux6saH6IrQGhMpDrPXWZWWS8n/RD+WZfKa6dSZwU+/QksfEadJEr/NfY+aP/CXFFK5JFg==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-range-requests/-/workbox-range-requests-6.6.0.tgz", + "integrity": "sha512-V3aICz5fLGq5DpSYEU8LxeXvsT//mRWzKrfBOIxzIdQnV/Wj7R+LyJVTczi4CQ4NwKhAaBVaSujI1cEjXW+hTw==", + "license": "MIT", "dependencies": { - "workbox-core": "6.5.4" + "workbox-core": "6.6.0" } }, "node_modules/workbox-recipes": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-recipes/-/workbox-recipes-6.5.4.tgz", - "integrity": "sha512-QZNO8Ez708NNwzLNEXTG4QYSKQ1ochzEtRLGaq+mr2PyoEIC1xFW7MrWxrONUxBFOByksds9Z4//lKAX8tHyUA==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-recipes/-/workbox-recipes-6.6.0.tgz", + "integrity": "sha512-TFi3kTgYw73t5tg73yPVqQC8QQjxJSeqjXRO4ouE/CeypmP2O/xqmB/ZFBBQazLTPxILUQ0b8aeh0IuxVn9a6A==", + "license": "MIT", "dependencies": { - "workbox-cacheable-response": "6.5.4", - "workbox-core": "6.5.4", - "workbox-expiration": "6.5.4", - "workbox-precaching": "6.5.4", - "workbox-routing": "6.5.4", - "workbox-strategies": "6.5.4" + "workbox-cacheable-response": "6.6.0", + "workbox-core": "6.6.0", + "workbox-expiration": "6.6.0", + "workbox-precaching": "6.6.0", + "workbox-routing": "6.6.0", + "workbox-strategies": "6.6.0" } }, "node_modules/workbox-routing": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-6.5.4.tgz", - "integrity": "sha512-apQswLsbrrOsBUWtr9Lf80F+P1sHnQdYodRo32SjiByYi36IDyL2r7BH1lJtFX8fwNHDa1QOVY74WKLLS6o5Pg==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-6.6.0.tgz", + "integrity": "sha512-x8gdN7VDBiLC03izAZRfU+WKUXJnbqt6PG9Uh0XuPRzJPpZGLKce/FkOX95dWHRpOHWLEq8RXzjW0O+POSkKvw==", + "license": "MIT", "dependencies": { - "workbox-core": "6.5.4" + "workbox-core": "6.6.0" } }, "node_modules/workbox-strategies": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-6.5.4.tgz", - "integrity": "sha512-DEtsxhx0LIYWkJBTQolRxG4EI0setTJkqR4m7r4YpBdxtWJH1Mbg01Cj8ZjNOO8etqfA3IZaOPHUxCs8cBsKLw==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-6.6.0.tgz", + "integrity": "sha512-eC07XGuINAKUWDnZeIPdRdVja4JQtTuc35TZ8SwMb1ztjp7Ddq2CJ4yqLvWzFWGlYI7CG/YGqaETntTxBGdKgQ==", + "license": "MIT", "dependencies": { - "workbox-core": "6.5.4" + "workbox-core": "6.6.0" } }, "node_modules/workbox-streams": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-streams/-/workbox-streams-6.5.4.tgz", - "integrity": "sha512-FXKVh87d2RFXkliAIheBojBELIPnWbQdyDvsH3t74Cwhg0fDheL1T8BqSM86hZvC0ZESLsznSYWw+Va+KVbUzg==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-streams/-/workbox-streams-6.6.0.tgz", + "integrity": "sha512-rfMJLVvwuED09CnH1RnIep7L9+mj4ufkTyDPVaXPKlhi9+0czCu+SJggWCIFbPpJaAZmp2iyVGLqS3RUmY3fxg==", + "license": "MIT", "dependencies": { - "workbox-core": "6.5.4", - "workbox-routing": "6.5.4" + "workbox-core": "6.6.0", + "workbox-routing": "6.6.0" } }, "node_modules/workbox-sw": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-sw/-/workbox-sw-6.5.4.tgz", - "integrity": "sha512-vo2RQo7DILVRoH5LjGqw3nphavEjK4Qk+FenXeUsknKn14eCNedHOXWbmnvP4ipKhlE35pvJ4yl4YYf6YsJArA==" + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-sw/-/workbox-sw-6.6.0.tgz", + "integrity": "sha512-R2IkwDokbtHUE4Kus8pKO5+VkPHD2oqTgl+XJwh4zbF1HyjAbgNmK/FneZHVU7p03XUt9ICfuGDYISWG9qV/CQ==", + "license": "MIT" }, "node_modules/workbox-webpack-plugin": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-webpack-plugin/-/workbox-webpack-plugin-6.5.4.tgz", - "integrity": "sha512-LmWm/zoaahe0EGmMTrSLUi+BjyR3cdGEfU3fS6PN1zKFYbqAKuQ+Oy/27e4VSXsyIwAw8+QDfk1XHNGtZu9nQg==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-webpack-plugin/-/workbox-webpack-plugin-6.6.0.tgz", + "integrity": "sha512-xNZIZHalboZU66Wa7x1YkjIqEy1gTR+zPM+kjrYJzqN7iurYZBctBLISyScjhkJKYuRrZUP0iqViZTh8rS0+3A==", + "license": "MIT", "dependencies": { "fast-json-stable-stringify": "^2.1.0", "pretty-bytes": "^5.4.1", "upath": "^1.2.0", "webpack-sources": "^1.4.3", - "workbox-build": "6.5.4" + "workbox-build": "6.6.0" }, "engines": { "node": ">=10.0.0" @@ -29644,27 +19742,40 @@ "webpack": "^4.4.0 || ^5.9.0" } }, - "node_modules/workbox-window": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-window/-/workbox-window-6.5.4.tgz", - "integrity": "sha512-HnLZJDwYBE+hpG25AQBO8RUWBJRaCsI9ksQJEp3aCOFCaG5kqaToAYXFRAHxzRluM2cQbGzdQF5rjKPWPA1fug==", + "node_modules/workbox-webpack-plugin/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==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/workbox-webpack-plugin/node_modules/webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "license": "MIT", "dependencies": { - "@types/trusted-types": "^2.0.2", - "workbox-core": "6.5.4" + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" } }, - "node_modules/worker-farm": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", - "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", + "node_modules/workbox-window": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-window/-/workbox-window-6.6.0.tgz", + "integrity": "sha512-L4N9+vka17d16geaJXXRjENLFldvkWy7JyGxElRD0JvBxvFEd8LOhr+uXCcar/NzAmIBRv9EZ+M+Qr4mOoBITw==", + "license": "MIT", "dependencies": { - "errno": "~0.1.7" + "@types/trusted-types": "^2.0.2", + "workbox-core": "6.6.0" } }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -29677,75 +19788,35 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/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/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==", + "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/wrap-ansi/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==" - }, - "node_modules/wrap-ansi/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==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" + "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", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - }, - "node_modules/write": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", - "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", - "dependencies": { - "mkdirp": "^0.5.1" - }, - "engines": { - "node": ">=4" - } + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" }, "node_modules/write-file-atomic": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "license": "ISC", "dependencies": { "imurmurhash": "^0.1.4", "is-typedarray": "^1.0.0", @@ -29753,57 +19824,44 @@ "typedarray-to-buffer": "^3.1.5" } }, - "node_modules/write/node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, "node_modules/ws": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", - "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", - "devOptional": true, - "dependencies": { - "async-limiter": "~1.0.0" + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "license": "MIT", + "engines": { + "node": ">=8.3.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": "3.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", - "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==" + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", + "license": "Apache-2.0" }, "node_modules/xmlchars": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" - }, - "node_modules/xregexp": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.4.1.tgz", - "integrity": "sha512-2u9HwfadaJaY9zHtRRnH6BY6CQVNQKkYm3oLtC9gJXXzfsbACg5X5e4EZZGVAH+YIfa+QA9lsFQTTe3HURF3ag==", - "dev": true, - "dependencies": { - "@babel/runtime-corejs3": "^7.12.1" - } - }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "engines": { - "node": ">=0.4" - } + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "license": "MIT" }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", "engines": { "node": ">=10" } @@ -29811,12 +19869,14 @@ "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "license": "ISC" }, "node_modules/yaml": { "version": "1.10.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "license": "ISC", "engines": { "node": ">= 6" } @@ -29825,6 +19885,7 @@ "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "license": "MIT", "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", @@ -29839,45 +19900,19 @@ } }, "node_modules/yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "dev": true, - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - }, - "node_modules/yargs-parser/node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/yargs/node_modules/yargs-parser": { "version": "20.2.9", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "license": "ISC", "engines": { "node": ">=10" } }, - "node_modules/ylru": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/ylru/-/ylru-1.3.2.tgz", - "integrity": "sha512-RXRJzMiK6U2ye0BlGGZnmpwJDPgakn6aNQ0A7gHRbD4I0uvK4TW6UqkK1V0pp9jskjJBAXd3dRrbzWkqJ+6cxA==", - "devOptional": true, - "engines": { - "node": ">= 4.0.0" - } - }, "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==", + "license": "MIT", "engines": { "node": ">=10" }, diff --git a/modules/lo_dash_react_components/package.json b/modules/lo_dash_react_components/package.json index 09a9a0c0f..33ce92deb 100644 --- a/modules/lo_dash_react_components/package.json +++ b/modules/lo_dash_react_components/package.json @@ -2,83 +2,61 @@ "name": "lo_dash_react_components", "version": "0.0.1", "description": "React and Dash components for Learning Observer dashboards", - "repository": { - "type": "git", - "url": "git://github.com/ETS-Next-Gen/lo-dash-react-components.git" - }, - "bugs": { - "url": "https://github.com/ETS-Next-Gen/lo-dash-react-components/issues" - }, - "homepage": "https://github.com/ETS-Next-Gen/lo-dash-react-components", "main": "build/index.js", "scripts": { - "webpack-start": "webpack-serve --config ./webpack.serve.config.js --open", - "describe-webpack-start": "echo Run react app (without dash) with WebPack, which is Dash's default.", + "webpack-start": "webpack serve --config ./webpack.serve.config.js --open", "react-start": "react-scripts start", - "describe-react-start": "echo Run react app (without dash) with React Scripts, which probably gives the most rapid workflow.", "dash-start": "nodemon", - "describe-dash-start": "echo Run full dash app with nodemon. This requires a rebuild each time files change.", - "validate-init": "python _validate_init.py", - "prepublishOnly": "npm run validate-init", "build:js": "webpack --mode production", "build:backends": "dash-generate-components ./src/lib/components lo_dash_react_components -p package-info.json --r-prefix '' --jl-prefix '' --ignore \\.test\\.", - "build:backends-activated": "(. venv/bin/activate || venv\\scripts\\activate && npm run build:py_and_r)", "build": "npm run build-css && npm run build:js && npm run build:backends", - "build:activated": "npm run build:js && npm run build:backends-activated", - "build-css": "node-sass --recursive src/lib -o lo_dash_react_components/css", - "watch-css": "node-sass --recursive src/lib -o lo_dash_react_components/css --watch", + "build-css": "sass --load-path=node_modules src/lib:lo_dash_react_components/css && postcss lo_dash_react_components/css/*.css --replace", + "watch-css": "sass --load-path=node_modules src/lib:lo_dash_react_components/css --watch", "start-all": "npm-run-all --parallel watch-css react-start webpack-start dash-start", - "clean-build": "rm -rf dist/ && rm -rf build/", - "build:python": "npm run clean-build && python setup.py sdist bdist_wheel" + "clean-build:python": "rm -rf dist/ && rm -rf build/", + "build:python": "npm run clean-build:python && python setup.py sdist bdist_wheel" }, "author": "Piotr Mitros ", "license": "AGPL-3.0", + "engines": { + "node": ">=22.0.0" + }, "dependencies": { - "@testing-library/jest-dom": "^5.16.5", - "@testing-library/react": "^13.4.0", - "@testing-library/user-event": "^13.5.0", - "bootstrap": "^5.2.3", - "node-sass": "^9.0.0", - "nodemon": "^2.0.21", - "ramda": "^0.26.1", - "react": "^18.2.0", - "react-bootstrap": "^2.7.2", - "react-dom": "^18.2.0", - "react-router": "^6.8.2", - "react-router-dom": "^6.8.2", + "bootstrap": "^5.3.3", + "ramda": "^0.30.1", + "react": "^18.3.1", + "react-bootstrap": "^2.10.5", + "react-dom": "^18.3.1", + "react-router-dom": "^6.28.0", "react-scripts": "^5.0.1", - "react-tooltip": "^5.20.0", - "recharts": "^2.4.3", - "web-vitals": "^2.1.4" + "react-tooltip": "^5.28.0", + "recharts": "^2.13.3", + "web-vitals": "^4.2.4" }, "devDependencies": { - "@babel/core": "^7.5.4", - "@babel/plugin-proposal-object-rest-spread": "^7.5.4", - "@babel/preset-env": "^7.5.4", - "@babel/preset-react": "^7.0.0", - "@plotly/dash-component-plugins": "^1.2.0", - "@plotly/webpack-dash-dynamic-import": "^1.2.0", - "babel-eslint": "^10.0.2", - "babel-loader": "^8.0.6", - "copyfiles": "^2.1.1", - "css-loader": "^3.0.0", - "eslint": "^6.0.1", - "eslint-config-prettier": "^6.0.0", - "eslint-plugin-import": "^2.18.0", - "eslint-plugin-react": "^7.14.2", + "@babel/core": "^7.26.0", + "@babel/preset-env": "^7.26.0", + "@babel/preset-react": "^7.25.9", + "autoprefixer": "^10.4.20", + "babel-loader": "^9.2.1", + "css-loader": "^7.1.2", + "eslint-config-prettier": "^9.1.0", + "eslint-config-standard": "^17.1.0", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-promise": "^6.0.0", + "eslint-plugin-standard": "^4.1.0", + "nodemon": "^3.1.7", "npm-run-all": "^4.1.5", - "prop-types": "^15.7.2", - "react-docgen": "^4.1.1", - "style-loader": "^0.23.1", - "styled-jsx": "^5.1.2", - "terser-webpack-plugin": "^2.3.0", - "webpack": "4.37.0", - "webpack-cli": "3.3.6", - "webpack-serve": "3.1.0" - }, - "engines": { - "npm": ">=8.11.0", - "node": ">=16.0.0 <17.0.0" + "postcss": "^8.4.47", + "postcss-loader": "^8.1.1", + "prettier": "^3.3.3", + "sass": "^1.80.6", + "style-loader": "^4.0.0", + "tailwindcss": "^3.4.14", + "webpack": "^5.96.1", + "webpack-cli": "^5.1.4", + "webpack-dev-server": "^5.1.0" }, "browserslist": { "production": [ @@ -87,8 +65,8 @@ "not op_mini all" ], "development": [ - "last 1 firefox version", "last 1 chrome version", + "last 1 firefox version", "last 1 safari version" ] } diff --git a/modules/lo_dash_react_components/src/lib/components/DAProblemDisplay.react.js b/modules/lo_dash_react_components/src/lib/components/DAProblemDisplay.react.js index 3e18b9f44..871fe60b8 100644 --- a/modules/lo_dash_react_components/src/lib/components/DAProblemDisplay.react.js +++ b/modules/lo_dash_react_components/src/lib/components/DAProblemDisplay.react.js @@ -418,7 +418,7 @@ class DAProblemDisplay extends React.Component { return (
- + {/**/} diff --git a/modules/lo_dash_react_components/src/lib/components/WOAnnotatedText.react.js b/modules/lo_dash_react_components/src/lib/components/WOAnnotatedText.react.js index 27593c548..a50e35e20 100644 --- a/modules/lo_dash_react_components/src/lib/components/WOAnnotatedText.react.js +++ b/modules/lo_dash_react_components/src/lib/components/WOAnnotatedText.react.js @@ -9,7 +9,7 @@ import 'react-tooltip/dist/react-tooltip.css'; * WOAnnotatedText */ export default class WOAnnotatedText extends Component { - constructor(props) { + constructor (props) { super(props); this.state = { selectedItem: null @@ -22,14 +22,14 @@ export default class WOAnnotatedText extends Component { return split.map((line, index) => ( {line} - {split.length-1 === index ? :
} + {split.length - 1 === index ? :
}
)); } return str; - } + }; - render() { + render () { const { breakpoints, text, className } = this.props; const breaks = new Set(); @@ -53,13 +53,13 @@ export default class WOAnnotatedText extends Component { matchingBreaks.forEach(b => { ids[b] = ids[b].concat({ tooltip: obj.tooltip, style: obj.style }); }); - }) + }); const chunks = Array(breaksList.length - 1); let curr, textChunk; for (let i = 0; i < chunks.length; i++) { curr = ids[breaksList[i]]; - textChunk = text.substring(breaksList[i], breaksList[i+1]); + textChunk = text.substring(breaksList[i], breaksList[i + 1]); if (curr.length === 0) { chunks[i] = { text: textChunk, @@ -79,7 +79,7 @@ export default class WOAnnotatedText extends Component { if (chunks.length === 0) { return
{this.replaceNewLines(text)} -
+
; } // TODO figure out how empty breakpoints @@ -93,8 +93,7 @@ export default class WOAnnotatedText extends Component {
{chunks.map((chunk, index) => ( chunk.annotated - ? - HTML DOM Events
- - - - - - - - - Writing Analysis - - - -
+ + + + + + + + + + + + Writing Analysis + + + + +
- +
-
- - +
+ + + \ No newline at end of file diff --git a/learning_observer/learning_observer/static/webapp.js b/learning_observer/learning_observer/static/webapp.js index d1626e706..e69082753 100644 --- a/learning_observer/learning_observer/static/webapp.js +++ b/learning_observer/learning_observer/static/webapp.js @@ -31,6 +31,18 @@ function ajax(config) } } +function initializeBurgerMenu() { + document.querySelectorAll('.navbar-burger').forEach(burger => { + burger.addEventListener('click', () => { + const target = burger.dataset.target; + const targetMenu = document.getElementById(target); + burger.classList.toggle('is-active'); + targetMenu.classList.toggle('is-active'); + }); + }); +} + + requirejs( // TODO: Clean up absolute paths. We hardcoded these for now, due to refactor. @@ -52,11 +64,12 @@ requirejs( function(config, tool_list, d3, mustache, showdown, fontawesome, unauth, login, courses, course, tool, navbar_li, info, auth_info) { // Parse client configuration. config = JSON.parse(config); - console.log(tool_list); + // console.log(tool_list); tool_list = JSON.parse(tool_list); - console.log(tool_list); - console.log(auth_info); - console.log(JSON.stringify(auth_info)); + // console.log(tool_list); + // console.log(auth_info); + // console.log(JSON.stringify(auth_info)); + // Add libraries config.d3 = d3; config.ajax = ajax(config); @@ -152,7 +165,7 @@ requirejs( // * course_json are the course properties // Merged, we can render tools for courses! joined = Object.assign({}, course_json, tool_list[i]); - console.log(joined); + // console.log(joined); tools += mustache.render(tool, joined); } course_json['tools'] = tools; @@ -225,10 +238,16 @@ requirejs( } function loggedin_navbar_menu() { - d3.select(".main-navbar-menu").html(mustache.render(navbar_li, { - 'user_name': user_info()['name'], - 'user_picture': user_info()['picture'] - })); + const navbarMenu = d3.select("#mainNavbar"); + const navbarBurger = d3.select(".navbar-burger"); + + navbarMenu.html(mustache.render(navbar_li, { + 'user_name': user_info()['name'], + 'user_picture': user_info()['picture'] + })); + navbarMenu.classed("is-hidden", false); + navbarBurger.classed("is-hidden", false); + initializeBurgerMenu(); } function setup_page() { From 760c5e1f9600a40bfccd1fcb997b548f2074b8d6 Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Tue, 1 Apr 2025 11:20:18 -0400 Subject: [PATCH 33/88] =?UTF-8?q?added=20new=20options=20to=20llm=20dashbo?= =?UTF-8?q?ard=20-=20students=20per=20row,=20height,=20indivi=E2=80=A6=20(?= =?UTF-8?q?#216)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Improvements to both dashboards: - set students per row, student tile height, and hide headers - document selector component for fetching docs from latest, by assignment, or by timestamp - linted js code AI Assistant dashboard specific improvements: - ability to add custom placeholders - ability to upload files (pdf, md, txt, docx) for the placeholder value Communication protocol - updated to return `students.` instead of `` --- .gitignore | 4 +- VERSION | 2 +- learning_observer/VERSION | 2 +- learning_observer/learning_observer/cache.py | 6 +- .../communication_protocol/executor.py | 5 +- .../communication_protocol/util.py | 1 - .../learning_observer/dashboard.py | 10 +- learning_observer/learning_observer/google.py | 13 + .../learning_observer/rosters.py | 4 +- learning_observer/learning_observer/routes.py | 2 + .../LODocumentSourceSelectorAIO.py | 167 +++++++ .../lo_dash_react_components/__init__.py | 1 + modules/lo_dash_react_components/package.json | 2 +- .../src/lib/components/LOPanelLayout.react.js | 5 +- modules/wo_bulk_essay_analysis/VERSION | 1 + modules/wo_bulk_essay_analysis/pyproject.toml | 3 + modules/wo_bulk_essay_analysis/setup.cfg | 5 +- modules/wo_bulk_essay_analysis/setup.py | 13 - .../wo_bulk_essay_analysis/assets/scripts.js | 429 +++++++++++++----- .../wo_bulk_essay_analysis/assets/styles.css | 15 + .../dashboard/layout.py | 313 +++++++++---- .../wo_bulk_essay_analysis/gpt.py | 1 + .../wo_bulk_essay_analysis/module.py | 39 +- modules/wo_classroom_text_highlighter/VERSION | 2 +- .../assets/scripts.js | 140 +++--- .../dash_dashboard.py | 25 +- modules/writing_observer/VERSION | 2 +- .../writing_observer/aggregator.py | 24 +- .../writing_observer/document_timestamps.py | 11 +- .../writing_observer/module.py | 52 ++- 30 files changed, 943 insertions(+), 356 deletions(-) create mode 100644 modules/lo_dash_react_components/lo_dash_react_components/LODocumentSourceSelectorAIO.py create mode 100644 modules/wo_bulk_essay_analysis/VERSION create mode 100644 modules/wo_bulk_essay_analysis/pyproject.toml delete mode 100644 modules/wo_bulk_essay_analysis/setup.py create mode 100644 modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/assets/styles.css diff --git a/.gitignore b/.gitignore index d951ebd57..65002d617 100644 --- a/.gitignore +++ b/.gitignore @@ -37,4 +37,6 @@ LanguageTool-5.4 package-lock.json learning_observer/learning_observer/static_data/google/ learning_observer/learning_observer/static_data/admins.yaml -.ipynb_checkpoints/ \ No newline at end of file +.ipynb_checkpoints/ +.eggs/ +.next/ \ No newline at end of file diff --git a/VERSION b/VERSION index 3b7eea53e..2e2f7e4a0 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0+2025.04.01T14.51.04.751Z.f8ffbdbc.berickson.course.list.homepage.improvements +0.1.0+2025.04.01T15.05.13.407Z.44725993.berickson.022025.gpt.dashboard.updates diff --git a/learning_observer/VERSION b/learning_observer/VERSION index 3b7eea53e..0abb94587 100644 --- a/learning_observer/VERSION +++ b/learning_observer/VERSION @@ -1 +1 @@ -0.1.0+2025.04.01T14.51.04.751Z.f8ffbdbc.berickson.course.list.homepage.improvements +0.1.0+2025.03.27T20.51.04.053Z.57041e9f.berickson.022025.gpt.dashboard.updates diff --git a/learning_observer/learning_observer/cache.py b/learning_observer/learning_observer/cache.py index 180bb7aaf..ab11259cd 100644 --- a/learning_observer/learning_observer/cache.py +++ b/learning_observer/learning_observer/cache.py @@ -8,8 +8,8 @@ cache_backend = None -def create_key_from_args(*args, **kwargs): - key_dict = {'args': args, 'kwargs': kwargs} +def create_key_from_args(func, *args, **kwargs): + key_dict = {'func': str(func), 'args': args, 'kwargs': kwargs} key_str = json.dumps(key_dict, sort_keys=True) return key_str @@ -37,7 +37,7 @@ async def wrapper(*args, **kwargs): return await func(*args, **kwargs) # process item if the cache is present - key = create_key_from_args(args, kwargs) + key = create_key_from_args(func, args, kwargs) if key in await cache_backend.keys(): return await cache_backend[key] result = await func(*args, **kwargs) diff --git a/learning_observer/learning_observer/communication_protocol/executor.py b/learning_observer/learning_observer/communication_protocol/executor.py index 3b0fd0ea9..c161ecf73 100644 --- a/learning_observer/learning_observer/communication_protocol/executor.py +++ b/learning_observer/learning_observer/communication_protocol/executor.py @@ -93,6 +93,9 @@ async def call_dispatch(functions, function_name, args, kwargs): ... learning_observer.communication_protocol.exception.DAGExecutionException: ('Function double did not execute properly during call.', 'call_dispatch', {'function_name': 'double', 'args': [None], 'kwargs': {}, 'error': 'Input cannot be None'}, ...) """ + # TODO add in provenance to the call + # this probably requires switching to an async generator instead of regular return + provenance = {'function_name': function_name, 'args': args, 'kwargs': kwargs} try: function = functions[function_name] result = function(*args, **kwargs) @@ -528,6 +531,7 @@ async def hack_handle_keys(function, STUDENTS=None, STUDENTS_path=None, RESOURCE We create a list of fields needed for the `make_key()` function as well as the provenance associated with each. These are zipped together and returned to the user. """ + # TODO do something if `func` is not found func = next((item for item in learning_observer.module_loader.reducers() if item['id'] == function), None) fields_and_provenances = None if STUDENTS is not None and RESOURCES is None: @@ -703,7 +707,6 @@ async def visit(node_name): # We've already done this one. if node_name in visited: return nodes[node_name] - # Execute all the child nodes await walk_dict(nodes[node_name]) diff --git a/learning_observer/learning_observer/communication_protocol/util.py b/learning_observer/learning_observer/communication_protocol/util.py index fc3f8f8c4..b48eb9c71 100644 --- a/learning_observer/learning_observer/communication_protocol/util.py +++ b/learning_observer/learning_observer/communication_protocol/util.py @@ -2,7 +2,6 @@ This file provides utility functions specific to the communication protocol. ''' -import inspect import learning_observer.communication_protocol.query as q import learning_observer.communication_protocol.exception diff --git a/learning_observer/learning_observer/dashboard.py b/learning_observer/learning_observer/dashboard.py index cc11a3862..8c83b7746 100644 --- a/learning_observer/learning_observer/dashboard.py +++ b/learning_observer/learning_observer/dashboard.py @@ -560,10 +560,15 @@ def _find_student_or_resource(d): provenance = d['provenance'] output = [] if 'STUDENT' in provenance: + output.append('students') output.append(provenance['STUDENT']['user_id']) if 'RESOURCE' in provenance: - output.append('documents') - output.append(provenance['RESOURCE']['doc_id']) + if 'doc_id' in provenance['RESOURCE']: + output.append('documents') + output.append(provenance['RESOURCE']['doc_id']) + if 'assignment_id' in provenance['RESOURCE']: + output.append('assignments') + output.append(provenance['RESOURCE']['assignment_id']) if output: return output return _find_student_or_resource(provenance) @@ -629,6 +634,7 @@ async def _execute_dag(dag_query, target, params): await _drive_generator(generator, dag_query['kwargs']) # Handle rescheduling the execution of the DAG for fresh data + # TODO add some way to specific specific endpoint delays dag_delay = dag_query['kwargs'].get('rerun_dag_delay', 10) if dag_delay < 0: # if dag_delay is negative, we skip repeated execution diff --git a/learning_observer/learning_observer/google.py b/learning_observer/learning_observer/google.py index 60cf3ebd6..f1e45f899 100644 --- a/learning_observer/learning_observer/google.py +++ b/learning_observer/learning_observer/google.py @@ -381,6 +381,19 @@ def clean_course_list(google_json): return courses +@register_cleaner('course_work', 'assignments') +def clean_course_work(google_json): + ''' + Google's course work is one object deeper than we'd like, and update_time + sort order is nicer. This will clean it up a bit + ''' + assignments = google_json.get('courseWork', []) + assignments.sort( + key=lambda x: x.get('update_time', 0) + ) + return assignments + + # Google Docs def _force_text_length(text, length): ''' diff --git a/learning_observer/learning_observer/rosters.py b/learning_observer/learning_observer/rosters.py index 4329eb5bf..6e66baf3b 100644 --- a/learning_observer/learning_observer/rosters.py +++ b/learning_observer/learning_observer/rosters.py @@ -446,9 +446,9 @@ async def memoize_courseroster_runtime(runtime, course_id): individual nodes are handled: static, dynamic (current), or memoized. ''' @learning_observer.cache.async_memoization() - async def memoization_layer(c): + async def course_roster_memoization_layer(c): return await courseroster_runtime(runtime, c) - return await memoization_layer(course_id) + return await course_roster_memoization_layer(course_id) async def courseroster(request, course_id): diff --git a/learning_observer/learning_observer/routes.py b/learning_observer/learning_observer/routes.py index 5034b81bb..b1fa0adc4 100644 --- a/learning_observer/learning_observer/routes.py +++ b/learning_observer/learning_observer/routes.py @@ -240,6 +240,8 @@ def register_auth_webapp_views(app): debug_log("Running with Google authentication") app.add_routes([ aiohttp.web.get( + # TODO only allow the available sign-in options found in pmss + # '/auth/login/{provider:google|canvas|schoology}', '/auth/login/{provider:google}', handler=learning_observer.auth.social_handler), ]) diff --git a/modules/lo_dash_react_components/lo_dash_react_components/LODocumentSourceSelectorAIO.py b/modules/lo_dash_react_components/lo_dash_react_components/LODocumentSourceSelectorAIO.py new file mode 100644 index 000000000..8025ba28f --- /dev/null +++ b/modules/lo_dash_react_components/lo_dash_react_components/LODocumentSourceSelectorAIO.py @@ -0,0 +1,167 @@ +''' +This file creates an All-In-One component for the Learning +Observer server connection. This handles updating data from the +server (based on individual tree updates), storing any errors +that occured, and showing the time since it was last updated. +''' +from dash import html, dcc, clientside_callback, Output, Input, State, MATCH +import dash_bootstrap_components as dbc +import datetime +import uuid + +class LODocumentSourceSelectorAIO(dbc.Card): + class ids: + source_selector = lambda aio_id: { + 'component': 'LODocumentSourceSelectorAIO', + 'subcomponent': 'source_selector', + 'aio_id': aio_id + } + assignment_wrapper = lambda aio_id: { + 'component': 'LODocumentSourceSelectorAIO', + 'subcomponent': 'assignment_wrapper', + 'aio_id': aio_id + } + assignment_input = lambda aio_id: { + 'component': 'LODocumentSourceSelectorAIO', + 'subcomponent': 'assignment_input', + 'aio_id': aio_id + } + datetime_wrapper = lambda aio_id: { + 'component': 'LODocumentSourceSelectorAIO', + 'subcomponent': 'datetime_wrapper', + 'aio_id': aio_id + } + date_input = lambda aio_id: { + 'component': 'LODocumentSourceSelectorAIO', + 'subcomponent': 'date_input', + 'aio_id': aio_id + } + timestamp_input = lambda aio_id: { + 'component': 'LODocumentSourceSelectorAIO', + 'subcomponent': 'timestamp_input', + 'aio_id': aio_id + } + kwargs_store = lambda aio_id: { + 'component': 'LODocumentSourceSelectorAIO', + 'subcomponent': 'kwargs_store', + 'aio_id': aio_id + } + apply = lambda aio_id: { + 'component': 'LODocumentSourceSelectorAIO', + 'subcomponent': 'apply', + 'aio_id': aio_id + } + + ids = ids + + def __init__(self, aio_id=None): + if aio_id is None: + aio_id = str(uuid.uuid4()) + + + card_body = dbc.CardBody([ + dbc.Label('Source'), + dbc.RadioItems( + id=self.ids.source_selector(aio_id), + options={'latest': 'Latest Document', + 'assignment': 'Assignment', + 'timestamp': 'Specific Time'}, + inline=True, + value='latest'), + html.Div('Additional Arguments'), + html.Div([ + dbc.RadioItems(id=self.ids.assignment_input(aio_id)), + ], id=self.ids.assignment_wrapper(aio_id)), + html.Div([ + dbc.InputGroup([ + dcc.DatePickerSingle( + id=self.ids.date_input(aio_id), + date=datetime.date.today()), + dbc.Input( + type='time', + id=self.ids.timestamp_input(aio_id), + value=datetime.datetime.now().strftime("%H:%M")) + ]) + ], id=self.ids.datetime_wrapper(aio_id)), + dbc.Button('Apply', id=self.ids.apply(aio_id), class_name='mt-1', n_clicks=0), + dcc.Store(id=self.ids.kwargs_store(aio_id), data={'src': 'latest'}) + ]) + component = [ + dbc.CardHeader('Document Source'), + card_body + ] + super().__init__(component) + + # Update data + clientside_callback( + '''function (clicks, src, assignment, date, time) { + if (clicks === 0) { return window.dash_clientside.no_update; } + let kwargs = {}; + if (src === 'assignment') { + kwargs.assignment = assignment; + } else if (src === 'timestamp') { + kwargs.requested_timestamp = new Date(`${date}T${time}`).getTime().toString() + } + return {src, kwargs}; + } + ''', + Output(ids.kwargs_store(MATCH), 'data'), + Input(ids.apply(MATCH), 'n_clicks'), + State(ids.source_selector(MATCH), 'value'), + State(ids.assignment_input(MATCH), 'value'), + State(ids.date_input(MATCH), 'date'), + State(ids.timestamp_input(MATCH), 'value'), + ) + + clientside_callback( + '''function (src) { + if (src === 'assignment') { + return ['d-none', '']; + } else if (src === 'timestamp') { + return ['', 'd-none'] + } + return ['d-none', 'd-none']; + } + ''', + Output(ids.datetime_wrapper(MATCH), 'className'), + Output(ids.assignment_wrapper(MATCH), 'className'), + Input(ids.source_selector(MATCH), 'value'), + ) + + clientside_callback( + '''async function (id, hash) { + if (hash.length === 0) { return window.dash_clientside.no_update; } + const decoded = decode_string_dict(hash.slice(1)); + if (!decoded.course_id) { return window.dash_clientside.no_update; } + const response = await fetch(`${window.location.protocol}//${window.location.hostname}:${window.location.port}/google/course_work/${decoded.course_id}`); + const data = await response.json(); + const options = data.courseWork.map(function (item) { + return { label: item.title, value: item.id }; + }); + return options; + } + ''', + Output(ids.assignment_input(MATCH), 'options'), + Input(ids.source_selector(MATCH), 'id'), + Input('_pages_location', 'hash'), + ) + + clientside_callback( + '''function (src, assignment, date, time, current) { + if (src === 'assignment' & (assignment === undefined | current.kwargs?.assignment === assignment)) { + return true; + } + if (src === 'timestamp' & current.kwargs?.requested_timestamp === new Date(`${date}T${time}`).getTime().toString()) { + return true; + } + if (src === 'latest' & current.src === 'latest') { return true; } + return false; + } + ''', + Output(ids.apply(MATCH), 'disabled'), + Input(ids.source_selector(MATCH), 'value'), + Input(ids.assignment_input(MATCH), 'value'), + Input(ids.date_input(MATCH), 'date'), + Input(ids.timestamp_input(MATCH), 'value'), + Input(ids.kwargs_store(MATCH), 'data'), + ) diff --git a/modules/lo_dash_react_components/lo_dash_react_components/__init__.py b/modules/lo_dash_react_components/lo_dash_react_components/__init__.py index 4c6fecb3a..f22c1ea64 100644 --- a/modules/lo_dash_react_components/lo_dash_react_components/__init__.py +++ b/modules/lo_dash_react_components/lo_dash_react_components/__init__.py @@ -12,6 +12,7 @@ from .LOConnectionStatusAIO import LOConnectionStatusAIO from .LOConnectionAIO import LOConnectionAIO +from .LODocumentSourceSelectorAIO import LODocumentSourceSelectorAIO from .ProfileSidebarAIO import ProfileSidebarAIO if not hasattr(_dash, '__plotly_dash') and not hasattr(_dash, 'development'): diff --git a/modules/lo_dash_react_components/package.json b/modules/lo_dash_react_components/package.json index 8c2fe18f9..9679e22d4 100644 --- a/modules/lo_dash_react_components/package.json +++ b/modules/lo_dash_react_components/package.json @@ -14,7 +14,7 @@ "watch-css": "sass src/lib:lo_dash_react_components/css --watch", "start-all": "npm-run-all --parallel watch-css react-start webpack-start dash-start", "clean-build:python": "rm -rf dist/ && rm -rf build/", - "build:python": "npm run clean-build:python && python setup.py sdist bdist_wheel" + "build:python": "npm run clean-build:python && npm run build && python setup.py sdist bdist_wheel" }, "author": "Piotr Mitros ", "license": "AGPL-3.0", diff --git a/modules/lo_dash_react_components/src/lib/components/LOPanelLayout.react.js b/modules/lo_dash_react_components/src/lib/components/LOPanelLayout.react.js index e794cac0c..deb6282a4 100644 --- a/modules/lo_dash_react_components/src/lib/components/LOPanelLayout.react.js +++ b/modules/lo_dash_react_components/src/lib/components/LOPanelLayout.react.js @@ -24,7 +24,7 @@ export default class LOPanelLayout extends Component { {leftPanels.map(panel =>
{panel.children} @@ -36,7 +36,7 @@ export default class LOPanelLayout extends Component { {rightPanels.map(panel =>
{panel.children} @@ -77,6 +77,7 @@ LOPanelLayout.propTypes = { width: PropTypes.string, offset: PropTypes.number, side: PropTypes.string, + className: PropTypes.string, id: PropTypes.string.isRequired })), diff --git a/modules/wo_bulk_essay_analysis/VERSION b/modules/wo_bulk_essay_analysis/VERSION new file mode 100644 index 000000000..2e2f7e4a0 --- /dev/null +++ b/modules/wo_bulk_essay_analysis/VERSION @@ -0,0 +1 @@ +0.1.0+2025.04.01T15.05.13.407Z.44725993.berickson.022025.gpt.dashboard.updates diff --git a/modules/wo_bulk_essay_analysis/pyproject.toml b/modules/wo_bulk_essay_analysis/pyproject.toml new file mode 100644 index 000000000..8fe2f47af --- /dev/null +++ b/modules/wo_bulk_essay_analysis/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["setuptools>=42", "wheel"] +build-backend = "setuptools.build_meta" diff --git a/modules/wo_bulk_essay_analysis/setup.cfg b/modules/wo_bulk_essay_analysis/setup.cfg index 524a8e0b1..a4db1fe6d 100644 --- a/modules/wo_bulk_essay_analysis/setup.cfg +++ b/modules/wo_bulk_essay_analysis/setup.cfg @@ -2,12 +2,15 @@ name = Writing Observer Automated Essay Feedback description = Dashboard for interfacing a classroom of essays with automated feedback url = https://github.com/ETS-Next-Gen/writing_observer -version = 0.1 +version = file:VERSION [options] packages = find: include_package_data = true +[options.package_data] +wo_bulk_essay_analysis = assets/* + [options.entry_points] lo_modules = wo_bulk_essay_analysis = wo_bulk_essay_analysis.module diff --git a/modules/wo_bulk_essay_analysis/setup.py b/modules/wo_bulk_essay_analysis/setup.py deleted file mode 100644 index e562a4cba..000000000 --- a/modules/wo_bulk_essay_analysis/setup.py +++ /dev/null @@ -1,13 +0,0 @@ -''' -Rather minimalistic install script. To install, run `python -setup.py develop` or just install via requirements.txt -''' - -from setuptools import setup, find_packages - -setup( - name="wo_bulk_essay_analysis", - package_data={ - 'wo_bulk_essay_analysis': ['assets/*'], - } -) diff --git a/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/assets/scripts.js b/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/assets/scripts.js index fe5c7756a..6a00a5647 100644 --- a/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/assets/scripts.js +++ b/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/assets/scripts.js @@ -8,17 +8,11 @@ if (!window.dash_clientside) { pdfjsLib.GlobalWorkerOptions.workerSrc = '/static/3rd_party/pdf.worker.min.js'; -const createStudentCard = async function (s, prompt) { - // TODO this ought to come from the comm protocol - const document = Object.keys(s.documents)[0]; - const student = s.documents[document]; +const createStudentCard = async function (s, prompt, width, height, showHeader) { + const selectedDocument = s.doc_id || Object.keys(s.documents || {})[0] || ''; + const student = s.documents?.[selectedDocument] ?? {}; const promptHash = await hashObject({ prompt }); - const header = { - namespace: 'dash_bootstrap_components', - type: 'CardHeader', - props: { children: student.profile.name.full_name } - }; const studentText = { namespace: 'lo_dash_react_components', type: 'WOAnnotatedText', @@ -32,8 +26,8 @@ const createStudentCard = async function (s, prompt) { } }; const feedbackMessage = { - namespace: 'dash_html_components', - type: 'Div', + namespace: DASH_CORE_COMPONENTS, + type: 'Markdown', props: { children: student?.feedback ? student.feedback : '', className: student?.feedback ? 'p-1 overflow-auto' : '', @@ -58,40 +52,56 @@ const createStudentCard = async function (s, prompt) { }; const feedback = promptHash === student.option_hash ? feedbackMessage : feedbackLoading; const feedbackOrError = 'error' in student ? errorMessage : feedback; - const body = { - namespace: 'lo_dash_react_components', - type: 'LOPanelLayout', - props: { - children: studentText, - panels: [{ children: feedbackOrError, id: 'feedback-text', width: '40%' }], - shown: ['feedback-text'], - className: 'overflow-auto p-1' + const userId = student?.user_id || '0'; + const studentTile = createDashComponent( + LO_DASH_REACT_COMPONENTS, 'WOStudentTextTile', + { + showHeader, + studentInfo: formatStudentData(s, []), + selectedDocument, + childComponent: studentText, + id: { type: 'WOAIAssistStudentTileText', index: userId }, + currentOptionHash: promptHash, + style: { height: `${height}px` } } - }; - const card = { - namespace: 'dash_bootstrap_components', - type: 'Card', - props: { - children: [header, body], - style: { maxHeight: '375px' } - } - }; - return { - namespace: 'dash_bootstrap_components', - type: 'Col', - props: { - children: card, - id: student.user_id, - xs: 12, - lg: 6, - xxl: 4 + ); + const tileWrapper = createDashComponent( + DASH_HTML_COMPONENTS, 'Div', + { + className: 'position-relative mb-2', + children: [ + studentTile, + createDashComponent( + DASH_BOOTSTRAP_COMPONENTS, 'Card', + { children: feedbackOrError, body: true } + ), + createDashComponent( + DASH_BOOTSTRAP_COMPONENTS, 'Button', + { + id: { type: 'WOAIAssistStudentTileExpand', index: userId }, + children: createDashComponent(DASH_HTML_COMPONENTS, 'I', { className: 'fas fa-expand' }), + class_name: 'position-absolute top-0 end-0 m-1', + color: 'transparent' + } + ) + ], + id: { type: 'WOAIAssistStudentTile', index: userId }, + style: { width: `${(100 - width) / width}%` } } - }; + ); + return tileWrapper; }; +/** + * Check for if we should trigger loading on a student or not. + * @param {*} s student + * @param {*} promptHash current hash of prompts + * @returns true if student's selected document's hash is the same as promptHash + */ const checkForResponse = function (s, promptHash) { - const document = Object.keys(s.documents)[0]; - const student = s.documents[document]; + if (!('documents' in s)) { return false; } + const selectedDocument = s.doc_id || Object.keys(s.documents || {})[0] || ''; + const student = s.documents[selectedDocument]; return promptHash === student.option_hash; }; @@ -103,36 +113,58 @@ const charactersAfterChar = function (str, char) { return str.slice(commaIndex + 1).trim(); }; +// Helper functions for extracting text from files const extractPDF = async function (base64String) { - const pdfData = atob(charactersAfterChar(base64String, ',')) + const pdfData = atob(charactersAfterChar(base64String, ',')); // Use PDF.js to load and parse the PDF - const pdf = await pdfjsLib.getDocument({ data: pdfData }).promise + const pdf = await pdfjsLib.getDocument({ data: pdfData }).promise; - const totalPages = pdf.numPages - const allTextPromises = [] + const totalPages = pdf.numPages; + const allTextPromises = []; for (let pageNumber = 1; pageNumber <= totalPages; pageNumber++) { const pageTextPromise = pdf.getPage(pageNumber).then(function (page) { - return page.getTextContent() + return page.getTextContent(); }).then(function (textContent) { - return textContent.items.map(item => item.str).join(' ') - }) + return textContent.items.map(item => item.str).join(' '); + }); - allTextPromises.push(pageTextPromise) + allTextPromises.push(pageTextPromise); } - const allTexts = await Promise.all(allTextPromises) - const allText = allTexts.join('\n') + const allTexts = await Promise.all(allTextPromises); + const allText = allTexts.join('\n'); + + return allText; +}; + +const extractTXT = async function (base64String) { + return atob(charactersAfterChar(base64String, ',')); +}; + +const extractMD = async function (base64String) { + return atob(charactersAfterChar(base64String, ',')); +}; - return allText +const extractDOCX = async function (base64String) { + const arrayBuffer = Uint8Array.from(atob(charactersAfterChar(base64String, ',')), c => c.charCodeAt(0)).buffer; + const result = await mammoth.extractRawText({ arrayBuffer }); + return result.value; // The raw text +}; + +const fileTextExtractors = { + pdf: extractPDF, + txt: extractTXT, + md: extractMD, + docx: extractDOCX }; window.dash_clientside.bulk_essay_feedback = { /** * Sends data to server via websocket */ - send_to_loconnection: async function (state, hash, clicks, docSrc, docDate, docTime, query, systemPrompt, tags) { + send_to_loconnection: async function (state, hash, clicks, docKwargs, query, systemPrompt, tags) { if (state === undefined) { return window.dash_clientside.no_update; } @@ -143,11 +175,11 @@ window.dash_clientside.bulk_essay_feedback = { decoded.gpt_prompt = ''; decoded.message_id = ''; - decoded.doc_source = docSrc; - decoded.requested_timestamp = new Date(`${docDate}T${docTime}`).getTime().toString(); + decoded.doc_source = docKwargs.src; + decoded.doc_source_kwargs = docKwargs.kwargs; // TODO what is a reasonable time to wait inbetween subsequent calls for // the same arguments - decoded.rerun_dag_delay = 120; + decoded.rerun_dag_delay = 30; const trig = window.dash_clientside.callback_context.triggered[0]; if (trig.prop_id.includes('bulk-essay-analysis-submit-btn')) { @@ -161,8 +193,8 @@ window.dash_clientside.bulk_essay_feedback = { const message = { wo: { - execution_dag: 'wo_bulk_essay_analysis', - target_exports: ['gpt_bulk'], + execution_dag: 'writing_observer', + target_exports: ['gpt_bulk', 'document_list', 'document_sources'], kwargs: decoded } }; @@ -195,9 +227,9 @@ window.dash_clientside.bulk_essay_feedback = { */ update_input_history_on_query_submission: async function (clicks, query, history) { if (clicks > 0) { - return ['', history.concat(query)] + return history.concat(query); } - return [query, window.dash_clientside.no_update] + return window.dash_clientside.no_update; }, /** @@ -221,50 +253,51 @@ window.dash_clientside.bulk_essay_feedback = { /** * update student cards based on new data in storage */ - updateStudentGridOutput: async function (wsStorageData, history) { + updateStudentGridOutput: async function (wsStorageData, history, width, height, showHeader) { if (!wsStorageData) { return 'No students'; } const currPrompt = history.length > 0 ? history[history.length - 1] : ''; let output = []; - for (const student in wsStorageData) { - output = output.concat(await createStudentCard(wsStorageData[student], currPrompt)); + for (const student in wsStorageData.students) { + output = output.concat(await createStudentCard(wsStorageData.students[student], currPrompt, width, height, showHeader)); } return output; }, /** - * show attachment panel upon uploading document and populate fields - * - * updates the following - * - extracted text from uploaded file - * - default attachment name (based on filename) - * - whether we show the attachment upload panel + * Uploads file content as str */ - open_and_populate_attachment_panel: async function (contents, filename, timestamp, shown) { + handleFileUploadToTextField: async function (contents, filename, timestamp) { if (filename === undefined) { - return ['', '', shown]; + return ''; } - let data = '' - if (filename.endsWith('.pdf')) { - data = await extractPDF(contents); + let data = ''; + try { + const filetype = charactersAfterChar(filename, '.'); + if (filetype in fileTextExtractors) { + data = await fileTextExtractors[filetype](contents); + } else { + console.error('Unsupported file type'); + } + } catch (error) { + console.error('Error extracting text from file:', error); } - // TODO add support for docx-like files - return [data, filename.slice(0, filename.lastIndexOf('.')), shown.concat('attachment')]; + return data; }, /** * append tag in curly braces to input */ add_tag_to_input: function (clicks, curr, store) { - const trig = window.dash_clientside.callback_context.triggered[0] - const trigProp = trig.prop_id - const trigJSON = JSON.parse(trigProp.slice(0, trigProp.lastIndexOf('.'))) + const trig = window.dash_clientside.callback_context.triggered[0]; + const trigProp = trig.prop_id; + const trigJSON = JSON.parse(trigProp.slice(0, trigProp.lastIndexOf('.'))); if (trig.value > 0) { - return curr.concat(` {${trigJSON.index}}`) + return curr.concat(` {${trigJSON.index}}`); } - return window.dash_clientside.no_update + return window.dash_clientside.no_update; }, /** @@ -300,13 +333,36 @@ window.dash_clientside.bulk_essay_feedback = { * - save button disbaled status * - helper text for why we are disabled */ - disable_attachment_save_button: function (label, tags) { - if (label.length === 0) { - return [true, 'Please add a unique label to your attachment'] - } else if (tags.includes(label)) { - return [true, `Label ${label} is already in use.`] + disableAttachmentSaveButton: function (label, content, currentTagStore, replacementId) { + const tags = Object.keys(currentTagStore); + if (label.length === 0 & content.length === 0) { + return [true, '']; + } else if (label.length === 0) { + return [true, 'Add a label for your content']; + } else if (content.length === 0) { + return [true, 'Add content for your label']; + } else if ((!replacementId | replacementId !== label) & tags.includes(label)) { + return [true, `Label ${label} is already in use.`]; } - return [false, ''] + return [false, '']; + }, + + /** + * Opens the tag modal when users want to add a new one or edit an + * existing tag. + */ + openTagAddModal: function (clicks, editClicks, currentTagStore, ids) { + const triggeredItem = window.dash_clientside.callback_context?.triggered_id ?? null; + if (!triggeredItem) { return window.dash_clientside.no_update; } + if (triggeredItem === 'bulk-essay-analysis-tags-add-open-btn') { + return [true, null, '', '']; + } + const id = triggeredItem.index; + const index = ids.findIndex(item => item.index === id); + if (editClicks[index]) { + return [true, id, id, currentTagStore[id]]; + } + return window.dash_clientside.no_update; }, /** @@ -315,32 +371,81 @@ window.dash_clientside.bulk_essay_feedback = { update_tag_buttons: function (tagStore) { const tagLabels = Object.keys(tagStore); const tags = tagLabels.map((val) => { - const button = { - namespace: 'dash_bootstrap_components', - type: 'Button', - props: { + const isStudentText = val === 'student_text'; + const button = createDashComponent( + DASH_BOOTSTRAP_COMPONENTS, 'Button', + { children: val, - id: { type: 'bulk-essay-analysis-tag', index: val }, + id: { type: 'bulk-essay-analysis-tags-tag', index: val }, n_clicks: 0, - size: 'sm', - color: 'secondary' + color: isStudentText ? 'warning' : 'info' } - }; - return button; + ); + const editButton = createDashComponent( + DASH_BOOTSTRAP_COMPONENTS, 'Button', + { + children: createDashComponent(DASH_HTML_COMPONENTS, 'I', { className: 'fas fa-edit' }), + id: { type: 'bulk-essay-analysis-tags-tag-edit', index: val }, + n_clicks: 0, + color: 'info' + } + ); + const deleteButton = createDashComponent( + DASH_CORE_COMPONENTS, 'ConfirmDialogProvider', + { + children: createDashComponent( + DASH_BOOTSTRAP_COMPONENTS, 'Button', + { + children: createDashComponent(DASH_HTML_COMPONENTS, 'I', { className: 'fas fa-trash' }), + color: 'info' + } + ), + id: { type: 'bulk-essay-analysis-tags-tag-delete', index: val }, + message: `Are you sure you want to delete the \`${val}\` placeholder?` + } + ); + const buttons = isStudentText ? [button] : [button, editButton, deleteButton]; + const buttonGroup = createDashComponent( + DASH_BOOTSTRAP_COMPONENTS, 'ButtonGroup', + { + children: buttons, + class_name: `${isStudentText ? '' : 'prompt-variable-tag'} ms-1 mb-1` + } + ); + return buttonGroup; }); return tags; }, /** - * Save attachment to tag storage + * Save placeholder to browser storage and close edit placeholder modal */ - save_attachment: function (clicks, label, text, tagStore, shown) { + savePlaceholder: function (clicks, label, text, replacementId, tagStore) { if (clicks > 0) { - const newStore = tagStore - newStore[label] = text - return [newStore, shown.filter(item => item !== 'attachment')] + const newStore = tagStore; + if (!!replacementId & replacementId !== label) { + delete newStore[replacementId]; + } + newStore[label] = text; + return [newStore, false]; + } + return window.dash_clientside.no_update; + }, + + /** + * Remove placeholder from store on confirm dialogue yes + */ + removePlaceholder: function (clicks, tagStore, ids) { + const triggeredItem = window.dash_clientside.callback_context?.triggered_id ?? null; + if (!triggeredItem) { return window.dash_clientside.no_update; } + const id = triggeredItem.index; + const index = ids.findIndex(item => item.index === id); + if (clicks[index]) { + const newStore = tagStore; + delete newStore[id]; + return newStore; } - return tagStore + return window.dash_clientside.no_update; }, /** @@ -364,13 +469,10 @@ window.dash_clientside.bulk_essay_feedback = { return [text, true, error]; }, - disable_doc_src_datetime: function (value) { - if (value === 'ts') { - return [false, false]; - } - return [true, true]; - }, - + /** + * Iterate over students and figure out if any of them have not loaded + * yet. We hash the last history item to compare to. + */ updateLoadingInformation: async function (wsStorageData, history) { const noLoading = [false, 0, '']; if (!wsStorageData) { @@ -378,11 +480,118 @@ window.dash_clientside.bulk_essay_feedback = { } const currentPrompt = history.length > 0 ? history[history.length - 1] : ''; const promptHash = await hashObject({ prompt: currentPrompt }); - const returnedResponses = Object.values(wsStorageData).filter(student => checkForResponse(student, promptHash)).length; - const totalStudents = Object.keys(wsStorageData).length; + const returnedResponses = Object.values(wsStorageData.students).filter(student => checkForResponse(student, promptHash)).length; + const totalStudents = Object.keys(wsStorageData.students).length; if (totalStudents === returnedResponses) { return noLoading; } const loadingProgress = returnedResponses / totalStudents + 0.1; const outputText = `Fetching responses from server. This will take a few minutes. (${returnedResponses}/${totalStudents} received)`; return [true, loadingProgress, outputText]; + }, + + adjustTileSize: function (width, height, studentIds) { + const total = studentIds.length; + return [ + Array(total).fill({ width: `${(100 - width) / width}%` }), + Array(total).fill({ height: `${height}px` }) + ]; + }, + + selectStudentForExpansion: function (clicks, shownPanels, ids) { + const triggeredItem = window.dash_clientside.callback_context?.triggered_id ?? null; + if (!triggeredItem) { return window.dash_clientside.no_update; } + let id = null; + if (triggeredItem?.type === 'WOAIAssistStudentTileExpand') { + id = triggeredItem?.index; + if (clicks[ids.findIndex(item => item.index === id)]) { + shownPanels = shownPanels.concat('bulk-essay-analysis-expanded-student-panel'); + } + } else { + return window.dash_clientside.no_update; + } + return [id, shownPanels]; + }, + + expandSelectedStudent: async function (selectedStudent, wsData, showHeader, history) { + console.log('wsData', wsData); + if (!selectedStudent | !(selectedStudent in wsData.students)) { + return window.dash_clientside.no_update; + } + const prompt = history.length > 0 ? history[history.length - 1] : ''; + const s = wsData.students[selectedStudent]; + const selectedDocument = s.doc_id || Object.keys(s.documents || {})[0] || ''; + const document = Object.keys(s.documents)[0]; + const student = s.documents[document]; + const promptHash = await hashObject({ prompt }); + + const studentText = { + namespace: 'lo_dash_react_components', + type: 'WOAnnotatedText', + props: { text: student.text, breakpoints: [], className: 'border-end' } + }; + const errorMessage = { + namespace: 'dash_html_components', + type: 'Div', + props: { + children: 'An error occurred while processing the text.' + } + }; + const feedbackMessage = { + namespace: DASH_CORE_COMPONENTS, + type: 'Markdown', + props: { + children: student?.feedback ? student.feedback : '', + className: student?.feedback ? 'p-1 overflow-auto' : '', + style: { whiteSpace: 'pre-line' } + } + }; + const feedbackLoading = { + namespace: 'dash_html_components', + type: 'Div', + props: { + children: [{ + namespace: 'dash_bootstrap_components', + type: 'Spinner', + props: {} + }, { + namespace: 'dash_html_components', + type: 'Div', + props: { children: 'Waiting for a response.' } + }], + className: 'text-center' + } + }; + const feedback = promptHash === student.option_hash ? feedbackMessage : feedbackLoading; + const feedbackOrError = 'error' in student ? errorMessage : feedback; + const studentTile = createDashComponent( + LO_DASH_REACT_COMPONENTS, 'WOStudentTextTile', + { + showHeader, + studentInfo: formatStudentData(s, []), + selectedDocument, + childComponent: studentText, + id: { type: 'WOAIAssistStudentTileText', index: student.user_id }, + currentOptionHash: promptHash + } + ); + const individualWrapper = createDashComponent( + DASH_HTML_COMPONENTS, 'Div', + { + className: '', + children: [ + studentTile, + createDashComponent( + DASH_BOOTSTRAP_COMPONENTS, 'Card', + { children: feedbackOrError, body: true, className: 'individual-student-feedback' } + ) + ] + } + ); + return individualWrapper; + }, + + closeExpandedStudent: function (clicks, shown) { + if (!clicks) { return window.dash_clientside.no_update; } + shown = shown.filter(item => item !== 'bulk-essay-analysis-expanded-student-panel'); + return shown; } }; diff --git a/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/assets/styles.css b/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/assets/styles.css new file mode 100644 index 000000000..9e9d73472 --- /dev/null +++ b/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/assets/styles.css @@ -0,0 +1,15 @@ +.individual-student-feedback { + position: -webkit-sticky; + position: sticky; + bottom: 0; + min-height: 250px; +} + +.prompt-variable-tag button:last-child { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +.prompt-variable-tag>div:last-child { + display: inline; +} diff --git a/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/dashboard/layout.py b/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/dashboard/layout.py index 45123ef2c..5df3c0f7e 100644 --- a/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/dashboard/layout.py +++ b/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/dashboard/layout.py @@ -6,6 +6,7 @@ from dash_renderjson import DashRenderjson import datetime import lo_dash_react_components as lodrc +import random from dash import html, dcc, clientside_callback, ClientsideFunction, Output, Input, State, ALL @@ -24,28 +25,63 @@ panel_layout = f'{prefix}-panel-layout' -_advanced_toggle = f'{prefix}-advanced-toggle' -_advanced_collapse = f'{prefix}-advanced-collapse' +_advanced = f'{prefix}-advanced' +_advanced_doc_src = f'{_advanced}-document-source' +_advanced_toggle = f'{_advanced}-toggle' +_advanced_collapse = f'{_advanced}-collapse' +_advanced_width = f'{_advanced}-width' +_advanced_height = f'{_advanced}-height' +_advanced_hide_header = f'{_advanced}-hide-header' -system_input = f'{prefix}-system-prompt-input' -# document source DOM ids -doc_src = f'{prefix}-doc-src' -doc_src_date = f'{prefix}-doc-src-date' -doc_src_timestamp = f'{prefix}-doc-src-timestamp' - -# attachment upload DOM ids -attachment_upload = f'{prefix}-attachment-upload' -attachment_label = f'{prefix}-attachment-label' -attachment_extracted_text = f'{prefix}-attachment-extracted-text' -attachment_save = f'{prefix}-attachment-save' -attachment_warning_message = f'{prefix}-attachment-warning-message' -attachment_store = f'{prefix}-attachment-store' +_system_input = f'{prefix}-system-prompt-input' +_system_input_tooltip = f'{_system_input}-tooltip' # placeholder DOM ids -tags = f'{prefix}-tags' -placeholder_tooltip = f'{prefix}-placeholder-tooltip' -tag = f'{prefix}-tag' -tag_store = f'{prefix}-tags-store' +_tags = f'{prefix}-tags' +placeholder_tooltip = f'{_tags}-placeholder-tooltip' +tag = f'{_tags}-tag' +_tag_edit = f'{tag}-edit' +_tag_delete = f'{tag}-delete' +tag_store = f'{_tags}-tags-store' +_tag_add = f'{_tags}-add' +_tag_replacement_id = f'{_tag_add}-replacement-id' +_tag_add_modal = f'{_tag_add}-modal' +_tag_add_open = f'{_tag_add}-open-btn' +_tag_add_label = f'{_tag_add}-label' +_tag_add_text = f'{_tag_add}-text' +_tag_add_upload = f'{_tag_add}-upload' +_tag_add_warning = f'{_tag_add}-warning' +_tag_add_save = f'{_tag_add}-save' +tag_modal = dbc.Modal([ + dbc.ModalHeader('Add Placeholder'), + dbc.ModalBody([ + dbc.Input(id=_tag_replacement_id, class_name='d-none'), + dbc.Label('Label'), + dbc.Input( + placeholder='Name your placeholder (e.g., "Narrative Grade 8 Rubric")', + id=_tag_add_label, + value='' + ), + dbc.Label('Contents'), + dbc.Textarea( + placeholder='Enter text here... Uploading a file replaces this content', + id=_tag_add_text, + style={'height': '300px'}, + value='' + ), + dbc.Button( + dcc.Upload( + [html.I(className='fas fa-plus me-1'), 'Upload'], + accept='.txt,.md,.pdf,.docx', + id=_tag_add_upload + ) + ) + ]), + dbc.ModalFooter([ + html.Small(id=_tag_add_warning, className='text-danger'), + dbc.Button('Save', class_name='ms-auto', id=_tag_add_save), + ]) +], id=_tag_add_modal, is_open=False) # prompt history DOM ids history_body = f'{prefix}-history-body' @@ -60,13 +96,41 @@ submit = f'{prefix}-submit-btn' submit_warning_message = f'{prefix}-submit-warning-msg' +_student_data_wrapper = f'{prefix}-student-data' grid = f'{prefix}-essay-grid' +# Expanded student +_expanded_student = f'{prefix}-expanded-student' +_expanded_student_selected = f'{_expanded_student}-selected' +_expanded_student_panel = f'{_expanded_student}-panel' +_expanded_student_child = f'{_expanded_student}-child' +_expanded_student_close = f'{_expanded_student}-close' +expanded_student_component = html.Div([ + html.Div([ + html.H3('Individual Student', className='d-inline-block'), + dbc.Button( + html.I(className='fas fa-close'), + className='float-end', id=_expanded_student_close, + color='transparent'), + ]), + dbc.Input(id=_expanded_student_selected, class_name='d-none'), + html.Div(id=_expanded_student_child) +], className='p-2') + # default prompts -system_prompt = 'You are an assistant to a language arts teacher in a school setting. '\ - 'Your task is to help the teacher assess, understand, and provide feedback on student essays.' +system_prompt = 'You are a helpful assistant for grade school teachers. Your task is to analyze '\ + 'student writing and provide clear, constructive, and age-appropriate feedback. '\ + 'Focus on key writing traits such as clarity, creativity, grammar, and organization. '\ + 'When summarizing, highlight the main ideas and key details. Always maintain a '\ + 'positive and encouraging tone to support student growth.' -starting_prompt = 'Provide 3 bullet points summarizing the following text:\n{student_text}' +starting_prompt = [ + 'Provide 3 bullet points summarizing this text:\n{student_text}', + 'List 3 strengths in this student\'s writing. Use bullet points and focus on creativity or clear ideas:\n{student_text}', + 'Find 2-3 grammar or spelling errors in this text. For each, quote the sentence and suggest a fix:\n{student_text}', + 'Identify 1) Main theme 2) Best sentence 3) One area to improve. Use numbered responses:\n{student_text}', + 'Give one specific compliment and one gentle suggestion to improve this story:\n{student_text}' +] def layout(): @@ -76,19 +140,18 @@ def layout(): # advanced menu for system prompt advanced = [ html.Div([ - dbc.Label('System prompt'), - dbc.Textarea(id=system_input, value=system_prompt) - ]), - html.Div([ - dbc.Label('Document Source'), - dbc.RadioItems(options=[ - {'label': 'Latest Document', 'value': 'latest' }, - {'label': 'Specific Time', 'value': 'ts'}, - ], value='latest', id=doc_src), - dbc.InputGroup([ - dcc.DatePickerSingle(id=doc_src_date, date=datetime.date.today()), - dbc.Input(type='time', id=doc_src_timestamp, value=datetime.datetime.now().strftime("%H:%M")) - ]) + lodrc.LODocumentSourceSelectorAIO(aio_id=_advanced_doc_src), + dbc.Card([ + dbc.CardHeader('View Options'), + dbc.CardBody([ + dbc.Label('Students per row'), + dbc.Input(type='number', min=1, max=10, value=3, step=1, id=_advanced_width), + dbc.Label('Height of student tile'), + dcc.Slider(min=100, max=800, marks=None, value=350, id=_advanced_height), + dbc.Label('Student name headers'), + dbc.Switch(value=True, id=_advanced_hide_header, label='Show/Hide'), + ]) + ]), ]) ] @@ -99,45 +162,38 @@ def layout(): dcc.Store(id=history_store, data=[]) ], class_name='h-100') - # attachment information panel - attachment_panel = dbc.Card([ - dbc.CardHeader('Upload'), - dbc.CardBody([ - dbc.Label('What is this?'), - dbc.Input(placeholder='e.g. argumentative attachment', id=attachment_label, value=''), - dbc.Label('Extracted text from attachment'), - dbc.Textarea(value='', id=attachment_extracted_text, style={'height': '300px'}) - ]), - dbc.CardFooter([ - html.Small(id=attachment_warning_message, className='text-danger'), - dbc.Button('Save', id=attachment_save, color='primary', n_clicks=0, class_name='float-end') - ]), - dcc.Store(id=attachment_store, data='') - ], class_name='h-100') - # query creator panel input_panel = dbc.Card([ dbc.CardHeader('Prompt Input'), - # TODO figure out the proper way to create new tags/upload docs - # then remove the `class_name='d-none'` from this button. - dbc.Button(dcc.Upload([html.I(className='fas fa-plus me-1'), 'Upload'], accept='.pdf', id=attachment_upload), class_name='d-none'), dbc.CardBody([ - dbc.Textarea(id=query_input, value=starting_prompt, class_name='h-100', style={'minHeight': '150px'}), + dbc.Label([ + 'System prompt', + html.I(className='fas fa-circle-question ms-1', id=_system_input_tooltip) + ]), + dbc.Tooltip( + "A system prompt guides the AI's responses. It sets the context for how the AI should analyze or summarize student text.", + target=_system_input_tooltip + ), + dbc.Textarea(id=_system_input, value=system_prompt, style={'minHeight': '120px'}), + dbc.Label('Query'), + dbc.Textarea(id=query_input, value=random.choice(starting_prompt), class_name='h-100', style={'minHeight': '150px'}), html.Div([ html.Span([ 'Placeholders', html.I(className='fas fa-circle-question ms-1', id=placeholder_tooltip) ], className='me-1'), - html.Span([], id=tags), + html.Span([], id=_tags), + dbc.Button([html.I(className='fas fa-add me-1'), 'Add'], id=_tag_add_open, class_name='ms-1 mb-1') ], className='mt-1'), dbc.Tooltip( - 'Click a placeholder to insert it into your prompt. Upon submission, it will be replaced with the corresponding value.', + 'Click a placeholder to insert it into your query. Upon submission, it will be replaced with the corresponding value.', target=placeholder_tooltip ), + tag_modal, dcc.Store(id=tag_store, data={'student_text': ''}), ]), dbc.CardFooter([ - html.Small(id=submit_warning_message, className='text-danger'), + html.Small(id=submit_warning_message, className='text-secondary'), dbc.Button('Submit', color='primary', id=submit, n_clicks=0, class_name='float-end') ]) ]) @@ -154,7 +210,7 @@ def layout(): # overall container cont = dbc.Container([ - html.H2('Writing Observer - AskGPT'), + html.H1('Writing Observer - Classroom AI Feedback Assistant'), dbc.InputGroup([ dbc.InputGroupText(lodrc.LOConnectionAIO(aio_id=_websocket)), dbc.Button([html.I(className='fas fa-cog me-1'), 'Advanced'], id=_advanced_toggle), @@ -165,7 +221,6 @@ def layout(): input_panel, panels=[ {'children': history_favorite_panel, 'width': '30%', 'id': 'history-favorite'}, - {'children': attachment_panel, 'width': '40%', 'id': 'attachment'}, ], shown=['history-favorite'], id=panel_layout @@ -173,19 +228,19 @@ def layout(): alert_component, html.H3('Student Text', className='mt-1'), loading_component, - dbc.Row(id=grid, class_name='g-4'), + lodrc.LOPanelLayout( + html.Div(id=grid, className='d-flex justify-content-between flex-wrap'), + panels=[ + {'children': expanded_student_component, + 'width': '30%', 'id': _expanded_student_panel, + 'side': 'right', 'className': 'vh-100 overflow-auto'} + ], + id=_student_data_wrapper, shown=[] + ), ], fluid=True) return html.Div(cont) -# disbale document date/time options -clientside_callback( - ClientsideFunction(namespace='bulk_essay_feedback', function_name='disable_doc_src_datetime'), - Output(doc_src_date, 'disabled'), - Output(doc_src_timestamp, 'disabled'), - Input(doc_src, 'value') -) - # Toggle if the advanced menu collapse is open or not clientside_callback( ClientsideFunction(namespace=_namespace, function_name='toggleAdvanced'), @@ -201,11 +256,9 @@ def layout(): Input(lodrc.LOConnectionAIO.ids.websocket(_websocket), 'state'), # used for initial setup Input('_pages_location', 'hash'), Input(submit, 'n_clicks'), - Input(doc_src, 'value'), - Input(doc_src_date, 'date'), - Input(doc_src_timestamp, 'value'), + Input(lodrc.LODocumentSourceSelectorAIO.ids.kwargs_store(_advanced_doc_src), 'data'), State(query_input, 'value'), - State(system_input, 'value'), + State(_system_input, 'value'), State(tag_store, 'data'), ) @@ -217,13 +270,12 @@ def layout(): Output(submit_warning_message, 'children'), Input(query_input, 'value'), Input(_loading_collapse, 'is_open'), - State(tag_store, 'data') + Input(tag_store, 'data') ) # add submitted query to history and clear input clientside_callback( ClientsideFunction(namespace='bulk_essay_feedback', function_name='update_input_history_on_query_submission'), - Output(query_input, 'value'), Output(history_store, 'data'), Input(submit, 'n_clicks'), State(query_input, 'value'), @@ -238,16 +290,27 @@ def layout(): Input(history_store, 'data') ) +# Toggle if the add placeholder is open or not +clientside_callback( + ClientsideFunction(namespace=_namespace, function_name='openTagAddModal'), + Output(_tag_add_modal, 'is_open'), + Output(_tag_replacement_id, 'value'), + Output(_tag_add_label, 'value'), + Output(_tag_add_text, 'value'), + Input(_tag_add_open, 'n_clicks'), + Input({'type': _tag_edit, 'index': ALL}, 'n_clicks'), + State(tag_store, 'data'), + State({'type': _tag_edit, 'index': ALL}, 'id'), +) + # show attachment panel upon uploading document and populate fields clientside_callback( - ClientsideFunction(namespace='bulk_essay_feedback', function_name='open_and_populate_attachment_panel'), - Output(attachment_extracted_text, 'value'), - Output(attachment_label, 'value'), - Output(panel_layout, 'shown'), - Input(attachment_upload, 'contents'), - Input(attachment_upload, 'filename'), - Input(attachment_upload, 'last_modified'), - State(panel_layout, 'shown') + ClientsideFunction(namespace='bulk_essay_feedback', function_name='handleFileUploadToTextField'), + Output(_tag_add_text, 'value', allow_duplicate=True), + Input(_tag_add_upload, 'contents'), + Input(_tag_add_upload, 'filename'), + Input(_tag_add_upload, 'last_modified'), + prevent_initial_call=True ) clientside_callback( @@ -263,7 +326,10 @@ def layout(): ClientsideFunction(namespace=_namespace, function_name='updateStudentGridOutput'), Output(grid, 'children'), Input(lodrc.LOConnectionAIO.ids.ws_store(_websocket), 'data'), - Input(history_store, 'data') + Input(history_store, 'data'), + Input(_advanced_width, 'value'), + Input(_advanced_height, 'value'), + Input(_advanced_hide_header, 'value') ) # append tag in curly braces to input @@ -278,30 +344,42 @@ def layout(): # enable/disable the save attachment button if tag is already in use/blank clientside_callback( - ClientsideFunction(namespace='bulk_essay_feedback', function_name='disable_attachment_save_button'), - Output(attachment_save, 'disabled'), - Output(attachment_warning_message, 'children'), - Input(attachment_label, 'value'), - State({'type': tag, 'index': ALL}, 'value') + ClientsideFunction(namespace='bulk_essay_feedback', function_name='disableAttachmentSaveButton'), + Output(_tag_add_save, 'disabled'), + Output(_tag_add_warning, 'children'), + Input(_tag_add_label, 'value'), + Input(_tag_add_text, 'value'), + State(tag_store, 'data'), + State(_tag_replacement_id, 'value') ) # populate word bank of tags clientside_callback( ClientsideFunction(namespace='bulk_essay_feedback', function_name='update_tag_buttons'), - Output(tags, 'children'), + Output(_tags, 'children'), Input(tag_store, 'data') ) -# save attachment to tag storage +# save placeholder to storage clientside_callback( - ClientsideFunction(namespace='bulk_essay_feedback', function_name='save_attachment'), + ClientsideFunction(namespace='bulk_essay_feedback', function_name='savePlaceholder'), Output(tag_store, 'data'), - Output(panel_layout, 'shown', allow_duplicate=True), - Input(attachment_save, 'n_clicks'), - State(attachment_label, 'value'), - State(attachment_extracted_text, 'value'), + Output(_tag_add_modal, 'is_open', allow_duplicate=True), + Input(_tag_add_save, 'n_clicks'), + State(_tag_add_label, 'value'), + State(_tag_add_text, 'value'), + State(_tag_replacement_id, 'value'), State(tag_store, 'data'), - State(panel_layout, 'shown'), + prevent_initial_call=True +) + +# remove placeholder from storage +clientside_callback( + ClientsideFunction(namespace='bulk_essay_feedback', function_name='removePlaceholder'), + Output(tag_store, 'data', allow_duplicate=True), + Input({'type': _tag_delete, 'index': ALL}, 'submit_n_clicks'), + State(tag_store, 'data'), + State({'type': _tag_delete, 'index': ALL}, 'id'), prevent_initial_call=True ) @@ -314,3 +392,44 @@ def layout(): Input(lodrc.LOConnectionAIO.ids.ws_store(_websocket), 'data'), Input(history_store, 'data') ) + +# Adjust student tile size +clientside_callback( + ClientsideFunction(namespace=_namespace, function_name='adjustTileSize'), + Output({'type': 'WOAIAssistStudentTile', 'index': ALL}, 'style', allow_duplicate=True), + Output({'type': 'WOAIAssistStudentTileText', 'index': ALL}, 'style', allow_duplicate=True), + Input(_advanced_width, 'value'), + Input(_advanced_height, 'value'), + State({'type': 'WOAIAssistStudentTile', 'index': ALL}, 'id'), + prevent_initial_call=True +) + +# Expand a single student +clientside_callback( + ClientsideFunction(namespace=_namespace, function_name='selectStudentForExpansion'), + Output(_expanded_student_selected, 'value'), + Output(_student_data_wrapper, 'shown', allow_duplicate=True), + Input({'type': 'WOAIAssistStudentTileExpand', 'index': ALL}, 'n_clicks'), + State(_student_data_wrapper, 'shown'), + State({'type': 'WOAIAssistStudentTile', 'index': ALL}, 'id'), + prevent_initial_call=True +) + +# Update expanded children based on selected student +clientside_callback( + ClientsideFunction(namespace=_namespace, function_name='expandSelectedStudent'), + Output(_expanded_student_child, 'children'), + Input(_expanded_student_selected, 'value'), + Input(lodrc.LOConnectionAIO.ids.ws_store(_websocket), 'data'), + Input(_advanced_hide_header, 'value'), + Input(history_store, 'data'), +) + +# Close expanded student +clientside_callback( + ClientsideFunction(namespace=_namespace, function_name='closeExpandedStudent'), + Output(_student_data_wrapper, 'shown', allow_duplicate=True), + Input(_expanded_student_close, 'n_clicks'), + State(_student_data_wrapper, 'shown'), + prevent_initial_call=True +) diff --git a/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/gpt.py b/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/gpt.py index c2790fa07..7562f9ef8 100644 --- a/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/gpt.py +++ b/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/gpt.py @@ -1,4 +1,5 @@ import learning_observer.communication_protocol.integration +import learning_observer.cache import learning_observer.prestartup import learning_observer.settings diff --git a/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/module.py b/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/module.py index d4c9f951c..0f180f754 100644 --- a/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/module.py +++ b/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/module.py @@ -6,15 +6,15 @@ import wo_bulk_essay_analysis.dashboard.layout -NAME = "Writing Observer - AskGPT" +NAME = "Writing Observer - Classroom AI Feedback Assistant" DASH_PAGES = [ { "MODULE": wo_bulk_essay_analysis.dashboard.layout, "LAYOUT": wo_bulk_essay_analysis.dashboard.layout.layout, "ASSETS": 'assets', - "TITLE": "AskGPT", - "DESCRIPTION": "The AskGPT is a robust educational tool that leverages AI to simultaneously analyze and provide feedback on large batches of essays, delivering comprehensive insights and constructive critiques for educators in diverse group settings.", + "TITLE": "Classroom AI Feedback Assistant", + "DESCRIPTION": "The Classroom AI Feedback Assistant is a robust educational tool that leverages AI to simultaneously analyze and provide feedback on large batches of essays, delivering comprehensive insights and constructive critiques for educators in diverse group settings.", "SUBPATH": "bulk-essay-analysis", "CSS": [ thirdparty_url("css/bootstrap.min.css"), @@ -22,34 +22,15 @@ ], "SCRIPTS": [ thirdparty_url('pdf.js'), - thirdparty_url('pdf.worker.js') + thirdparty_url('pdf.worker.js'), + thirdparty_url('mammoth.js') ] } ] -gpt_bulk_essay = q.call('wo_bulk_essay_analysis.gpt_essay_prompt') - -EXECUTION_DAG = { - 'execution_dag': { - 'gpt_map': q.map( - gpt_bulk_essay, - values=q.variable('writing_observer.docs'), - value_path='text', - func_kwargs={'prompt': q.parameter('gpt_prompt'), 'system_prompt': q.parameter('system_prompt'), 'tags': q.parameter('tags', required=False, default={})}, - parallel=True - ), - 'gpt_bulk': q.join(LEFT=q.variable('gpt_map'), LEFT_ON='provenance.provenance.provenance.STUDENT.value.user_id', RIGHT=q.variable('writing_observer.roster'), RIGHT_ON='user_id') - }, - 'exports': { - 'gpt_bulk': { - 'returns': 'gpt_bulk', - 'parameters': ['course_id', 'gpt_prompt', 'system_prompt'], - 'output': '' - } - } -} THIRD_PARTY = { + # PDF parser for reading in files clientside 'pdf.js': { 'url': 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.9.179/pdf.min.js', 'hash': { @@ -64,6 +45,14 @@ '3ebb7dad9946bd3d00bdcd29527dc753fde4b950b2a7a052bd8f66ee643bb736767' } }, + # Docx parser for reading in files clientside + 'mammoth.js': { + 'url': 'https://cdnjs.cloudflare.com/ajax/libs/mammoth/1.9.0/mammoth.browser.min.js', + 'hash': { + '1.9.0': '7e77162c6d0103528615896ba72fcca385ab2f64699cd06d744a6d740c16179' + '322e02e2d45adf1c4d8720f6c8ac7c54e19c6a061eb0814f2abb4b80738d8766a' + } + }, "css/bootstrap.min.css": d.BOOTSTRAP_MIN_CSS, "css/fontawesome_all.css": d.FONTAWESOME_CSS, "webfonts/fa-solid-900.woff2": d.FONTAWESOME_WOFF2, diff --git a/modules/wo_classroom_text_highlighter/VERSION b/modules/wo_classroom_text_highlighter/VERSION index e5a2081ce..2e2f7e4a0 100644 --- a/modules/wo_classroom_text_highlighter/VERSION +++ b/modules/wo_classroom_text_highlighter/VERSION @@ -1 +1 @@ -0.1.0+2025.02.26T16.23.19.270Z.e6d405f7.master +0.1.0+2025.04.01T15.05.13.407Z.44725993.berickson.022025.gpt.dashboard.updates diff --git a/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/assets/scripts.js b/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/assets/scripts.js index 134662c72..683081ea0 100644 --- a/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/assets/scripts.js +++ b/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/assets/scripts.js @@ -8,6 +8,7 @@ if (!window.dash_clientside) { } const DASH_HTML_COMPONENTS = 'dash_html_components'; +const DASH_CORE_COMPONENTS = 'dash_core_components'; const DASH_BOOTSTRAP_COMPONENTS = 'dash_bootstrap_components'; const LO_DASH_REACT_COMPONENTS = 'lo_dash_react_components'; @@ -57,49 +58,43 @@ function simpleHash (str) { // TODO some of this will move to the communication protocol, but for now // it lives here -// Currently the system only handles grabbing the first document available -// from the student and populates it under latest. We shouldn't hardcode -// anything like latest here and instead pull it from the communication protocol function formatStudentData (student, selectedHighlights) { - // TODO this ought to come from the comm protocol - const document = Object.keys(student.documents)[0]; - - // TODO make sure the comm protocol is providing the doc id - const highlightBreakpoints = selectedHighlights.reduce((acc, option) => { - const offsets = student.documents[document][option.id]?.offsets || []; - if (offsets) { - const modifiedOffsets = offsets.map(offset => { - return { - id: '', - tooltip: option.label, - start: offset[0], - offset: offset[1], - style: { backgroundColor: option.types.highlight.color } - }; - }); - acc = acc.concat(modifiedOffsets); - } - return acc; - }, []); - // const availableDocuments = Object.keys(student.docs).map(id => ({ - // id, - // title: student.docs[id].title || id - // })); - // availableDocuments.push({ id: 'latest', title: 'Latest' }); - const availableDocuments = [{ id: 'latest', title: 'Latest' }] - // TODO currently we only populate the latest data of the student documents - // this is currently the muddiest part of the data flow and ought to be - // cleaned up. + let profile = {}; + const documents = {}; + for (const document in student.documents || []) { + const breakpoints = selectedHighlights.reduce((acc, option) => { + const offsets = student.documents[document][option.id]?.offsets || []; + if (offsets) { + const modifiedOffsets = offsets.map(offset => { + return { + id: '', + tooltip: option.label, + start: offset[0], + offset: offset[1], + style: { backgroundColor: option.types.highlight.color } + }; + }); + acc = acc.concat(modifiedOffsets); + } + return acc; + }, []); + const text = student.documents[document].text; + const optionHash = student.documents[document].option_hash; + profile = student.documents[document].profile; + documents[document] = { text, optionHash, breakpoints }; + } + let availableDocuments = []; + if ('availableDocuments' in student) { + availableDocuments = Object.keys(student.availableDocuments).map(id => ({ + id, + title: student.availableDocuments[id].title || id, + last_access: student.availableDocuments[id].last_access || null + })); + } return { - profile: student.documents[document].profile, + profile, availableDocuments, - documents: { - latest: { - text: student.documents[document].text, - breakpoints: highlightBreakpoints, - optionHash: student.documents[document].option_hash - } - } + documents }; } @@ -114,7 +109,7 @@ window.dash_clientside.wo_classroom_text_highlighter = { * @param {string} urlHash query string from hash for determining course id * @returns stringified json object that is sent to the communication protocl */ - sendToLOConnection: async function (wsReadyState, urlHash, fullOptions) { + sendToLOConnection: async function (wsReadyState, urlHash, docKwargs, fullOptions) { if (wsReadyState === undefined) { return window.dash_clientside.no_update; } @@ -127,11 +122,12 @@ window.dash_clientside.wo_classroom_text_highlighter = { const nlpOptions = determineSelectedNLPOptionsList(fullOptions); decodedParams.nlp_options = nlpOptions; decodedParams.option_hash = optionsHash; + decodedParams.doc_source = docKwargs.src; + decodedParams.doc_source_kwargs = docKwargs.kwargs; const outgoingMessage = { wo_classroom_text_highlighter_query: { execution_dag: 'writing_observer', - // TODO add `doc_list` here when available - target_exports: ['docs_with_nlp_annotations'], + target_exports: ['docs_with_nlp_annotations', 'document_sources', 'document_list'], kwargs: decodedParams } }; @@ -151,11 +147,11 @@ window.dash_clientside.wo_classroom_text_highlighter = { if (!clicks) { return window.dash_clientside.no_update; } - const optionPrefix = 'wo-classroom-text-highlighter-options' + const optionPrefix = 'wo-classroom-text-highlighter-options'; if (shown.includes(optionPrefix)) { shown = shown.filter(item => item !== optionPrefix); } else { - shown = shown.concat(optionPrefix); + shown = shown.concat(optionPrefix); } return shown; }, @@ -163,7 +159,7 @@ window.dash_clientside.wo_classroom_text_highlighter = { closeOptions: function (clicks, shown) { if (!clicks) { return window.dash_clientside.no_update; } shown = shown.filter(item => item !== 'wo-classroom-text-highlighter-options'); - return shown + return shown; }, adjustTileSize: function (width, height, studentIds) { @@ -189,7 +185,7 @@ window.dash_clientside.wo_classroom_text_highlighter = { */ populateOutput: async function (wsStorageData, options, width, height, showHeader) { // console.log('wsStorageData', wsStorageData); - if (!wsStorageData) { + if (!wsStorageData?.students) { return 'No students'; } let output = []; @@ -202,16 +198,15 @@ window.dash_clientside.wo_classroom_text_highlighter = { const selectedMetrics = options.filter(option => option.types?.metric?.value); const optionHash = await hashObject(options); - - for (const student in wsStorageData) { + const students = wsStorageData.students; + for (const student in students) { + const selectedDocument = students[student].doc_id || Object.keys(students[student].documents || {})[0] || ''; const studentTile = createDashComponent( LO_DASH_REACT_COMPONENTS, 'WOStudentTextTile', { showHeader, - studentInfo: formatStudentData(wsStorageData[student], selectedHighlights), - // TODO the selectedDocument ought to remain the same upon updating the student object - // i.e. it should be pulled from the current client student state - selectedDocument: 'latest', + studentInfo: formatStudentData(students[student], selectedHighlights), + selectedDocument, childComponent: createDashComponent(LO_DASH_REACT_COMPONENTS, 'WOAnnotatedText', {}), id: { type: 'WOStudentTextTile', index: student }, currentOptionHash: optionHash, @@ -227,17 +222,17 @@ window.dash_clientside.wo_classroom_text_highlighter = { createDashComponent( DASH_BOOTSTRAP_COMPONENTS, 'Button', { - id: { type: 'WOStudentTileExpand', index: student}, - children: createDashComponent(DASH_HTML_COMPONENTS, 'I', {className: 'fas fa-expand'}), + id: { type: 'WOStudentTileExpand', index: student }, + children: createDashComponent(DASH_HTML_COMPONENTS, 'I', { className: 'fas fa-expand' }), class_name: 'position-absolute top-0 end-0 m-1', - color: 'light' + color: 'transparent' } ) ], - id: { type: 'WOStudentTile', index: student}, + id: { type: 'WOStudentTile', index: student }, style: styleStudentTile(width, height) } - ) + ); output = output.concat(tileWrapper); } return output; @@ -271,12 +266,13 @@ window.dash_clientside.wo_classroom_text_highlighter = { updateLoadingInformation: async function (wsStorageData, nlpOptions) { const noLoading = [false, 0, '']; - if (!wsStorageData) { + if (!wsStorageData?.students) { return noLoading; } + const students = wsStorageData.students; const promptHash = await hashObject(nlpOptions); - const returnedResponses = Object.values(wsStorageData).filter(student => checkForResponse(student, promptHash)).length; - const totalStudents = Object.keys(wsStorageData).length; + const returnedResponses = Object.values(students).filter(student => checkForResponse(student, promptHash)).length; + const totalStudents = Object.keys(students).length; if (totalStudents === returnedResponses) { return noLoading; } const loadingProgress = returnedResponses / totalStudents + 0.1; const outputText = `Fetching responses from server. This will take a few minutes. (${returnedResponses}/${totalStudents} received)`; @@ -292,44 +288,44 @@ window.dash_clientside.wo_classroom_text_highlighter = { if (!currentChild) { return window.dash_clientside.no_update; } id = currentChild?.props.id.index; } else if (triggeredItem?.type === 'WOStudentTileExpand') { - id = triggeredItem?.index + id = triggeredItem?.index; shownPanels = shownPanels.concat('wo-classroom-text-highlighter-expanded-student-panel'); } else { return window.dash_clientside.no_update; } - index = ids.findIndex(item => item.index === id); - child = children[index][0] - return [child, shownPanels] + const index = ids.findIndex(item => item.index === id); + child = children[index][0]; + return [child, shownPanels]; }, closeExpandedStudent: function (clicks, shown) { if (!clicks) { return window.dash_clientside.no_update; } shown = shown.filter(item => item !== 'wo-classroom-text-highlighter-expanded-student-panel'); - return shown + return shown; }, updateLegend: function (options) { const selectedHighlights = options.filter(option => option.types?.highlight?.value); if (selectedHighlights.length === 0) { - return ['No options selected. Click on the `Highlight Options` to select them.', 0] + return ['No options selected. Click on the `Highlight Options` to select them.', 0]; } let output = selectedHighlights.map(highlight => { - const color = highlight.types.highlight.color + const color = highlight.types.highlight.color; const legendItem = createDashComponent( DASH_HTML_COMPONENTS, 'Div', { children: [ createDashComponent( DASH_HTML_COMPONENTS, 'Span', - { style: { width: '0.875rem', height: '0.875rem', backgroundColor: color, display: 'inline-block', marginRight: '0.5rem' }} + { style: { width: '0.875rem', height: '0.875rem', backgroundColor: color, display: 'inline-block', marginRight: '0.5rem' } } ), highlight.label ] } - ) - return legendItem + ); + return legendItem; }); - output = output.concat('Note: words in the student text may have multiple highlights. Hover over a word for the full list of which options apply') + output = output.concat('Note: words in the student text may have multiple highlights. Hover over a word for the full list of which options apply'); return [output, selectedHighlights.length]; } }; diff --git a/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/dash_dashboard.py b/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/dash_dashboard.py index add6b385f..c02c9c0f0 100644 --- a/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/dash_dashboard.py +++ b/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/dash_dashboard.py @@ -36,6 +36,7 @@ _options_close = f'{_prefix}-options-close' # TODO abstract these into a more generic options component _options_prefix = f'{_prefix}-options' +_options_doc_src = f'{_options_prefix}-document-source' _options_width = f'{_options_prefix}-width' _options_height = f'{_options_prefix}-height' _options_hide_header = f'{_options_prefix}-hide-names' @@ -49,13 +50,18 @@ className='float-end', id=_options_close, color='transparent'), ]), - html.H4('View Options'), - dbc.Label('Students per row'), - dbc.Input(type='number', min=1, max=10, value=3, step=1, id=_options_width), - dbc.Label('Height of student tile'), - dcc.Slider(min=100, max=800, marks=None, value=500, id=_options_height), - dbc.Label('Student name headers'), - dbc.Switch(value=True, id=_options_hide_header, label='Show/Hide'), + lodrc.LODocumentSourceSelectorAIO(aio_id=_options_doc_src), + dbc.Card([ + dbc.CardHeader('View Options'), + dbc.CardBody([ + dbc.Label('Students per row'), + dbc.Input(type='number', min=1, max=10, value=3, step=1, id=_options_width), + dbc.Label('Height of student tile'), + dcc.Slider(min=100, max=800, marks=None, value=500, id=_options_height), + dbc.Label('Student name headers'), + dbc.Switch(value=True, id=_options_hide_header, label='Show/Hide'), + ]) + ]), html.H4('Highlight Options'), wo_classroom_text_highlighter.preset_component.create_layout(), lodrc.WOSettings(id=_options_text_information, options=wo_classroom_text_highlighter.options.OPTIONS, className='table table-striped align-middle') @@ -127,7 +133,9 @@ def layout(): html.Div(id=_output, className='d-flex justify-content-between flex-wrap'), panels=[ {'children': options_component, 'width': '30%', 'id': _options_prefix, 'side': 'left' }, - {'children': expanded_student_component, 'width': '30%', 'id': _expanded_student_panel, 'side': 'right' } + {'children': expanded_student_component, + 'width': '30%', 'id': _expanded_student_panel, + 'side': 'right', 'className': 'vh-100 overflow-auto'} ], id=_options_collapse, shown=[] ), @@ -143,6 +151,7 @@ def layout(): Output(lodrc.LOConnectionAIO.ids.websocket(_websocket), 'send'), Input(lodrc.LOConnectionAIO.ids.websocket(_websocket), 'state'), # used for initial setup Input('_pages_location', 'hash'), + Input(lodrc.LODocumentSourceSelectorAIO.ids.kwargs_store(_options_doc_src), 'data'), Input(_options_text_information, 'options') ) diff --git a/modules/writing_observer/VERSION b/modules/writing_observer/VERSION index a8ceee523..2e2f7e4a0 100644 --- a/modules/writing_observer/VERSION +++ b/modules/writing_observer/VERSION @@ -1 +1 @@ -0.1.0+2025.01.02T16.11.54.892Z.0ba3c08e.berickson.requirements.cleanup +0.1.0+2025.04.01T15.05.13.407Z.44725993.berickson.022025.gpt.dashboard.updates diff --git a/modules/writing_observer/writing_observer/aggregator.py b/modules/writing_observer/writing_observer/aggregator.py index 188de004a..85b3809c5 100644 --- a/modules/writing_observer/writing_observer/aggregator.py +++ b/modules/writing_observer/writing_observer/aggregator.py @@ -414,7 +414,7 @@ async def latest_data(runtime, student_data, options=None): @learning_observer.communication_protocol.integration.publish_function('google.fetch_assignment_docs') -async def fetch_assignment_docs(runtime, course_id, assignment_id): +async def fetch_assignment_docs(runtime, course_id, kwargs=None): ''' Invoke the Google API to retrieve a list of students, where each student possesses a collection of documents associated with the specified assignment. @@ -422,4 +422,24 @@ async def fetch_assignment_docs(runtime, course_id, assignment_id): I wasn't sure where to put this code, so I just tossed it here for now. This entire file needs a bit of reworking, what's a little more? ''' - return await learning_observer.google.assigned_docs(runtime, courseId=course_id, courseWorkId=assignment_id) + if kwargs is None: + kwargs = {} + assignment_id = kwargs.get('assignment') + output = [] + if assignment_id: + output = await learning_observer.google.assigned_docs(runtime, courseId=course_id, courseWorkId=assignment_id) + async for student in learning_observer.util.ensure_async_generator(output): + s = {} + s['doc_id'] = student['documents'][0]['id'] + # HACK a piece above the source selector in the communication protocol + # expects all items returned to have the same provenance. This mirrors + # the provenance that will be returned by the other sources. + # TODO modify the source selector to handle the provenance + provenance = { + 'provenance': {'STUDENT': { + 'value': {'user_id': student['user_id']}, + 'user_id': student['user_id'] + }} + } + s['provenance'] = provenance + yield s diff --git a/modules/writing_observer/writing_observer/document_timestamps.py b/modules/writing_observer/writing_observer/document_timestamps.py index b7c9f5285..4f872655d 100644 --- a/modules/writing_observer/writing_observer/document_timestamps.py +++ b/modules/writing_observer/writing_observer/document_timestamps.py @@ -13,6 +13,8 @@ def select_source(sources, source): within the protocol, we could make it so the system only runs the requested source node. TODO make this a dispatch type within the protocol + TODO add provenance at this layer. Each source might have a different + provenance structure. This should create one to use. ''' if source not in sources: raise KeyError(f'Source, `{source}`, not found in available sources: {sources.keys()}') @@ -20,15 +22,16 @@ def select_source(sources, source): @learning_observer.communication_protocol.integration.publish_function('writing_observer.fetch_doc_at_timestamp') -async def fetch_doc_at_timestamp(overall_timestamps, requested_timestamp=None): +async def fetch_doc_at_timestamp(overall_timestamps, kwargs=None): ''' Iterate over a list of students and determine their latest document - in reference to the `requested_timestamp`. + in reference to the `kwargs.requested_timestamp`. `requested_timestamp` should be a string of ms since unix epoch ''' - # output = [] - # TODO this should be an async gen + if kwargs is None: + kwargs = {} + requested_timestamp = kwargs.get('requested_timestamp', None) async for student in overall_timestamps: timestamps = student.get('timestamps', {}) student['doc_id'] = '' diff --git a/modules/writing_observer/writing_observer/module.py b/modules/writing_observer/writing_observer/module.py index da1f03ace..2c82bf5d4 100644 --- a/modules/writing_observer/writing_observer/module.py +++ b/modules/writing_observer/writing_observer/module.py @@ -78,6 +78,18 @@ nlp_source = learning_observer.settings.module_setting('writing_observer', setting='nlp_source') lt_single_source = learning_observer.settings.module_setting('writing_observer', setting='languagetool_individual_source') lt_group_source = learning_observer.settings.module_setting('writing_observer', setting='languagetool_source') + +gpt_bulk_essay = q.call('wo_bulk_essay_analysis.gpt_essay_prompt') + +# Document sources +document_sources = source_selector( + sources={'timestamp': q.variable('docs_at_ts'), + 'latest': q.variable('doc_ids'), + 'assignment': q.variable('assignment_docs') + }, + source=q.parameter('doc_source', required=False, default='latest') +) + EXECUTION_DAG = { "execution_dag": { "roster": course_roster(runtime=q.parameter("runtime"), course_id=q.parameter("course_id", required=True)), @@ -122,18 +134,28 @@ 'tagged_doc_list': q.join(LEFT=q.variable('unwind_tags'), RIGHT=q.variable('unwind_doc_list'), LEFT_ON='doc_id', RIGHT_ON='doc.id'), 'grouped_doc_list_by_student': group_docs_by(items=q.variable('tagged_doc_list'), value_path='provenance.provenance.value.user_id'), 'tagged_docs_per_student': q.join(LEFT=q.variable('roster'), RIGHT=q.variable('grouped_doc_list_by_student'), LEFT_ON='user_id', RIGHT_ON='user_id'), + # Student document list + 'document_list': q.select(q.keys('writing_observer.document_list', STUDENTS=q.variable('roster'), STUDENTS_path='user_id'), fields={'docs': 'availableDocuments'}), # the following nodes just fetches docs related to an assignment on Google Classroom - 'assignment_docs': assignment_documents(runtime=q.parameter('runtime'), course_id=q.parameter('course_id', required=True), assignment_id=q.parameter('assignment_id', required=True)), + 'assignment_docs': assignment_documents(runtime=q.parameter('runtime'), course_id=q.parameter('course_id', required=True), kwargs=q.parameter('doc_source_kwargs')), # fetch the doc less than or equal to a timestamp 'timestamped_docs': q.select(q.keys('writing_observer.document_access_timestamps', STUDENTS=q.variable('roster'), STUDENTS_path='user_id'), fields={'timestamps': 'timestamps'}), - 'docs_at_ts': document_access_ts(overall_timestamps=q.variable('timestamped_docs'), requested_timestamp=q.parameter('requested_timestamp')), + 'docs_at_ts': document_access_ts(overall_timestamps=q.variable('timestamped_docs'), kwargs=q.parameter('doc_source_kwargs')), # figure out where to source document ids from # current options include `ts` for a given timestamp # or `latest` for the most recently accessed - 'doc_sources': source_selector(sources={'ts': q.variable('docs_at_ts'), 'latest': q.variable('doc_ids')}, source=q.parameter('doc_source', required=False, default='latest')), + 'doc_sources': document_sources, + 'gpt_map': q.map( + gpt_bulk_essay, + values=q.variable('docs'), + value_path='text', + func_kwargs={'prompt': q.parameter('gpt_prompt'), 'system_prompt': q.parameter('system_prompt'), 'tags': q.parameter('tags', required=False, default={})}, + parallel=True + ), + 'gpt_bulk': q.join(LEFT=q.variable('gpt_map'), LEFT_ON='provenance.provenance.provenance.STUDENT.value.user_id', RIGHT=q.variable('roster'), RIGHT_ON='user_id'), }, "exports": { "docs_with_roster": { @@ -141,6 +163,26 @@ "parameters": ["course_id"], "output": "" }, + "roster": { + "returns": "roster", + "parameters": ["course_id"], + "output": "" + }, + "document_list": { + "returns": "document_list", + "parameters": ["course_id"], + "output": "" + }, + "document_sources": { + "returns": "doc_sources", + "parameters": ["course_id"], + "output": "" + }, + 'gpt_bulk': { + 'returns': 'gpt_bulk', + 'parameters': ['course_id', 'gpt_prompt', 'system_prompt'], + 'output': '' + }, "docs_with_nlp_annotations": { "returns": "nlp_combined", "parameters": ["course_id", "nlp_options"], @@ -165,10 +207,6 @@ 'returns': 'tagged_docs_per_student', 'parameters': ['course_id', 'tag_path'] }, - 'assignment_docs': { - 'returns': 'assignment_docs', - 'parameters': ['course_id', 'assignment_id'] - }, 'latest_doc_ids': { 'returns': 'latest_doc_ids', 'parameters': ['course_id'] From 4470b701eb417cd8d95e7e7e5b20d45d7a891d0a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Apr 2025 11:23:36 -0400 Subject: [PATCH 34/88] Bump @babel/helpers in /modules/lo_dash_react_components (#217) Bumps [@babel/helpers](https://github.com/babel/babel/tree/HEAD/packages/babel-helpers) from 7.26.0 to 7.26.10. - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.26.10/packages/babel-helpers) --- updated-dependencies: - dependency-name: "@babel/helpers" dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .../package-lock.json | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/modules/lo_dash_react_components/package-lock.json b/modules/lo_dash_react_components/package-lock.json index 6e528a352..e3bd2774a 100644 --- a/modules/lo_dash_react_components/package-lock.json +++ b/modules/lo_dash_react_components/package-lock.json @@ -433,25 +433,25 @@ } }, "node_modules/@babel/helpers": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", - "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.10.tgz", + "integrity": "sha512-UPYc3SauzZ3JGgj87GgZ89JVdC5dj0AoetR5Bw6wj4niittNyFh6+eOGonYvJ1ao6B8lEa3Q3klS7ADZ53bc5g==", "license": "MIT", "dependencies": { - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.0" + "@babel/template": "^7.26.9", + "@babel/types": "^7.26.10" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", - "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.10.tgz", + "integrity": "sha512-6aQR2zGE/QFi8JpDLjUZEPYOs7+mhKXm86VaKFiLP35JQwQb6bwUE+XbvkH0EptsYhbNBSUGaUBLKqxH1xSgsA==", "license": "MIT", "dependencies": { - "@babel/types": "^7.26.0" + "@babel/types": "^7.26.10" }, "bin": { "parser": "bin/babel-parser.js" @@ -2031,14 +2031,14 @@ } }, "node_modules/@babel/template": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", - "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz", + "integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/parser": "^7.25.9", - "@babel/types": "^7.25.9" + "@babel/code-frame": "^7.26.2", + "@babel/parser": "^7.26.9", + "@babel/types": "^7.26.9" }, "engines": { "node": ">=6.9.0" @@ -2063,9 +2063,9 @@ } }, "node_modules/@babel/types": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", - "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.10.tgz", + "integrity": "sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ==", "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.25.9", From 9eea22fb94dc4042c086e84e424c84dc80df886c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Apr 2025 11:23:54 -0400 Subject: [PATCH 35/88] Bump @babel/runtime in /modules/lo_dash_react_components (#218) Bumps [@babel/runtime](https://github.com/babel/babel/tree/HEAD/packages/babel-runtime) from 7.26.0 to 7.27.0. - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.27.0/packages/babel-runtime) --- updated-dependencies: - dependency-name: "@babel/runtime" dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/lo_dash_react_components/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/lo_dash_react_components/package-lock.json b/modules/lo_dash_react_components/package-lock.json index e3bd2774a..eb2735ec8 100644 --- a/modules/lo_dash_react_components/package-lock.json +++ b/modules/lo_dash_react_components/package-lock.json @@ -2019,9 +2019,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", - "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz", + "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==", "license": "MIT", "dependencies": { "regenerator-runtime": "^0.14.0" From 7e282384b6225eb1502faf2bee3bd38d8a1f73c9 Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Thu, 3 Apr 2025 08:12:41 -0400 Subject: [PATCH 36/88] languagetool startup and highlight dashboard improvements LanguageTool startup is now mostly contained within LanguageTool. Additionally, languagetool now functions with an async generator in the communication protocol. Initially this PR was supposed to integrate LT options with the highlight dashboard but selecting errors to figure out which ones are being made makes zero sense. The options are there but not attached to the highlight dashboard as of now. We likely want some way of informing the teacher which LT items are occurring before presenting them highlight options. --- VERSION | 2 +- .../src/lib/components/WOSettings.react.js | 127 ++++++++++----- modules/wo_classroom_text_highlighter/VERSION | 2 +- .../assets/scripts.js | 59 ++++--- .../dash_dashboard.py | 16 +- .../wo_classroom_text_highlighter/options.py | 24 ++- .../preset_component.py | 3 +- .../writing_observer/awe_nlp.py | 2 +- .../writing_observer/languagetool.py | 60 ++----- .../writing_observer/languagetool_features.py | 128 +++++++++++++++ .../writing_observer/nlp_indicators.py | 147 ++++++++++-------- .../writing_observer/stub_nlp.py | 2 +- 12 files changed, 378 insertions(+), 194 deletions(-) create mode 100644 modules/writing_observer/writing_observer/languagetool_features.py diff --git a/VERSION b/VERSION index 2e2f7e4a0..276855ab5 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0+2025.04.01T15.05.13.407Z.44725993.berickson.022025.gpt.dashboard.updates +0.1.0+2025.04.02T18.12.55.218Z.4a854783.berickson.languagetool.highlight.integration diff --git a/modules/lo_dash_react_components/src/lib/components/WOSettings.react.js b/modules/lo_dash_react_components/src/lib/components/WOSettings.react.js index 2295b8f0b..9b431e8a2 100644 --- a/modules/lo_dash_react_components/src/lib/components/WOSettings.react.js +++ b/modules/lo_dash_react_components/src/lib/components/WOSettings.react.js @@ -13,81 +13,130 @@ function generateNewHighlightColor () { } function sortOptionsIntoTree (options) { - // Create a map of options by their ids const optionsMap = new Map(); options.forEach(option => optionsMap.set(option.id, option)); - // Initialize an array to store the sorted options const sortedOptions = []; - // Function to recursively add children to the sorted array function addChildren (parentId, depth) { options .filter(option => option.parent === parentId) .forEach(option => { sortedOptions.push({ ...option, depth }); - addChildren(option.id, depth + 1); // Recursively add children + addChildren(option.id, depth + 1); }); } - // Start by adding top-level items (those with an empty parent) addChildren('', 0); return sortedOptions; } -/** - * WOSettings is a generic settings interface. - * User can define - */ export default class WOSettings extends Component { constructor (props) { super(props); + this.state = { + collapsed: {} // Tracks which rows are collapsed + }; this.handleRowEvent = this.handleRowEvent.bind(this); this.renderRow = this.renderRow.bind(this); + this.toggleCollapse = this.toggleCollapse.bind(this); } handleRowEvent (event, key, type, colorPicker = false) { - const { setProps, options } = this.props; - const oldOptions = structuredClone(options); - const current = oldOptions.find(option => option.id === key); + const { setProps, value } = this.props; + const currentValue = structuredClone(value); + if (!(key in currentValue)) { + currentValue[key] = {}; + } if (colorPicker) { - current.types[type].color = event.target.value; + currentValue[key][type].color = event.target.value; } else { const { checked } = event.target; - current.types[type].value = checked; - current.types[type].color = current.types[type].color || generateNewHighlightColor(); + if (!(type in currentValue[key])) { + currentValue[key][type] = {}; + } + currentValue[key][type].value = checked; + if (type === 'highlight') { + currentValue[key][type].color = currentValue[key][type].color || generateNewHighlightColor(); + } } - setProps({ options: oldOptions }); + setProps({ value: currentValue }); + } + + toggleCollapse (id) { + this.setState(prevState => ({ + collapsed: { + ...prevState.collapsed, + [id]: !prevState.collapsed[id] + } + })); } - renderRow (row) { - const highlightCell = (row.types && 'highlight' in row.types) + renderRow (row, allRows) { + const { collapsed } = this.state; + const { value } = this.props; + const hasChildren = allRows.some(option => option.parent === row.id); + const isCollapsed = collapsed[row.id] || false; + + const highlightCell = row.types && 'highlight' in row.types ? (<> - this.handleRowEvent(e, row.id, 'highlight')} /> - {row.types.highlight.value - ? this.handleRowEvent(e, row.id, 'highlight', true)} /> + this.handleRowEvent(e, row.id, 'highlight')} + /> + {value[row.id]?.highlight.value + ? ( this.handleRowEvent(e, row.id, 'highlight', true)} + />) : null} ) : null; const metricCell = (row.types && 'metric' in row.types) - ? this.handleRowEvent(e, row.id, 'metric')} /> + ? this.handleRowEvent(e, row.id, 'metric')} /> : null; + return ( - - {'\u00A0'.repeat(row.depth * 2) + row.label} - {highlightCell} - {/* {metricCell} */} - + <> + + +
{row.label}
+ {hasChildren && ( + + )} + + {highlightCell} + {metricCell} + + {/* Render children rows if not collapsed */} + {!isCollapsed && + allRows + .filter(child => child.parent === row.id) + .map(child => this.renderRow(child, allRows))} + ); } render () { const { id, className, options } = this.props; const rows = sortOptionsIntoTree(options); - // TODO due to a HACK with passing data to the child component of - // the student tiles, we currently only support a single child and - // expect it to be the highlighted text component. + return ( - {/* */} + - {rows.map((r) => this.renderRow(r))} + {rows + .filter(row => row.parent === '') // Start with top-level rows + .map(row => this.renderRow(row, rows))}
Name HighlightMetricMetric
); } -}; +} WOSettings.defaultProps = { id: '', className: '', - options: {} + options: [], + value: {} }; WOSettings.propTypes = { @@ -143,6 +195,11 @@ WOSettings.propTypes = { PropTypes.object, PropTypes.undefined ]) - })) + })), + + /** + * Dictionary of selected items + */ + value: PropTypes.object }; diff --git a/modules/wo_classroom_text_highlighter/VERSION b/modules/wo_classroom_text_highlighter/VERSION index 2e2f7e4a0..276855ab5 100644 --- a/modules/wo_classroom_text_highlighter/VERSION +++ b/modules/wo_classroom_text_highlighter/VERSION @@ -1 +1 @@ -0.1.0+2025.04.01T15.05.13.407Z.44725993.berickson.022025.gpt.dashboard.updates +0.1.0+2025.04.02T18.12.55.218Z.4a854783.berickson.languagetool.highlight.integration diff --git a/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/assets/scripts.js b/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/assets/scripts.js index 683081ea0..6ae74e4da 100644 --- a/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/assets/scripts.js +++ b/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/assets/scripts.js @@ -17,11 +17,12 @@ function createDashComponent (namespace, type, props) { return { namespace, type, props }; } -function determineSelectedNLPOptionsList (options) { - return options.filter(option => - (option.types?.highlight?.value === true) || - (option.types?.metric?.value === true) - ).map(option => option.id); +function determineSelectedNLPOptionsList (optionsObj) { + if (optionsObj === undefined | optionsObj === null) { return []; } + return Object.keys(optionsObj).filter(id => + optionsObj[id].highlight?.value === true || + optionsObj[id].metric?.value === true + ); } // TODO this ought to move to a more common place like liblo.js @@ -71,7 +72,7 @@ function formatStudentData (student, selectedHighlights) { tooltip: option.label, start: offset[0], offset: offset[1], - style: { backgroundColor: option.types.highlight.color } + style: { backgroundColor: option.highlight.color } }; }); acc = acc.concat(modifiedOffsets); @@ -109,7 +110,7 @@ window.dash_clientside.wo_classroom_text_highlighter = { * @param {string} urlHash query string from hash for determining course id * @returns stringified json object that is sent to the communication protocl */ - sendToLOConnection: async function (wsReadyState, urlHash, docKwargs, fullOptions) { + sendToLOConnection: async function (wsReadyState, urlHash, docKwargs, nlpValue) { if (wsReadyState === undefined) { return window.dash_clientside.no_update; } @@ -118,8 +119,8 @@ window.dash_clientside.wo_classroom_text_highlighter = { const decodedParams = decode_string_dict(urlHash.slice(1)); if (!decodedParams.course_id) { return window.dash_clientside.no_update; } - const optionsHash = await hashObject(fullOptions); - const nlpOptions = determineSelectedNLPOptionsList(fullOptions); + const optionsHash = await hashObject(nlpValue); + const nlpOptions = determineSelectedNLPOptionsList(nlpValue); decodedParams.nlp_options = nlpOptions; decodedParams.option_hash = optionsHash; decodedParams.doc_source = docKwargs.src; @@ -172,8 +173,8 @@ window.dash_clientside.wo_classroom_text_highlighter = { return Array(total).fill(show ? 'd-none' : ''); }, - updateCurrentOptionHash: async function (options, ids) { - const optionHash = await hashObject(options); + updateCurrentOptionHash: async function (value, ids) { + const optionHash = await hashObject(value); const total = ids.length; return Array(total).fill(optionHash); }, @@ -183,21 +184,33 @@ window.dash_clientside.wo_classroom_text_highlighter = { * @param {*} wsStorageData information stored in the websocket store * @returns Dash object to be displayed on page */ - populateOutput: async function (wsStorageData, options, width, height, showHeader) { + populateOutput: async function (wsStorageData, value, width, height, showHeader, options) { // console.log('wsStorageData', wsStorageData); if (!wsStorageData?.students) { return 'No students'; } let output = []; - const selectedHighlights = options.filter(option => option.types?.highlight?.value); + const selectedHighlights = options.reduce(function(filtered, option) { + if (value?.[option.id]?.highlight?.value) { + const selected = {...option, ...value[option.id]}; + filtered.push(selected); + } + return filtered; + }, []); // TODO do something with the selected metrics/progress bars/etc. // currently due to a HACK with how we pass data to the `childComponent` // we are only able to have a single child and we expect it to be the // `WOAnnotatedText` component. - const selectedMetrics = options.filter(option => option.types?.metric?.value); + const selectedMetrics = options.reduce(function(filtered, option) { + if (value?.[option.id]?.metric?.value) { + const selected = {...option, ...value[option.id]}; + filtered.push(selected); + } + return filtered; + }, []); - const optionHash = await hashObject(options); + const optionHash = await hashObject(value); const students = wsStorageData.students; for (const student in students) { const selectedDocument = students[student].doc_id || Object.keys(students[student].documents || {})[0] || ''; @@ -264,13 +277,13 @@ window.dash_clientside.wo_classroom_text_highlighter = { return data[preset]; }, - updateLoadingInformation: async function (wsStorageData, nlpOptions) { + updateLoadingInformation: async function (wsStorageData, nlpValue) { const noLoading = [false, 0, '']; if (!wsStorageData?.students) { return noLoading; } const students = wsStorageData.students; - const promptHash = await hashObject(nlpOptions); + const promptHash = await hashObject(nlpValue); const returnedResponses = Object.values(students).filter(student => checkForResponse(student, promptHash)).length; const totalStudents = Object.keys(students).length; if (totalStudents === returnedResponses) { return noLoading; } @@ -304,13 +317,19 @@ window.dash_clientside.wo_classroom_text_highlighter = { return shown; }, - updateLegend: function (options) { - const selectedHighlights = options.filter(option => option.types?.highlight?.value); + updateLegend: function (value, options) { + const selectedHighlights = options.reduce(function(filtered, option) { + if (value?.[option.id]?.highlight?.value) { + const selected = {...option, ...value[option.id]}; + filtered.push(selected); + } + return filtered; + }, []); if (selectedHighlights.length === 0) { return ['No options selected. Click on the `Highlight Options` to select them.', 0]; } let output = selectedHighlights.map(highlight => { - const color = highlight.types.highlight.color; + const color = highlight.highlight.color; const legendItem = createDashComponent( DASH_HTML_COMPONENTS, 'Div', { diff --git a/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/dash_dashboard.py b/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/dash_dashboard.py index c02c9c0f0..18712240e 100644 --- a/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/dash_dashboard.py +++ b/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/dash_dashboard.py @@ -152,7 +152,7 @@ def layout(): Input(lodrc.LOConnectionAIO.ids.websocket(_websocket), 'state'), # used for initial setup Input('_pages_location', 'hash'), Input(lodrc.LODocumentSourceSelectorAIO.ids.kwargs_store(_options_doc_src), 'data'), - Input(_options_text_information, 'options') + Input(_options_text_information, 'value') ) # Build the UI based on what we've received from the @@ -161,10 +161,11 @@ def layout(): ClientsideFunction(namespace=_namespace, function_name='populateOutput'), Output(_output, 'children'), Input(lodrc.LOConnectionAIO.ids.ws_store(_websocket), 'data'), - Input(_options_text_information, 'options'), + Input(_options_text_information, 'value'), Input(_options_width, 'value'), Input(_options_height, 'value'), Input(_options_hide_header, 'value'), + State(_options_text_information, 'options'), ) # Toggle if the options collapse is open or not @@ -206,7 +207,7 @@ def layout(): clientside_callback( ClientsideFunction(namespace=_namespace, function_name='updateCurrentOptionHash'), Output({'type': 'WOStudentTextTile', 'index': ALL}, 'currentOptionHash'), - Input(_options_text_information, 'options'), + Input(_options_text_information, 'value'), State({'type': 'WOStudentTextTile', 'index': ALL}, 'id'), ) @@ -248,14 +249,14 @@ def layout(): Output(wo_classroom_text_highlighter.preset_component._store, 'data'), Input(wo_classroom_text_highlighter.preset_component._add_button, 'n_clicks'), State(wo_classroom_text_highlighter.preset_component._add_input, 'value'), - State(_options_text_information, 'options'), + State(_options_text_information, 'value'), State(wo_classroom_text_highlighter.preset_component._store, 'data') ) # Apply clicked preset clientside_callback( ClientsideFunction(namespace=_namespace, function_name='applyPreset'), - Output(_options_text_information, 'options'), + Output(_options_text_information, 'value'), Input({'type': wo_classroom_text_highlighter.preset_component._set_item, 'index': ALL}, 'n_clicks'), State(wo_classroom_text_highlighter.preset_component._store, 'data'), prevent_initial_call=True @@ -268,7 +269,7 @@ def layout(): Output(_loading_progress, 'value'), Output(_loading_information, 'children'), Input(lodrc.LOConnectionAIO.ids.ws_store(_websocket), 'data'), - Input(_options_text_information, 'options') + Input(_options_text_information, 'value') ) # Update legend @@ -276,5 +277,6 @@ def layout(): ClientsideFunction(namespace=_namespace, function_name='updateLegend'), Output(_legend_children, 'children'), Output(_options_toggle_count, 'children'), - Input(_options_text_information, 'options') + Input(_options_text_information, 'value'), + State(_options_text_information, 'options') ) diff --git a/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/options.py b/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/options.py index 6b29036f1..1ca677c3c 100644 --- a/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/options.py +++ b/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/options.py @@ -1,18 +1,15 @@ -import copy import writing_observer.nlp_indicators +import writing_observer.languagetool_features parents = [] OPTIONS = [ - {'id': indicator['id'], 'types': {'highlight': {}, 'metric': {}}, 'label': indicator['name'], 'parent': ''} + {'id': indicator['id'], 'types': ['highlight'], 'label': indicator['name'], 'parent': indicator['category']} for indicator in writing_observer.nlp_indicators.INDICATOR_JSONS ] - -# TODO currently each preset is the full list of options with specific -# values being set to true/including a color. We ought to just store -# the true values and their respective colors. -# Though if we keep the entire list in the preset, we can choose colors -# for non-true values before they are selected. +for category, label in writing_observer.nlp_indicators.INDICATOR_CATEGORIES.items(): + OPTIONS.append({'id': category, 'label': label, 'parent': 'text_information'}) +OPTIONS.append({'id': 'text_information', 'label': 'Text Information', 'parent': ''}) # Set of colors to use for highlighting with presets HIGHLIGHTING_COLORS = [ @@ -55,13 +52,10 @@ def add_preset_to_presets(key, value): from the `PRESETS_TO_CREATE` object. ''' color_index = 0 - preset = copy.deepcopy(OPTIONS) - for option in preset: - if option['id'] in value: - option['types']['highlight']['value'] = True - option['types']['highlight']['color'] = HIGHLIGHTING_COLORS[color_index] - color_index += 1 - + preset = {} + for option in value: + preset[option] = {'highlight': {'value': True, 'color': HIGHLIGHTING_COLORS[color_index]}} + color_index += 1 PRESETS[key] = preset diff --git a/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/preset_component.py b/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/preset_component.py index dd9c86c69..5a142d56f 100644 --- a/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/preset_component.py +++ b/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/preset_component.py @@ -28,8 +28,9 @@ def create_layout(): add_preset, html.Div(id=_tray), # TODO we ought to store the presets on the server instead of browser storage + # TODO we need to migrate the old options to new ones dcc.Store(id=_store, data=wo_classroom_text_highlighter.options.PRESETS, storage_type='local') - ]) + ], id=_prefix) # disabled add preset when name already exists diff --git a/modules/writing_observer/writing_observer/awe_nlp.py b/modules/writing_observer/writing_observer/awe_nlp.py index 8eb616204..b6d286fd9 100644 --- a/modules/writing_observer/writing_observer/awe_nlp.py +++ b/modules/writing_observer/writing_observer/awe_nlp.py @@ -154,7 +154,7 @@ def process_text(text, options=None): if item not in writing_observer.nlp_indicators.INDICATORS: continue indicator = writing_observer.nlp_indicators.INDICATORS[item] - (id, label, infoType, select, filterInfo, summaryType) = indicator + (id, label, infoType, select, filterInfo, summaryType, category) = indicator results[id] = outputIndicator(doc, select, infoType, stype=summaryType, text=text, added_filter=filterInfo) results[id].update({ "label": label, diff --git a/modules/writing_observer/writing_observer/languagetool.py b/modules/writing_observer/writing_observer/languagetool.py index 9a4d3e750..c9fdecd70 100644 --- a/modules/writing_observer/writing_observer/languagetool.py +++ b/modules/writing_observer/writing_observer/languagetool.py @@ -1,5 +1,4 @@ import pmss -import requests import learning_observer.cache import learning_observer.communication_protocol.integration @@ -13,6 +12,10 @@ client = None DEFAULT_PORT = 8081 lt_started = False +# TODO fill this in +STUB_LANGUAGETOOL_OUTPUT = { + 'languagetool_stub': 'This is stub output. It will probably break something until we clean this up.' +} pmss.register_field( name='use_languagetool', @@ -47,46 +50,22 @@ def check_languagetool_running(): if learning_observer.settings.module_setting('writing_observer', 'use_languagetool'): host = learning_observer.settings.module_setting('writing_observer', 'languagetool_host') port = learning_observer.settings.module_setting('writing_observer', 'languagetool_port') + # TODO LanguageTool Client also accepts a full server url, we ought to fetch that from pmss - # HACK the following code is a hack to check if the LanguageTool Server is up and running or not - # We ought to set the LT Client object on startup (here); however, - # the LT Client has no way of telling us whether the Server is running or not. - # i.e. the LT Client runs normally even without the server running. - # Thus, we manually make a request to the server to and check the response. - global lt_started + global client, lt_started try: - resp = requests.get(f'http://{host}:{port}/v2/check', params={'text': 'test', 'language': 'en-US'}) - lt_started = resp.status_code == 200 - except requests.ConnectionError: - pass - - if not lt_started: + client = languagetoolClient.languagetoolClient(port=port, host=host) + lt_started = True + except RuntimeError as e: raise learning_observer.prestartup.StartupCheck( - f'LanguageTool Server was not found running on port {port}.\n' - 'Please make sure to run the LanguageTool Server before starting Learning Observer.\n' - 'From within your Python environment, run\n' - '```python\nfrom awe_languagetool import languagetoolServer\nlanguagetoolServer.runServer()\n```.\n' - 'If the LanguageTool is already running on a diffrent port, make sure to adjust ' - 'the `writing_observer.languagetool_port` setting in the `creds.yaml`.' - ) + f'Unable to start LanguageTool Client.\n{e}' + ) from e else: debug_log('WARNING:: We are not configured to try and use to LanguageTool. '\ 'Set `modules.writing_observer.use_languagetool: true` in `creds.yaml` '\ 'to enable the usage of the LanguageTool client.') -def initialize_client(): - ''' - Language Tool requires a client to connect with the server. - This method checks to see if we've created a client yet and - initializes one if we haven't. - ''' - global client - if client is None: - port = learning_observer.settings.module_setting('writing_observer', 'languagetool_port') - client = languagetoolClient.languagetoolClient(port=port) - - @learning_observer.communication_protocol.integration.publish_function('writing_observer.languagetool') async def process_texts(texts): ''' @@ -95,24 +74,13 @@ async def process_texts(texts): We use a closure to allow the system to initialize the memoization KVS. ''' - - initialize_client() - - if not lt_started: - error_text = 'The LanguageTool server has not started. '\ - 'Set `modules.writing_observer.use_languagetool: true` in `creds.yaml` '\ - 'to enable the usage of the LanguageTool client.' - raise ConnectionError(error_text) - @learning_observer.cache.async_memoization() async def process_text(text): return await client.summarizeText(text) - output = [] - for t in texts: + async for t in texts: text = t.get('text', '') - text_data = await process_text(text) + text_data = await process_text(text) if lt_started else STUB_LANGUAGETOOL_OUTPUT text_data['text'] = text text_data['provenance'] = t['provenance'] - output.append(text_data) - return output + yield text_data diff --git a/modules/writing_observer/writing_observer/languagetool_features.py b/modules/writing_observer/writing_observer/languagetool_features.py new file mode 100644 index 000000000..a76350c16 --- /dev/null +++ b/modules/writing_observer/writing_observer/languagetool_features.py @@ -0,0 +1,128 @@ +'''This file lists all the options for LanguageTool (LT) +so we can query specific items. + +Note that all features are calculated at once when +querying LT. Listing all the features here allows +dashboards pull out specific items from the returned +query. + +TODO figure out the best data structure for storing +the available features +''' + +# Each tuple in AVAILABLE_ITEMS contains the full path of +# the categories. +AVAILABLE_ITEMS = [ + ('Capitalization', 'Unnecessary capitalization'), + ('Capitalization', 'Acronyms'), + ('Capitalization', 'Abbreviations'), + ('Capitalization', 'Hyphenated Letter Case'), + ('Capitalization', 'Sentence Start'), + ('Capitalization', 'Proper noun case'), + ('Capitalization', 'First person singular caps'), + ('Grammar', 'Extra function word'), + ('Grammar', 'Nonstandard copula'), + ('Grammar', 'Tag question error'), + ('Grammar', 'Missing function word'), + ('Grammar', 'Wrong verb tense'), + ('Grammar', 'Conjunction error'), + ('Grammar', 'Bare possessive'), + ('Grammar', 'Wrong word order'), + ('Grammar', 'Pronoun/antecedent agreement'), + ('Grammar', 'Plural error'), + ('Grammar', 'Missing content word'), + ('Grammar', 'Repeated words'), + ('Grammar', 'Pronoun case'), + ('Grammar', 'Article error'), + ('Grammar', 'Complement error'), + ('Grammar', 'Sentence fragment'), + ('Grammar', 'Comparative error'), + ('Grammar', 'Superlative error'), + ('Grammar', 'Tense error'), + ('Grammar', 'Wrong verb form'), + ('Grammar', 'Wrong part of speech'), + ('Grammar', 'Subject/verb agreement'), + ('Grammar', 'Wrong past participle'), + ('Grammar', 'Negation error'), + ('Possible Typo', 'Misplaced space'), + ('Possible Typo', 'Missing space'), + ('Possible Typo', 'Incorrect plural possessive'), + ('Possible Typo', 'Wrong case'), + ('Possible Typo', 'Missing hyphen'), + ('Possible Typo', 'Wrong contraction'), + ('Possible Typo', 'Possessive as contraction'), + ('Possible Typo', 'Wrong letter'), + ('Possible Typo', 'Terminology'), + ('Possible Typo', 'Reversed letters'), + ('Possible Typo', 'Possessive as plural'), + ('Possible Typo', 'Extra space'), + ('Possible Typo', 'Bare possessive'), + ('Possible Typo', 'Missing punctuation'), + ('Possible Typo', 'Extra letter'), + ('Possible Typo', 'Missing period'), + ('Possible Typo', 'Plural as possessive'), + ('Possible Typo', 'Repeated words'), + ('Possible Typo', 'Contraction without apostrophe'), + ('Possible Typo', 'Missing letter'), + ('Punctuation', 'Missing end punctuation'), + ('Punctuation', 'Oxford comma'), + ('Punctuation', 'Conjunction'), + ('Punctuation', 'Abbreviation'), + ('Punctuation', 'Comparison'), + ('Punctuation', 'Restrictive modifier'), + ('Punctuation', 'Compound sentence'), + ('Punctuation', 'Compound adjective'), + ('Punctuation', 'Sentence modifier'), + ('Punctuation', 'Vocative'), + ('Semantics', 'Date inconsistency'), + ('Semantics', 'Meaning mismatch'), + ('Spelling', 'Number in Name'), + ('Spelling', 'Misused idiom'), + ('Spelling', 'Missing letter'), + ('Spelling', 'Extra letter'), + # ('Spelling', 'Possible Typo'), + ('Spelling', 'Added or dropped t/ed'), + ('Spelling', 'Phonetic spelling'), + ('Spelling', 'Inconsistent Spelling'), + ('Spelling', 'Homonym'), + ('Spelling', 'Unknown word'), + ('Style', 'Formality'), + ('Style', 'Word choice'), + ('Style', 'Repetition'), + ('Style', 'Wikipedia'), + ('Style', 'British English'), + ('Style', 'Readability'), + ('Style', 'Profanity'), + ('Style', 'Awkward language'), + ('Style', 'Redundancy'), + ('Style', 'Informal'), + ('Style', 'Indian English Only'), + ('Typography', 'Roman Numerals'), + ('Typography', 'Math'), + ('Typography', 'Date format'), + ('Typography', 'Missing space'), + ('Typography', 'Repeated punctuation'), + ('Typography', 'Headings'), + ('Typography', 'Redundant punctuation'), + ('Typography', 'Extra whitespace'), + ('Typography', 'Paragraph Indent'), + ('Typography', 'Time format'), + ('Typography', 'Special character'), + ('Typography', 'Currency'), + ('Usage', 'Confused words'), + ('Usage', 'Missing preposition'), + ('Usage', 'Misused idiom'), + ('Usage', 'Wrong preposition'), + ('Usage', 'Wrong verb'), + ('Usage', 'Extra preposition'), + ('Word Boundaries', 'Missing hyphen'), + ('Word Boundaries', 'Extra space'), + ('Word Boundaries', 'Prefix as word'), + ('Word Boundaries', 'Missing space') +] + +CATEGORIES = [ + 'Grammar', 'Semantics', 'Style', 'Usage', + 'Capitalization', 'Possible Typo', 'Punctuation', + 'Spelling', 'Typography', 'Word Boundaries' +] diff --git a/modules/writing_observer/writing_observer/nlp_indicators.py b/modules/writing_observer/writing_observer/nlp_indicators.py index f3468e761..38c307fdc 100644 --- a/modules/writing_observer/writing_observer/nlp_indicators.py +++ b/modules/writing_observer/writing_observer/nlp_indicators.py @@ -21,92 +21,92 @@ # filter (if needed), summary function to use SPAN_INDICATORS = [ # language - ('Academic Language', 'Token', 'is_academic', None, 'percent'), - ('Informal Language', 'Token', 'vwp_interactive', None, 'percent'), - ('Latinate Words', 'Token', 'is_latinate', None, 'percent'), - ('Opinion Words', 'Token', 'vwp_evaluation', None, 'total'), - ('Emotion Words', 'Token', 'vwp_emotionword', None, 'percent'), + ('Academic Language', 'Token', 'is_academic', None, 'percent', 'language'), + ('Informal Language', 'Token', 'vwp_interactive', None, 'percent', 'language'), + ('Latinate Words', 'Token', 'is_latinate', None, 'percent', 'language'), + ('Opinion Words', 'Token', 'vwp_evaluation', None, 'total', 'language'), + ('Emotion Words', 'Token', 'vwp_emotionword', None, 'percent', 'language'), # vwp_emotion_states looks for noun/emotion word pairs (takes a lot of resources) - ignoring for now # Argumentation # ('Argumentation', 'Token', 'vwp_argumentation', None, 'percent'), # most resource heavy - ignoring for now - ('Argument Words', 'Token', 'vwp_argumentword', None, 'percent'), # more surfacey # TODO needs new label - ('Explicit argument', 'Token', 'vwp_explicit_argument', None, 'percent'), # surfacey # TODO needs new label + ('Argument Words', 'Token', 'vwp_argumentword', None, 'percent', 'argumentation'), # more surfacey # TODO needs new label + ('Explicit argument', 'Token', 'vwp_explicit_argument', None, 'percent', 'argumentation'), # surfacey # TODO needs new label # statements - ('Statements of Opinion', 'Doc', 'vwp_statements_of_opinion', None, 'percent'), - ('Statements of Fact', 'Doc', 'vwp_statements_of_fact', None, 'percent'), + ('Statements of Opinion', 'Doc', 'vwp_statements_of_opinion', None, 'percent', 'statements'), + ('Statements of Fact', 'Doc', 'vwp_statements_of_fact', None, 'percent', 'statements'), # Transitions # eventually we want to exclude \n\n as transitions using `[('!=', ['introductory'])]` # however the introductory category also includes "let us" and "let's" # no highlighting is shown on the new lines, so we won't remove it for now. - ('Transition Words', 'Doc', 'transitions', None, 'counts'), + ('Transition Words', 'Doc', 'transitions', None, 'counts', 'transitions'), # - ('Positive Transition Words', 'Doc', 'transitions', [('==', ['positive'])], 'total'), - ('Conditional Transition Words', 'Doc', 'transitions', [('==', ['conditional'])], 'total'), - ('Consequential Transition Words', 'Doc', 'transitions', [('==', ['consequential'])], 'total'), - ('Contrastive Transition Words', 'Doc', 'transitions', [('==', ['contrastive'])], 'total'), - ('Counterpoint Transition Words', 'Doc', 'transitions', [('==', ['counterpoint'])], 'total'), - ('Comparative Transition Words', 'Doc', 'transitions', [('==', ['comparative'])], 'total'), - ('Cross Referential Transition Words', 'Doc', 'transitions', [('==', ['crossreferential'])], 'total'), - ('Illustrative Transition Words', 'Doc', 'transitions', [('==', ['illustrative'])], 'total'), - ('Negative Transition Words', 'Doc', 'transitions', [('==', ['negative'])], 'total'), - ('Emphatic Transition Words', 'Doc', 'transitions', [('==', ['emphatic'])], 'total'), - ('Evenidentiary Transition Words', 'Doc', 'transitions', [('==', ['evidentiary'])], 'total'), - ('General Transition Words', 'Doc', 'transitions', [('==', ['general'])], 'total'), - ('Ordinal Transition Words', 'Doc', 'transitions', [('==', ['ordinal'])], 'total'), - ('Purposive Transition Words', 'Doc', 'transitions', [('==', ['purposive'])], 'total'), - ('Periphrastic Transition Words', 'Doc', 'transitions', [('==', ['periphrastic'])], 'total'), - ('Hypothetical Transition Words', 'Doc', 'transitions', [('==', ['hypothetical'])], 'total'), - ('Summative Transition Words', 'Doc', 'transitions', [('==', ['summative'])], 'total'), - ('Introductory Transition Words', 'Doc', 'transitions', [('==', ['introductory'])], 'total'), + ('Positive Transition Words', 'Doc', 'transitions', [('==', ['positive'])], 'total', 'transitions'), + ('Conditional Transition Words', 'Doc', 'transitions', [('==', ['conditional'])], 'total', 'transitions'), + ('Consequential Transition Words', 'Doc', 'transitions', [('==', ['consequential'])], 'total', 'transitions'), + ('Contrastive Transition Words', 'Doc', 'transitions', [('==', ['contrastive'])], 'total', 'transitions'), + ('Counterpoint Transition Words', 'Doc', 'transitions', [('==', ['counterpoint'])], 'total', 'transitions'), + ('Comparative Transition Words', 'Doc', 'transitions', [('==', ['comparative'])], 'total', 'transitions'), + ('Cross Referential Transition Words', 'Doc', 'transitions', [('==', ['crossreferential'])], 'total', 'transitions'), + ('Illustrative Transition Words', 'Doc', 'transitions', [('==', ['illustrative'])], 'total', 'transitions'), + ('Negative Transition Words', 'Doc', 'transitions', [('==', ['negative'])], 'total', 'transitions'), + ('Emphatic Transition Words', 'Doc', 'transitions', [('==', ['emphatic'])], 'total', 'transitions'), + ('Evenidentiary Transition Words', 'Doc', 'transitions', [('==', ['evidentiary'])], 'total', 'transitions'), + ('General Transition Words', 'Doc', 'transitions', [('==', ['general'])], 'total', 'transitions'), + ('Ordinal Transition Words', 'Doc', 'transitions', [('==', ['ordinal'])], 'total', 'transitions'), + ('Purposive Transition Words', 'Doc', 'transitions', [('==', ['purposive'])], 'total', 'transitions'), + ('Periphrastic Transition Words', 'Doc', 'transitions', [('==', ['periphrastic'])], 'total', 'transitions'), + ('Hypothetical Transition Words', 'Doc', 'transitions', [('==', ['hypothetical'])], 'total', 'transitions'), + ('Summative Transition Words', 'Doc', 'transitions', [('==', ['summative'])], 'total', 'transitions'), + ('Introductory Transition Words', 'Doc', 'transitions', [('==', ['introductory'])], 'total', 'transitions'), # pos_ - ('Adjectives', 'Token', 'pos_', [('==', ['ADJ'])], 'total'), - ('Adverbs', 'Token', 'pos_', [('==', ['ADV'])], 'total'), - ('Nouns', 'Token', 'pos_', [('==', ['NOUN'])], 'total'), - ('Proper Nouns', 'Token', 'pos_', [('==', ['PROPN'])], 'total'), - ('Verbs', 'Token', 'pos_', [('==', ['VERB'])], 'total'), - ('Numbers', 'Token', 'pos_', [('==', ['NUM'])], 'total'), - ('Prepositions', 'Token', 'pos_', [('==', ['ADP'])], 'total'), - ('Coordinating Conjunction', 'Token', 'pos_', [('==', ['CCONJ'])], 'total'), - ('Subordinating Conjunction', 'Token', 'pos_', [('==', ['SCONJ'])], 'total'), - ('Auxiliary Verb', 'Token', 'pos_', [('==', ['AUX'])], 'total'), - ('Pronoun', 'Token', 'pos_', [('==', ['PRON'])], 'total'), + ('Adjectives', 'Token', 'pos_', [('==', ['ADJ'])], 'total', 'pos'), + ('Adverbs', 'Token', 'pos_', [('==', ['ADV'])], 'total', 'pos'), + ('Nouns', 'Token', 'pos_', [('==', ['NOUN'])], 'total', 'pos'), + ('Proper Nouns', 'Token', 'pos_', [('==', ['PROPN'])], 'total', 'pos'), + ('Verbs', 'Token', 'pos_', [('==', ['VERB'])], 'total', 'pos'), + ('Numbers', 'Token', 'pos_', [('==', ['NUM'])], 'total', 'pos'), + ('Prepositions', 'Token', 'pos_', [('==', ['ADP'])], 'total', 'pos'), + ('Coordinating Conjunction', 'Token', 'pos_', [('==', ['CCONJ'])], 'total', 'pos'), + ('Subordinating Conjunction', 'Token', 'pos_', [('==', ['SCONJ'])], 'total', 'pos'), + ('Auxiliary Verb', 'Token', 'pos_', [('==', ['AUX'])], 'total', 'pos'), + ('Pronoun', 'Token', 'pos_', [('==', ['PRON'])], 'total', 'pos'), # sentence variety # The general 'Sentence Types' will return a complex object of all sentence types # that we do not yet handle. # ('Sentence Types', 'Doc', 'sentence_types', None, 'counts'), - ('Simple Sentences', 'Doc', 'sentence_types', [('==', ['Simple'])], 'total'), - ('Simple with Complex Predicates', 'Doc', 'sentence_types', [('==', ['SimpleComplexPred'])], 'total'), - ('Simple with Compound Predicates', 'Doc', 'sentence_types', [('==', ['SimpleCompoundPred'])], 'total'), - ('Simple with Compound Complex Predicates', 'Doc', 'sentence_types', [('==', ['SimpleCompoundComplexPred'])], 'total'), - ('Compound Sentences', 'Doc', 'sentence_types', [('==', ['Compound'])], 'total'), - ('Complex Sentences', 'Doc', 'sentence_types', [('==', ['Complex'])], 'total'), - ('Compound Complex Sentences', 'Doc', 'sentence_types', [('==', ['CompoundComplex'])], 'total'), + ('Simple Sentences', 'Doc', 'sentence_types', [('==', ['Simple'])], 'total', 'sentence_type'), + ('Simple with Complex Predicates', 'Doc', 'sentence_types', [('==', ['SimpleComplexPred'])], 'total', 'sentence_type'), + ('Simple with Compound Predicates', 'Doc', 'sentence_types', [('==', ['SimpleCompoundPred'])], 'total', 'sentence_type'), + ('Simple with Compound Complex Predicates', 'Doc', 'sentence_types', [('==', ['SimpleCompoundComplexPred'])], 'total', 'sentence_type'), + ('Compound Sentences', 'Doc', 'sentence_types', [('==', ['Compound'])], 'total', 'sentence_type'), + ('Complex Sentences', 'Doc', 'sentence_types', [('==', ['Complex'])], 'total', 'sentence_type'), + ('Compound Complex Sentences', 'Doc', 'sentence_types', [('==', ['CompoundComplex'])], 'total', 'sentence_type'), # Sources/Attributes/Citations/Quotes - ('Information Sources', 'Token', 'vwp_source', None, 'percent'), - ('Attributions', 'Token', 'vwp_attribution', None, 'percent'), - ('Citations', 'Token', 'vwp_cite', None, 'percent'), - ('Quoted Words', 'Token', 'vwp_quoted', None, 'percent'), + ('Information Sources', 'Token', 'vwp_source', None, 'percent', 'source_information'), + ('Attributions', 'Token', 'vwp_attribution', None, 'percent', 'source_information'), + ('Citations', 'Token', 'vwp_cite', None, 'percent', 'source_information'), + ('Quoted Words', 'Token', 'vwp_quoted', None, 'percent', 'source_information'), # Dialogue - ('Direct Speech Verbs', 'Doc', 'vwp_direct_speech', None, 'percent'), - ('Indirect Speech', 'Token', 'vwp_in_direct_speech', None, 'percent'), + ('Direct Speech Verbs', 'Doc', 'vwp_direct_speech', None, 'percent', 'dialogue'), + ('Indirect Speech', 'Token', 'vwp_in_direct_speech', None, 'percent', 'dialogue'), # vwp_quoted - already used above # tone - ('Positive Tone', 'Token', 'vwp_tone', [('>', [.4])], 'percent'), - ('Negative Tone', 'Token', 'vwp_tone', [('<', [-.4])], 'percent'), + ('Positive Tone', 'Token', 'vwp_tone', [('>', [.4])], 'percent', 'tone'), + ('Negative Tone', 'Token', 'vwp_tone', [('<', [-.4])], 'percent', 'tone'), # details - ('Concrete Details', 'Token', 'concrete_details', None, 'percent'), - ('Main Idea Sentences', 'Doc', 'main_ideas', None, 'total'), - ('Supporting Idea Sentences', 'Doc', 'supporting_ideas', None, 'total'), - ('Supporting Detail Sentences', 'Doc', 'supporting_details', None, 'total'), + ('Concrete Details', 'Token', 'concrete_details', None, 'percent', 'details'), + ('Main Idea Sentences', 'Doc', 'main_ideas', None, 'total', 'details'), + ('Supporting Idea Sentences', 'Doc', 'supporting_ideas', None, 'total', 'details'), + ('Supporting Detail Sentences', 'Doc', 'supporting_details', None, 'total', 'details'), # Other items - ('Polysyllabic Words', 'Token', 'nSyll', [('>', [3])], 'percent'), - ('Low Frequency Words', 'Token', 'max_freq', [('<', [4])], 'percent'), - ('Sentences', 'Doc', 'sents', None, 'total'), - ('Paragraphs', 'Doc', 'delimiter_\n', None, 'total'), - ('Character Trait Words', 'Token', 'vwp_character', None, 'percent'), - ('In Past Tense', 'Token', 'in_past_tense_scope', None, 'percent'), - ('Explicit Claims', 'Doc', 'vwp_propositional_attitudes', None, 'percent'), - ('Social Awareness', 'Doc', 'vwp_social_awareness', None, 'percent') + ('Polysyllabic Words', 'Token', 'nSyll', [('>', [3])], 'percent', 'other'), + ('Low Frequency Words', 'Token', 'max_freq', [('<', [4])], 'percent', 'other'), + ('Sentences', 'Doc', 'sents', None, 'total', 'other'), + ('Paragraphs', 'Doc', 'delimiter_\n', None, 'total', 'other'), + ('Character Trait Words', 'Token', 'vwp_character', None, 'percent', 'other'), + ('In Past Tense', 'Token', 'in_past_tense_scope', None, 'percent', 'other'), + ('Explicit Claims', 'Doc', 'vwp_propositional_attitudes', None, 'percent', 'other'), + ('Social Awareness', 'Doc', 'vwp_social_awareness', None, 'percent', 'other') ] # Create indicator dict to easily refer to each tuple above by name @@ -125,8 +125,23 @@ class NLPIndicators(dataobject): parent: str filters: list function: str + category: str # tooltip: str indicators = map(lambda ind: NLPIndicators(*ind), INDICATOR_W_IDS) INDICATOR_JSONS = [asdict(ind) for ind in indicators] + +INDICATOR_CATEGORIES = { + 'language': 'Language', + 'argumentation': 'Argumentation', + 'statements': 'Statements', + 'transitions': 'Transition Words', + 'pos': 'Parts of Speech', + 'sentence_type': 'Sentence Types', + 'source_information': 'Source Information', + 'dialogue': 'Dialogue', + 'tone': 'Tone', + 'details': 'Details', + 'other': 'Other' +} diff --git a/modules/writing_observer/writing_observer/stub_nlp.py b/modules/writing_observer/writing_observer/stub_nlp.py index bca9c352b..a9ce89811 100644 --- a/modules/writing_observer/writing_observer/stub_nlp.py +++ b/modules/writing_observer/writing_observer/stub_nlp.py @@ -137,7 +137,7 @@ async def process_texts(writing_data, options=None): if option not in writing_observer.nlp_indicators.INDICATORS: continue indicator = writing_observer.nlp_indicators.INDICATORS[option] - (id, label, infoType, select, filterInfo, summaryType) = indicator + (id, label, infoType, select, filterInfo, summaryType, category) = indicator results[id] = {} state = random.getstate() random.seed(id) From c336d5da8bdcc85e6f394a4ef7120c848db9ae3f Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Thu, 3 Apr 2025 09:17:04 -0400 Subject: [PATCH 37/88] fixed module names --- VERSION | 2 +- modules/wo_bulk_essay_analysis/VERSION | 2 +- modules/wo_bulk_essay_analysis/setup.cfg | 2 +- modules/wo_classroom_text_highlighter/VERSION | 2 +- modules/wo_classroom_text_highlighter/setup.cfg | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/VERSION b/VERSION index 276855ab5..21b05a126 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0+2025.04.02T18.12.55.218Z.4a854783.berickson.languagetool.highlight.integration +0.1.0+2025.04.03T13.17.04.073Z.7e282384.master diff --git a/modules/wo_bulk_essay_analysis/VERSION b/modules/wo_bulk_essay_analysis/VERSION index 2e2f7e4a0..21b05a126 100644 --- a/modules/wo_bulk_essay_analysis/VERSION +++ b/modules/wo_bulk_essay_analysis/VERSION @@ -1 +1 @@ -0.1.0+2025.04.01T15.05.13.407Z.44725993.berickson.022025.gpt.dashboard.updates +0.1.0+2025.04.03T13.17.04.073Z.7e282384.master diff --git a/modules/wo_bulk_essay_analysis/setup.cfg b/modules/wo_bulk_essay_analysis/setup.cfg index a4db1fe6d..9f5cfb10d 100644 --- a/modules/wo_bulk_essay_analysis/setup.cfg +++ b/modules/wo_bulk_essay_analysis/setup.cfg @@ -1,5 +1,5 @@ [metadata] -name = Writing Observer Automated Essay Feedback +name = WO Bulk Essay Analysis description = Dashboard for interfacing a classroom of essays with automated feedback url = https://github.com/ETS-Next-Gen/writing_observer version = file:VERSION diff --git a/modules/wo_classroom_text_highlighter/VERSION b/modules/wo_classroom_text_highlighter/VERSION index 276855ab5..21b05a126 100644 --- a/modules/wo_classroom_text_highlighter/VERSION +++ b/modules/wo_classroom_text_highlighter/VERSION @@ -1 +1 @@ -0.1.0+2025.04.02T18.12.55.218Z.4a854783.berickson.languagetool.highlight.integration +0.1.0+2025.04.03T13.17.04.073Z.7e282384.master diff --git a/modules/wo_classroom_text_highlighter/setup.cfg b/modules/wo_classroom_text_highlighter/setup.cfg index 9987017ad..5ad128574 100644 --- a/modules/wo_classroom_text_highlighter/setup.cfg +++ b/modules/wo_classroom_text_highlighter/setup.cfg @@ -1,5 +1,5 @@ [metadata] -name = Writing Observer Classroom Text Highlighter +name = WO Classroom Text Highlighter version = file:VERSION description = Use this as a base template for creating new modules on the Learning Observer. From 3bdcc7c94250daba86cbea1d5a3b156d055cda3a Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Thu, 3 Apr 2025 10:43:39 -0400 Subject: [PATCH 38/88] small js bug fixes for dashboards --- VERSION | 2 +- .../src/lib/components/WOSettings.react.js | 4 ++-- modules/wo_bulk_essay_analysis/VERSION | 2 +- .../wo_bulk_essay_analysis/assets/scripts.js | 15 +-------------- 4 files changed, 5 insertions(+), 18 deletions(-) diff --git a/VERSION b/VERSION index 21b05a126..80aee927e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0+2025.04.03T13.17.04.073Z.7e282384.master +0.1.0+2025.04.03T14.43.39.953Z.c336d5da.master diff --git a/modules/lo_dash_react_components/src/lib/components/WOSettings.react.js b/modules/lo_dash_react_components/src/lib/components/WOSettings.react.js index 9b431e8a2..1783aacdc 100644 --- a/modules/lo_dash_react_components/src/lib/components/WOSettings.react.js +++ b/modules/lo_dash_react_components/src/lib/components/WOSettings.react.js @@ -79,7 +79,7 @@ export default class WOSettings extends Component { const hasChildren = allRows.some(option => option.parent === row.id); const isCollapsed = collapsed[row.id] || false; - const highlightCell = row.types && 'highlight' in row.types + const highlightCell = row.types && row.types.includes('highlight') ? (<> ) : null; - const metricCell = (row.types && 'metric' in row.types) + const metricCell = (row.types && row.types.includes('metric')) ? this.handleRowEvent(e, row.id, 'metric')} /> : null; diff --git a/modules/wo_bulk_essay_analysis/VERSION b/modules/wo_bulk_essay_analysis/VERSION index 21b05a126..80aee927e 100644 --- a/modules/wo_bulk_essay_analysis/VERSION +++ b/modules/wo_bulk_essay_analysis/VERSION @@ -1 +1 @@ -0.1.0+2025.04.03T13.17.04.073Z.7e282384.master +0.1.0+2025.04.03T14.43.39.953Z.c336d5da.master diff --git a/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/assets/scripts.js b/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/assets/scripts.js index 6a00a5647..55cdbae0d 100644 --- a/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/assets/scripts.js +++ b/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/assets/scripts.js @@ -210,18 +210,6 @@ window.dash_clientside.bulk_essay_feedback = { return !isOpen; }, - /** - * parse message from websocket to the data and error store - */ - receive_ws_message: function (message) { - const data = JSON.parse(message.data).wo.gpt_bulk || []; - if (data.error !== undefined) { - console.error('Error received from server', data.error); - return [[], data.error]; - } - return [data, false]; - }, - /** * adds submitted query to history and clear input */ @@ -512,8 +500,7 @@ window.dash_clientside.bulk_essay_feedback = { }, expandSelectedStudent: async function (selectedStudent, wsData, showHeader, history) { - console.log('wsData', wsData); - if (!selectedStudent | !(selectedStudent in wsData.students)) { + if (!selectedStudent | !(selectedStudent in (wsData.students || {}))) { return window.dash_clientside.no_update; } const prompt = history.length > 0 ? history[history.length - 1] : ''; From bee90d117fba7cf781cc4306e80a66dde8bf423b Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Tue, 6 May 2025 09:32:37 -0400 Subject: [PATCH 39/88] add process metrics to dashboards Added process metrics (time on task and currently active) to both dashboards. They are currently just badges that appear at the top of student cards. This pull request also cleans up a handful of dashboard functionality. Added basic semaphore logic to LLMs (just Ollama for now) to prevent overloading it. Added initial workflow for comm protocol select to use multiget. --- VERSION | 2 +- learning_observer/VERSION | 2 +- learning_observer/learning_observer/cache.py | 2 +- .../communication_protocol/executor.py | 43 +++-- .../learning_observer/dashboard.py | 8 +- .../learning_observer/downloads.py | 18 +- learning_observer/learning_observer/kvs.py | 8 + .../learning_observer/redis_connection.py | 9 +- .../src/lib/components/LONameTag.scss | 1 - .../src/lib/components/WOSettings.react.js | 16 +- .../lib/components/WOStudentTextTile.react.js | 110 ++++------- .../components/WOStudentTextTile.testdata.js | 53 +----- modules/lo_gpt/lo_gpt/gpt.py | 35 ++-- modules/wo_bulk_essay_analysis/VERSION | 2 +- .../wo_bulk_essay_analysis/assets/scripts.js | 113 ++++++++---- .../wo_bulk_essay_analysis/assets/styles.css | 7 - .../dashboard/layout.py | 70 +++++-- modules/wo_classroom_text_highlighter/VERSION | 2 +- .../assets/scripts.js | 172 +++++++++--------- .../dash_dashboard.py | 26 ++- .../wo_classroom_text_highlighter/options.py | 14 +- .../preset_component.py | 6 + modules/writing_observer/VERSION | 2 +- .../writing_observer/module.py | 10 +- 24 files changed, 398 insertions(+), 333 deletions(-) diff --git a/VERSION b/VERSION index 80aee927e..82e6a0cee 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0+2025.04.03T14.43.39.953Z.c336d5da.master +0.1.0+2025.05.01T19.10.46.507Z.bc7bcb4c.berickson.202504.process.metrics diff --git a/learning_observer/VERSION b/learning_observer/VERSION index 0abb94587..82e6a0cee 100644 --- a/learning_observer/VERSION +++ b/learning_observer/VERSION @@ -1 +1 @@ -0.1.0+2025.03.27T20.51.04.053Z.57041e9f.berickson.022025.gpt.dashboard.updates +0.1.0+2025.05.01T19.10.46.507Z.bc7bcb4c.berickson.202504.process.metrics diff --git a/learning_observer/learning_observer/cache.py b/learning_observer/learning_observer/cache.py index ab11259cd..280cd39b5 100644 --- a/learning_observer/learning_observer/cache.py +++ b/learning_observer/learning_observer/cache.py @@ -9,7 +9,7 @@ def create_key_from_args(func, *args, **kwargs): - key_dict = {'func': str(func), 'args': args, 'kwargs': kwargs} + key_dict = {'func': str(func.__name__), 'args': args, 'kwargs': kwargs} key_str = json.dumps(key_dict, sort_keys=True) return key_str diff --git a/learning_observer/learning_observer/communication_protocol/executor.py b/learning_observer/learning_observer/communication_protocol/executor.py index c161ecf73..4ce83511a 100644 --- a/learning_observer/learning_observer/communication_protocol/executor.py +++ b/learning_observer/learning_observer/communication_protocol/executor.py @@ -414,31 +414,44 @@ async def handle_select(keys, fields=learning_observer.communication_protocol.qu if fields is None or fields == learning_observer.communication_protocol.query.SelectFields.Missing: fields_to_keep = {} + # Collect all keys from the async generator + keys_list = [] async for k in ensure_async_generator(keys): if isinstance(k, dict) and 'key' in k: - # output from query added to response later - query_response_element = { - 'provenance': { - 'key': k['key'], - 'provenance': k['provenance'] - } - } + keys_list.append(k) else: raise DAGExecutionException( f'Key not formatted correctly for select: {k}', inspect.currentframe().f_code.co_name, {'keys': keys, 'fields': fields} ) - resulting_value = await learning_observer.kvs.KVS()[k['key']] + + # Batch fetch all values from KVS + kvs = learning_observer.kvs.KVS() + kvs_keys = [k['key'] for k in keys_list] + resulting_values = await kvs.multiget(kvs_keys) + + # Process each key and its corresponding value + for k, resulting_value in zip(keys_list, resulting_values): + query_response_element = { + 'provenance': { + 'key': k['key'], + 'provenance': k['provenance'] + } + } + + # Use default value if KVS returned None if resulting_value is None: - # the reducer has not run yet, so we return the default value from the module - resulting_value = k['default'] + resulting_value = k.get('default', None) - # keep all current fields except for provenance (already prepared) + # Determine fields to keep based on the current resulting_value if fields is All if fields == learning_observer.communication_protocol.query.SelectFields.All: - fields_to_keep = {k: k for k in resulting_value.keys() if k != 'provenance'} + current_fields_to_keep = {key: key for key in resulting_value.keys() if key != 'provenance'} + else: + current_fields_to_keep = fields_to_keep - for f in fields_to_keep: + # Populate the query response element with the specified fields + for f in current_fields_to_keep: try: value = get_nested_dict_value(resulting_value, f) except KeyError as e: @@ -447,8 +460,8 @@ async def handle_select(keys, fields=learning_observer.communication_protocol.qu inspect.currentframe().f_code.co_name, {'target': resulting_value, 'key': f, 'exception': e} ).to_dict() - # add necessary outputs to query response - query_response_element[fields_to_keep[f]] = value + query_response_element[current_fields_to_keep[f]] = value + yield query_response_element diff --git a/learning_observer/learning_observer/dashboard.py b/learning_observer/learning_observer/dashboard.py index 8c83b7746..7799773b4 100644 --- a/learning_observer/learning_observer/dashboard.py +++ b/learning_observer/learning_observer/dashboard.py @@ -631,7 +631,7 @@ async def _execute_dag(dag_query, target, params): # Create DAG generator and drive generator = await _create_dag_generator(dag_query, target, request) - await _drive_generator(generator, dag_query['kwargs']) + await _drive_generator(generator, dag_query['kwargs'], target=target) # Handle rescheduling the execution of the DAG for fresh data # TODO add some way to specific specific endpoint delays @@ -642,15 +642,15 @@ async def _execute_dag(dag_query, target, params): await asyncio.sleep(dag_delay) await _execute_dag(dag_query, target, params) - async def _drive_generator(generator, dag_kwargs): + async def _drive_generator(generator, dag_kwargs, target=None): '''For each item in the generator, this method creates an update to send to the client. ''' async for item in generator: scope = _find_student_or_resource(item) update_path = ".".join(scope) - if 'option_hash' in dag_kwargs: - item['option_hash'] = dag_kwargs['option_hash'] + if 'option_hash' in dag_kwargs and target is not None: + item[f'option_hash_{target}'] = dag_kwargs['option_hash'] await _send_update({'op': 'update', 'path': update_path, 'value': item}) send_batches_task = asyncio.create_task(_batch_send()) diff --git a/learning_observer/learning_observer/downloads.py b/learning_observer/learning_observer/downloads.py index d57afc2d8..9e76b1ef9 100644 --- a/learning_observer/learning_observer/downloads.py +++ b/learning_observer/learning_observer/downloads.py @@ -88,9 +88,12 @@ "5.3.1": "d099dac0135309466dc6208aaa973584843a3efbb40b2c96eb7c179f5f20f" "80def35bbc1a7a0b08c9d5bdbed6b8e780ba7d013d18e4019e04fd82a19c076a1f8", "5.3.3": "54b69b378be9029cb841bce9f33e111148231ce38ae389601c10ee1fec93b" - "bfb84839e84911e9e32e9e026a182e7225fd8531dc8344ba94ef4b467852e7162d5" + "bfb84839e84911e9e32e9e026a182e7225fd8531dc8344ba94ef4b467852e7162d5", + "5.3.5": "3c9aa2c118ba4cbb2f54b2ca87528c453503ab5545983b6f5d10c6f04abd9" + "d4feb003c80c59a092857808e439642ab45e38369980288e7afb5a11e0e7946270e" }, "tested_versions": [ + 'https://cdn.jsdelivr.net/npm/bootswatch@5.3.5/dist/minty/bootstrap.min.css', 'https://cdn.jsdelivr.net/npm/bootswatch@5.3.3/dist/minty/bootstrap.min.css', "https://cdn.jsdelivr.net/npm/bootswatch@5.3.1/dist/minty/bootstrap.min.css", 'https://cdn.jsdelivr.net/npm/bootswatch@5.1.3/dist/minty/bootstrap.min.css', @@ -102,9 +105,12 @@ "6.1.1": "535a5f3e40bc8ddf475b56c1a39a5406052b524413dea331c4e683ca99e39" "6dbbc11fdce1f8355730a73c52ac6a1062de1938406c6af8e4361fd346106acb6b0", "6.3.0": "1496214e7421773324f4b332127ea77bec822fc6739292ebb19c6abcc22a5" - "6248e0634b4e0ca0c2fcac14dc10b8d01fa17febaa35f46731201d1ffd0ab482dd7" + "6248e0634b4e0ca0c2fcac14dc10b8d01fa17febaa35f46731201d1ffd0ab482dd7", + "6.7.2": "1bd8a997cca7c7abb44f3335a338feb34e69694c3cf4f2cae11ef12ed73f4" + "f120dc15e680835f88dd71df199fb6596bea05cb569b0fb0822e8fd3ec58c6e0833" }, "tested_versions": [ + "https://use.fontawesome.com/releases/v6.7.2/css/all.css", "https://use.fontawesome.com/releases/v6.3.0/css/all.css", "https://use.fontawesome.com/releases/v6.1.1/css/all.css" ] @@ -115,7 +121,9 @@ "6.1.1": "6d3fe769cc40a5790ea2e09fb775f1bd3b130d2fdae1dd552f69559e7ca4c" "a047862f795da0024737e59e3bcc7446f6eec1bab173758aef0b97ba89d722ffbde", "6.3.0": "d50c68cd4b3312f50deb66ac8ab5c37b2d4161f4e00ea077" - "326ae76769dac650dd19e65dee8d698ba2f86a69537f38cf4010ff45227211cee8b382d9b567257a" + "326ae76769dac650dd19e65dee8d698ba2f86a69537f38cf4010ff45227211cee8b382d9b567257a", + "6.7.2": "140ad609f15616dff39a7ed2efa080283bb28e13e6fe00987a39774887aa2" + "a8279f48c36816753e7522474580ee49b937e9d549674b7382f73548f159a206036" } } FONTAWESOME_TTF = { @@ -124,7 +132,9 @@ "6.1.1": "0fdd341671021d04304186c197001cf2e888d3028baaf9a5dec0f0e496959" "666e8a2e34aae8e79904f8e9b4c0ccae40249897cce5f5ae58d12cc1b3985e588d6", "6.3.0": "5a2c2b010a2496e4ed832ede8620f3bbfa9374778f3d63e4" - "5a4aab041e174dafd9fffd3229b8b36f259cf2ef46ae7bf5cb041e280f2939884652788fc1e8ce58" + "5a4aab041e174dafd9fffd3229b8b36f259cf2ef46ae7bf5cb041e280f2939884652788fc1e8ce58", + "6.7.2": "8a0a8fc8fcf0b676588bfe28f8b1d154a16af951d31f3a527a9661189c4c2" + "317b2d5b3b95a3e7c4a77377988fb6852e6d5dae6df48ad07957a62ae22fd2ac0bb" } } diff --git a/learning_observer/learning_observer/kvs.py b/learning_observer/learning_observer/kvs.py index 469761b7f..325c7b8d8 100644 --- a/learning_observer/learning_observer/kvs.py +++ b/learning_observer/learning_observer/kvs.py @@ -191,6 +191,14 @@ async def remove(self, key): await self.connect() return await learning_observer.redis_connection.delete(key) + async def multiget(self, keys): + ''' + Fetch multiple items from the KVS via `mget` + ''' + await self.connect() + items = await learning_observer.redis_connection.mget(keys) + return [json.loads(item) if item is not None else None for item in items] + class EphemeralRedisKVS(_RedisKVS): ''' diff --git a/learning_observer/learning_observer/redis_connection.py b/learning_observer/learning_observer/redis_connection.py index d62534f62..7b6d1494d 100644 --- a/learning_observer/learning_observer/redis_connection.py +++ b/learning_observer/learning_observer/redis_connection.py @@ -73,11 +73,18 @@ async def keys(): async def get(key): ''' - Get a key. We should eventually do multi-gets. Returns a future. + Get a key. Returns a future. ''' return await (await connection()).get(key) +async def mget(keys): + ''' + Get mutliple keys. Returns a future. + ''' + return await (await connection()).mget(keys) + + async def set(key, value, expiry=None): ''' Set a key. We should eventually do multi-sets. Returns a future. diff --git a/modules/lo_dash_react_components/src/lib/components/LONameTag.scss b/modules/lo_dash_react_components/src/lib/components/LONameTag.scss index 611345779..4670217be 100644 --- a/modules/lo_dash_react_components/src/lib/components/LONameTag.scss +++ b/modules/lo_dash_react_components/src/lib/components/LONameTag.scss @@ -6,7 +6,6 @@ span.name-tag-photo { font-family: monospace; text-align: center; - width: 42px; background-color: white; padding: 0.25rem; border-radius: 50%; diff --git a/modules/lo_dash_react_components/src/lib/components/WOSettings.react.js b/modules/lo_dash_react_components/src/lib/components/WOSettings.react.js index 1783aacdc..5ab1a7ee2 100644 --- a/modules/lo_dash_react_components/src/lib/components/WOSettings.react.js +++ b/modules/lo_dash_react_components/src/lib/components/WOSettings.react.js @@ -36,7 +36,9 @@ export default class WOSettings extends Component { constructor (props) { super(props); this.state = { - collapsed: {} // Tracks which rows are collapsed + collapsed: {}, + showHighlight: props.options.some(option => option.types && option.types.includes('highlight')), + showMetric: props.options.some(option => option.types && option.types.includes('metric')) }; this.handleRowEvent = this.handleRowEvent.bind(this); this.renderRow = this.renderRow.bind(this); @@ -74,7 +76,7 @@ export default class WOSettings extends Component { } renderRow (row, allRows) { - const { collapsed } = this.state; + const { collapsed, showHighlight, showMetric } = this.state; const { value } = this.props; const hasChildren = allRows.some(option => option.parent === row.id); const isCollapsed = collapsed[row.id] || false; @@ -85,6 +87,7 @@ export default class WOSettings extends Component { type="checkbox" checked={value[row.id]?.highlight.value || false} onChange={(e) => this.handleRowEvent(e, row.id, 'highlight')} + className='me-1' /> {value[row.id]?.highlight.value ? ( )} - {highlightCell} - {metricCell} + {showHighlight && {highlightCell}} + {showMetric && {metricCell}} {/* Render children rows if not collapsed */} {!isCollapsed && @@ -135,6 +138,7 @@ export default class WOSettings extends Component { render () { const { id, className, options } = this.props; + const { showHighlight, showMetric } = this.state; const rows = sortOptionsIntoTree(options); return ( @@ -146,8 +150,8 @@ export default class WOSettings extends Component { Name - Highlight - Metric + {showHighlight && Highlight} + {showMetric && Metric} diff --git a/modules/lo_dash_react_components/src/lib/components/WOStudentTextTile.react.js b/modules/lo_dash_react_components/src/lib/components/WOStudentTextTile.react.js index 611b1600b..1bfecbf43 100644 --- a/modules/lo_dash_react_components/src/lib/components/WOStudentTextTile.react.js +++ b/modules/lo_dash_react_components/src/lib/components/WOStudentTextTile.react.js @@ -1,77 +1,41 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; +import Button from 'react-bootstrap/Button'; +import ButtonGroup from 'react-bootstrap/ButtonGroup'; import Card from 'react-bootstrap/Card'; -import Form from 'react-bootstrap/Form'; import LONameTag from './LONameTag.react'; -function createGoogleDocumentURL (docId) { - return `https://docs.google.com/document/d/${docId}`; -} - /** * WOStudentTextTile */ export default class WOStudentTextTile extends Component { - constructor (props) { - super(props); - this.handleDocumentSelectChange = this.handleDocumentSelectChange.bind(this); - } - - handleDocumentSelectChange (event) { - this.props.setProps({ selectedDocument: event.target.value }); - } - render () { - const { id, className, style, showHeader, studentInfo, selectedDocument, currentOptionHash, childComponent } = this.props; - // HACK we need to pass the appropriate student information into the child component - childComponent.props._dashprivate_layout.props = { ...studentInfo.documents[selectedDocument] }; - - const documentIsSelected = selectedDocument && studentInfo.documents[selectedDocument]; - const isLoading = documentIsSelected && currentOptionHash !== studentInfo.documents[selectedDocument].optionHash; + const { id, className, style, showName, profile, currentStudentHash, currentOptionHash, childComponent, additionalButtons } = this.props; + const isLoading = currentOptionHash !== currentStudentHash; let bodyClassName = isLoading ? 'loading' : ''; bodyClassName = `${bodyClassName} overflow-auto position-relative`; - const loadedItem = documentIsSelected - ? <>{childComponent} - :
Document information not found.
; - - // TODO the chunk of commented code allows for linking directly to the selected document - // and allows the user to select which document they wish to see at a given moment. - // Neither of these features are currently available due to limitations with the communication - // protocol. For now they are being commented out so users are not inclined to use them. return ( - + - {/* - - - - - {studentInfo.availableDocuments.map(doc => ( - - ))} - */} + + {isLoading && ( + + )} + {additionalButtons && additionalButtons} + - {isLoading && ( -
-
- Loading... -
- )} - {loadedItem} + {childComponent} ); @@ -80,13 +44,9 @@ export default class WOStudentTextTile extends Component { WOStudentTextTile.defaultProps = { className: '', - showHeader: true, + showName: true, style: {}, - studentInfo: { - profile: {}, - availableDocuments: [], - documents: {} - } + profile: {} }; WOStudentTextTile.propTypes = { @@ -110,18 +70,19 @@ WOStudentTextTile.propTypes = { * Determine whether the header with the student * name should be visible or not */ - showHeader: PropTypes.bool, + showName: PropTypes.bool, /** - * Which document is currently selected for this student + * Hash of the current options, used to determine if we + * should be in a loading state or not. */ - selectedDocument: PropTypes.string, + currentOptionHash: PropTypes.string, /** - * Hash of the current options, used to determine if we + * Hash of the current student, used to determine if we * should be in a loading state or not. */ - currentOptionHash: PropTypes.string, + currentStudentHash: PropTypes.string, /** * Component to use for within the card body @@ -129,23 +90,14 @@ WOStudentTextTile.propTypes = { childComponent: PropTypes.node, /** - * The breakpoints of our text + * Buttons to add to the button group + */ + additionalButtons: PropTypes.node, + + /** + * The profile of the student */ - studentInfo: PropTypes.exact({ - profile: PropTypes.object, - availableDocuments: PropTypes.arrayOf(PropTypes.exact({ - id: PropTypes.string, - title: PropTypes.string - })), - documents: PropTypes.object - // objectOf( - // PropTypes.shape({ - // text: PropTypes.string, - // breakpoints: PropTypes.arrayOf(PropTypes.any), - // optionHash: PropTypes.string - // }) - // ) - }), + profile: PropTypes.object, /** * Dash-assigned callback that should be called to report property changes diff --git a/modules/lo_dash_react_components/src/lib/components/WOStudentTextTile.testdata.js b/modules/lo_dash_react_components/src/lib/components/WOStudentTextTile.testdata.js index 0d297f065..c12774038 100644 --- a/modules/lo_dash_react_components/src/lib/components/WOStudentTextTile.testdata.js +++ b/modules/lo_dash_react_components/src/lib/components/WOStudentTextTile.testdata.js @@ -1,51 +1,16 @@ const testData = { id: 'example', - showHeader: true, + showName: true, currentOptionHash: '123', - studentInfo: { - availableDocuments: [ - { id: '1_2V-Npp1L0G3cw4lcH_ENSo_y_OV1BP3s8NdnwaFbVw', title: 'Document A' }, - { id: 'docB', title: 'Document B' }, - { id: 'docC', title: 'Document C' } - ], - profile: { - email_address: 'example@example.com', - name: { - family_name: 'Doe', - full_name: 'John Doe', - given_name: 'John' - }, - photo_url: '//lh3.googleusercontent.com/a/default-user' + currentStudentHash: '123', + profile: { + email_address: 'example@example.com', + name: { + family_name: 'Doe', + full_name: 'John Doe', + given_name: 'John' }, - documents: { - '1_2V-Npp1L0G3cw4lcH_ENSo_y_OV1BP3s8NdnwaFbVw': { - optionHash: '123', - text: "This summer was AMAZING!!! First, I went to the beach with my family for two whole weeks! We stayed in this super cool beach house that had its own private pool and hot tub. My siblings and I spent hours playing in the waves and building sandcastles on the beach. We even went on a snorkeling trip and saw some really cool fish!\n\nWhen we weren't at the beach, I hung out with my friends and we had a blast! We went to the trampoline park and played laser tag. I also started reading this really good book called \"The Giver\" and it was sooo good that I couldn't put it down.\n\nMy parents took me and my siblings on a road trip to visit our grandparents in another state. It was a pretty long drive, but we made some great memories along the way. We stopped at a few theme parks and went on some really cool rides. My favorite one was this roller coaster that had loops and corkscrews!\n\nAt home, I started working on my own little garden project. I planted some flowers and herbs, and even tried to grow my own tomatoes (which didn't quite work out as planned...). It was pretty cool seeing everything grow and flourish.\n\nOverall, this summer was definitely the best one yet!", - breakpoints: [ - { - id: 'split0', - tooltip: 'This is the first tooltip', - start: 220, - offset: 5, - style: { textDecoration: 'underline' } - }, - { - id: 'split1', - tooltip: 'This is a tooltip', - start: 240, - offset: 25, - style: { textDecoration: 'underline' } - }, - { - id: 'split2', - tooltip: 'This is another tooltip', - start: 310, - offset: 15, - style: { backgroundColor: 'green' } - } - ] - } - } + photo_url: '//lh3.googleusercontent.com/a/default-user' } }; diff --git a/modules/lo_gpt/lo_gpt/gpt.py b/modules/lo_gpt/lo_gpt/gpt.py index 69ee8acf1..b3eac5085 100644 --- a/modules/lo_gpt/lo_gpt/gpt.py +++ b/modules/lo_gpt/lo_gpt/gpt.py @@ -12,17 +12,19 @@ APIs so the code can be abstracted to the parent class. ''' import aiohttp +import asyncio import json import loremipsum import os +import random -import learning_observer.communication_protocol.integration from learning_observer.log_event import debug_log import learning_observer.prestartup import learning_observer.settings gpt_responder = None SYSTEM_PROMPT_DEFAULT = 'You are a helper agent, please help fulfill user requests.' +LLM_SEMAPHOR = {} class GPTAPI: @@ -111,6 +113,10 @@ def __init__(self, **kwargs): 'ollama run \n```') self.ollama_host = 'http://localhost:11434' + global LLM_SEMAPHOR + if 'ollama' not in LLM_SEMAPHOR: + LLM_SEMAPHOR['ollama'] = asyncio.Semaphore(os.getenv('OLLAMA_NUM_PARALLEL', 1)) + async def chat_completion(self, prompt, system_prompt): '''Ollama only returns a single item compared to GPT returning a list ''' @@ -120,15 +126,16 @@ async def chat_completion(self, prompt, system_prompt): {'role': 'user', 'content': prompt} ] content = {'model': self.model, 'messages': messages, 'stream': False} - async with aiohttp.ClientSession() as session: - async with session.post(url, json=content) as resp: - json_resp = await resp.json(content_type=None) - if resp.status == 200: - return json_resp['message']['content'] - error = 'Error occured while making Ollama request' - if 'error' in json_resp: - error += f"\n{json_resp['error']['message']}" - raise GPTRequestErorr(error) + async with LLM_SEMAPHOR['ollama']: + async with aiohttp.ClientSession() as session: + async with session.post(url, json=content) as resp: + json_resp = await resp.json(content_type=None) + if resp.status == 200: + return json_resp['message']['content'] + error = 'Error occured while making Ollama request' + if 'error' in json_resp: + error += f"\n{json_resp['error']['message']}" + raise GPTRequestErorr(error) class StubGPT(GPTAPI): @@ -137,8 +144,14 @@ class StubGPT(GPTAPI): def __init__(self, **kwargs): super().__init__() + global LLM_SEMAPHOR + if 'stub' not in LLM_SEMAPHOR: + LLM_SEMAPHOR['stub'] = asyncio.Semaphore(2) + async def chat_completion(self, prompt, system_prompt): - return "\n".join(loremipsum.get_paragraphs(1)) + async with LLM_SEMAPHOR['stub']: + await asyncio.sleep(random.randint(5, 15)) + return "\n".join(loremipsum.get_paragraphs(1)) GPT_RESPONDERS = { diff --git a/modules/wo_bulk_essay_analysis/VERSION b/modules/wo_bulk_essay_analysis/VERSION index 80aee927e..4dd337e23 100644 --- a/modules/wo_bulk_essay_analysis/VERSION +++ b/modules/wo_bulk_essay_analysis/VERSION @@ -1 +1 @@ -0.1.0+2025.04.03T14.43.39.953Z.c336d5da.master +0.1.0+2025.04.17T11.52.09.536Z.37e99b72.berickson.202504.process.metrics diff --git a/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/assets/scripts.js b/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/assets/scripts.js index 55cdbae0d..a055d73cb 100644 --- a/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/assets/scripts.js +++ b/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/assets/scripts.js @@ -8,7 +8,7 @@ if (!window.dash_clientside) { pdfjsLib.GlobalWorkerOptions.workerSrc = '/static/3rd_party/pdf.worker.min.js'; -const createStudentCard = async function (s, prompt, width, height, showHeader) { +const createStudentCard = async function (s, prompt, width, height, showName, selectedMetrics) { const selectedDocument = s.doc_id || Object.keys(s.documents || {})[0] || ''; const student = s.documents?.[selectedDocument] ?? {}; const promptHash = await hashObject({ prompt }); @@ -16,8 +16,17 @@ const createStudentCard = async function (s, prompt, width, height, showHeader) const studentText = { namespace: 'lo_dash_react_components', type: 'WOAnnotatedText', - props: { text: student.text, breakpoints: [], className: 'border-end' } + props: { text: student.text, breakpoints: [] } }; + const studentTileChild = createDashComponent( + DASH_HTML_COMPONENTS, 'Div', + { + children: [ + createProcessTags({ ...student }, selectedMetrics), + studentText + ] + } + ); const errorMessage = { namespace: 'dash_html_components', type: 'Div', @@ -50,19 +59,29 @@ const createStudentCard = async function (s, prompt, width, height, showHeader) className: 'text-center' } }; - const feedback = promptHash === student.option_hash ? feedbackMessage : feedbackLoading; + const feedback = promptHash === student.option_hash_gpt_bulk ? feedbackMessage : feedbackLoading; const feedbackOrError = 'error' in student ? errorMessage : feedback; - const userId = student?.user_id || '0'; + const userId = student?.user_id; + if (!userId) { return {}; } const studentTile = createDashComponent( LO_DASH_REACT_COMPONENTS, 'WOStudentTextTile', { - showHeader, - studentInfo: formatStudentData(s, []), + showName, + profile: student?.profile || {}, selectedDocument, - childComponent: studentText, + childComponent: studentTileChild, id: { type: 'WOAIAssistStudentTileText', index: userId }, currentOptionHash: promptHash, - style: { height: `${height}px` } + currentStudentHash: student.option_hash_gpt_bulk, + style: { height: `${height}px` }, + additionalButtons: createDashComponent( + DASH_BOOTSTRAP_COMPONENTS, 'Button', + { + id: { type: 'WOAIAssistStudentTileExpand', index: userId }, + children: createDashComponent(DASH_HTML_COMPONENTS, 'I', { className: 'fas fa-expand' }), + color: 'transparent' + } + ) } ); const tileWrapper = createDashComponent( @@ -75,15 +94,6 @@ const createStudentCard = async function (s, prompt, width, height, showHeader) DASH_BOOTSTRAP_COMPONENTS, 'Card', { children: feedbackOrError, body: true } ), - createDashComponent( - DASH_BOOTSTRAP_COMPONENTS, 'Button', - { - id: { type: 'WOAIAssistStudentTileExpand', index: userId }, - children: createDashComponent(DASH_HTML_COMPONENTS, 'I', { className: 'fas fa-expand' }), - class_name: 'position-absolute top-0 end-0 m-1', - color: 'transparent' - } - ) ], id: { type: 'WOAIAssistStudentTile', index: userId }, style: { width: `${(100 - width) / width}%` } @@ -98,11 +108,11 @@ const createStudentCard = async function (s, prompt, width, height, showHeader) * @param {*} promptHash current hash of prompts * @returns true if student's selected document's hash is the same as promptHash */ -const checkForResponse = function (s, promptHash) { +const checkForResponse = function (s, promptHash, options) { if (!('documents' in s)) { return false; } const selectedDocument = s.doc_id || Object.keys(s.documents || {})[0] || ''; const student = s.documents[selectedDocument]; - return promptHash === student.option_hash; + return options.every(option => promptHash === student[`option_hash_${option}`]); }; const charactersAfterChar = function (str, char) { @@ -160,6 +170,8 @@ const fileTextExtractors = { docx: extractDOCX }; +const AIAssistantLoadingQueries = ['gpt_bulk', 'time_on_task', 'activity']; + window.dash_clientside.bulk_essay_feedback = { /** * Sends data to server via websocket @@ -179,7 +191,7 @@ window.dash_clientside.bulk_essay_feedback = { decoded.doc_source_kwargs = docKwargs.kwargs; // TODO what is a reasonable time to wait inbetween subsequent calls for // the same arguments - decoded.rerun_dag_delay = 30; + decoded.rerun_dag_delay = 120; const trig = window.dash_clientside.callback_context.triggered[0]; if (trig.prop_id.includes('bulk-essay-analysis-submit-btn')) { @@ -194,7 +206,7 @@ window.dash_clientside.bulk_essay_feedback = { const message = { wo: { execution_dag: 'writing_observer', - target_exports: ['gpt_bulk', 'document_list', 'document_sources'], + target_exports: ['gpt_bulk', 'document_list', 'document_sources', 'time_on_task', 'activity'], kwargs: decoded } }; @@ -203,11 +215,23 @@ window.dash_clientside.bulk_essay_feedback = { return window.dash_clientside.no_update; }, - toggleAdvanced: function (clicks, isOpen) { + toggleAdvanced: function (clicks, shown) { if (!clicks) { return window.dash_clientside.no_update; } - return !isOpen; + const optionPrefix = 'bulk-essay-analysis-advanced-collapse'; + if (shown.includes(optionPrefix)) { + shown = shown.filter(item => item !== optionPrefix); + } else { + shown = shown.concat(optionPrefix); + } + return shown; + }, + + closeAdvanced: function (clicks, shown) { + if (!clicks) { return window.dash_clientside.no_update; } + shown = shown.filter(item => item !== 'bulk-essay-analysis-advanced-collapse'); + return shown; }, /** @@ -241,15 +265,16 @@ window.dash_clientside.bulk_essay_feedback = { /** * update student cards based on new data in storage */ - updateStudentGridOutput: async function (wsStorageData, history, width, height, showHeader) { + updateStudentGridOutput: async function (wsStorageData, history, width, height, showName, value, options) { if (!wsStorageData) { return 'No students'; } const currPrompt = history.length > 0 ? history[history.length - 1] : ''; + const selectedMetrics = fetchSelectedItemsFromOptions(value, options, 'metric'); let output = []; for (const student in wsStorageData.students) { - output = output.concat(await createStudentCard(wsStorageData.students[student], currPrompt, width, height, showHeader)); + output = output.concat(await createStudentCard(wsStorageData.students[student], currPrompt, width, height, showName, selectedMetrics)); } return output; }, @@ -468,7 +493,7 @@ window.dash_clientside.bulk_essay_feedback = { } const currentPrompt = history.length > 0 ? history[history.length - 1] : ''; const promptHash = await hashObject({ prompt: currentPrompt }); - const returnedResponses = Object.values(wsStorageData.students).filter(student => checkForResponse(student, promptHash)).length; + const returnedResponses = Object.values(wsStorageData.students).filter(student => checkForResponse(student, promptHash, AIAssistantLoadingQueries)).length; const totalStudents = Object.keys(wsStorageData.students).length; if (totalStudents === returnedResponses) { return noLoading; } const loadingProgress = returnedResponses / totalStudents + 0.1; @@ -490,8 +515,12 @@ window.dash_clientside.bulk_essay_feedback = { let id = null; if (triggeredItem?.type === 'WOAIAssistStudentTileExpand') { id = triggeredItem?.index; - if (clicks[ids.findIndex(item => item.index === id)]) { + const index = ids.findIndex(item => item.index === id); + if (clicks[index]) { shownPanels = shownPanels.concat('bulk-essay-analysis-expanded-student-panel'); + } else { + // No clicks occurred so we should keep the ID as it was + id = window.dash_clientside.no_update; } } else { return window.dash_clientside.no_update; @@ -499,7 +528,7 @@ window.dash_clientside.bulk_essay_feedback = { return [id, shownPanels]; }, - expandSelectedStudent: async function (selectedStudent, wsData, showHeader, history) { + expandSelectedStudent: async function (selectedStudent, wsData, showName, history, value, options) { if (!selectedStudent | !(selectedStudent in (wsData.students || {}))) { return window.dash_clientside.no_update; } @@ -509,12 +538,23 @@ window.dash_clientside.bulk_essay_feedback = { const document = Object.keys(s.documents)[0]; const student = s.documents[document]; const promptHash = await hashObject({ prompt }); + const selectedMetrics = fetchSelectedItemsFromOptions(value, options, 'metric'); + // TODO some of this can easily be abstracted const studentText = { namespace: 'lo_dash_react_components', type: 'WOAnnotatedText', - props: { text: student.text, breakpoints: [], className: 'border-end' } + props: { text: student.text, breakpoints: [] } }; + const studentTileChild = createDashComponent( + DASH_HTML_COMPONENTS, 'Div', + { + children: [ + createProcessTags({ ...student }, selectedMetrics), + studentText + ] + } + ); const errorMessage = { namespace: 'dash_html_components', type: 'Div', @@ -527,7 +567,7 @@ window.dash_clientside.bulk_essay_feedback = { type: 'Markdown', props: { children: student?.feedback ? student.feedback : '', - className: student?.feedback ? 'p-1 overflow-auto' : '', + className: student?.feedback ? 'p-1' : '', style: { whiteSpace: 'pre-line' } } }; @@ -547,17 +587,18 @@ window.dash_clientside.bulk_essay_feedback = { className: 'text-center' } }; - const feedback = promptHash === student.option_hash ? feedbackMessage : feedbackLoading; + const feedback = promptHash === student.option_hash_gpt_bulk ? feedbackMessage : feedbackLoading; const feedbackOrError = 'error' in student ? errorMessage : feedback; const studentTile = createDashComponent( LO_DASH_REACT_COMPONENTS, 'WOStudentTextTile', { - showHeader, - studentInfo: formatStudentData(s, []), + showName, + profile: student?.profile || {}, selectedDocument, - childComponent: studentText, + childComponent: studentTileChild, id: { type: 'WOAIAssistStudentTileText', index: student.user_id }, - currentOptionHash: promptHash + currentOptionHash: promptHash, + currentStudentHash: student.option_hash_gpt_bulk } ); const individualWrapper = createDashComponent( @@ -568,7 +609,7 @@ window.dash_clientside.bulk_essay_feedback = { studentTile, createDashComponent( DASH_BOOTSTRAP_COMPONENTS, 'Card', - { children: feedbackOrError, body: true, className: 'individual-student-feedback' } + { children: feedbackOrError, body: true } ) ] } diff --git a/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/assets/styles.css b/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/assets/styles.css index 9e9d73472..b134da7e3 100644 --- a/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/assets/styles.css +++ b/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/assets/styles.css @@ -1,10 +1,3 @@ -.individual-student-feedback { - position: -webkit-sticky; - position: sticky; - bottom: 0; - min-height: 250px; -} - .prompt-variable-tag button:last-child { border-top-left-radius: 0; border-bottom-left-radius: 0; diff --git a/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/dashboard/layout.py b/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/dashboard/layout.py index 5df3c0f7e..59ae6ae3c 100644 --- a/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/dashboard/layout.py +++ b/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/dashboard/layout.py @@ -4,12 +4,13 @@ ''' import dash_bootstrap_components as dbc from dash_renderjson import DashRenderjson -import datetime import lo_dash_react_components as lodrc import random from dash import html, dcc, clientside_callback, ClientsideFunction, Output, Input, State, ALL +import wo_classroom_text_highlighter.options + # TODO pull this flag from settings DEBUG_FLAG = True @@ -29,9 +30,11 @@ _advanced_doc_src = f'{_advanced}-document-source' _advanced_toggle = f'{_advanced}-toggle' _advanced_collapse = f'{_advanced}-collapse' +_advanced_close = f'{_advanced}-close' _advanced_width = f'{_advanced}-width' _advanced_height = f'{_advanced}-height' _advanced_hide_header = f'{_advanced}-hide-header' +_advanced_text_information = f'{_advanced}-text-information' _system_input = f'{prefix}-system-prompt-input' _system_input_tooltip = f'{_system_input}-tooltip' @@ -138,22 +141,36 @@ def layout(): Generic layout function to create dashboard ''' # advanced menu for system prompt - advanced = [ + advanced = html.Div([ html.Div([ - lodrc.LODocumentSourceSelectorAIO(aio_id=_advanced_doc_src), - dbc.Card([ - dbc.CardHeader('View Options'), - dbc.CardBody([ - dbc.Label('Students per row'), - dbc.Input(type='number', min=1, max=10, value=3, step=1, id=_advanced_width), - dbc.Label('Height of student tile'), - dcc.Slider(min=100, max=800, marks=None, value=350, id=_advanced_height), - dbc.Label('Student name headers'), - dbc.Switch(value=True, id=_advanced_hide_header, label='Show/Hide'), - ]) - ]), + html.H3('Settings', className='d-inline-block'), + dbc.Button( + html.I(className='fas fa-close'), + className='float-end', id=_advanced_close, + color='transparent'), + ]), + lodrc.LODocumentSourceSelectorAIO(aio_id=_advanced_doc_src), + dbc.Card([ + dbc.CardHeader('View Options'), + dbc.CardBody([ + dbc.Label('Students per row'), + dbc.Input(type='number', min=1, max=10, value=2, step=1, id=_advanced_width), + dbc.Label('Height of student tile'), + dcc.Slider(min=100, max=800, marks=None, value=350, id=_advanced_height), + dbc.Label('Student profile'), + dbc.Switch(value=True, id=_advanced_hide_header, label='Show/Hide'), + ]) + ]), + dbc.Card([ + dbc.CardHeader('Information Options'), + dbc.CardBody(lodrc.WOSettings( + id=_advanced_text_information, + options=wo_classroom_text_highlighter.options.PROCESS_OPTIONS, + value=wo_classroom_text_highlighter.options.DEFAULT_VALUE, + className='table table-striped align-middle' + )) ]) - ] + ]) # history panel history_favorite_panel = dbc.Card([ @@ -216,7 +233,6 @@ def layout(): dbc.Button([html.I(className='fas fa-cog me-1'), 'Advanced'], id=_advanced_toggle), lodrc.ProfileSidebarAIO(class_name='rounded-0 rounded-end', color='secondary'), ], class_name='mb-1'), - dbc.Collapse(advanced, id=_advanced_collapse, class_name='mb-1'), lodrc.LOPanelLayout( input_panel, panels=[ @@ -231,9 +247,10 @@ def layout(): lodrc.LOPanelLayout( html.Div(id=grid, className='d-flex justify-content-between flex-wrap'), panels=[ + {'children': advanced, 'width': '30%', 'id': _advanced_collapse, 'side': 'left' }, {'children': expanded_student_component, 'width': '30%', 'id': _expanded_student_panel, - 'side': 'right', 'className': 'vh-100 overflow-auto'} + 'side': 'right'} ], id=_student_data_wrapper, shown=[] ), @@ -244,9 +261,18 @@ def layout(): # Toggle if the advanced menu collapse is open or not clientside_callback( ClientsideFunction(namespace=_namespace, function_name='toggleAdvanced'), - Output(_advanced_collapse, 'is_open'), + Output(_student_data_wrapper, 'shown', allow_duplicate=True), Input(_advanced_toggle, 'n_clicks'), - State(_advanced_collapse, 'is_open') + State(_student_data_wrapper, 'shown'), + prevent_initial_call=True +) + +clientside_callback( + ClientsideFunction(namespace=_namespace, function_name='closeAdvanced'), + Output(_student_data_wrapper, 'shown', allow_duplicate=True), + Input(_advanced_close, 'n_clicks'), + State(_student_data_wrapper, 'shown'), + prevent_initial_call=True ) # send request on websocket @@ -329,7 +355,9 @@ def layout(): Input(history_store, 'data'), Input(_advanced_width, 'value'), Input(_advanced_height, 'value'), - Input(_advanced_hide_header, 'value') + Input(_advanced_hide_header, 'value'), + Input(_advanced_text_information, 'value'), + State(_advanced_text_information, 'options') ) # append tag in curly braces to input @@ -423,6 +451,8 @@ def layout(): Input(lodrc.LOConnectionAIO.ids.ws_store(_websocket), 'data'), Input(_advanced_hide_header, 'value'), Input(history_store, 'data'), + Input(_advanced_text_information, 'value'), + State(_advanced_text_information, 'options') ) # Close expanded student diff --git a/modules/wo_classroom_text_highlighter/VERSION b/modules/wo_classroom_text_highlighter/VERSION index 21b05a126..daf8882e5 100644 --- a/modules/wo_classroom_text_highlighter/VERSION +++ b/modules/wo_classroom_text_highlighter/VERSION @@ -1 +1 @@ -0.1.0+2025.04.03T13.17.04.073Z.7e282384.master +0.1.0+2025.04.16T16.38.16.396Z.ab096d59.berickson.202504.process.metrics diff --git a/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/assets/scripts.js b/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/assets/scripts.js index 6ae74e4da..7510b2d37 100644 --- a/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/assets/scripts.js +++ b/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/assets/scripts.js @@ -57,52 +57,63 @@ function simpleHash (str) { return hash.toString(16); } -// TODO some of this will move to the communication protocol, but for now -// it lives here -function formatStudentData (student, selectedHighlights) { - let profile = {}; - const documents = {}; - for (const document in student.documents || []) { - const breakpoints = selectedHighlights.reduce((acc, option) => { - const offsets = student.documents[document][option.id]?.offsets || []; - if (offsets) { - const modifiedOffsets = offsets.map(offset => { - return { - id: '', - tooltip: option.label, - start: offset[0], - offset: offset[1], - style: { backgroundColor: option.highlight.color } - }; - }); - acc = acc.concat(modifiedOffsets); - } - return acc; - }, []); - const text = student.documents[document].text; - const optionHash = student.documents[document].option_hash; - profile = student.documents[document].profile; - documents[document] = { text, optionHash, breakpoints }; - } - let availableDocuments = []; - if ('availableDocuments' in student) { - availableDocuments = Object.keys(student.availableDocuments).map(id => ({ - id, - title: student.availableDocuments[id].title || id, - last_access: student.availableDocuments[id].last_access || null - })); - } - return { - profile, - availableDocuments, - documents - }; +function formatStudentData (document, selectedHighlights) { + const breakpoints = selectedHighlights.reduce((acc, option) => { + const offsets = document[option.id]?.offsets || []; + if (offsets) { + const modifiedOffsets = offsets.map(offset => { + return { + id: '', + tooltip: option.label, + start: offset[0], + offset: offset[1], + style: { backgroundColor: option.highlight.color } + }; + }); + acc = acc.concat(modifiedOffsets); + } + return acc; + }, []); + const text = document.text; + return { text, breakpoints }; } function styleStudentTile (width, height) { return { width: `${(100 - width) / width}%`, height: `${height}px` }; } +function fetchSelectedItemsFromOptions (value, options, type) { + return options.reduce(function(filtered, option) { + if (value?.[option.id]?.[type]?.value) { + const selected = {...option, ...value[option.id]}; + filtered.push(selected); + } + return filtered; + }, []); +} + +function createProcessTags (document, metrics) { + const children = metrics.map(metric => { + switch (metric.id) { + case 'time_on_task': + return createDashComponent( + DASH_BOOTSTRAP_COMPONENTS, 'Badge', + { children: `${rendertime2(document[metric.id])} on task`, className: 'me-1' } + ); + case 'status': + const color = document[metric.id] === 'active' ? 'success' : 'warning'; + return createDashComponent( + DASH_BOOTSTRAP_COMPONENTS, 'Badge', + { children: document[metric.id], color } + ); + default: + break + } + }); + return createDashComponent(DASH_HTML_COMPONENTS, 'Div', { children, className: 'sticky-top' }) +} +const ClassroomTextHighlightLoadingQueries = ['docs_with_nlp_annotations', 'time_on_task', 'activity']; + window.dash_clientside.wo_classroom_text_highlighter = { /** * Send updated queries to the communication protocol. @@ -128,7 +139,7 @@ window.dash_clientside.wo_classroom_text_highlighter = { const outgoingMessage = { wo_classroom_text_highlighter_query: { execution_dag: 'writing_observer', - target_exports: ['docs_with_nlp_annotations', 'document_sources', 'document_list'], + target_exports: ['docs_with_nlp_annotations', 'document_sources', 'document_list', 'time_on_task', 'activity'], kwargs: decodedParams } }; @@ -184,63 +195,59 @@ window.dash_clientside.wo_classroom_text_highlighter = { * @param {*} wsStorageData information stored in the websocket store * @returns Dash object to be displayed on page */ - populateOutput: async function (wsStorageData, value, width, height, showHeader, options) { + populateOutput: async function (wsStorageData, value, width, height, showName, options) { // console.log('wsStorageData', wsStorageData); if (!wsStorageData?.students) { return 'No students'; } let output = []; - const selectedHighlights = options.reduce(function(filtered, option) { - if (value?.[option.id]?.highlight?.value) { - const selected = {...option, ...value[option.id]}; - filtered.push(selected); - } - return filtered; - }, []); - // TODO do something with the selected metrics/progress bars/etc. - // currently due to a HACK with how we pass data to the `childComponent` - // we are only able to have a single child and we expect it to be the - // `WOAnnotatedText` component. - const selectedMetrics = options.reduce(function(filtered, option) { - if (value?.[option.id]?.metric?.value) { - const selected = {...option, ...value[option.id]}; - filtered.push(selected); - } - return filtered; - }, []); + const selectedHighlights = fetchSelectedItemsFromOptions(value, options, 'highlight'); + const selectedMetrics = fetchSelectedItemsFromOptions(value, options, 'metric'); const optionHash = await hashObject(value); const students = wsStorageData.students; for (const student in students) { const selectedDocument = students[student].doc_id || Object.keys(students[student].documents || {})[0] || ''; + const studentTileChild = createDashComponent( + DASH_HTML_COMPONENTS, 'Div', + { + children: [ + createProcessTags({ ...students[student].documents[selectedDocument] }, selectedMetrics), + createDashComponent( + LO_DASH_REACT_COMPONENTS, 'WOAnnotatedText', + formatStudentData({ ...students[student].documents[selectedDocument] }, selectedHighlights) + ) + ] + } + ); const studentTile = createDashComponent( LO_DASH_REACT_COMPONENTS, 'WOStudentTextTile', { - showHeader, - studentInfo: formatStudentData(students[student], selectedHighlights), + showName, + profile: students[student].documents[selectedDocument]?.profile || {}, selectedDocument, - childComponent: createDashComponent(LO_DASH_REACT_COMPONENTS, 'WOAnnotatedText', {}), + childComponent: studentTileChild, id: { type: 'WOStudentTextTile', index: student }, + currentStudentHash: students[student].documents[selectedDocument]?.option_hash_docs_with_nlp_annotations, currentOptionHash: optionHash, - className: 'h-100' + className: 'h-100', + additionalButtons: createDashComponent( + DASH_BOOTSTRAP_COMPONENTS, 'Button', + { + id: { type: 'WOStudentTileExpand', index: student }, + children: createDashComponent(DASH_HTML_COMPONENTS, 'I', { className: 'fas fa-expand' }), + color: 'transparent' + } + ) } ); const tileWrapper = createDashComponent( DASH_HTML_COMPONENTS, 'Div', { - className: 'position-relative mb-2', + className: 'mb-2', children: [ studentTile, - createDashComponent( - DASH_BOOTSTRAP_COMPONENTS, 'Button', - { - id: { type: 'WOStudentTileExpand', index: student }, - children: createDashComponent(DASH_HTML_COMPONENTS, 'I', { className: 'fas fa-expand' }), - class_name: 'position-absolute top-0 end-0 m-1', - color: 'transparent' - } - ) ], id: { type: 'WOStudentTile', index: student }, style: styleStudentTile(width, height) @@ -284,7 +291,7 @@ window.dash_clientside.wo_classroom_text_highlighter = { } const students = wsStorageData.students; const promptHash = await hashObject(nlpValue); - const returnedResponses = Object.values(students).filter(student => checkForResponse(student, promptHash)).length; + const returnedResponses = Object.values(students).filter(student => checkForResponse(student, promptHash, ClassroomTextHighlightLoadingQueries)).length; const totalStudents = Object.keys(students).length; if (totalStudents === returnedResponses) { return noLoading; } const loadingProgress = returnedResponses / totalStudents + 0.1; @@ -318,15 +325,12 @@ window.dash_clientside.wo_classroom_text_highlighter = { }, updateLegend: function (value, options) { - const selectedHighlights = options.reduce(function(filtered, option) { - if (value?.[option.id]?.highlight?.value) { - const selected = {...option, ...value[option.id]}; - filtered.push(selected); - } - return filtered; - }, []); + const selectedHighlights = fetchSelectedItemsFromOptions(value, options, 'highlight'); + const selectedMetrics = fetchSelectedItemsFromOptions(value, options, 'metric'); + const total = selectedHighlights.length + selectedMetrics.length; + if (selectedHighlights.length === 0) { - return ['No options selected. Click on the `Highlight Options` to select them.', 0]; + return ['No options selected. Click on the `Options` to select them.', total]; } let output = selectedHighlights.map(highlight => { const color = highlight.highlight.color; @@ -345,6 +349,6 @@ window.dash_clientside.wo_classroom_text_highlighter = { return legendItem; }); output = output.concat('Note: words in the student text may have multiple highlights. Hover over a word for the full list of which options apply'); - return [output, selectedHighlights.length]; + return [output, total]; } }; diff --git a/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/dash_dashboard.py b/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/dash_dashboard.py index 18712240e..6a86b6d6b 100644 --- a/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/dash_dashboard.py +++ b/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/dash_dashboard.py @@ -55,16 +55,25 @@ dbc.CardHeader('View Options'), dbc.CardBody([ dbc.Label('Students per row'), - dbc.Input(type='number', min=1, max=10, value=3, step=1, id=_options_width), + dbc.Input(type='number', min=1, max=10, value=2, step=1, id=_options_width), dbc.Label('Height of student tile'), dcc.Slider(min=100, max=800, marks=None, value=500, id=_options_height), - dbc.Label('Student name headers'), + dbc.Label('Student profile'), dbc.Switch(value=True, id=_options_hide_header, label='Show/Hide'), ]) ]), - html.H4('Highlight Options'), - wo_classroom_text_highlighter.preset_component.create_layout(), - lodrc.WOSettings(id=_options_text_information, options=wo_classroom_text_highlighter.options.OPTIONS, className='table table-striped align-middle') + dbc.Card([ + dbc.CardHeader('Information Options'), + dbc.CardBody([ + wo_classroom_text_highlighter.preset_component.create_layout(), + lodrc.WOSettings( + id=_options_text_information, + options=wo_classroom_text_highlighter.options.OPTIONS, + value=wo_classroom_text_highlighter.options.DEFAULT_VALUE, + className='table table-striped align-middle' + ) + ]) + ]) ], className='p-2') # Legend @@ -103,7 +112,7 @@ dbc.InputGroupText(lodrc.LOConnectionAIO(aio_id=_websocket)), dbc.Button([ html.I(className='fas fa-cog me-1'), - 'Highlight Options (', + 'Options (', html.Span('0', id=_options_toggle_count), ')' ], id=_options_toggle), @@ -111,7 +120,6 @@ 'Legend', id=_legend_button, color='primary'), dbc.Popover( - 'No options selected. Click on the `Highlight Options` to select them.', id=_legend_children, target=_legend_button, trigger='click', body=True, placement='bottom'), lodrc.ProfileSidebarAIO(class_name='rounded-0 rounded-end', color='secondary'), @@ -135,7 +143,7 @@ def layout(): {'children': options_component, 'width': '30%', 'id': _options_prefix, 'side': 'left' }, {'children': expanded_student_component, 'width': '30%', 'id': _expanded_student_panel, - 'side': 'right', 'className': 'vh-100 overflow-auto'} + 'side': 'right'} ], id=_options_collapse, shown=[] ), @@ -196,7 +204,7 @@ def layout(): # Handle showing or hiding the student tile header clientside_callback( ClientsideFunction(namespace=_namespace, function_name='showHideHeader'), - Output({'type': 'WOStudentTextTile', 'index': ALL}, 'showHeader'), + Output({'type': 'WOStudentTextTile', 'index': ALL}, 'showName'), Input(_options_hide_header, 'value'), State({'type': 'WOStudentTextTile', 'index': ALL}, 'id'), ) diff --git a/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/options.py b/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/options.py index 1ca677c3c..07a9ab5aa 100644 --- a/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/options.py +++ b/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/options.py @@ -1,9 +1,12 @@ import writing_observer.nlp_indicators import writing_observer.languagetool_features -parents = [] - -OPTIONS = [ +PROCESS_OPTIONS = [ + {'id': 'process_information', 'label': 'Process Information', 'parent': ''}, + {'id': 'time_on_task', 'label': 'Time on Task', 'types': ['metric'], 'parent': 'process_information'}, + {'id': 'status', 'label': 'Status', 'types': ['metric'], 'parent': 'process_information'} +] +OPTIONS = PROCESS_OPTIONS + [ {'id': indicator['id'], 'types': ['highlight'], 'label': indicator['name'], 'parent': indicator['category']} for indicator in writing_observer.nlp_indicators.INDICATOR_JSONS ] @@ -11,6 +14,11 @@ OPTIONS.append({'id': category, 'label': label, 'parent': 'text_information'}) OPTIONS.append({'id': 'text_information', 'label': 'Text Information', 'parent': ''}) +DEFAULT_VALUE = { + 'time_on_task': {'metric': {'value': True}}, + 'status': {'metric': {'value': True}} +} + # Set of colors to use for highlighting with presets HIGHLIGHTING_COLORS = [ "#FFD700", # Golden Yellow diff --git a/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/preset_component.py b/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/preset_component.py index 5a142d56f..3dfeac035 100644 --- a/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/preset_component.py +++ b/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/preset_component.py @@ -10,6 +10,7 @@ _prefix = 'option-preset' _store = f'{_prefix}-store' _add_input = f'{_prefix}-add-input' +_add_help = f'{_prefix}-add-help' _add_button = f'{_prefix}-add-button' _tray = f'{_prefix}-tray' _set_item = f'{_prefix}-set-item' @@ -19,6 +20,11 @@ def create_layout(): add_preset = dbc.InputGroup([ dbc.Input(id=_add_input, placeholder='Preset name', type='text', value=''), + dbc.InputGroupText(html.I(className='fas fa-circle-question'), id=_add_help), + dbc.Tooltip( + 'Save the current selected information as a preset for quick use in the future.', + target=_add_help + ), dbc.Button([ html.I(className='fas fa-plus me-1'), 'Preset' diff --git a/modules/writing_observer/VERSION b/modules/writing_observer/VERSION index 2e2f7e4a0..c309e668b 100644 --- a/modules/writing_observer/VERSION +++ b/modules/writing_observer/VERSION @@ -1 +1 @@ -0.1.0+2025.04.01T15.05.13.407Z.44725993.berickson.022025.gpt.dashboard.updates +0.1.0+2025.04.07T20.48.55.419Z.3bdcc7c9.berickson.202504.process.metrics diff --git a/modules/writing_observer/writing_observer/module.py b/modules/writing_observer/writing_observer/module.py index 2c82bf5d4..dad2f01a3 100644 --- a/modules/writing_observer/writing_observer/module.py +++ b/modules/writing_observer/writing_observer/module.py @@ -101,9 +101,8 @@ 'nlp_sep_proc': q.select(q.keys('writing_observer.nlp_components', STUDENTS=q.variable('roster'), STUDENTS_path='user_id', RESOURCES=q.variable("doc_ids"), RESOURCES_path='doc_id'), fields='All'), 'nlp_combined': q.join(LEFT=q.variable(nlp_source), LEFT_ON='provenance.provenance.STUDENT.value.user_id', RIGHT=q.variable('roster'), RIGHT_ON='user_id'), # error dashboard activity map nodes - 'time_on_task': q.select(q.keys('writing_observer.time_on_task', STUDENTS=q.variable("roster"), STUDENTS_path='user_id', RESOURCES=q.variable("doc_sources"), RESOURCES_path='doc_id'), fields={'saved_ts': 'last_ts'}), + 'time_on_task': q.select(q.keys('writing_observer.time_on_task', STUDENTS=q.variable("roster"), STUDENTS_path='user_id', RESOURCES=q.variable("doc_sources"), RESOURCES_path='doc_id'), fields={'saved_ts': 'last_ts', 'total_time_on_task': 'time_on_task'}), 'activity_map': q.map(determine_activity, q.variable('time_on_task'), value_path='last_ts'), - 'activity_combined': q.join(LEFT=q.variable('activity_map'), LEFT_ON='provenance.value.provenance.provenance.STUDENT.value.user_id', RIGHT=q.variable('roster'), RIGHT_ON='user_id'), # single student language tool nodes 'single_student_latest_doc': q.select(q.keys('writing_observer.last_document', STUDENTS=q.parameter("student_id", required=True), STUDENTS_path='user_id'), fields={'document_id': 'doc_id'}), 'single_timestamped_docs': q.select(q.keys('writing_observer.document_access_timestamps', STUDENTS=q.parameter("student_id", required=True), STUDENTS_path='user_id'), fields={'timestamps': 'timestamps'}), @@ -189,7 +188,12 @@ "output": "" }, "activity": { - "returns": "activity_combined", + "returns": "activity_map", + "parameters": ["course_id"], + "output": "" + }, + "time_on_task": { + "returns": "time_on_task", "parameters": ["course_id"], "output": "" }, From e4e4adf41c74c6d475418a521713e27987528e98 Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Wed, 11 Jun 2025 07:48:01 -0500 Subject: [PATCH 40/88] LTI auth + Canvas/Schoology integration Added LTI auth workflow as well as canvas and schoology integrations. This included abstracting some of the google endpoint cleaner code out so we could reuse it. --- VERSION | 2 +- learning_observer/VERSION | 2 +- .../learning_observer/auth/__init__.py | 1 + .../learning_observer/auth/lti_sso.py | 343 ++++++++++++ .../learning_observer/auth/social_sso.py | 4 +- .../learning_observer/client_config.py | 1 + learning_observer/learning_observer/google.py | 489 ------------------ .../integrations/__init__.py | 30 ++ .../learning_observer/integrations/canvas.py | 146 ++++++ .../learning_observer/integrations/google.py | 251 +++++++++ .../integrations/schoology.py | 115 ++++ .../learning_observer/integrations/util.py | 301 +++++++++++ .../learning_observer/rosters.py | 87 +++- learning_observer/learning_observer/routes.py | 26 +- .../learning_observer/settings.py | 3 +- .../static/modules/login.html | 2 + learning_observer/learning_observer/util.py | 8 + .../learning_observer/webapp_helpers.py | 12 +- modules/writing_observer/VERSION | 2 +- .../writing_observer/awe_nlp.py | 1 + requirements.txt | 1 + 21 files changed, 1304 insertions(+), 523 deletions(-) create mode 100644 learning_observer/learning_observer/auth/lti_sso.py delete mode 100644 learning_observer/learning_observer/google.py create mode 100644 learning_observer/learning_observer/integrations/__init__.py create mode 100644 learning_observer/learning_observer/integrations/canvas.py create mode 100644 learning_observer/learning_observer/integrations/google.py create mode 100644 learning_observer/learning_observer/integrations/schoology.py create mode 100644 learning_observer/learning_observer/integrations/util.py diff --git a/VERSION b/VERSION index 82e6a0cee..f9873b800 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0+2025.05.01T19.10.46.507Z.bc7bcb4c.berickson.202504.process.metrics +0.1.0+2025.06.11T12.17.15.667Z.233ef706.202505.berickson.lti.integration diff --git a/learning_observer/VERSION b/learning_observer/VERSION index 82e6a0cee..f9873b800 100644 --- a/learning_observer/VERSION +++ b/learning_observer/VERSION @@ -1 +1 @@ -0.1.0+2025.05.01T19.10.46.507Z.bc7bcb4c.berickson.202504.process.metrics +0.1.0+2025.06.11T12.17.15.667Z.233ef706.202505.berickson.lti.integration diff --git a/learning_observer/learning_observer/auth/__init__.py b/learning_observer/learning_observer/auth/__init__.py index 5edc27b78..3e6be7858 100644 --- a/learning_observer/learning_observer/auth/__init__.py +++ b/learning_observer/learning_observer/auth/__init__.py @@ -70,6 +70,7 @@ # Specific authentication schemes from learning_observer.auth.social_sso import social_handler from learning_observer.auth.password import password_auth +from learning_observer.auth.lti_sso import handle_oidc_authorize, handle_oidc_launch, check_oidc_login # Code below does sanity checks on configuration # diff --git a/learning_observer/learning_observer/auth/lti_sso.py b/learning_observer/learning_observer/auth/lti_sso.py new file mode 100644 index 000000000..e6bcd3ba3 --- /dev/null +++ b/learning_observer/learning_observer/auth/lti_sso.py @@ -0,0 +1,343 @@ +''' +Generic LTI 1.3 Single Sign-On handler for multiple providers. + +Implements LTI 1.3's OpenID Connect (OIDC) authentication handshake. +OIDC establishes a secure SSO channel between Learning Management +Systems (LMS) and our tool. This allows the LMS to install our +application under specific contexts (courses). + +The workflow is split across 2 functions + +`handle_oidc_authorize` +1. The LMS sends request for OIDC parameters to this endpoint +2. We create parameters with metadata and send back + +`handle_oidc_launch` +3. If authorized, the LMS sends back encrypted data to this endpoint +4. We decrypt, to get information about the user +5. We verify ourselves against the LMS to generate an auth token +6. User and auth information is stored in the session +''' + +import aiohttp +import aiohttp_session +import jwt +import pmss +import re +import time +import uuid +from aiohttp import web +from urllib.parse import urlencode + +import learning_observer.auth.utils +import learning_observer.constants as constants +import learning_observer.settings + +from learning_observer.log_event import debug_log + +''' +Items in settings should look like +{ + 'auth_uri': 'https://canvas.instructure.com/api/lti/authorize_redirect', + 'jwks_uri': 'https://canvas.instructure.com/api/lti/security/jwks', + 'token_uri': 'https://canvas.instructure.com/login/oauth2/token', + 'client_id': 'YOUR_CLIENT_ID', + 'redirect_uri': 'https://yourdomain.com/lti/canvas/launch', + 'private_key_path': './keys/canvas/private.pem' +} +''' +pmss.register_field( + name='auth_uri', + type=pmss.pmsstypes.TYPES.string, + description="Where to redirect the user's OIDC parameters", + required=True +) +pmss.register_field( + name='jwks_uri', + type=pmss.pmsstypes.TYPES.string, + description="The server's jwks endpoint", + required=True +) +pmss.register_field( + name='token_uri', + type=pmss.pmsstypes.TYPES.string, + description="Where to trade verified users for oauth tokens", + required=True +) +pmss.register_field( + name='redirect_uri', + type=pmss.pmsstypes.TYPES.string, + description='Where to redirect a user after successful OAuth login', + required=True +) +pmss.register_field( + name='private_key_path', + type=pmss.pmsstypes.TYPES.string, + description='Path to where private key is stored', + required=True +) + + +# TODO this code is copied from lo/lo/integrations/canvas.py +# it should abstracted properly +def _extract_course_id_from_url(url): + if match := re.search(r'/courses/(\d+)/names_and_role', url): + return match.group(1) + return None + + +# TODO: Implement persistent configuration storage +# TODO: Add rate limiting for JWKS requests +# TODO: Implement CSRF protection for state parameter + +# =============================== +# JWKS/JWT Validation Utilities +# =============================== + +JWKS_CACHE = {} + + +async def _decode_and_validate_lti_token(provider: str, token: str) -> dict: + ''' + This uses the provider's public key to decode the token + provided by the LMS to our launch endpoint. + + The JSON Web Key Sets (JWKS) are used for verifying + JSON Web Tokens (JWT) that were issued and signed. + ''' + async def get_public_key(kid: str): + '''Get RSA public key from our JWKS to verify token signature + ''' + async def fetch_jwks() -> dict: + '''This fetches and caches JWKS from an external service. + ''' + jwks_uri = learning_observer.settings.pmss_settings.jwks_uri(types=['auth', 'lti', provider]) + + if provider not in JWKS_CACHE: + async with aiohttp.ClientSession() as session: + async with session.get(jwks_uri) as response: + JWKS_CACHE[provider] = await response.json() + return JWKS_CACHE[provider] + + def find_jwk(jwks: dict) -> dict: + return next((k for k in jwks.get('keys', []) if k.get('kid') == kid), None) + + if jwk := find_jwk(await fetch_jwks()): + return jwt.algorithms.RSAAlgorithm.from_jwk(jwk) + return None + + # Extract unverified headers and claims from JWT + # We need to extract some information from the token so + # we can know the `kid`, `aud`, and `iss`. + headers = jwt.get_unverified_header(token) + claims = jwt.decode(token, options={'verify_signature': False}) + + if not (public_key := await get_public_key(headers.get('kid'))): + raise jwt.exceptions.InvalidTokenError('Invalid key ID') + + return jwt.decode( + token, + public_key, + algorithms=['RS256'], + audience=claims.get('aud'), + issuer=claims.get('iss') + ) + + +# ====================== +# OIDC Authorization Flow +# ====================== +# Implements LTI 1.3's OpenID Connect (OIDC) authentication handshake. +# OIDC establishes a secure SSO channel between Learning Management +# Systems (LMS) and our tool. + +# ====================== +# OIDC Login Handler +# ====================== + + +async def handle_oidc_authorize(request: web.Request) -> web.Response: + '''Initiate OIDC authorization flow. + Create OIDC request parameters and send them to the + LMS to verify. + + This function is registered to route `/lti/{provider}/login` + ''' + def create_oidc_params(provider, data: dict) -> dict: + return { + 'scope': 'openid', + 'response_type': 'id_token', + 'response_mode': 'form_post', + 'prompt': 'none', + 'client_id': learning_observer.settings.pmss_settings.client_id(types=['auth', 'lti', provider]), + # TODO this could easily be built from server/lti/{provider}/launch + 'redirect_uri': learning_observer.settings.pmss_settings.redirect_uri(types=['auth', 'lti', provider]), + # help confirm state with future responses + 'nonce': str(uuid.uuid4()), + 'state': str(uuid.uuid4()), + # hints provided by LMS + 'login_hint': data.get('login_hint', ''), + 'lti_message_hint': data.get('lti_message_hint', '') + } + + provider = request.match_info['provider'] + data = await request.post() if request.method == 'POST' else request.query + + params = create_oidc_params(provider, data) + session = await aiohttp_session.get_session(request) + session['lti_state'] = params['state'] + session['lti_nonce'] = params['nonce'] + redirect_base_url = learning_observer.settings.pmss_settings.auth_uri(types=['auth', 'lti', provider]) + return web.HTTPFound( + location=f"{redirect_base_url}?{urlencode(params)}" + ) + + +# ====================== +# OIDC Launch Handler +# ====================== + + +async def handle_oidc_launch(request: web.Request) -> web.Response: + '''Process OIDC launch response + After the LMS server confirms our identity, it sends information + about the user to this endpoint. We exchange that information for + an API token and store it in the user's session. This token is + used later for getting the roster/assignments/etc. + + This function is registered to route `/lti/{provider}/launch` + ''' + def validate_oidc_state(session: dict, state: str) -> None: + '''Validate OIDC state parameter matches session''' + if session.get('lti_state') != state: + debug_log('LTI Launch invalid state') + raise web.HTTPBadRequest(text='Something went wrong.') + + def create_user_from_claims(claims: dict, provider: str) -> dict: + '''The created user is consistent with the rest of LO. + ''' + roles = claims.get('https://purl.imsglobal.org/spec/lti/claim/roles', []) + instructor_roles = [ + 'http://purl.imsglobal.org/vocab/lis/v2/institution/person#Administrator', + 'http://purl.imsglobal.org/vocab/lis/v2/institution/person#Instructor', + 'http://purl.imsglobal.org/vocab/lis/v2/membership#Instructor', + 'http://purl.imsglobal.org/vocab/lis/v2/system/person#SysAdmin' + ] + is_instructor = any(r in roles for r in instructor_roles) + + # Include the LTI Launch Context + # HACK in Canvas, each course has 2 IDs: + # 1. LTI compliant ID - `lti_context_id` + # 2. ID used to make API calls - `api_id` + context = claims.get('https://purl.imsglobal.org/spec/lti/claim/context', {}) + api_with_id = claims.get('https://purl.imsglobal.org/spec/lti-nrps/claim/namesroleservice', {}).get('context_memberships_url') + extracted_course_id = _extract_course_id_from_url(api_with_id) + id = context['id'] + lti_context = { + 'lti_context_id': id, + 'api_id': extracted_course_id if extracted_course_id else id, + 'provider': provider + } + + return { + constants.USER_ID: claims['sub'], + 'email': claims.get('email'), + 'name': claims.get('given_name'), + 'family_name': claims.get('family_name', ''), + 'picture': claims.get('picture', ''), + 'role': learning_observer.auth.ROLES.TEACHER if is_instructor else learning_observer.auth.ROLES.STUDENT, + 'authorized': is_instructor, + 'lti_context': lti_context + # TODO figure out backto. With google sso, we had a state we could store things in + # 'back_to': request.query.get('state') + } + + def generate_signed_client_authentication_jwt(provider) -> str: + ''' + Generates cryptographically signed JWTs for verifying identity. + Uses RSA-SHA256 to sign claims containing client identity (iss/sub), + token endpoint (aud), timestamp controls (iat/exp), and unique JTI. + + The token generated is used to obtain an OAuth token + ''' + def read_private_pem_key_from_file(path: str) -> str: + return open(path, 'r').read() + + private_key_path = learning_observer.settings.pmss_settings.private_key_path(types=['auth', 'lti', provider]) + private_key = read_private_pem_key_from_file(private_key_path) + + now = time.time() + client_id = learning_observer.settings.pmss_settings.client_id(types=['auth', 'lti', provider]) + token_uri = learning_observer.settings.pmss_settings.token_uri(types=['auth', 'lti', provider]) + + # Note the client is both the issuer and subject since + # it is proving its own identity + payload = { + 'iss': client_id, # issuer + 'sub': client_id, # subject + 'aud': token_uri, # audience + 'iat': now, # JWT issued at + 'exp': now + 300, # JWT expiration (5 minutes) + 'jti': str(uuid.uuid4()) # JWT ID + } + return jwt.encode(payload, private_key, algorithm='RS256') + + async def exchange_assertion_for_token(provider, assertion: str) -> str: + '''Exchange client assertion for access token''' + token_uri = learning_observer.settings.pmss_settings.token_uri(types=['auth', 'lti', provider]) + token_data = { + 'grant_type': 'client_credentials', + 'client_assertion_type': 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer', + 'client_assertion': assertion, + 'scope': ' '.join([ + # Can retrieve user data associated with the context the tool is installed in + 'https://purl.imsglobal.org/spec/lti-nrps/scope/contextmembership.readonly', + # Can view assignment data in the gradebook associated with the tool + 'https://purl.imsglobal.org/spec/lti-ags/scope/lineitem.readonly', + # Can create and update submission results for assignments associated with the tool + 'https://purl.imsglobal.org/spec/lti-ags/scope/score', + ]) + } + async with aiohttp.ClientSession() as session: + async with session.post(url=token_uri, data=token_data) as response: + if response.status != 200: + debug_log(f'LTI Launch Error during token exchange:\n{await response.text()}') + raise web.HTTPBadRequest(text='Something went wrong.') + return (await response.json())['access_token'] + + provider = request.match_info['provider'] + data = await request.post() + + try: + # Validate OIDC response + session = await aiohttp_session.get_session(request) + validate_oidc_state(session, data.get('state')) + + # Verify and decode JWT + claims = await _decode_and_validate_lti_token(provider, data.get('id_token')) + if claims['nonce'] != session.get('lti_nonce'): + debug_log('LTI Launch invalid nonce') + raise web.HTTPBadRequest(text='Something went wrong.') + + # Create user session + user_info = create_user_from_claims(claims, provider) + await learning_observer.auth.utils.update_session_user_info(request, user_info) + + # Exchange for access token + assertion = generate_signed_client_authentication_jwt(provider) + access_token = await exchange_assertion_for_token(provider, assertion) + + # Store auth headers + headers = {'Authorization': f'Bearer {access_token}'} + session[constants.AUTH_HEADERS] = headers + request[constants.AUTH_HEADERS] = headers + + return web.HTTPFound(location='/') + + except (jwt.PyJWTError, KeyError) as e: + debug_log(f'LTI Launch Authentication failed: {e}') + raise web.HTTPUnauthorized(text='Something went wrong.') + + +async def check_oidc_login(request): + return web.HTTPFound('/') diff --git a/learning_observer/learning_observer/auth/social_sso.py b/learning_observer/learning_observer/auth/social_sso.py index 0bad8f1b7..a12657957 100644 --- a/learning_observer/learning_observer/auth/social_sso.py +++ b/learning_observer/learning_observer/auth/social_sso.py @@ -41,7 +41,6 @@ import learning_observer.constants as constants import learning_observer.exceptions -import learning_observer.google import learning_observer.kvs import learning_observer.rosters import learning_observer.runtime @@ -163,6 +162,7 @@ async def _store_teacher_info_for_background_process(id, request): TODO remove this function and references when new, better workflows are established. ''' + import learning_observer.google debug_log("SocialSSO Storing teacher info: {} {}".format(id, request)) kvs = learning_observer.kvs.KVS() runtime = learning_observer.runtime.Runtime(request) @@ -189,7 +189,7 @@ async def _fetch_and_store_document(student, doc_id): doc = await learning_observer.google.doctext(runtime, documentId=doc_id) if 'text' not in doc: skipped_docs.add(doc_id) - debu_log("** SocialSSO Text not found: {} {}".format(student, doc_id)) + debug_log("** SocialSSO Text not found: {} {}".format(student, doc_id)) return await kvs.set(doc_key, doc) debug_log("** SocialSSO Text stored: {} {}".format(student, doc_id)) diff --git a/learning_observer/learning_observer/client_config.py b/learning_observer/learning_observer/client_config.py index 4b1c0d20e..4a2c86755 100644 --- a/learning_observer/learning_observer/client_config.py +++ b/learning_observer/learning_observer/client_config.py @@ -37,6 +37,7 @@ async def client_config_handler(request): "google_oauth": "google_oauth" in learning_observer.settings.settings['auth'], "password_auth": "password_file" in learning_observer.settings.settings['auth'], "http_basic_auth": learning_observer.auth.http_basic.http_auth_page_enabled(), + "lti_auth": "lti" in learning_observer.settings.settings['auth'], "theme": learning_observer.settings.settings['theme'] } diff --git a/learning_observer/learning_observer/google.py b/learning_observer/learning_observer/google.py deleted file mode 100644 index f1e45f899..000000000 --- a/learning_observer/learning_observer/google.py +++ /dev/null @@ -1,489 +0,0 @@ -''' -We will gradually move all of the Google-specific code into here. - -Our design goals: -- Easily call into Google APIs (Classroom, Drive, Docs, etc.) -- Be able to preprocess the data into standard formats - -On a high level, for each Google request, we plan to have a 4x4 grid: -- Web request and function call -- Cleaned versus raw data - -The Google APIs are well-designed (if poorly-documented, and with occasional -bugs), but usually return more data than we need, so we have cleaner functions. - -For a given call, we might have several cleaners. For example, for a Google Doc, -Google returns a massive JSON object containing everything. For most purposes, -we don't need all of that, and it's more convenient to work with a plain -text representation, and for downstream code to not need to understand this -JSON. However, for some algorithms, we might need additonal data of different -sorts. It's still more convenient to hand this back in something simplified for -analysis. -''' - -import collections -import itertools -import json -import recordclass -import string -import re - -import aiohttp -import aiohttp.web -import aiohttp_session - -import learning_observer.constants as constants -import learning_observer.settings as settings -import learning_observer.log_event -import learning_observer.util -import learning_observer.auth -import learning_observer.runtime -import learning_observer.prestartup - - -cache = None - - -GOOGLE_FIELDS = [ - 'alternateLink', 'calculationType', 'calendarId', 'courseGroupEmail', - 'courseId', 'courseState', 'creationTime', 'descriptionHeading', - 'displaySetting', 'emailAddress', 'enrollmentCode', 'familyName', - 'fullName', 'givenName', 'gradebookSettings', 'guardiansEnabled', - 'ownerId', 'photoUrl', 'teacherFolder', 'teacherGroupEmail', 'updateTime', - 'userId' -] - -# On in-take, we want to convert Google's CamelCase to LO's snake_case. This -# dictionary contains the conversions. -camel_to_snake = re.compile(r'(?>> ("hello {hi} my {bye}")] - ['hi', 'bye'] - ''' - # The parse returns a lot of context, which we discard. In particular, the - # last item is often about the suffix after the last parameter and may be - # `None` - return [f[1] for f in string.Formatter().parse(format_string) if f[1] is not None] - - -async def raw_google_ajax(runtime, target_url, **kwargs): - ''' - Make an AJAX call to Google, managing auth + auth. - - * runtime is a Runtime class containing request information. - * default_url is typically grabbed from ENDPOINTS - * ... and we pass the named parameters - ''' - request = runtime.get_request() - url = target_url.format(**kwargs) - user = await learning_observer.auth.get_active_user(request) - if constants.AUTH_HEADERS not in request or user is None: - raise aiohttp.web.HTTPUnauthorized(text="Please log in") # TODO: Consistent way to flag this - - cache_key = "raw_google/" + learning_observer.auth.encode_id('session', user[constants.USER_ID]) + '/' + learning_observer.util.url_pathname(url) - if settings.feature_flag('use_google_ajax') is not None: - value = await cache[cache_key] - if value is not None: - return learning_observer.util.translate_json_keys( - json.loads(value), - GOOGLE_TO_SNAKE - ) - async with aiohttp.ClientSession(loop=request.app.loop) as client: - async with client.get(url, headers=request[constants.AUTH_HEADERS]) as resp: - response = await resp.json() - learning_observer.log_event.log_ajax(target_url, response, request) - if settings.feature_flag('use_google_ajax') is not None: - await cache.set(cache_key, json.dumps(response, indent=2)) - return learning_observer.util.translate_json_keys( - response, - GOOGLE_TO_SNAKE - ) - - -def raw_access_partial(remote_url, name=None): - ''' - This is a helper which allows us to create a function which calls specific - Google APIs. - - To test this, try: - - print(await raw_document(request, documentId="some_google_doc_id")) - ''' - async def caller(request, **kwargs): - ''' - Make an AJAX request to Google - ''' - return await raw_google_ajax(request, remote_url, **kwargs) - setattr(caller, "__qualname__", name) - - return caller - - -@learning_observer.prestartup.register_startup_check -def connect_to_google_cache(): - '''Setup cache for requests to the Google API. - The cache is currently only used with the `use_google_ajax` - feature flag. - ''' - if not settings.feature_flag('google_routes'): - return - - for key in ['save_google_ajax', 'use_google_ajax', 'save_clean_ajax', 'use_clean_ajax']: - if settings.feature_flag(key): - global cache - try: - cache = learning_observer.kvs.KVS.google_cache() - except AttributeError: - error_text = 'The google_cache KVS is not configured.\n'\ - 'Please add a `google_cache` kvs item to the `kvs` '\ - 'key in `creds.yaml`.\n'\ - '```\ngoogle_cache:\n type: filesystem\n path: ./learning_observer/static_data/google\n'\ - ' subdirs: true\n```\nOR\n'\ - '```\ngoogle_cache:\n type: redis_ephemeral\n expiry: 600\n```' - raise learning_observer.prestartup.StartupCheck("Google KVS: " + error_text) - - -def initialize_and_register_routes(app): - ''' - This is a big 'ol function which might be broken into smaller ones at some - point. We: - - - Created debug routes to pass through AJAX requests to Google - - Created production APIs to have access to cleaned versions of said data - - Create local function calls to call from other pieces of code - within process - - We probably don't need all of this in production, but a lot of this is - very important for debugging. Having APIs is more useful than it looks, since - making use of Google APIs requires a lot of infrastructure (registering - apps, auth/auth, etc.) which we already have in place on dev / debug servers. - ''' - # For now, all of this is behind one big feature flag. In the future, - # we'll want seperate ones for the debugging tools and the production - # staff - if not settings.feature_flag('google_routes'): - return - - # Provide documentation on what we're doing - app.add_routes([ - aiohttp.web.get("/google", api_docs_handler) - ]) - - def make_ajax_raw_handler(remote_url): - ''' - This creates a handler to forward Google requests to the client. It's used - for debugging right now. We should think through APIs before relying on this. - ''' - async def ajax_passthrough(request): - ''' - And the actual handler.... - ''' - runtime = learning_observer.runtime.Runtime(request) - response = await raw_google_ajax( - runtime, - remote_url, - **request.match_info - ) - - return aiohttp.web.json_response(response) - return ajax_passthrough - - def make_cleaner_handler(raw_function, cleaner_function, name=None): - async def cleaner_handler(request): - ''' - ''' - response = cleaner_function( - await raw_function(request, **request.match_info) - ) - if isinstance(response, dict) or isinstance(response, list): - return aiohttp.web.json_response( - response - ) - elif isinstance(response, str): - return aiohttp.web.Response( - text=response - ) - else: - raise AttributeError(f"Invalid response type: {type(response)}") - if name is not None: - setattr(cleaner_handler, "__qualname__", name + "_handler") - - return cleaner_handler - - def make_cleaner_function(raw_function, cleaner_function, name=None): - async def cleaner_local(request, **kwargs): - google_response = await raw_function(request, **kwargs) - clean = cleaner_function(google_response) - return clean - if name is not None: - setattr(cleaner_local, "__qualname__", name) - return cleaner_local - - for e in ENDPOINTS: - function_name = f"raw_{e.name}" - raw_function = raw_access_partial(remote_url=e.remote_url, name=e.name) - globals()[function_name] = raw_function - cleaners = e._cleaners() - for c in cleaners: - app.add_routes([ - aiohttp.web.get( - cleaners[c]['local_url'], - make_cleaner_handler( - raw_function, - cleaners[c]['function'], - name=cleaners[c]['name'] - ) - ) - ]) - globals()[cleaners[c]['name']] = make_cleaner_function( - raw_function, - cleaners[c]['function'], - name=cleaners[c]['name'] - ) - app.add_routes([ - aiohttp.web.get( - e._local_url(), - make_ajax_raw_handler(e.remote_url) - ) - ]) - - -def api_docs_handler(request): - ''' - Return a list of available endpoints. - - Eventually, we should also document available function calls - ''' - response = "URL Endpoints:\n\n" - for endpoint in ENDPOINTS: - response += f"{endpoint._local_url()}\n" - cleaners = endpoint._cleaners() - for c in cleaners: - response += f" {cleaners[c]['local_url']}\n" - response += "\n\n Globals:" - if False: - response += str(globals()) - return aiohttp.web.Response(text=response) - - -def register_cleaner(data_source, cleaner_name): - ''' - This will register a cleaner function, for export both as a web service - and as a local function call. - ''' - def decorator(f): - found = False - for endpoint in ENDPOINTS: - if endpoint.name == data_source: - found = True - endpoint._add_cleaner( - cleaner_name, - { - 'function': f, - 'local_url': f'{endpoint._local_url()}/{cleaner_name}', - 'name': cleaner_name - } - ) - - if not found: - raise AttributeError(f"Data source {data_source} invalid; not found in endpoints.") - return f - - return decorator - - -# Rosters -@register_cleaner("course_roster", "roster") -def clean_course_roster(google_json): - ''' - Retrieve the roster for a course, alphabetically - ''' - students = google_json.get('students', []) - students.sort( - key=lambda x: x.get('name', {}).get('fullName', 'ZZ'), - ) - # Convert Google IDs to internal ideas (which are the same, but with a gc- prefix) - for student_json in students: - google_id = student_json['profile']['id'] - local_id = learning_observer.auth.google_id_to_user_id(google_id) - student_json[constants.USER_ID] = local_id - del student_json['profile']['id'] - - # For the present there is only one external id so we will add that directly. - if 'external_ids' not in student_json['profile']: - student_json['profile']['external_ids'] = [] - student_json['profile']['external_ids'].append({"source": "google", "id": google_id}) - return students - - -@register_cleaner("course_list", "courses") -def clean_course_list(google_json): - ''' - Google's course list is one object deeper than we'd like, and alphabetic - sort order is nicer. This will clean it up a bit - ''' - courses = google_json.get('courses', []) - courses.sort( - key=lambda x: x.get('name', 'ZZ'), - ) - return courses - - -@register_cleaner('course_work', 'assignments') -def clean_course_work(google_json): - ''' - Google's course work is one object deeper than we'd like, and update_time - sort order is nicer. This will clean it up a bit - ''' - assignments = google_json.get('courseWork', []) - assignments.sort( - key=lambda x: x.get('update_time', 0) - ) - return assignments - - -# Google Docs -def _force_text_length(text, length): - ''' - Force text to a given length, either concatenating or padding - - >>> force_text_length("Hello", 3) - >>> 'Hel' - - >>> force_text_length("Hello", 13) - >>> 'Hello ' - ''' - return text[:length] + " " * (length - len(text)) - - -def get_error_details(error): - messages = { - 403: 'Student working on private document.', - 404: 'Unable to fetch document.' - } - code = error['code'] - message = messages.get(code, 'Unknown error.') - return {'error': {'code': code, 'message': message}} - - -@register_cleaner("document", "doctext") -def extract_text_from_google_doc_json( - j, align=True, - EXTRACT_DEBUG_CHECKS=False): - ''' - Extract text from a Google Docs JSON object, ignoring formatting. - - There is an alignment issue between Google's and Python's handling - of Unicode. We can either: - * extract text faithfully (align=False) - * extract text with aligned indexes by cutting text / adding - spaces (align=True) - - This issue came up in text with a Russian flag unicode symbol - (referencing the current conflict). I tried various encodings, - and none quite matched Google 100%. - - Note that align=True doesn't necessarily give perfect local alignment - within text chunks, since we do have different lengths for something like - this flag. It does work okay globally. - ''' - # return error message for text - if 'error' in j: - return get_error_details(j['error']) - length = j['body']['content'][-1]['endIndex'] - elements = [a.get('paragraph', {}).get('elements', []) for a in j['body']['content']] - flat = sum(elements, []) - text_chunks = [f['textRun']['content'] for f in flat] - if align: - lengths = [f['endIndex'] - f['startIndex'] for f in flat] - text_chunks = [_force_text_length(chunk, length) for chunk, length in zip(text_chunks, lengths)] - text = ''.join(text_chunks) - - if EXTRACT_DEBUG_CHECKS: - print("Text length versus Google length:") - print(len(text), length) - print("We expect these to be off by one, since Google seems to starts at 1 (and Python at 0)") - if align: - print - print("Offsets (these should match):") - print(list(zip(itertools.accumulate(map(len, text_chunks)), itertools.accumulate(lengths)))) - - return {'text': text} - - -@register_cleaner("coursework_submissions", "assigned_docs") -def clean_assignment_docs(google_json): - ''' - Retrieve set of documents per student associated with an assignment - ''' - student_submissions = google_json.get('studentSubmissions', []) - for student_json in student_submissions: - google_id = student_json[constants.USER_ID] - local_id = learning_observer.auth.google_id_to_user_id(google_id) - student_json[constants.USER_ID] = local_id - docs = [d['driveFile'] for d in learning_observer.util.get_nested_dict_value(student_json, 'assignmentSubmission.attachments', []) if 'driveFile' in d] - student_json['documents'] = docs - # TODO we should probably remove some of the keys provided - return student_submissions - - -if __name__ == '__main__': - import json - import sys - j = json.load(open(sys.argv[1])) - # extract_text_from_google_doc_json(j, align=False, EXTRACT_DEBUG_CHECKS=True) - # extract_text_from_google_doc_json(j, align=True, EXTRACT_DEBUG_CHECKS=True) - output = clean_assignment_docs(j) - print(json.dumps(output, indent=2)) diff --git a/learning_observer/learning_observer/integrations/__init__.py b/learning_observer/learning_observer/integrations/__init__.py new file mode 100644 index 000000000..213faa972 --- /dev/null +++ b/learning_observer/learning_observer/integrations/__init__.py @@ -0,0 +1,30 @@ +import learning_observer.integrations.canvas +import learning_observer.integrations.google +import learning_observer.integrations.schoology +import learning_observer.settings + +INTEGRATIONS = {} + + +def register_integrations(app): + '''`routes.py:add_routes` calls this function to add the + integrated services as routes on the system + + This initializes INTEGRATIONS for other functions to reference + when making a call to course/rosters/assignments/etc. + ''' + # TODO the setting checks should be calling into `pmss_settings` instead of `settings` + if 'google_oauth' in learning_observer.settings.settings['auth']: + INTEGRATIONS['google'] = learning_observer.integrations.google.register_endpoints(app) + + if 'lti' not in learning_observer.settings.settings['auth']: + return + + if 'schoology' in learning_observer.settings.settings['auth']['lti']: + INTEGRATIONS['schoology'] = learning_observer.integrations.schoology.register_endpoints(app) + + canvas_providers = [k for k in learning_observer.settings.settings['auth']['lti'].keys() if 'canvas' in k] + for provider in canvas_providers: + provider_endpoint_registrar = learning_observer.integrations.canvas.setup_canvas_provider(provider) + # TODO check that provider doesn't already exist and is trying to be overwritten + INTEGRATIONS[provider] = provider_endpoint_registrar(app) diff --git a/learning_observer/learning_observer/integrations/canvas.py b/learning_observer/learning_observer/integrations/canvas.py new file mode 100644 index 000000000..cb6c9164a --- /dev/null +++ b/learning_observer/learning_observer/integrations/canvas.py @@ -0,0 +1,146 @@ +import pmss +import re + +import learning_observer.kvs +import learning_observer.constants as constants +import learning_observer.settings as settings + +from . import util + +pmss.register_field( + name='api_domain', + type=pmss.pmsstypes.TYPES.string, + description='Domain of the api calls', + required=True +) + + +def setup_canvas_provider(provider): + # TODO pull the base url from settings based on provider + base_url = settings.pmss_settings.api_domain(types=['auth', 'lti', provider]) + + ENDPOINTS = list(map(lambda x: util.Endpoint(*x, api_name=provider), [ + ('course_list', base_url + '/api/lti/courses/{courseId}/names_and_roles'), + ('course_roster', base_url + '/api/lti/courses/{courseId}/names_and_roles'), + ('course_lineitems', base_url + '/api/lti/courses/{courseId}/line_items'), + ])) + + register_cleaner = util.make_cleaner_registrar(ENDPOINTS) + + def register_canvas_endpoints(app): + if not settings.feature_flag('canvas_routes'): + return + + # Create the API routes and get back the functions + return util.register_endpoints( + app=app, + endpoints=ENDPOINTS, + api_name=provider, + feature_flag_name='canvas_routes' + ) + + def _extract_course_id_from_url(url): + if match := re.search(r'/courses/(\d+)/names_and_role', url): + return match.group(1) + return None + + @register_cleaner('course_list', 'courses') + def clean_course_list(canvas_json): + ''' + The LTI integration Canvas uses for auth occurs on a + course by course level. This cleaner wraps the current + course in a list. + ''' + context = canvas_json.get('context', {}) + if not (id := _extract_course_id_from_url(canvas_json.get('id', ''))): + raise ValueError('Canvas json did not provide a parsable id') + course = { + 'id': id, + 'name': context.get('label'), + 'title': context.get('title'), + 'lti_id': context.get('id') + } + cleaned = [course] + return cleaned + + async def _lookup_gid_by_email(email): + kvs = learning_observer.kvs.KVS() + key = f'email-studentID-mapping:{email}' + id = await kvs[key] + if id: + return f'gid-{id}' + return None + + async def _process_canvas_user_for_system(member): + # Skip if no canvas id + canvas_id = member.get('user_id') + if not canvas_id: return None + + # Skip non students + is_student = 'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner' in member.get('roles', []) + if not is_student: return None + + # Create user for our system + email = member.get('email') + local_id = await _lookup_gid_by_email(email) + if not local_id: + local_id = f'canvas-{canvas_id}' + + member[constants.USER_ID] = local_id + user = { + 'profile': { + 'name': { + 'given_name': member.get('given_name'), + 'family_name': member.get('family_name'), + 'full_name': member.get('name') + }, + 'email_address': email, + 'photo_url': member.get('picture') + }, + constants.USER_ID: local_id, + # TODO is this needed? Other roster functions in LO include it + # 'course_id': course_id + } + return user + + @register_cleaner('course_roster', 'roster') + async def clean_course_roster(canvas_json): + ''' + Retrieve and clean the roster for a Canvas course, alphabetically sorted + + Conforms to LTI NRPS v2 response format + https://www.imsglobal.org/spec/lti-nrps/v2p0 + ''' + members = canvas_json.get('members', []) + members.sort( + key=lambda x: x.get('name', 'ZZ'), + ) + # Process each student record + users = [] + for m in members: + user = await _process_canvas_user_for_system(m) + if user is not None: + users.append(user) + + return users + + @register_cleaner('course_lineitems', 'assignments') + def clean_course_assignments(canvas_json): + ''' + Clean course line items (assignments) from Canvas + + Conforms to LTI AGS response format + https://www.imsglobal.org/spec/lti-ags/v2p0 + ''' + line_items = canvas_json + if not isinstance(line_items, list): + # If it's not already a list, check for lineItems property that might contain the list + line_items = canvas_json.get('lineItems', []) + + # Sort by due date if available, otherwise by title + line_items.sort( + key=lambda x: x.get('endDateTime', x.get('label', 'ZZ')), + ) + return line_items + + return register_canvas_endpoints diff --git a/learning_observer/learning_observer/integrations/google.py b/learning_observer/learning_observer/integrations/google.py new file mode 100644 index 000000000..de7f85c9a --- /dev/null +++ b/learning_observer/learning_observer/integrations/google.py @@ -0,0 +1,251 @@ +''' +Google-specific API module for Learning Observer. + +This module provides access to various Google APIs (Classroom, Drive, Docs, etc.) +using the generic API infrastructure. + +The module provides: +- Raw data access to Google APIs +- Cleaned data from Google APIs in standardized formats +- Both web API routes and in-process function calls +''' + +import itertools +import json +import re + +import learning_observer.constants as constants +import learning_observer.auth +import learning_observer.kvs +import learning_observer.prestartup +import learning_observer.settings as settings +import learning_observer.util + +from . import util + + +# Cache for Google API responses +cache = None + +# Google field name mapping +GOOGLE_FIELDS = [ + 'alternateLink', 'calculationType', 'calendarId', 'courseGroupEmail', + 'courseId', 'courseState', 'creationTime', 'descriptionHeading', + 'displaySetting', 'emailAddress', 'enrollmentCode', 'familyName', + 'fullName', 'givenName', 'gradebookSettings', 'guardiansEnabled', + 'ownerId', 'photoUrl', 'teacherFolder', 'teacherGroupEmail', 'updateTime', + 'userId' +] + +# Convert Google's CamelCase to LO's snake_case +camel_to_snake = re.compile(r'(?>> force_text_length("Hello", 3) + >>> 'Hel' + + >>> force_text_length("Hello", 13) + >>> 'Hello ' + ''' + return text[:length] + " " * (length - len(text)) + + +def get_error_details(error): + messages = { + 403: 'Student working on private document.', + 404: 'Unable to fetch document.' + } + code = error['code'] + message = messages.get(code, 'Unknown error.') + return {'error': {'code': code, 'message': message}} + + +@register_cleaner("document", "doctext") +def extract_text_from_google_doc_json( + j, align=True, + EXTRACT_DEBUG_CHECKS=False): + ''' + Extract text from a Google Docs JSON object, ignoring formatting. + + There is an alignment issue between Google's and Python's handling + of Unicode. We can either: + * extract text faithfully (align=False) + * extract text with aligned indexes by cutting text / adding + spaces (align=True) + + This issue came up in text with a Russian flag unicode symbol + (referencing the current conflict). I tried various encodings, + and none quite matched Google 100%. + + Note that align=True doesn't necessarily give perfect local alignment + within text chunks, since we do have different lengths for something like + this flag. It does work okay globally. + ''' + # return error message for text + if 'error' in j: + return get_error_details(j['error']) + length = j['body']['content'][-1]['endIndex'] + elements = [a.get('paragraph', {}).get('elements', []) for a in j['body']['content']] + flat = sum(elements, []) + text_chunks = [f['textRun']['content'] for f in flat] + if align: + lengths = [f['endIndex'] - f['startIndex'] for f in flat] + text_chunks = [_force_text_length(chunk, length) for chunk, length in zip(text_chunks, lengths)] + text = ''.join(text_chunks) + + if EXTRACT_DEBUG_CHECKS: + print("Text length versus Google length:") + print(len(text), length) + print("We expect these to be off by one, since Google seems to starts at 1 (and Python at 0)") + if align: + print + print("Offsets (these should match):") + print(list(zip(itertools.accumulate(map(len, text_chunks)), itertools.accumulate(lengths)))) + + return {'text': text} + + +@register_cleaner("coursework_submissions", "assigned_docs") +def clean_assignment_docs(google_json): + ''' + Retrieve set of documents per student associated with an assignment + ''' + student_submissions = google_json.get('studentSubmissions', []) + for student_json in student_submissions: + google_id = student_json[constants.USER_ID] + local_id = learning_observer.auth.google_id_to_user_id(google_id) + student_json[constants.USER_ID] = local_id + docs = [d['driveFile'] for d in learning_observer.util.get_nested_dict_value(student_json, 'assignmentSubmission.attachments', []) if 'driveFile' in d] + student_json['documents'] = docs + # TODO we should probably remove some of the keys provided + return student_submissions + + +if __name__ == '__main__': + import json + import sys + j = json.load(open(sys.argv[1])) + # extract_text_from_google_doc_json(j, align=False, EXTRACT_DEBUG_CHECKS=True) + # extract_text_from_google_doc_json(j, align=True, EXTRACT_DEBUG_CHECKS=True) + output = clean_assignment_docs(j) + print(json.dumps(output, indent=2)) diff --git a/learning_observer/learning_observer/integrations/schoology.py b/learning_observer/learning_observer/integrations/schoology.py new file mode 100644 index 000000000..990b72360 --- /dev/null +++ b/learning_observer/learning_observer/integrations/schoology.py @@ -0,0 +1,115 @@ +import learning_observer.constants as constants +import learning_observer.kvs +import learning_observer.settings as settings + +from . import util + +API = 'schoology' + +ENDPOINTS = list(map(lambda x: util.Endpoint(**x, api_name=API), [ + {'name': 'course_list', 'remote_url': 'https://api.schoology.com/v1/sections/{courseId}/enrollments', 'headers': {'Accept': 'application/vnd.ims.lti-nrps.v2.membershipcontainer+json'}}, + {'name': 'course_roster', 'remote_url': 'https://api.schoology.com/v1/sections/{courseId}/enrollments', 'headers': {'Accept': 'application/vnd.ims.lti-nrps.v2.membershipcontainer+json'}}, + {'name': 'course_assignments', 'remote_url': 'https://api.schoology.com/v1/sections/{courseId}/assignments', 'headers': {'Accept': 'application/vnd.ims.lis.v2.lineitemcontainer+json'}}, +])) + +register_cleaner = util.make_cleaner_registrar(ENDPOINTS) + + +def register_endpoints(app): + ''' + ''' + if not settings.feature_flag('schoology_routes'): + return + + return util.register_endpoints( + app=app, + endpoints=ENDPOINTS, + api_name=API, + feature_flag_name='schoology_routes' + ) + + +@register_cleaner('course_list', 'courses') +def clean_course_list(schoology_json): + ''' + The LTI integration Schoology uses for auth occurs on a + course by course level. This cleaner wraps the current + course in a list. + ''' + context = schoology_json.get('context', {}) + course = { + 'id': context.get('id'), + 'name': context.get('label'), + 'title': context.get('title'), + } + return [course] + + +# TODO this already exists in a different place - it should live in only one place +async def _lookup_gid_by_email(email): + kvs = learning_observer.kvs.KVS() + key = f'email-studentID-mapping:{email}' + id = await kvs[key] + if id: + return f'gid-{id}' + return None + + +async def _process_schoology_user_for_system(member): + # Skip if no canvas id + canvas_id = member.get('user_id') + if not canvas_id: return None + + # Skip non students + is_student = 'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner' in member.get('roles', []) + if not is_student: return None + + # Create user for our system + email = member.get('email') + local_id = await _lookup_gid_by_email(email) + if not local_id: + local_id = f'canvas-{canvas_id}' + + member[constants.USER_ID] = local_id + user = { + 'profile': { + 'name': { + 'given_name': member.get('given_name'), + 'family_name': member.get('family_name'), + 'full_name': member.get('name') + }, + 'email_address': email, + 'photo_url': member.get('picture') + }, + constants.USER_ID: local_id, + # TODO is this needed? Other roster functions in LO include it + # 'course_id': course_id + } + return user + + +@register_cleaner('course_roster', 'roster') +async def clean_course_roster(schoology_json): + ''' + Retrieve and clean the roster for a Canvas course, alphabetically sorted + + Conforms to LTI NRPS v2 response format + https://www.imsglobal.org/spec/lti-nrps/v2p0 + ''' + print('clean_course_roster', schoology_json) + return + # Process each student record + # for m in members: + # user = await _process_schoology_user_for_system(m) + + +@register_cleaner('course_assignments', 'assignments') +def clean_course_assignments(schoology_json): + ''' + Clean course line items (assignments) from Schoology + + Conforms to LTI AGS response format + https://www.imsglobal.org/spec/lti-ags/v2p0 + ''' + print('clean_course_assignments', schoology_json) + return diff --git a/learning_observer/learning_observer/integrations/util.py b/learning_observer/learning_observer/integrations/util.py new file mode 100644 index 000000000..b56595cce --- /dev/null +++ b/learning_observer/learning_observer/integrations/util.py @@ -0,0 +1,301 @@ +''' +Generic API calling infrastructure that can be reused for different APIs. + +Design goals: +- Easily call into external APIs +- Be able to preprocess the data into standard formats + +On a high level, for each API request, we plan to have a 4x4 grid: +- Web request and function call +- Cleaned versus raw data + +For each specific API implementation, we'll have both raw data access and cleaner functions +to transform that data into more convenient formats. +''' +import inspect +import json +import recordclass +import string + +import aiohttp +import aiohttp.web + +import learning_observer.constants as constants +import learning_observer.settings as settings +import learning_observer.log_event +import learning_observer.auth +import learning_observer.runtime +import learning_observer.util + + +class Endpoint(recordclass.make_dataclass("Endpoint", ["name", "remote_url", "doc", "cleaners", "api_name", "headers"], defaults=["", None, None, None])): + def arguments(self): + return extract_parameters_from_format_string(self.remote_url) + + def _local_url(self): + parameters = "}/{".join(self.arguments()) + base_url = f"/{self.api_name}/{self.name}" + if len(parameters) == 0: + return base_url + else: + return base_url + "/{" + parameters + "}" + + def _add_cleaner(self, name, cleaner): + if self.cleaners is None: + self.cleaners = dict() + self.cleaners[name] = cleaner + if 'local_url' not in cleaner: + cleaner['local_url'] = self._local_url() + "/" + name + + def _cleaners(self): + if self.cleaners is None: + return [] + else: + return self.cleaners + + +def extract_parameters_from_format_string(format_string): + ''' + Extracts parameters from a format string. E.g. + + >>> ("hello {hi} my {bye}")] + ['hi', 'bye'] + ''' + # The parse returns a lot of context, which we discard. In particular, the + # last item is often about the suffix after the last parameter and may be + # `None` + return [f[1] for f in string.Formatter().parse(format_string) if f[1] is not None] + + +async def raw_api_ajax(runtime, target_url, key_translator=None, cache=None, cache_key_prefix=None, headers=None, **kwargs): + ''' + Make an AJAX call to an API, managing auth + auth. + + * runtime is a Runtime class containing request information. + * target_url is typically grabbed from ENDPOINTS + * key_translator is a dictionary to translate API keys to internal keys + * cache is an optional cache object + * cache_key_prefix is the prefix to use for cache keys + * ... and we pass the named parameters + ''' + request = runtime.get_request() + url = target_url.format(**kwargs) + user = await learning_observer.auth.get_active_user(request) + + if constants.AUTH_HEADERS not in request or user is None: + raise aiohttp.web.HTTPUnauthorized(text="Please log in") + + if headers is None: + headers = {} + headers.update(request.get(constants.AUTH_HEADERS, {})) + + cache_available = cache is not None and cache_key_prefix is not None + + if cache_available: + cache_key = f"{cache_key_prefix}/{learning_observer.auth.encode_id('session', user[constants.USER_ID])}/{learning_observer.util.url_pathname(url)}" + if settings.feature_flag('save_clean_ajax') is not None: + value = await cache[cache_key] + if value is not None: + response_data = json.loads(value) + if key_translator: + return learning_observer.util.translate_json_keys(response_data, key_translator) + return response_data + + async with aiohttp.ClientSession(loop=request.app.loop) as client: + async with client.get(url, headers=headers) as resp: + response = await resp.json() + learning_observer.log_event.log_ajax(target_url, response, request) + + if cache_available: + if settings.feature_flag('use_clean_ajax') is not None: + await cache.set(cache_key, json.dumps(response, indent=2)) + + if key_translator: + return learning_observer.util.translate_json_keys(response, key_translator) + return response + + +def raw_access_partial(remote_url, key_translator=None, cache=None, cache_key_prefix=None, name=None, headers=None): + ''' + This is a helper which allows us to create a function which calls specific + API endpoints. + ''' + async def caller(runtime, **kwargs): + ''' + Make an AJAX request to the API + ''' + return await raw_api_ajax( + runtime, + remote_url, + key_translator, + cache, + cache_key_prefix, + headers, + **kwargs + ) + + if name: + setattr(caller, "__qualname__", name) + + return caller + + +def register_endpoints(app, endpoints, api_name, key_translator=None, cache=None, cache_key_prefix=None, feature_flag_name=None): + ''' + Initialize API routes and handlers: + + - Creates debug routes to pass through AJAX requests to API + - Creates production APIs to have access to cleaned versions of data + - Creates local function calls to call from other pieces of code within process + + Parameters: + - app: The aiohttp application to which routes will be added + - endpoints: List of Endpoint objects describing API endpoints + - api_name: Name of the API (e.g., "google", "canvas") + - key_translator: Dictionary to translate API keys to internal keys + - cache: Cache object for API responses + - cache_key_prefix: Prefix for cache keys + - feature_flag_name: Feature flag to check before enabling routes + ''' + # Check feature flag if provided + if feature_flag_name and not settings.feature_flag(feature_flag_name): + return + + # Provide documentation on what we're doing + async def api_docs_handler(request): + '''Return a list of available endpoints.''' + response = f"{api_name.capitalize()} API Endpoints:\n" + for endpoint in endpoints: + response += f"{endpoint._local_url()}\n" + cleaners = endpoint._cleaners() + for c in cleaners: + response += f" {cleaners[c]['local_url']}\n" + return aiohttp.web.Response(text=response) + + app.add_routes([ + aiohttp.web.get(f"/{api_name}", api_docs_handler) + ]) + + def make_ajax_raw_handler(remote_url): + ''' + Creates a handler to forward API requests to the client. + ''' + async def ajax_passthrough(request): + '''The actual handler.''' + runtime = learning_observer.runtime.Runtime(request) + response = await raw_api_ajax( + runtime, + remote_url, + key_translator, + cache, + cache_key_prefix, + **request.match_info + ) + return aiohttp.web.json_response(response) + return ajax_passthrough + + def make_cleaner_handler(raw_function, cleaner_function, name=None): + async def cleaner_handler(request): + # TODO check if we need the runtime here + runtime = learning_observer.runtime.Runtime(request) + response = cleaner_function( + await raw_function(runtime, **request.match_info) + ) + if inspect.isawaitable(response): + response = await response + if isinstance(response, dict) or isinstance(response, list): + return aiohttp.web.json_response(response) + elif isinstance(response, str): + return aiohttp.web.Response(text=response) + else: + raise AttributeError(f"Invalid response type: {type(response)}") + + if name is not None: + setattr(cleaner_handler, "__qualname__", name + "_handler") + return cleaner_handler + + def make_cleaner_function(raw_function, cleaner_function, name=None): + async def cleaner_local(runtime, **kwargs): + api_response = await raw_function(runtime, **kwargs) + clean = cleaner_function(api_response) + if inspect.isawaitable(clean): + clean = await clean + return clean + if name is not None: + setattr(cleaner_local, "__qualname__", name) + return cleaner_local + + # Setup the global namespace using the provided namespace dict + result_functions = {} + + for e in endpoints: + function_name = f"raw_{e.name}" + raw_function = raw_access_partial( + remote_url=e.remote_url, + key_translator=key_translator, + cache=cache, + cache_key_prefix=cache_key_prefix, + name=e.name + ) + result_functions[function_name] = raw_function + cleaners = e._cleaners() + for c in cleaners: + app.add_routes([ + aiohttp.web.get( + cleaners[c]['local_url'], + make_cleaner_handler( + raw_function, + cleaners[c]['function'], + name=cleaners[c]['name'] + ) + ) + ]) + result_functions[cleaners[c]['name']] = make_cleaner_function( + raw_function, + cleaners[c]['function'], + name=cleaners[c]['name'] + ) + + app.add_routes([ + aiohttp.web.get( + e._local_url(), + make_ajax_raw_handler(e.remote_url) + ) + ]) + + return result_functions + + +def make_cleaner_registrar(endpoints): + ''' + Creates a register_cleaner function specific to a list of endpoints. + + Returns: + A function that can be used as a decorator to register cleaners. + ''' + def register_cleaner(data_source, cleaner_name): + ''' + Registers a cleaner function for export both as a web service + and as a local function call. + ''' + def decorator(f): + found = False + for endpoint in endpoints: + if endpoint.name == data_source: + found = True + endpoint._add_cleaner( + cleaner_name, + { + 'function': f, + 'local_url': f'{endpoint._local_url()}/{cleaner_name}', + 'name': cleaner_name + } + ) + + if not found: + raise AttributeError(f"Data source {data_source} invalid; not found in endpoints.") + return f + + return decorator + + return register_cleaner diff --git a/learning_observer/learning_observer/rosters.py b/learning_observer/learning_observer/rosters.py index 6e66baf3b..a0627657c 100644 --- a/learning_observer/learning_observer/rosters.py +++ b/learning_observer/learning_observer/rosters.py @@ -60,6 +60,7 @@ time. ''' +import inspect import json import os.path @@ -71,8 +72,9 @@ import learning_observer.auth as auth import learning_observer.cache +import learning_observer.communication_protocol.integration import learning_observer.constants as constants -import learning_observer.google +import learning_observer.integrations import learning_observer.kvs import learning_observer.log_event as log_event from learning_observer.log_event import debug_log @@ -80,13 +82,13 @@ import learning_observer.prestartup import learning_observer.runtime import learning_observer.settings as settings -import learning_observer.communication_protocol.integration +import learning_observer.util COURSE_URL = 'https://classroom.googleapis.com/v1/courses' ROSTER_URL = 'https://classroom.googleapis.com/v1/courses/{courseid}/students' -pmss.parser('roster_source', parent='string', choices=['google_api', 'all', 'test', 'filesystem'], transform=None) +pmss.parser('roster_source', parent='string', choices=['google', 'canvas', 'schoology', 'all', 'test', 'filesystem'], transform=None) pmss.register_field( name='source', type='roster_source', @@ -94,7 +96,7 @@ '`all`: aggregate all available students into a single class\n'\ '`test`: use sample course and student files\n'\ '`filesystem`: read rosters defined on filesystem\n'\ - '`google_api`: fetch from Google API', + '`google|schoology|canvas`: fetch from specific API', required=True ) @@ -349,19 +351,13 @@ def init(): ''' global ajax roster_source = settings.pmss_settings.source(types=['roster_data']) - if 'roster_data' not in settings.settings: - print(settings.settings) - raise learning_observer.prestartup.StartupCheck( - "Settings file needs a `roster_data` element with a `source` element. No `roster_data` element found." - ) - elif 'source' not in settings.settings['roster_data']: - raise learning_observer.prestartup.StartupCheck( - "Settings file needs a `roster_data` element with a `source` element. No `source` element found." - ) - elif roster_source in ['test', 'filesystem']: + if roster_source in ['test', 'filesystem']: ajax = synthetic_ajax + # Google, Canvas, and Schoology all use integrations instead of ajax when called elif roster_source in ["google_api"]: ajax = google_ajax + elif roster_source in ["canvas_api", 'schoology_api']: + pass elif roster_source in ["all"]: ajax = all_ajax else: @@ -369,7 +365,7 @@ def init(): "Settings file `roster_data` element should have `source` field\n" "set to either:\n" " test (retrieve from files courses.json and students.json)\n" - " google_api (retrieve roster data from Google)\n" + " google_api | canvas_api | schoology_api (retrieve roster data from an api)\n" " filesystem (retrieve roster data from file system hierarchy\n" " all (retrieve roster data as all students)" ) @@ -406,14 +402,55 @@ def init(): return ajax +async def run_additional_module_func(request, function_name, kwargs=None): + '''This function calls the `function_name` for one of our integrated LMSs. + This is used to call the LMSs `.courses` and `.roster` functions. + Returns result of the function, `None` otherwise. + ''' + if not kwargs: + kwargs = {} + + user = await auth.get_active_user(request) + + user_domain = learning_observer.util.get_domain_from_email(user.get('email')) + # TODO we ough to include provider in the attributes (need to test provider) + provider = user.get('lti_context', {}).get('provider') + roster_source = settings.pmss_settings.source(types=['roster_data'], attributes={'domain': user_domain}) + + # HACK/TODO since Canvas and Schoology are launched via an LTI, + # we need to pass a course to the courses - LTI applications are + # provided on a course-by-course basis, so fetching the courses + # just needs to provide the current course context. + if roster_source in ['canvas', 'schoology'] and function_name == 'corosterurses': + kwargs['courseId'] = user.get('lti_context', {}).get('api_id') + + if roster_source not in learning_observer.integrations.INTEGRATIONS: + debug_log(f'Provider `{roster_source}` not found in INTEGRATIONS. Available integrations: {learning_observer.integrations.INTEGRATIONS.keys()}') + return None + + runtime = learning_observer.runtime.Runtime(request) + func = learning_observer.integrations.INTEGRATIONS[roster_source].get(function_name, None) + if not func: + debug_log(f'Provider `{roster_source}` does not have function `{function_name}`.') + return None + + if callable(func): + result = func(runtime, **kwargs) + if inspect.isawaitable(result): + result = await result + return result + debug_log(f'No result from `{roster_source}.{function_name}`') + return None + + async def courselist(request): ''' List all of the courses a teacher manages: Helper ''' - # New code - if settings.pmss_settings.source(types=['roster_data']) in ["google_api"]: - runtime = learning_observer.runtime.Runtime(request) - return await learning_observer.google.courses(runtime) + course_list = await run_additional_module_func(request, 'courses') + if course_list: + return course_list + # TODO if course_list is falsey, the following code may fail if there if ajax is not defined. # Legacy code course_list = await ajax( @@ -445,7 +482,9 @@ async def memoize_courseroster_runtime(runtime, course_id): In the future, we ought to be able to specify how the values from individual nodes are handled: static, dynamic (current), or memoized. ''' - @learning_observer.cache.async_memoization() + # TODO the async memoization cache here is causing a ton of slowdown + # when trying to connect to a remote Redis instance. + # @learning_observer.cache.async_memoization() async def course_roster_memoization_layer(c): return await courseroster_runtime(runtime, c) return await course_roster_memoization_layer(course_id) @@ -455,10 +494,12 @@ async def courseroster(request, course_id): ''' List all of the students in a course: Helper ''' - if settings.pmss_settings.source(types=['roster_data']) in ["google_api"]: - runtime = learning_observer.runtime.Runtime(request) - return await learning_observer.google.roster(runtime, courseId=course_id) + roster = await run_additional_module_func(request, 'roster', kwargs={'courseId': course_id}) + if roster: + return roster + if not ajax: + return [] roster = await ajax( request, url=ROSTER_URL, diff --git a/learning_observer/learning_observer/routes.py b/learning_observer/learning_observer/routes.py index b1fa0adc4..8811f668c 100644 --- a/learning_observer/learning_observer/routes.py +++ b/learning_observer/learning_observer/routes.py @@ -22,7 +22,7 @@ import learning_observer.impersonate import learning_observer.incoming_student_event as incoming_student_event import learning_observer.dashboard -import learning_observer.google +import learning_observer.integrations import learning_observer.rosters as rosters import learning_observer.module_loader @@ -66,7 +66,7 @@ def tracemalloc_handler(request): register_static_routes(app) register_incoming_event_views(app) register_debug_routes(app) - learning_observer.google.initialize_and_register_routes(app) + learning_observer.integrations.register_integrations(app) app.add_routes([ aiohttp.web.get( @@ -240,12 +240,30 @@ def register_auth_webapp_views(app): debug_log("Running with Google authentication") app.add_routes([ aiohttp.web.get( - # TODO only allow the available sign-in options found in pmss - # '/auth/login/{provider:google|canvas|schoology}', '/auth/login/{provider:google}', handler=learning_observer.auth.social_handler), ]) + # TODO We ought to use pmss here, though at this time it is easier + # to check if a key exists this way + if 'lti' in settings.settings['auth']: + debug_log("Running with LTI authentication") + # TODO build provider syntax based on available providers + app.add_routes([ + aiohttp.web.post( + '/lti/{provider}/login', + handler=learning_observer.auth.handle_oidc_authorize), + aiohttp.web.get( + '/lti/{provider}/login', + handler=learning_observer.auth.handle_oidc_authorize), + aiohttp.web.post( + '/lti/{provider}/launch', + handler=learning_observer.auth.handle_oidc_launch), + aiohttp.web.get( + '/auth/login/lti', + learning_observer.auth.check_oidc_login) + ]) + if 'password_file' in settings.settings['auth']: debug_log("Running with password authentication") if not os.path.exists(settings.settings['auth']['password_file']): diff --git a/learning_observer/learning_observer/settings.py b/learning_observer/learning_observer/settings.py index 385d8a96a..017da21c5 100644 --- a/learning_observer/learning_observer/settings.py +++ b/learning_observer/learning_observer/settings.py @@ -225,7 +225,8 @@ def initialized(): # Not all of these are guaranteed to work on every branch of the codebase. AVAILABLE_FEATURE_FLAGS = [ 'uvloop', 'watchdog', 'auth_headers_page', 'merkle', 'save_google_ajax', 'use_google_ajax', - 'google_routes', 'save_clean_ajax', 'use_clean_ajax' + 'google_routes', 'save_clean_ajax', 'use_clean_ajax', + 'canvas_routes', 'schoology_routes' ] diff --git a/learning_observer/learning_observer/static/modules/login.html b/learning_observer/learning_observer/static/modules/login.html index fe4ab8e03..271e391de 100644 --- a/learning_observer/learning_observer/static/modules/login.html +++ b/learning_observer/learning_observer/static/modules/login.html @@ -50,6 +50,8 @@

{{ server_name }}

+
+

diff --git a/learning_observer/learning_observer/util.py b/learning_observer/learning_observer/util.py index 96140943d..4b0db2c8e 100644 --- a/learning_observer/learning_observer/util.py +++ b/learning_observer/learning_observer/util.py @@ -299,6 +299,14 @@ async def async_generator_to_list(gen): return result +def get_domain_from_email(email): + '''Helper function to extract the domain from an email address + ''' + if '@' in email: + return email.split('@')[1] + return None + + # And a test case if __name__ == '__main__': assert to_safe_filename('{') == '-123-' diff --git a/learning_observer/learning_observer/webapp_helpers.py b/learning_observer/learning_observer/webapp_helpers.py index a201c399d..4210a0548 100644 --- a/learning_observer/learning_observer/webapp_helpers.py +++ b/learning_observer/learning_observer/webapp_helpers.py @@ -66,9 +66,19 @@ def setup_session_storage(app): ''' This is a helper function to setup session storage. ''' + protocol = settings.pmss_settings.protocol() + cookie_params = {} + if protocol == 'https': + debug_log('Setting cookie parameters for HTTPS') + cookie_params = { + 'domain': settings.pmss_settings.hostname(), + 'secure': True, + 'samesite': 'None' + } aiohttp_session.setup(app, aiohttp_session.cookie_storage.EncryptedCookieStorage( learning_observer.auth.fernet_key(settings.pmss_settings.session_secret(types=['aio'])), - max_age=settings.pmss_settings.session_max_age(types=['aio']))) + max_age=settings.pmss_settings.session_max_age(types=['aio']), + **cookie_params)) def find_open_port(): diff --git a/modules/writing_observer/VERSION b/modules/writing_observer/VERSION index c309e668b..3fb21c1f9 100644 --- a/modules/writing_observer/VERSION +++ b/modules/writing_observer/VERSION @@ -1 +1 @@ -0.1.0+2025.04.07T20.48.55.419Z.3bdcc7c9.berickson.202504.process.metrics +0.1.0+2025.05.16T17.31.36.630Z.e1505886.202505.berickson.lti.integration diff --git a/modules/writing_observer/writing_observer/awe_nlp.py b/modules/writing_observer/writing_observer/awe_nlp.py index b6d286fd9..4028d1265 100644 --- a/modules/writing_observer/writing_observer/awe_nlp.py +++ b/modules/writing_observer/writing_observer/awe_nlp.py @@ -372,6 +372,7 @@ async def process_writings_with_caching(writing_data, options=None, mode=RUN_MOD async for writing in writing_data: text = writing.get('text', '') if len(text) == 0: + yield writing continue # Creating text hash and setting defaults diff --git a/requirements.txt b/requirements.txt index c9333e839..d052bba15 100644 --- a/requirements.txt +++ b/requirements.txt @@ -16,6 +16,7 @@ loremipsum @ git+https://github.com/testlabauto/loremipsum.git@b7bd71a6651207ef8 ipython ipykernel jsonschema +pyjwt lxml # pubsub names notebook From 28cb3ec99d7f7b545fe0a3edb7ba9c408b08bf35 Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Thu, 10 Jul 2025 08:52:19 -0400 Subject: [PATCH 41/88] Update rosters.py --- learning_observer/learning_observer/rosters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/learning_observer/learning_observer/rosters.py b/learning_observer/learning_observer/rosters.py index a0627657c..a1c09ab07 100644 --- a/learning_observer/learning_observer/rosters.py +++ b/learning_observer/learning_observer/rosters.py @@ -421,7 +421,7 @@ async def run_additional_module_func(request, function_name, kwargs=None): # we need to pass a course to the courses - LTI applications are # provided on a course-by-course basis, so fetching the courses # just needs to provide the current course context. - if roster_source in ['canvas', 'schoology'] and function_name == 'corosterurses': + if roster_source in ['canvas', 'schoology'] and function_name == 'courses': kwargs['courseId'] = user.get('lti_context', {}).get('api_id') if roster_source not in learning_observer.integrations.INTEGRATIONS: From a6724ed606e2253cfc5c4fe288e672ca0819dbd0 Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Wed, 16 Jul 2025 13:40:26 -0400 Subject: [PATCH 42/88] Workshop updates (#230) * Update the workshop files/instructions * updated template module --------- Co-authored-by: JohnDamilola --- VERSION | 2 +- docs/workshop.md | 9 ++++ .../learning_observer/creds.yaml.workshop | 1 + .../{{ cookiecutter.project_slug }}/VERSION | 2 +- .../assets/scripts.js | 45 +++++-------------- .../dash_dashboard.py | 21 +++------ .../my_layout.py | 17 +++---- 7 files changed, 34 insertions(+), 63 deletions(-) diff --git a/VERSION b/VERSION index f9873b800..cbbd3281b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0+2025.06.11T12.17.15.667Z.233ef706.202505.berickson.lti.integration +0.1.0+2025.07.16T17.39.45.896Z.a3b5acf4.workshop.updates diff --git a/docs/workshop.md b/docs/workshop.md index a79b6c664..b10d595f7 100644 --- a/docs/workshop.md +++ b/docs/workshop.md @@ -67,11 +67,20 @@ NOTE: All future commands should be ran starting from the repository's root dire Make sure you are on a fresh virtual environment. In `virtualenvwrapper`: +You can either run this + ```bash mkvirtualenv lo_workshop workon lo_workshop ``` +or run this to set up the virtual environment + +```bash +python -m venv lo_workshop +source lo_workshop/bin/activate +``` + Then run the install command: ```bash diff --git a/learning_observer/learning_observer/creds.yaml.workshop b/learning_observer/learning_observer/creds.yaml.workshop index c580cf039..afd7c0c10 100644 --- a/learning_observer/learning_observer/creds.yaml.workshop +++ b/learning_observer/learning_observer/creds.yaml.workshop @@ -1,3 +1,4 @@ +protocol: http config: run_mode: dev auth: diff --git a/modules/lo_template_module/{{ cookiecutter.project_slug }}/VERSION b/modules/lo_template_module/{{ cookiecutter.project_slug }}/VERSION index 367ae11d9..cbbd3281b 100644 --- a/modules/lo_template_module/{{ cookiecutter.project_slug }}/VERSION +++ b/modules/lo_template_module/{{ cookiecutter.project_slug }}/VERSION @@ -1 +1 @@ -0.1.0+2024.12.16T16.42.38.637Z.fa6150e4.berickson.versioning.workflow +0.1.0+2025.07.16T17.39.45.896Z.a3b5acf4.workshop.updates diff --git a/modules/lo_template_module/{{ cookiecutter.project_slug }}/{{ cookiecutter.project_slug }}/assets/scripts.js b/modules/lo_template_module/{{ cookiecutter.project_slug }}/{{ cookiecutter.project_slug }}/assets/scripts.js index f0f919126..5733e08c1 100644 --- a/modules/lo_template_module/{{ cookiecutter.project_slug }}/{{ cookiecutter.project_slug }}/assets/scripts.js +++ b/modules/lo_template_module/{{ cookiecutter.project_slug }}/{{ cookiecutter.project_slug }}/assets/scripts.js @@ -34,37 +34,18 @@ window.dash_clientside.{{ cookiecutter.project_slug }} = { return window.dash_clientside.no_update; }, - /** - * Process a message from LOConnection - * @param {object} incomingMessage object received from LOConnection - * @returns parsed data to local storage - */ - receiveWSMessage: async function (incomingMessage) { - // TODO the naming here is broken serverside. Notice above we - // called the target export `{{ cookiecutter.reducer }}_export`, i.e. the named - // export. Below, we need to call `{{ cookiecutter.project_slug }}_join_roster`, i.e. the name - // of the node. This ought to be cleaned up in the communication protocl. - const messageData = JSON.parse(incomingMessage.data).{{ cookiecutter.project_slug }}_query.{{ cookiecutter.reducer }}_join_roster || []; - if (messageData.error !== undefined) { - console.error('Error received from server', messageData.error); - return []; - } - return messageData; - }, - /** * Build the student UI components based on the stored websocket data * @param {*} wsStorageData information stored in the websocket store * @returns Dash object to be displayed on page */ - populateOutput: function(wsStorageData) { - if (!wsStorageData) { + populateOutput: function (wsStorageData) { + if (!wsStorageData?.students) { return 'No students'; } let output = [] // Iterate over students and create UI items for each - for (const student of wsStorageData) { - + for (const [student, value] of Object.entries(wsStorageData.students)) { // We define Dash components in JS via a dictionary // of where the component lives, what it is, and any // parameters we want to pass along to it. @@ -72,31 +53,27 @@ window.dash_clientside.{{ cookiecutter.project_slug }} = { // - `type`: the component to use // - `props`: any parameters the component expects // The following produces a LONameTag and Span wrapped in a Div - studentBadge = { + const studentBadge = { namespace: 'dash_html_components', type: 'Div', props: { children: [{ - namespace: 'lo_dash_react_components', + namespace: 'dash_html_components', props: { - profile: student.profile, - className: 'student-name-tag d-inline-block', - includeName: true, - id: `${student.user_id}-activity-img` + children: student }, - type: 'LONameTag' - },{ + type: 'Span' + }, { namespace: 'dash_html_components', props: { - children: ` - ${student.count} events`, + children: ` - ${value.count} events` }, type: 'Span' - }] } } - output = output.concat(studentBadge) + output = output.concat(studentBadge); } return output; } -} +}; diff --git a/modules/lo_template_module/{{ cookiecutter.project_slug }}/{{ cookiecutter.project_slug }}/dash_dashboard.py b/modules/lo_template_module/{{ cookiecutter.project_slug }}/{{ cookiecutter.project_slug }}/dash_dashboard.py index 05dbe2831..e82bf3cf2 100644 --- a/modules/lo_template_module/{{ cookiecutter.project_slug }}/{{ cookiecutter.project_slug }}/dash_dashboard.py +++ b/modules/lo_template_module/{{ cookiecutter.project_slug }}/{{ cookiecutter.project_slug }}/dash_dashboard.py @@ -23,35 +23,24 @@ _prefix = '{{ cookiecutter.project_hyphenated }}' _namespace = '{{ cookiecutter.project_slug }}' _websocket = f'{_prefix}-websocket' -_websocket_storage = f'{_prefix}-websocket-store' _output = f'{_prefix}-output' def layout(): ''' Function to define the page's layout. ''' - return my_layout(_websocket, _websocket_storage, _output) + return my_layout(_websocket, _output) # Send the initial state based on the url hash to LO. # If this is not included, nothing will be returned from # the communication protocol. clientside_callback( ClientsideFunction(namespace=_namespace, function_name='sendToLOConnection'), - Output(lodrc.LOConnectionStatusAIO.ids.websocket(_websocket), 'send'), - Input(lodrc.LOConnectionStatusAIO.ids.websocket(_websocket), 'state'), # used for initial setup + Output(lodrc.LOConnectionAIO.ids.websocket(_websocket), 'send'), + Input(lodrc.LOConnectionAIO.ids.websocket(_websocket), 'state'), # used for initial setup Input('_pages_location', 'hash') ) -# Handle receiving a message from the websocket. -# This step will parse the message and update the -# local storage accordingly. -clientside_callback( - ClientsideFunction(namespace=_namespace, function_name='receiveWSMessage'), - Output(_websocket_storage, 'data'), - Input(lodrc.LOConnectionStatusAIO.ids.websocket(_websocket), 'message'), - prevent_initial_call=True -) - # Build the UI based on what we've received from the # communicaton protocol # This clientside callback and the serverside callback below are @@ -59,13 +48,13 @@ def layout(): # clientside_callback( # ClientsideFunction(namespace=_namespace, function_name='populateOutput'), # Output(_output, 'children'), -# Input(_websocket_storage, 'data'), +# Input(lodrc.LOConnectionAIO.ids.ws_store(_websocket), 'data'), # ) @callback( Output(_output, 'children'), - Input(_websocket_storage, 'data'), + Input(lodrc.LOConnectionAIO.ids.ws_store(_websocket), 'data'), ) def populate_output(data): '''This method creates UI components for each student found diff --git a/modules/lo_template_module/{{ cookiecutter.project_slug }}/{{ cookiecutter.project_slug }}/my_layout.py b/modules/lo_template_module/{{ cookiecutter.project_slug }}/{{ cookiecutter.project_slug }}/my_layout.py index 70eea5739..f33b39111 100644 --- a/modules/lo_template_module/{{ cookiecutter.project_slug }}/{{ cookiecutter.project_slug }}/my_layout.py +++ b/modules/lo_template_module/{{ cookiecutter.project_slug }}/{{ cookiecutter.project_slug }}/my_layout.py @@ -2,22 +2,20 @@ import dash_bootstrap_components as dbc import lo_dash_react_components as lodrc -def my_layout(_websocket, _websocket_storage, _output): +def my_layout(_websocket, _output): ''' This is the layout for the static part of your dashboard which is loaded when the page first loads. * The data would be populated in a div with id _output. * We pass the _websocket so we can render a component letting us know when things updated - * We pass the _websocket_storage, although we really should bubble that up. ''' page_layout = html.Div(children=[ html.H1(children='{{ cookiecutter.project_name }}'), dbc.InputGroup([ - dbc.InputGroupText(lodrc.LOConnectionStatusAIO(aio_id=_websocket)), + dbc.InputGroupText(lodrc.LOConnectionAIO(aio_id=_websocket)), lodrc.ProfileSidebarAIO(class_name='rounded-0 rounded-end', color='secondary'), ]), - dcc.Store(id=_websocket_storage), html.H2('Output from reducers'), html.Div(id=_output) ]) @@ -29,13 +27,10 @@ def my_data_layout(data): This is the layout for the changing part of your dashboard populated from the data. ''' - if not data: + if not data or len(data.get('students', {})) == 0: return 'No students' output = [html.Div([ - lodrc.LONameTag( - profile=s['profile'], className='d-inline-block student-name-tag', - includeName=True, id=f'{s["user_id"]}-name-tag' - ), - html.Span(f' - {s["count"]} events') - ]) for s in data] + k, + html.Span(f' - {v["count"]} events') + ]) for k, v in data.get('students', {}).items()] return output From f61cb8b128e383003792d6959ac90ca9481d0901 Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Sun, 10 Aug 2025 16:58:27 -0400 Subject: [PATCH 43/88] Various improvements to serving as an LTI Supports Canvas and Schoology with some hacked solutions. Additionally added scripts for starting and stopping multiple instances. --- VERSION | 2 +- devops/single_server_instances/README.md | 36 ++++++++++++++++ .../start_lo_instances.sh | 39 ++++++++++++++++++ .../stop_lo_instances.sh | 29 +++++++++++++ learning_observer/VERSION | 2 +- .../learning_observer/downloads.py | 4 +- .../integrations/__init__.py | 5 ++- .../learning_observer/integrations/canvas.py | 19 +++------ .../integrations/schoology.py | 41 +++++++++---------- .../learning_observer/integrations/util.py | 13 +++++- .../learning_observer/log_event.py | 2 +- learning_observer/learning_observer/main.py | 2 +- .../learning_observer/rosters.py | 19 ++++++--- .../learning_observer/settings.py | 5 +++ modules/writing_observer/VERSION | 2 +- .../writing_observer/module.py | 5 +++ .../writing_observer/writing_analysis.py | 12 ++++++ scripts/map_emails_to_ids_in_kvs.py | 28 +++++++++++++ 18 files changed, 216 insertions(+), 49 deletions(-) create mode 100644 devops/single_server_instances/README.md create mode 100755 devops/single_server_instances/start_lo_instances.sh create mode 100755 devops/single_server_instances/stop_lo_instances.sh create mode 100644 scripts/map_emails_to_ids_in_kvs.py diff --git a/VERSION b/VERSION index cbbd3281b..23e7294b1 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0+2025.07.16T17.39.45.896Z.a3b5acf4.workshop.updates +0.1.0+2025.08.10T20.53.47.175Z.cc7dc75e.berickson.202507.new.lti.updates diff --git a/devops/single_server_instances/README.md b/devops/single_server_instances/README.md new file mode 100644 index 000000000..48ae6103d --- /dev/null +++ b/devops/single_server_instances/README.md @@ -0,0 +1,36 @@ +# Learning Observer β€” Instance Control Scripts + +This directory contains two bash scripts to start and stop multiple instances of the `learning_observer` application. + +## Files + +* **`start_lo_instances.sh`** β€” Launches one or more instances of the app on sequential ports, creates log files, and stores process IDs in a PID directory. +* **`stop_lo_instances.sh`** β€” Stops all running instances recorded in the PID directory. + +## Configuration + +Before use, edit the scripts to match your system: + +* `LEARNING_OBSERVER_LOC` β€” Path to your project code. +* `VIRTUALENV_PATH` β€” Path to your Python virtual environment. +* `LOGFILE_DEST` β€” Directory for logs (default `/var/log/learning_observer`). +* `START_PORT` β€” First port to use. +* `SCRIPT_NAME` β€” Command or Python file to run. + +## Usage + +Start instances (default: 1): + +```bash +./start_lo_instances.sh +./start_lo_instances.sh 3 # start 3 instances +``` + +Stop all instances: + +```bash +./stop_lo_instances.sh +``` + +Logs are saved in `LOGFILE_DEST`, and PIDs are stored in `LOGFILE_DEST/pids`. +You may need to change paths or permissions depending on your environment. diff --git a/devops/single_server_instances/start_lo_instances.sh b/devops/single_server_instances/start_lo_instances.sh new file mode 100755 index 000000000..c872430dc --- /dev/null +++ b/devops/single_server_instances/start_lo_instances.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +# === Config === +NUM_SERVERS=${1:-1} # default 1 server instance +START_PORT=9001 +LOGFILE_DEST="/var/log/learning_observer" +PIDFILE_DIR="$LOGFILE_DEST/pids" +LEARNING_OBSERVER_LOC="/path/to/your/code" +VIRTUALENV_PATH="/path/to/your/venv" +SCRIPT_NAME="learning_observer" + +# Create log + pid dirs if they don't exist +mkdir -p "$LOGFILE_DEST" +mkdir -p "$PIDFILE_DIR" + +# Timestamp for log grouping +LOG_DATE=$(date "+%m-%d-%Y--%H-%M-%S") + +# === Start Servers === +echo "Starting $NUM_SERVERS instances of $SCRIPT_NAME..." + +cd "$LEARNING_OBSERVER_LOC" +source "$VIRTUALENV_PATH/bin/activate" + +for ((i=0; i Log: $LOGFILE_NAME" + nohup python $SCRIPT_NAME --port $PORT > "$LOGFILE_NAME" 2>&1 & + PROCESS_ID=$! + echo $PROCESS_ID > "$PIDFILE_NAME" + echo " -> PID $PROCESS_ID logged to $PIDFILE_NAME" +done + +echo "βœ… All servers started." +echo "Run ./scripts/stop_lo_instances.sh to stop server processes." diff --git a/devops/single_server_instances/stop_lo_instances.sh b/devops/single_server_instances/stop_lo_instances.sh new file mode 100755 index 000000000..2a3243445 --- /dev/null +++ b/devops/single_server_instances/stop_lo_instances.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +# === Config === +LOGFILE_DEST="/var/log/learning_observer" +PIDFILE_DIR="$LOGFILE_DEST/pids" +SCRIPT_NAME="learning_observer" + +# === Stop All Servers === +echo "Stopping all $SCRIPT_NAME servers..." + +if [ ! -d "$PIDFILE_DIR" ]; then + echo "PID directory not found. Nothing to stop." + exit 1 +fi + +for PIDFILE in "$PIDFILE_DIR"/*.pid; do + if [ -f "$PIDFILE" ]; then + PID=$(cat "$PIDFILE") + if kill -0 "$PID" 2>/dev/null; then + echo "Stopping PID $PID from $PIDFILE" + kill "$PID" + else + echo "PID $PID not running, skipping." + fi + rm -f "$PIDFILE" + fi +done + +echo "βœ… All servers stopped." diff --git a/learning_observer/VERSION b/learning_observer/VERSION index f9873b800..5f2ef51aa 100644 --- a/learning_observer/VERSION +++ b/learning_observer/VERSION @@ -1 +1 @@ -0.1.0+2025.06.11T12.17.15.667Z.233ef706.202505.berickson.lti.integration +0.1.0+2025.08.07T20.03.33.270Z.c17a54d7.berickson.202507.new.lti.updates diff --git a/learning_observer/learning_observer/downloads.py b/learning_observer/learning_observer/downloads.py index 9e76b1ef9..abea04761 100644 --- a/learning_observer/learning_observer/downloads.py +++ b/learning_observer/learning_observer/downloads.py @@ -90,7 +90,9 @@ "5.3.3": "54b69b378be9029cb841bce9f33e111148231ce38ae389601c10ee1fec93b" "bfb84839e84911e9e32e9e026a182e7225fd8531dc8344ba94ef4b467852e7162d5", "5.3.5": "3c9aa2c118ba4cbb2f54b2ca87528c453503ab5545983b6f5d10c6f04abd9" - "d4feb003c80c59a092857808e439642ab45e38369980288e7afb5a11e0e7946270e" + "d4feb003c80c59a092857808e439642ab45e38369980288e7afb5a11e0e7946270e", + "5.3.6": "0d33ccd34e5244052264f2e30088c92bfd28efc2db229926932be93ba3cc4" + "8b5f8378e12cf030c50965d1aa62a4a1a8f74493293b320aa9ef0a6b98962ab83b8" }, "tested_versions": [ 'https://cdn.jsdelivr.net/npm/bootswatch@5.3.5/dist/minty/bootstrap.min.css', diff --git a/learning_observer/learning_observer/integrations/__init__.py b/learning_observer/learning_observer/integrations/__init__.py index 213faa972..d3f2086c8 100644 --- a/learning_observer/learning_observer/integrations/__init__.py +++ b/learning_observer/learning_observer/integrations/__init__.py @@ -20,9 +20,12 @@ def register_integrations(app): if 'lti' not in learning_observer.settings.settings['auth']: return - if 'schoology' in learning_observer.settings.settings['auth']['lti']: + # TODO we ought to check for what type of provider each lti setting needs + # then only register the needed set of providers + if any('schoology' in k for k in learning_observer.settings.settings['auth']['lti']): INTEGRATIONS['schoology'] = learning_observer.integrations.schoology.register_endpoints(app) + # TODO we ought to fetch the following information with PMSS canvas_providers = [k for k in learning_observer.settings.settings['auth']['lti'].keys() if 'canvas' in k] for provider in canvas_providers: provider_endpoint_registrar = learning_observer.integrations.canvas.setup_canvas_provider(provider) diff --git a/learning_observer/learning_observer/integrations/canvas.py b/learning_observer/learning_observer/integrations/canvas.py index cb6c9164a..fe6dbaa0f 100644 --- a/learning_observer/learning_observer/integrations/canvas.py +++ b/learning_observer/learning_observer/integrations/canvas.py @@ -16,7 +16,6 @@ def setup_canvas_provider(provider): - # TODO pull the base url from settings based on provider base_url = settings.pmss_settings.api_domain(types=['auth', 'lti', provider]) ENDPOINTS = list(map(lambda x: util.Endpoint(*x, api_name=provider), [ @@ -63,15 +62,7 @@ def clean_course_list(canvas_json): cleaned = [course] return cleaned - async def _lookup_gid_by_email(email): - kvs = learning_observer.kvs.KVS() - key = f'email-studentID-mapping:{email}' - id = await kvs[key] - if id: - return f'gid-{id}' - return None - - async def _process_canvas_user_for_system(member): + def _process_canvas_user_for_system(member, google_id): # Skip if no canvas id canvas_id = member.get('user_id') if not canvas_id: return None @@ -82,7 +73,7 @@ async def _process_canvas_user_for_system(member): # Create user for our system email = member.get('email') - local_id = await _lookup_gid_by_email(email) + local_id = google_id if not local_id: local_id = f'canvas-{canvas_id}' @@ -116,9 +107,11 @@ async def clean_course_roster(canvas_json): key=lambda x: x.get('name', 'ZZ'), ) # Process each student record + emails = [m.get('email') for m in members] + google_ids = await util.lookup_gids_by_emails(emails) users = [] - for m in members: - user = await _process_canvas_user_for_system(m) + for member, google_id in zip(members, google_ids): + user = _process_canvas_user_for_system(member, google_id) if user is not None: users.append(user) diff --git a/learning_observer/learning_observer/integrations/schoology.py b/learning_observer/learning_observer/integrations/schoology.py index 990b72360..5ab47943e 100644 --- a/learning_observer/learning_observer/integrations/schoology.py +++ b/learning_observer/learning_observer/integrations/schoology.py @@ -7,8 +7,8 @@ API = 'schoology' ENDPOINTS = list(map(lambda x: util.Endpoint(**x, api_name=API), [ - {'name': 'course_list', 'remote_url': 'https://api.schoology.com/v1/sections/{courseId}/enrollments', 'headers': {'Accept': 'application/vnd.ims.lti-nrps.v2.membershipcontainer+json'}}, - {'name': 'course_roster', 'remote_url': 'https://api.schoology.com/v1/sections/{courseId}/enrollments', 'headers': {'Accept': 'application/vnd.ims.lti-nrps.v2.membershipcontainer+json'}}, + {'name': 'course_list', 'remote_url': 'https://lti-service.svc.schoology.com/lti-service/tool/{clientId}/services/names-roles/v2p0/membership/{courseId}', 'headers': {'Accept': 'application/vnd.ims.lti-nrps.v2.membershipcontainer+json'}}, + {'name': 'course_roster', 'remote_url': 'https://lti-service.svc.schoology.com/lti-service/tool/{clientId}/services/names-roles/v2p0/membership/{courseId}', 'headers': {'Accept': 'application/vnd.ims.lti-nrps.v2.membershipcontainer+json'}}, {'name': 'course_assignments', 'remote_url': 'https://api.schoology.com/v1/sections/{courseId}/assignments', 'headers': {'Accept': 'application/vnd.ims.lis.v2.lineitemcontainer+json'}}, ])) @@ -45,28 +45,19 @@ def clean_course_list(schoology_json): return [course] -# TODO this already exists in a different place - it should live in only one place -async def _lookup_gid_by_email(email): - kvs = learning_observer.kvs.KVS() - key = f'email-studentID-mapping:{email}' - id = await kvs[key] - if id: - return f'gid-{id}' - return None - - -async def _process_schoology_user_for_system(member): +def _process_schoology_user_for_system(member, google_id): # Skip if no canvas id canvas_id = member.get('user_id') if not canvas_id: return None # Skip non students is_student = 'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner' in member.get('roles', []) - if not is_student: return None + if not is_student: + return None # Create user for our system email = member.get('email') - local_id = await _lookup_gid_by_email(email) + local_id = google_id if not local_id: local_id = f'canvas-{canvas_id}' @@ -96,11 +87,17 @@ async def clean_course_roster(schoology_json): Conforms to LTI NRPS v2 response format https://www.imsglobal.org/spec/lti-nrps/v2p0 ''' - print('clean_course_roster', schoology_json) - return - # Process each student record - # for m in members: - # user = await _process_schoology_user_for_system(m) + members = schoology_json.get('members', []) + users = [] + + emails = [m.get('email') for m in members] + google_ids = await util.lookup_gids_by_emails(emails) + + for member, google_id in zip(members, google_ids): + user = _process_schoology_user_for_system(member, google_id) + if user is not None: + users.append(user) + return users @register_cleaner('course_assignments', 'assignments') @@ -111,5 +108,5 @@ def clean_course_assignments(schoology_json): Conforms to LTI AGS response format https://www.imsglobal.org/spec/lti-ags/v2p0 ''' - print('clean_course_assignments', schoology_json) - return + # print('Schoology assignments TODO', schoology_json) + return [] diff --git a/learning_observer/learning_observer/integrations/util.py b/learning_observer/learning_observer/integrations/util.py index b56595cce..4058b228c 100644 --- a/learning_observer/learning_observer/integrations/util.py +++ b/learning_observer/learning_observer/integrations/util.py @@ -22,6 +22,7 @@ import learning_observer.constants as constants import learning_observer.settings as settings +import learning_observer.kvs import learning_observer.log_event import learning_observer.auth import learning_observer.runtime @@ -235,7 +236,8 @@ async def cleaner_local(runtime, **kwargs): key_translator=key_translator, cache=cache, cache_key_prefix=cache_key_prefix, - name=e.name + name=e.name, + headers=e.headers ) result_functions[function_name] = raw_function cleaners = e._cleaners() @@ -299,3 +301,12 @@ def decorator(f): return decorator return register_cleaner + + +async def lookup_gids_by_emails(emails): + '''Fetch a set of google ids based on a list of emails + ''' + kvs = learning_observer.kvs.KVS() + keys = [f'email-studentID-mapping:{email}' for email in emails] + ids = await kvs.multiget(keys) + return ids diff --git a/learning_observer/learning_observer/log_event.py b/learning_observer/learning_observer/log_event.py index 4f16b686c..fb1b76a22 100644 --- a/learning_observer/learning_observer/log_event.py +++ b/learning_observer/learning_observer/log_event.py @@ -280,7 +280,7 @@ def debug_log(*args): # Print to file. Only helpful for development. if LogDestination.FILE in DEBUG_LOG_DESTINATIONS: - with open(paths.logs("debug.log"), "a") as fp: + with open(paths.logs("debug.log"), "a", encoding='utf-8') as fp: fp.write(message.strip() + "\n") # Ideally, we'd like to be able to log these somewhere which won't cause cascading failures. diff --git a/learning_observer/learning_observer/main.py b/learning_observer/learning_observer/main.py index 9d27fde3c..17486aa21 100644 --- a/learning_observer/learning_observer/main.py +++ b/learning_observer/learning_observer/main.py @@ -63,7 +63,7 @@ def configure_event_loop(): debug_log("Running without uvloop") -port = None +port = getattr(args, 'port', None) runmode = None diff --git a/learning_observer/learning_observer/rosters.py b/learning_observer/learning_observer/rosters.py index a1c09ab07..959693c87 100644 --- a/learning_observer/learning_observer/rosters.py +++ b/learning_observer/learning_observer/rosters.py @@ -88,7 +88,11 @@ COURSE_URL = 'https://classroom.googleapis.com/v1/courses' ROSTER_URL = 'https://classroom.googleapis.com/v1/courses/{courseid}/students' -pmss.parser('roster_source', parent='string', choices=['google', 'canvas', 'schoology', 'all', 'test', 'filesystem'], transform=None) +# TODO we need to treat canvas sources as individuals since they could +# come from different servers. Whereas schoology always calls into the +# same api endpoints. +# i.e. the roster_source name for canvas is config dependent +pmss.parser('roster_source', parent='string', choices=['google', 'x-canvas', 'schoology', 'all', 'test', 'filesystem'], transform=None) pmss.register_field( name='source', type='roster_source', @@ -412,17 +416,20 @@ async def run_additional_module_func(request, function_name, kwargs=None): user = await auth.get_active_user(request) + # Grab roster source based on user user_domain = learning_observer.util.get_domain_from_email(user.get('email')) - # TODO we ough to include provider in the attributes (need to test provider) provider = user.get('lti_context', {}).get('provider') - roster_source = settings.pmss_settings.source(types=['roster_data'], attributes={'domain': user_domain}) + roster_source = settings.pmss_settings.source(types=['roster_data'], attributes={'domain': user_domain, 'provider': provider}) # HACK/TODO since Canvas and Schoology are launched via an LTI, # we need to pass a course to the courses - LTI applications are # provided on a course-by-course basis, so fetching the courses # just needs to provide the current course context. - if roster_source in ['canvas', 'schoology'] and function_name == 'courses': + # HACK/TODO there ought to be a better way to determine if schoology or canvas is present + if ('canvas' in roster_source or 'schoology' in roster_source) and function_name == 'courses': kwargs['courseId'] = user.get('lti_context', {}).get('api_id') + if roster_source == 'schoology': + kwargs['clientId'] = settings.pmss_settings.client_id(types=['auth', 'lti', provider]) if roster_source not in learning_observer.integrations.INTEGRATIONS: debug_log(f'Provider `{roster_source}` not found in INTEGRATIONS. Available integrations: {learning_observer.integrations.INTEGRATIONS.keys()}') @@ -448,7 +455,7 @@ async def courselist(request): List all of the courses a teacher manages: Helper ''' course_list = await run_additional_module_func(request, 'courses') - if course_list: + if course_list is not None: return course_list # TODO if course_list is falsey, the following code may fail if there if ajax is not defined. @@ -495,7 +502,7 @@ async def courseroster(request, course_id): List all of the students in a course: Helper ''' roster = await run_additional_module_func(request, 'roster', kwargs={'courseId': course_id}) - if roster: + if roster is not None: return roster if not ajax: diff --git a/learning_observer/learning_observer/settings.py b/learning_observer/learning_observer/settings.py index 017da21c5..7df530caa 100644 --- a/learning_observer/learning_observer/settings.py +++ b/learning_observer/learning_observer/settings.py @@ -91,6 +91,11 @@ def parse_and_validate_arguments(): help='Launce the Learning Observer application. This can be used with `--ipython-console` and `--ipython-kernel`.', default=True, nargs='?', const=True, type=str_to_bool) + parser.add_argument( + '--port', + help='Which port to start the system on. Overrides any port listed in a configuration file.', + type=int) + args = parser.parse_args() if not os.path.exists(args.config_file): diff --git a/modules/writing_observer/VERSION b/modules/writing_observer/VERSION index 3fb21c1f9..c98f3678a 100644 --- a/modules/writing_observer/VERSION +++ b/modules/writing_observer/VERSION @@ -1 +1 @@ -0.1.0+2025.05.16T17.31.36.630Z.e1505886.202505.berickson.lti.integration +0.1.0+2025.08.07T19.58.28.937Z.ed90597d.berickson.202507.new.lti.updates diff --git a/modules/writing_observer/writing_observer/module.py b/modules/writing_observer/writing_observer/module.py index dad2f01a3..009262cef 100644 --- a/modules/writing_observer/writing_observer/module.py +++ b/modules/writing_observer/writing_observer/module.py @@ -316,6 +316,11 @@ 'function': writing_observer.writing_analysis.languagetool_process, 'default': {'text': '', 'category_counts': {}, 'matches': [], 'subcategory_counts': {}, 'wordcounts': {}} }, + { + 'context': "org.mitros.writing_analytics", + 'scope': writing_observer.writing_analysis.student_scope, + 'function': writing_observer.writing_analysis.student_profile, + } ] diff --git a/modules/writing_observer/writing_observer/writing_analysis.py b/modules/writing_observer/writing_observer/writing_analysis.py index c2cb9b33b..7ebe5dd15 100644 --- a/modules/writing_observer/writing_observer/writing_analysis.py +++ b/modules/writing_observer/writing_observer/writing_analysis.py @@ -229,6 +229,18 @@ async def event_count(event, internal_state): return state, state +@kvs_pipeline(scope=student_scope, null_state={}) +async def student_profile(event, internal_state): + '''Store profile information for a given id + ''' + email = event['client'].get('chrome_identity', {}).get('email') + id = event['client'].get('auth', {}).get('safe_user_id') + if email != internal_state.get('email') or id != internal_state.get('user_id'): + state = {'email': email, 'google_id': id} + return state, state + return False, False + + @kvs_pipeline(scope=gdoc_scope, null_state={}) async def nlp_components(event, internal_state): '''HACK the reducers need this method to query data diff --git a/scripts/map_emails_to_ids_in_kvs.py b/scripts/map_emails_to_ids_in_kvs.py new file mode 100644 index 000000000..953269bbe --- /dev/null +++ b/scripts/map_emails_to_ids_in_kvs.py @@ -0,0 +1,28 @@ +import asyncio + +import learning_observer.kvs +import learning_observer.offline +import learning_observer.stream_analytics.helpers as sa_helpers + +import writing_observer.writing_analysis + + +def create_key(email): + return f'email-studentID-mapping:{email}' + + +async def run(): + learning_observer.offline.init('creds.yaml') + kvs = learning_observer.kvs.KVS() + reducer_function_name = sa_helpers.fully_qualified_function_name(writing_observer.writing_analysis.student_profile) + all_keys = await kvs.keys() + keys = [k for k in all_keys if 'Internal' in k and reducer_function_name in k] + values = await kvs.multiget(keys) + for profile in values: + if 'email' not in profile or 'google_id' not in profile: + continue + await kvs.set(create_key(profile['email']), profile['google_id']) + + +if __name__ == '__main__': + asyncio.run(run()) From 4d8e2b48fdde05c008af8acf9ca8755538c149d2 Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Fri, 15 Aug 2025 09:29:30 -0400 Subject: [PATCH 44/88] added nginx conf --- VERSION | 2 +- devops/single_server_instances/README.md | 38 +++++++++++++ .../nginx.conf.example | 55 +++++++++++++++++++ 3 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 devops/single_server_instances/nginx.conf.example diff --git a/VERSION b/VERSION index 23e7294b1..5efb812a6 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0+2025.08.10T20.53.47.175Z.cc7dc75e.berickson.202507.new.lti.updates +0.1.0+2025.08.15T13.29.30.819Z.f61cb8b1.master diff --git a/devops/single_server_instances/README.md b/devops/single_server_instances/README.md index 48ae6103d..f2123849b 100644 --- a/devops/single_server_instances/README.md +++ b/devops/single_server_instances/README.md @@ -34,3 +34,41 @@ Stop all instances: Logs are saved in `LOGFILE_DEST`, and PIDs are stored in `LOGFILE_DEST/pids`. You may need to change paths or permissions depending on your environment. + +## Nginx Settings + +The file `nginx.conf.example` provides a sample configration for Nginx when you start 4 instances of LO. +First, these settings split the incoming events and all other traffic between 2 upstream servers. +Each upstream server balances connections between 2 instances of Learning Observer. + +```text +Incoming Request + β”‚ + β–Ό ++---------------+ +| NGINX | ++---------------+ + β”‚ + β–Ό + Path starts + with "/wsapi/in/"? + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + Yesβ”‚ β”‚No + β–Ό β–Ό ++------------------+ +-----------------+ +| wsapi_in_backend | | general_backend | +| | | | ++-------+----------+ +--------+--------+ + β”‚ β”‚ + +----+----+ +----+----+ Balanced by least + | App 1 | | App 3 | connections `least_conn` + | :9001 | | :9003 | + +---------+ +---------+ + +----+----+ +----+----+ + | App 2 | | App 4 | + | :9002 | | :9004 | + +---------+ +---------+ +``` + +Note: these are settings to add to your nginx configuration. +You will likely have other settings, such as ssl certificates. diff --git a/devops/single_server_instances/nginx.conf.example b/devops/single_server_instances/nginx.conf.example new file mode 100644 index 000000000..571ba0f2d --- /dev/null +++ b/devops/single_server_instances/nginx.conf.example @@ -0,0 +1,55 @@ +# Upstreams +upstream wsapi_in_backend { + least_conn; + server 127.0.0.1:9001; + server 127.0.0.1:9002; +} + +upstream general_backend { + least_conn; + server 127.0.0.1:9003; + server 127.0.0.1:9004; +} + +# Simple CORS preflight detection +map $request_method $cors_preflight { + default 0; + OPTIONS 1; +} + +server { + # Common proxy headers for everything + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + + # --- Split route for /wsapi/in/ --- + location /wsapi/in/ { + proxy_pass http://wsapi_in_backend; + proxy_read_timeout 86400; + + add_header 'Access-Control-Allow-Origin' '*'; + add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, HEAD'; + add_header 'Access-Control-Allow-Headers' 'Authorization, Origin, X-Requested-With, Content-Type, Accept'; + + if ($cors_preflight) { + return 200; + } + } + + # --- Everything else goes to general backend --- + location / { + proxy_pass http://general_backend; + proxy_read_timeout 86400; + + add_header 'Access-Control-Allow-Origin' '*'; + add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, HEAD'; + add_header 'Access-Control-Allow-Headers' 'Authorization, Origin, X-Requested-With, Content-Type, Accept'; + + if ($cors_preflight) { + return 200; + } + } +} From 381ec667a0e5267e63245032b07533a779c0b549 Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Fri, 19 Sep 2025 10:50:00 -0400 Subject: [PATCH 45/88] restructured documentation (#236) --- VERSION | 2 +- autodocs/.gitignore | 1 + autodocs/api.rst | 1 + autodocs/concepts.rst | 18 +++++++++ autodocs/conf.py | 38 ++++++++++++++++++- autodocs/development.rst | 21 ---------- autodocs/docs | 1 + autodocs/how-to.rst | 14 +++++++ autodocs/images.rst | 10 ----- autodocs/index.rst | 19 +++++++--- autodocs/modules.rst | 14 +++---- autodocs/reference.rst | 16 ++++++++ autodocs/system_design.rst | 16 -------- autodocs/tutorials.rst | 11 ++++++ docs/{ => concepts}/architecture.md | 0 docs/{ => concepts}/auth.md | 0 docs/{ => concepts}/events.md | 0 docs/{ => concepts}/history.md | 0 docs/{ => concepts}/privacy.md | 0 docs/{ => concepts}/reducers.md | 0 docs/{ => concepts}/scaling.md | 0 docs/{ => concepts}/system_design.md | 6 +-- docs/{ => concepts}/technologies.md | 0 docs/{ => how-to}/config.md | 0 docs/{ => how-to}/dashboards.md | 0 docs/{ => how-to}/docker.md | 0 docs/{ => how-to}/extension.md | 0 docs/{ => how-to}/interactive_environments.md | 0 docs/{ => reference}/code_quality.md | 0 docs/{ => reference}/documentation.md | 0 docs/{ => reference}/linting.md | 0 docs/{ => reference}/testing.md | 0 docs/{ => reference}/versioning.md | 0 docs/{ => tutorials}/install.md | 0 docs/{ => tutorials}/workshop.md | 0 docs/{ => tutorials}/workshop_creds.md | 0 36 files changed, 121 insertions(+), 67 deletions(-) create mode 100644 autodocs/concepts.rst delete mode 100644 autodocs/development.rst create mode 120000 autodocs/docs create mode 100644 autodocs/how-to.rst delete mode 100644 autodocs/images.rst create mode 100644 autodocs/reference.rst delete mode 100644 autodocs/system_design.rst create mode 100644 autodocs/tutorials.rst rename docs/{ => concepts}/architecture.md (100%) rename docs/{ => concepts}/auth.md (100%) rename docs/{ => concepts}/events.md (100%) rename docs/{ => concepts}/history.md (100%) rename docs/{ => concepts}/privacy.md (100%) rename docs/{ => concepts}/reducers.md (100%) rename docs/{ => concepts}/scaling.md (100%) rename docs/{ => concepts}/system_design.md (96%) rename docs/{ => concepts}/technologies.md (100%) rename docs/{ => how-to}/config.md (100%) rename docs/{ => how-to}/dashboards.md (100%) rename docs/{ => how-to}/docker.md (100%) rename docs/{ => how-to}/extension.md (100%) rename docs/{ => how-to}/interactive_environments.md (100%) rename docs/{ => reference}/code_quality.md (100%) rename docs/{ => reference}/documentation.md (100%) rename docs/{ => reference}/linting.md (100%) rename docs/{ => reference}/testing.md (100%) rename docs/{ => reference}/versioning.md (100%) rename docs/{ => tutorials}/install.md (100%) rename docs/{ => tutorials}/workshop.md (100%) rename docs/{ => tutorials}/workshop_creds.md (100%) diff --git a/VERSION b/VERSION index 5efb812a6..a5a5ba116 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0+2025.08.15T13.29.30.819Z.f61cb8b1.master +0.1.0+2025.09.18T20.00.55.977Z.4d8e2b48.berickson.2025.document.restructure diff --git a/autodocs/.gitignore b/autodocs/.gitignore index 9c36f954f..90045b728 100644 --- a/autodocs/.gitignore +++ b/autodocs/.gitignore @@ -1,3 +1,4 @@ _build/ generated/ apidocs/ +module_readmes/ diff --git a/autodocs/api.rst b/autodocs/api.rst index ab7dcdf04..2d744d115 100644 --- a/autodocs/api.rst +++ b/autodocs/api.rst @@ -2,5 +2,6 @@ API === .. toctree:: + :maxdepth: 4 apidocs/index diff --git a/autodocs/concepts.rst b/autodocs/concepts.rst new file mode 100644 index 000000000..f6c06c98e --- /dev/null +++ b/autodocs/concepts.rst @@ -0,0 +1,18 @@ +Concepts +============= + +Explanations of key ideas, principles, and background knowledge. + +.. toctree:: + :maxdepth: 1 + :titlesonly: + + docs/concepts/architecture.md + docs/concepts/auth.md + docs/concepts/events.md + docs/concepts/history.md + docs/concepts/privacy.md + docs/concepts/reducers.md + docs/concepts/scaling.md + docs/concepts/system_design.md + docs/concepts/technologies.md diff --git a/autodocs/conf.py b/autodocs/conf.py index 84d390af0..55ec33798 100644 --- a/autodocs/conf.py +++ b/autodocs/conf.py @@ -1,5 +1,8 @@ -import sys import os +import pathlib +import shutil +import sphinx.util +import sys # Configuration file for the Sphinx documentation builder. # # For the full list of built-in configuration values, see the documentation: @@ -9,7 +12,7 @@ # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information project = 'Learning Observer' -copyright = '2023, Bradley Erickson' +copyright = '2020-2025, Bradley Erickson' author = 'Bradley Erickson' # -- General configuration --------------------------------------------------- @@ -26,6 +29,9 @@ '../modules/writing_observer/writing_observer' ] +autodoc2_output_dir = 'apidocs' +autodoc2_member_order = 'bysource' + source_suffix = { '.rst': 'restructuredtext', '.md': 'markdown', @@ -40,3 +46,31 @@ html_theme = 'alabaster' html_static_path = ['_static'] + +LOGGER = sphinx.util.logging.getLogger(__name__) + + +def _copy_module_readmes(app): + """Populate ``module_readmes`` with module README files.""" + + docs_root = pathlib.Path(__file__).parent.resolve() + modules_root = docs_root.parent / 'modules' + destination_root = docs_root / 'module_readmes' + + if not modules_root.exists(): + LOGGER.warning("modules directory %s was not found", modules_root) + return + + if destination_root.exists(): + shutil.rmtree(destination_root) + destination_root.mkdir(parents=True, exist_ok=True) + + readme_paths = sorted(modules_root.glob('*/README.md')) + for readme_path in readme_paths: + module_name = readme_path.parent.name + destination_path = destination_root / f"{module_name}.md" + shutil.copy2(readme_path, destination_path) + + +def setup(app): + app.connect('builder-inited', _copy_module_readmes) diff --git a/autodocs/development.rst b/autodocs/development.rst deleted file mode 100644 index f32772264..000000000 --- a/autodocs/development.rst +++ /dev/null @@ -1,21 +0,0 @@ -.. _development: - -Development ------------ - -.. include:: ../docs/documentation.md - :parser: myst_parser.sphinx_ -.. include:: ../docs/interactive_environments.md - :parser: myst_parser.sphinx_ -.. include:: ../docs/privacy.md - :parser: myst_parser.sphinx_ -.. include:: ../docs/config.md - :parser: myst_parser.sphinx_ -.. include:: ../docs/linting.md - :parser: myst_parser.sphinx_ -.. include:: ../docs/testing.md - :parser: myst_parser.sphinx_ -.. include:: ../docs/versioning.md - :parser: myst_parser.sphinx_ -.. include:: ../docs/technologies.md - :parser: myst_parser.sphinx_ diff --git a/autodocs/docs b/autodocs/docs new file mode 120000 index 000000000..6246dffc3 --- /dev/null +++ b/autodocs/docs @@ -0,0 +1 @@ +../docs/ \ No newline at end of file diff --git a/autodocs/how-to.rst b/autodocs/how-to.rst new file mode 100644 index 000000000..534354882 --- /dev/null +++ b/autodocs/how-to.rst @@ -0,0 +1,14 @@ +How-to +============= + +Practical instructions to solve specific problems or achieve goals. + +.. toctree:: + :maxdepth: 1 + :titlesonly: + + docs/how-to/config.md + docs/how-to/dashboards.md + docs/how-to/docker.md + docs/how-to/extension.md + docs/how-to/interactive_environments.md diff --git a/autodocs/images.rst b/autodocs/images.rst deleted file mode 100644 index eae7f3bfa..000000000 --- a/autodocs/images.rst +++ /dev/null @@ -1,10 +0,0 @@ -.. _images: - -Images -================= - -.. image:: ../docs/_images/block.png - -.. image:: ../docs/_images/lo_block.png - -.. image:: ../docs/_images/mmnd.png diff --git a/autodocs/index.rst b/autodocs/index.rst index df1bee6e3..d4509ecf0 100644 --- a/autodocs/index.rst +++ b/autodocs/index.rst @@ -12,14 +12,21 @@ per-student writing data, and aggegators to make dashboards. We've tested this in math and writing, but our focus is on writing process data. +Our documentation is organized into four main categories, each serving a different purpose. You can explore them below: + +- :doc:`Tutorials ` - Step-by-step guides to help you learn by doing. +- :doc:`Concepts ` - Explanations of key ideas and background knowledge. +- :doc:`How-To ` - Practical instructions to solve specific goals. +- :doc:`Reference ` - Detailed API/configuration information. + .. toctree:: - :maxdepth: 2 + :hidden: + :maxdepth: 3 - development - system_design - modules - extension - api + tutorials + concepts + how-to + reference Additional Information ---------------------- diff --git a/autodocs/modules.rst b/autodocs/modules.rst index dd2b9f08a..b267a6da9 100644 --- a/autodocs/modules.rst +++ b/autodocs/modules.rst @@ -1,11 +1,9 @@ -.. _modules: - Modules ----------- +The module READMEs are collected automatically during the Sphinx build. + +.. toctree:: + :maxdepth: 1 + :glob: -.. include:: ../modules/lo_dash_react_components/README.md - :parser: myst_parser.sphinx_ -.. include:: ../modules/lo_event/README.md - :parser: myst_parser.sphinx_ -.. include:: ../modules/writing_observer/README.md - :parser: myst_parser.sphinx_ + module_readmes/* diff --git a/autodocs/reference.rst b/autodocs/reference.rst new file mode 100644 index 000000000..5df3f458d --- /dev/null +++ b/autodocs/reference.rst @@ -0,0 +1,16 @@ +Reference +============= + +Detailed, structured information about APIs, configurations, and technical details. + +.. toctree:: + :maxdepth: 1 + :titlesonly: + + docs/reference/code_quality.md + docs/reference/documentation.md + docs/reference/linting.md + docs/reference/testing.md + docs/reference/versioning.md + modules + api diff --git a/autodocs/system_design.rst b/autodocs/system_design.rst deleted file mode 100644 index 43c818d49..000000000 --- a/autodocs/system_design.rst +++ /dev/null @@ -1,16 +0,0 @@ -.. _system_design: - -System Design -------------- -.. include:: ../docs/architecture.md - :parser: myst_parser.sphinx_ -.. include:: ../docs/auth.md - :parser: myst_parser.sphinx_ -.. include:: ../docs/system_design.md - :parser: myst_parser.sphinx_ -.. include:: ../docs/events.md - :parser: myst_parser.sphinx_ -.. include:: ../docs/reducers.md - :parser: myst_parser.sphinx_ -.. include:: ../learning_observer/learning_observer/communication_protocol/README.md - :parser: myst_parser.sphinx_ diff --git a/autodocs/tutorials.rst b/autodocs/tutorials.rst new file mode 100644 index 000000000..fb73b2ba8 --- /dev/null +++ b/autodocs/tutorials.rst @@ -0,0 +1,11 @@ +Tutorials +============= + +Step-by-step guides to help you learn by doing. + +.. toctree:: + :maxdepth: 1 + :titlesonly: + + docs/tutorials/workshop.md + docs/tutorials/install.md diff --git a/docs/architecture.md b/docs/concepts/architecture.md similarity index 100% rename from docs/architecture.md rename to docs/concepts/architecture.md diff --git a/docs/auth.md b/docs/concepts/auth.md similarity index 100% rename from docs/auth.md rename to docs/concepts/auth.md diff --git a/docs/events.md b/docs/concepts/events.md similarity index 100% rename from docs/events.md rename to docs/concepts/events.md diff --git a/docs/history.md b/docs/concepts/history.md similarity index 100% rename from docs/history.md rename to docs/concepts/history.md diff --git a/docs/privacy.md b/docs/concepts/privacy.md similarity index 100% rename from docs/privacy.md rename to docs/concepts/privacy.md diff --git a/docs/reducers.md b/docs/concepts/reducers.md similarity index 100% rename from docs/reducers.md rename to docs/concepts/reducers.md diff --git a/docs/scaling.md b/docs/concepts/scaling.md similarity index 100% rename from docs/scaling.md rename to docs/concepts/scaling.md diff --git a/docs/system_design.md b/docs/concepts/system_design.md similarity index 96% rename from docs/system_design.md rename to docs/concepts/system_design.md index 6dc2a9ab5..e4a15ae4e 100644 --- a/docs/system_design.md +++ b/docs/concepts/system_design.md @@ -20,7 +20,7 @@ Our goal is to build a system which will: In other words: -![](_images/block.png) +![](../_images/block.png) Internally, the system takes a stream of events from each learner, and routes it to one or more analytics modules. Each of these modules @@ -37,7 +37,7 @@ through instructors for such an aggregation, and only aggregate where data has changed, so that with large numbers of instructors, the system merely updates dashboards less quickly: -![](_images/lo_block.png) +![](../_images/lo_block.png) Although at present, reduce operations are per-student, and aggregations per-class, in the future, we envision: @@ -51,6 +51,6 @@ aggregations per-class, in the future, we envision: Data will be stored in a git-like Merkle tree format: -![](_images/mmnd.png) +![](../_images/mmnd.png) We'll document this in more detail later. \ No newline at end of file diff --git a/docs/technologies.md b/docs/concepts/technologies.md similarity index 100% rename from docs/technologies.md rename to docs/concepts/technologies.md diff --git a/docs/config.md b/docs/how-to/config.md similarity index 100% rename from docs/config.md rename to docs/how-to/config.md diff --git a/docs/dashboards.md b/docs/how-to/dashboards.md similarity index 100% rename from docs/dashboards.md rename to docs/how-to/dashboards.md diff --git a/docs/docker.md b/docs/how-to/docker.md similarity index 100% rename from docs/docker.md rename to docs/how-to/docker.md diff --git a/docs/extension.md b/docs/how-to/extension.md similarity index 100% rename from docs/extension.md rename to docs/how-to/extension.md diff --git a/docs/interactive_environments.md b/docs/how-to/interactive_environments.md similarity index 100% rename from docs/interactive_environments.md rename to docs/how-to/interactive_environments.md diff --git a/docs/code_quality.md b/docs/reference/code_quality.md similarity index 100% rename from docs/code_quality.md rename to docs/reference/code_quality.md diff --git a/docs/documentation.md b/docs/reference/documentation.md similarity index 100% rename from docs/documentation.md rename to docs/reference/documentation.md diff --git a/docs/linting.md b/docs/reference/linting.md similarity index 100% rename from docs/linting.md rename to docs/reference/linting.md diff --git a/docs/testing.md b/docs/reference/testing.md similarity index 100% rename from docs/testing.md rename to docs/reference/testing.md diff --git a/docs/versioning.md b/docs/reference/versioning.md similarity index 100% rename from docs/versioning.md rename to docs/reference/versioning.md diff --git a/docs/install.md b/docs/tutorials/install.md similarity index 100% rename from docs/install.md rename to docs/tutorials/install.md diff --git a/docs/workshop.md b/docs/tutorials/workshop.md similarity index 100% rename from docs/workshop.md rename to docs/tutorials/workshop.md diff --git a/docs/workshop_creds.md b/docs/tutorials/workshop_creds.md similarity index 100% rename from docs/workshop_creds.md rename to docs/tutorials/workshop_creds.md From fb5631b36627b02b73c52d9d759d93cde07b84c8 Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Mon, 22 Sep 2025 15:03:01 -0400 Subject: [PATCH 46/88] Updated extension to only connect when relevant tabs are present (#235) * updated extension to only connect when relevant tabs are present * updated urls * added terminate event to the server to close files --- VERSION | 2 +- extension/writing-process/src/background.js | 130 +++++++++++------- extension/writing-process/src/manifest.json | 3 +- extension/writing-process/src/writing.js | 13 ++ learning_observer/VERSION | 2 +- .../incoming_student_event.py | 61 ++++++-- 6 files changed, 151 insertions(+), 60 deletions(-) diff --git a/VERSION b/VERSION index a5a5ba116..7e456cd3f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0+2025.09.18T20.00.55.977Z.4d8e2b48.berickson.2025.document.restructure +0.1.0+2025.09.15T18.58.19.025Z.cb6381ff.berickson.202509.extension.fixes diff --git a/extension/writing-process/src/background.js b/extension/writing-process/src/background.js index 5396ce383..236a91d3e 100644 --- a/extension/writing-process/src/background.js +++ b/extension/writing-process/src/background.js @@ -28,34 +28,54 @@ import { localStorageInfo, sessionStorageInfo } from 'lo_event/lo_event/metadata // } /* and other async logic */); // websocketLogger( callback ); +// Track which tabs currently have an active content script and the state of +// our logger so that we only initialize logging when needed. +const activeContentTabs = new Set(); +let loEventActive = false; +let loggers = []; +const manifestVersion = chrome.runtime.getManifest().version; + + // We are not sure if this should be done within `websocketLogger()`'s `init` // or one level up. -const loggers = [ - consoleLogger(), - websocketLogger(WEBSOCKET_SERVER_URL) -] - -loEvent.init( - 'org.mitros.writing_analytics', - '0.01', - loggers, - { - debugLevel: loEventDebug.LEVEL.SIMPLE, - metadata: [ - browserInfo(), - chromeAuth(), - localStorageInfo(), - sessionStorageInfo(), - ] - } -); -loEvent.go(); +function startLogger () { + if (loEventActive) return; + loggers = [ + consoleLogger(), + websocketLogger(WEBSOCKET_SERVER_URL) + ]; + loEvent.init( + 'org.mitros.writing_analytics', + manifestVersion, + loggers, + { + debugLevel: loEventDebug.LEVEL.SIMPLE, + // TODO document what we have currently and what we want + metadata: [ + browserInfo(), + chromeAuth(), + localStorageInfo(), + sessionStorageInfo(), + ] + } + ); + loEvent.go(); + loEventActive = true; + loEvent.logEvent('extension_loaded', {}); + logFromServiceWorker('Extension loaded'); +} + +function stopLogger () { + if (!loEventActive) return; + loEvent.logEvent('terminate', {}); + loEventActive = false; +} // Function to serve as replacement for // chrome.extension.getBackgroundPage().console.log(event); because it is not allowed in V3 // It logs the event to the console for debugging. function logFromServiceWorker(event) { - console.log(event); + console.log(event); } function this_a_google_docs_save(request) { @@ -69,7 +89,7 @@ function this_a_google_docs_save(request) { went from a conservative regexp to a liberal one. We should confirm this never catches extra requests, though. */ - if(request.url.match(/.*:\/\/docs\.google\.com\/document\/(.*)\/save/i)) { + if (request.url.match(/.*:\/\/docs\.google\.com\/document\/(.*)\/save/i)) { return true; } return false; @@ -85,7 +105,7 @@ function this_a_google_docs_bind(request) { https://stackoverflow.com/questions/6831916/is-it-possible-to-monitor-http-traffic-in-chrome-using-an-extension#6832018 */ - if(request.url.match(/.*:\/\/docs\.google\.com\/document\/(.*)\/bind/i)) { + if (request.url.match(/.*:\/\/docs\.google\.com\/document\/(.*)\/bind/i)) { return true; } return false; @@ -106,10 +126,29 @@ chrome.storage.sync.get(['process_server'], function(result) { // Listen for the keystroke messages from the page script and forward to the server. chrome.runtime.onMessage.addListener( - function(request, sender, sendResponse) { - //chrome.extension.getBackgroundPage().console.log("Got message"); - //chrome.extension.getBackgroundPage().console.log(request); - //console.log(sender); + function (request, sender, sendResponse) { + // Lifecycle messages from content scripts manage the logger state + if (request?.type === 'content_script_ready') { + if (sender.tab?.id !== undefined) { + activeContentTabs.add(sender.tab.id); + if (!loEventActive) { + startLogger(); + } + } + return; + } else if (request?.type === 'content_script_unloading') { + if (sender.tab?.id !== undefined) { + activeContentTabs.delete(sender.tab.id); + if (activeContentTabs.size === 0) { + stopLogger(); + } + } + return; + } + // Forward analytics events only when the logger is active + if (!loEventActive) { + return; + } request['wa_source'] = 'client_page'; loEvent.logEvent(request['event'], request); } @@ -142,31 +181,35 @@ chrome.webRequest.onBeforeRequest.addListener( especially new pages. They do inject a lot of noise, though, and from there, being able to easily ignore these is nice. */ - function(request) { + function (request) { + // No logger availaber + if (!loEventActive) { + return; + } //chrome.extension.getBackgroundPage().console.log("Web request url:"+request.url); var formdata = {}; let event; - if(request.requestBody) { + if (request.requestBody) { formdata = request.requestBody.formData; } - if(!formdata) { + if (!formdata) { formdata = {}; } - if(RAW_DEBUG) { + if (RAW_DEBUG) { loEvent.logEvent('raw_http_request', { - 'url': request.url, + 'url': request.url, 'form_data': formdata }); } - if(this_a_google_docs_save(request)){ + if (this_a_google_docs_save(request)) { //chrome.extension.getBackgroundPage().console.log("Google Docs bundles "+request.url); try { /* We should think through which time stamps we should log. These are all subtly different: browser event versus request timestamp, as well as user time zone versus GMT. */ event = { - 'doc_id': googledocs_id_from_url(request.url), + 'doc_id': googledocs_id_from_url(request.url), 'url': request.url, 'bundles': JSON.parse(formdata.bundles), 'rev': formdata.rev, @@ -174,12 +217,12 @@ chrome.webRequest.onBeforeRequest.addListener( }; logFromServiceWorker(event); loEvent.logEvent('google_docs_save', event); - } catch(err) { + } catch (err) { /* Oddball events, like text selections. */ event = { - 'doc_id': googledocs_id_from_url(request.url), + 'doc_id': googledocs_id_from_url(request.url), 'url': request.url, 'formdata': formdata, 'rev': formdata.rev, @@ -187,10 +230,10 @@ chrome.webRequest.onBeforeRequest.addListener( }; loEvent.logEvent('google_docs_save_extra', event); } - } else if(this_a_google_docs_bind(request)) { + } else if (this_a_google_docs_bind(request)) { logFromServiceWorker(request); } else { - logFromServiceWorker("Not a save or bind: "+request.url); + logFromServiceWorker("Not a save or bind: " + request.url); } }, { urls: ["*://docs.google.com/*"] }, @@ -202,10 +245,10 @@ chrome.webRequest.onBeforeRequest.addListener( chrome.runtime.onInstalled.addListener(reinjectContentScripts); async function reinjectContentScripts() { for (const contentScript of chrome.runtime.getManifest().content_scripts) { - for (const tab of await chrome.tabs.query({url: contentScript.matches})) { + for (const tab of await chrome.tabs.query({ url: contentScript.matches })) { // re-inject content script await chrome.scripting.executeScript({ - target: {tabId: tab.id, allFrames: true}, + target: { tabId: tab.id, allFrames: true }, files: contentScript.js, }, function () { if (!chrome.runtime.lastError) { @@ -215,10 +258,3 @@ async function reinjectContentScripts() { } } } - -// Let the server know we've loaded. -loEvent.logEvent("extension_loaded", {}); - -// And let the console know we've loaded -// chrome.extension.getBackgroundPage().console.log("Loaded"); remove -logFromServiceWorker("Loaded"); diff --git a/extension/writing-process/src/manifest.json b/extension/writing-process/src/manifest.json index f663bf58f..ad7c064a0 100644 --- a/extension/writing-process/src/manifest.json +++ b/extension/writing-process/src/manifest.json @@ -17,7 +17,8 @@ "storage", "nativeMessaging", "scripting", - "activeTab" + "activeTab", + "tabs" ], "icons": { "48": "assets/lousy-fountain-pen-48.png" diff --git a/extension/writing-process/src/writing.js b/extension/writing-process/src/writing.js index f318f404d..1e63f9df1 100644 --- a/extension/writing-process/src/writing.js +++ b/extension/writing-process/src/writing.js @@ -10,6 +10,19 @@ import { googledocs_id_from_url, treeget } from './writing_common'; General Utility Functions */ +// Notify the service worker that this content script is active. We also +// provide a hook to let the service worker know when the script is about to +// unload so it can perform any necessary cleanup (for example, shutting down +// loggers when no tabs are active). +if (chrome.runtime?.id !== undefined) { + chrome.runtime.sendMessage({ type: 'content_script_ready' }); + window.addEventListener('beforeunload', () => { + if (chrome.runtime?.id !== undefined) { + chrome.runtime.sendMessage({ type: 'content_script_unloading' }); + } + }); +} + function log_error(error_string) { /* We should send errors to the server, but for now, we diff --git a/learning_observer/VERSION b/learning_observer/VERSION index 5f2ef51aa..7e456cd3f 100644 --- a/learning_observer/VERSION +++ b/learning_observer/VERSION @@ -1 +1 @@ -0.1.0+2025.08.07T20.03.33.270Z.c17a54d7.berickson.202507.new.lti.updates +0.1.0+2025.09.15T18.58.19.025Z.cb6381ff.berickson.202509.extension.fixes diff --git a/learning_observer/learning_observer/incoming_student_event.py b/learning_observer/learning_observer/incoming_student_event.py index b8a996b25..53479bbd6 100644 --- a/learning_observer/learning_observer/incoming_student_event.py +++ b/learning_observer/learning_observer/incoming_student_event.py @@ -187,6 +187,14 @@ async def handle_incoming_client_event(metadata): # The adapter allows us to handle old event formats adapter = learning_observer.adapters.adapter.EventAdapter() + handler_log_closed = False + + def close_handler_log(): + nonlocal handler_log_closed + if not handler_log_closed: + log_event.close_logfile(filename) + handler_log_closed = True + async def handler(request, client_event): ''' This is the handler for incoming client events. @@ -206,11 +214,16 @@ async def handler(request, client_event): log_event.log_event( json.dumps(event, sort_keys=True), filename, preencoded=True, timestamp=True) + if client_event.get("event") == "terminate": + debug_log("Terminate event received; closing handler log file") + close_handler_log() + return [] await pipeline(event) # when the handler garbage collected (no more events are being passed through), # close the log file associated with this connection - weakref.finalize(handler, log_event.close_logfile, filename) + weakref.finalize(handler, close_handler_log) + handler.close = close_handler_log return handler @@ -291,20 +304,32 @@ def decode_and_log_event(msg): ) COUNT += 1 + decoder_log_closed = False + + def close_decoder_logfile(): + nonlocal decoder_log_closed + if not decoder_log_closed: + log_event.close_logfile(filename) + decoder_log_closed = True + async def decode_and_log_event(events): ''' Take an aiohttp web sockets message, log it, and return a clean event. ''' - async for msg in events: - if isinstance(msg, dict): - json_event = msg - else: - json_event = json.loads(msg.data) - log_event.log_event(json_event, filename=filename) - yield json_event - # done processing events, can close logfile now - log_event.close_logfile(filename) + try: + async for msg in events: + if isinstance(msg, dict): + json_event = msg + else: + json_event = json.loads(msg.data) + log_event.log_event(json_event, filename=filename) + yield json_event + finally: + # done processing events, can close logfile now + close_decoder_logfile() + + decode_and_log_event.close = close_decoder_logfile return decode_and_log_event @@ -434,6 +459,21 @@ async def decode_lock_fields(events): event.update(lock_fields) yield event + async def handle_terminate_events(events): + '''Stop processing when a terminate event is received.''' + async for event in events: + if event.get('event') == 'terminate': + debug_log('Terminate event received; shutting down connection and cleaning up logs.') + handler_close = getattr(event_handler, 'close', None) + if callable(handler_close): + handler_close() + decoder_close = getattr(decoder_and_logger, 'close', None) + if callable(decoder_close): + decoder_close() + await ws.close() + return + yield event + async def filter_blacklist_events(events): '''This function stops the event pipeline if sources should be blocked. @@ -498,6 +538,7 @@ async def process_ws_message_through_pipeline(): events = process_message_from_ws() events = decoder_and_logger(events) events = decode_lock_fields(events) + events = handle_terminate_events(events) events = handle_auth_events(events) events = filter_blacklist_events(events) events = process_blob_storage_events(events) From eb453bf71b8ec57b94888d8aab21abcfe2085e95 Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Tue, 23 Sep 2025 11:55:02 -0400 Subject: [PATCH 47/88] added some tutorials --- Makefile | 8 - VERSION | 2 +- autodocs/tutorials.rst | 2 +- docs/tutorials/cookiecutter-module.md | 83 +++++++++ docs/tutorials/install.md | 167 +++++++++++------- extension/writing-process/.gitignore | 2 + learning_observer/VERSION | 2 +- .../learning_observer/rosters.py | 4 + 8 files changed, 199 insertions(+), 71 deletions(-) create mode 100644 docs/tutorials/cookiecutter-module.md diff --git a/Makefile b/Makefile index 212c25dc6..a45c90b55 100644 --- a/Makefile +++ b/Makefile @@ -57,14 +57,6 @@ install-dev: install-packages: pip install -e learning_observer/[${PACKAGES}] - # Just a little bit of dependency hell... - # On Python3.11 with tensorflow, we get some odd errors - # regarding compatibility with `protobuf`. Some installation - # files are missing from the protobuf binary on pip. - # Using the `--no-binary` option includes all files. - pip uninstall -y protobuf - pip install --no-binary=protobuf protobuf==4.25 - # Testing commands test: @if [ -z "$(PKG)" ]; then echo "No module specified, please try again with \"make test PKG=path/to/module\""; exit 1; fi diff --git a/VERSION b/VERSION index 7e456cd3f..0fbb6683f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0+2025.09.15T18.58.19.025Z.cb6381ff.berickson.202509.extension.fixes +0.1.0+2025.09.23T15.55.02.340Z.fb5631b3.master diff --git a/autodocs/tutorials.rst b/autodocs/tutorials.rst index fb73b2ba8..580567507 100644 --- a/autodocs/tutorials.rst +++ b/autodocs/tutorials.rst @@ -7,5 +7,5 @@ Step-by-step guides to help you learn by doing. :maxdepth: 1 :titlesonly: - docs/tutorials/workshop.md docs/tutorials/install.md + docs/tutorials/cookiecutter-module.md diff --git a/docs/tutorials/cookiecutter-module.md b/docs/tutorials/cookiecutter-module.md new file mode 100644 index 000000000..97245f6e5 --- /dev/null +++ b/docs/tutorials/cookiecutter-module.md @@ -0,0 +1,83 @@ +# Tutorial: Build and Run a Module from the Cookiecutter Template + +This tutorial walks through generating a new Learning Observer module from the cookiecutter template, installing it, and seeing it run inside the system. We assume you already have the development environment installed and can start the stack with `make run`. + +## 1. Prepare your environment + +1. Activate the Python environment you use for Learning Observer development. +2. Make sure the `cookiecutter` command is available. If you have not installed it yet, run: + + ```bash + pip install cookiecutter + ``` + +3. From the repository root, change into the `modules/` directory: + + ```bash + cd modules/ + ``` + +## 2. Generate a module from the template + +1. Run cookiecutter against the Learning Observer template: + + ```bash + cookiecutter lo_template_module/ + ``` + +2. Fill in the prompts with information for your module. At minimum you will supply: + * **project_name** – Human-friendly title that appears in the UI. + * **project_short_description** – A one-line summary shown with the module. + * **reducer** – The name of the default reducer function the template creates. +3. Cookiecutter writes a new module directory inside `modules/`. If you entered `Revision Counter` as the name, the generated package would live in `modules/revision_counter/`. + +The template scaffolds all of the pieces Learning Observer expects, including a reducer, Dash layout, entry points, and packaging configuration so the module can be installed like any other Python package. You'll find these generated files within `modules//`, including `module.py`, `reducers.py`, the Dash dashboard, and the accompanying `setup.cfg` that defines how the package is exposed. + +## 3. Explore the generated code (optional but recommended) + +1. Inspect `module.py` to see the metadata exposed to Learning Observer, the default execution DAG, reducer list, and Dash page configuration. The file lives in `modules///module.py`. +2. Review `reducers.py` to understand how the template reducer counts events and where to extend it for your own analytics. You can find it next to `module.py` in the generated package directory. +3. Open `dash_dashboard.py` to learn how the generated layout publishes the reducer output on a Dash page. Use this as a starting point for your own visualizations. + +## 4. Install the module in editable mode + +Installing the module registers its entry point so Learning Observer can discover it. From the repository root run: + +```bash +pip install -e modules// +``` + +Replace `` with the directory created in step 2 (for example, `modules/revision_counter/`). The template’s `setup.cfg` already declares the `lo_modules` entry point that exposes `module.py` to the system, so no additional registration is required. + +## 5. Start Learning Observer + +1. Return to a terminal at the repository root. +2. Launch the stack: + + ```bash + make run + ``` + +3. Wait for the services to come up, then open `http://localhost:8888/` in a browser. Your new module should appear on the home screen because the template registers it as a course dashboard card by default inside `module.py`. + +## 6. Stream sample data to exercise the module + +To see live data, send synthetic writing events using the helper script. + +1. Open a second terminal with your environment activated. +2. Run the streaming script from the repository root: + + ```bash + python scripts/stream_writing.py --streams=5 + ``` + + This sends five concurrent simulated students worth of Google Docs events to the default local endpoint using the helper found at `scripts/stream_writing.py`. +3. Refresh your browser. The default reducer counts incoming events, so you should see the totals increase on the Dash page included with the template. Both the reducer and the dashboard live alongside `module.py` in your generated package. + +## 7. Next steps + +* Customize the reducer in `reducers.py` to compute the metrics your dashboard requires. +* Expand the Dash layout to visualize your new metrics. +* Add additional reducers, exports, or pages to `module.py` as your module grows. + +With these steps you have a working, template-based module running end-to-end inside Learning Observer. From here you can iterate on analytics and UI changes quickly by editing the generated files and reloading the server. diff --git a/docs/tutorials/install.md b/docs/tutorials/install.md index 0d3099820..7b08e81c6 100644 --- a/docs/tutorials/install.md +++ b/docs/tutorials/install.md @@ -1,73 +1,120 @@ -# Formative Process for Writing - Installation Instructions -Last updated: 16-JAN-2020 +# Tutorial: Install + +Use this tutorial to get the Learning Observer running on your machine. It walks you through the exact commands to run and the order to run them in, so you can follow along step by step. + +## Before you begin + +Make sure your computer meets these requirements: + +1. **Operating system** – A Unix-style system works best. We regularly test on Ubuntu. macOS generally works as well. Windows users should install the project inside [Windows Subsystem for Linux (WSL)](workshop/wsl-install.md) before continuing. +2. **Python** – Install Python 3.10 or 3.11 (any version newer than 3.9 is expected to work). +3. **Package manager (recommended)** – Have a virtual environment tool ready. We prefer [`virtualenvwrapper`](https://pypi.org/project/virtualenvwrapper/), but you can use `python -m venv`, Conda, or another tool you like. +4. **Optional tools** – + - [Valkey](https://valkey.io/) or Redis as a key–value store if you plan to run production-style deployments (this tutorial uses on-disk storage instead). + - Docker 26.1 if you want to experiment with the container-based workflow. The steps below focus on the native installation. + +Have two terminal windows or tabs open. We will run the server in one and use the other for helper scripts. + +## Step 1 – Download the project + +1. Open a terminal and clone the repository: + + ```bash + git clone https://github.com/ETS-Next-Gen/writing_observer.git lo_tutorial + ``` + + If you have an SSH key configured with GitHub, you can use: + + ```bash + git clone git@github.com:ETS-Next-Gen/writing_observer.git lo_tutorial + ``` + +2. Change into the new directory: + + ```bash + cd lo_tutorial/ + ``` + + All commands that follow run from this repository root unless the tutorial notes otherwise. + +## Step 2 – Create and activate a Python environment + +Choose one of the options below to create an isolated environment for the tutorial. + +*With `virtualenvwrapper`:* -## Install Chrome Extension -* Set up a github SSH key. -* Download the extension code: ```bash -cd -git clone https://github.com/ETS-Next-Gen/writing_analysis.git writing_analysis +mkvirtualenv lo_env +workon lo_env ``` -* Navigate to `chrome://extensions`. -* Click on "Load Unpacked". Select ~/writing_anlysis/extensions - -## Create an AWS account and an EC2 instance. -* Select Ubuntu, nano AMI. The cost should be 0.5 cents per hour. -* In security groups, add HTTP, HTTPS rules. Open up only to your computer's IP address (the client). -* Launch instance. -* Suppose your instance public DNS is {ec2_ip}, e.g., ec2-18-223-122-172.us-east-2.compute.amazonaws.com. -* Create aSSH key pair, save PEM file say under ~/.ssh, chmod to u+r. - -## Set up the EC2 instance. -* SSH into the machine: `bash ssh -i {pem_file} ubuntu@{ec2_ip}`. -* (Optional) Create a user account: sudo useradd {user} -* Download the server code (same repository as the extension): + +*With `venv` (built into Python):* + ```bash -cd -git clone https://github.com/ETS-Next-Gen/writing_analysis.git writing_analysis +python -m venv lo_env +source lo_env/bin/activate ``` -* Install Ansible. + +Verify that the shell prompt shows the environment name before continuing. + ```bash -sudo apt-get update -sudo apt-get upgrade -sudo apt-get install ansible -```` -* Configure Ansible. -sudo pico /etc/ansible/hosts -Add -``` -[localhost] -127.0.0.1 -``` -* `cd ~/writing_analysis/configuration`. -* Run `sudo ansible-playbook local.yaml`. This may take a while on an EC2 nanon machine. -If all goes well, you should see an output with no errors, like this: +(lo_env) user@pc:~/lo_tutorial$ ``` -bash -... -PLAY RECAP ****************************************************************************************** -127.0.0.1 : ok=5 changed=4 unreachable=0 failed=0 -``` -* Navigate to http://{ec2_ip}; you should see the message "Welcome to nginx!" if it's working. -## Obtain a free domain name -* Go to noip.com. -* Sign up +## Step 3 – Install Python dependencies + +1. Make sure `pip` itself is up to date (optional but helpful): + + ```bash + pip install --upgrade pip + ``` + +2. Install all project requirements: + + ```bash + make install + ``` + + This command downloads Python packages and builds local assets. Depending on your network speed, it can take a few minutes. + +## Step 4 – Apply the workshop configuration + +The project ships with an example configuration tailored for workshops. Copy it into place so the application starts with sensible defaults such as file-based storage and relaxed authentication. -## Obtain a free SSL Certificate Using Certbot -* Run the following commands: ```bash -sudo apt-get install software-properties-common -Sudo add-apt-repository universe -sudo add-apt-repository ppa:certbot/certbot -sudo apt-get update -sudo apt-get install certbot python-certbot-nginx -``` +cp learning_observer/learning_observer/creds.yaml.workshop learning_observer/creds.yaml ``` -bash -sudo certbot --nginx + +If you are curious about the changes, compare it with the example file: + +```bash +diff -u learning_observer/learning_observer/creds.yaml.example learning_observer/creds.yaml ``` --- Put in your {mydomain}.hopto.org address. --- Choose 1 - no redirect. -## Stand up a backend server on the EC2 instance. +## Step 5 – Start the Learning Observer + +1. Launch the development server: + + ```bash + make run + ``` + +2. The first run performs several setup tasks (such as generating role files), so it may exit after downloading dependencies. When the command finishes, run it again: + + ```bash + make run + ``` + + Repeat until the command reports that the system is ready and stays running. + +## Step 6 – Verify everything worked + +Once the server is running, open a browser and go to one of the following URLs: + +- `http://localhost:8888/` +- `http://0.0.0.0:8888/` +- `http://127.0.0.1:8888/` + +You should see the Learning Observer dashboard with a list of courses and analytics modules. Because the workshop configuration disables authentication, you can immediately click around and start experimenting. + +## Next steps diff --git a/extension/writing-process/.gitignore b/extension/writing-process/.gitignore index 9a7d305e4..5905017c8 100644 --- a/extension/writing-process/.gitignore +++ b/extension/writing-process/.gitignore @@ -1,3 +1,5 @@ dist/ node_modules/ **/*.bundle.js* +public/ +release.zip diff --git a/learning_observer/VERSION b/learning_observer/VERSION index 7e456cd3f..0fbb6683f 100644 --- a/learning_observer/VERSION +++ b/learning_observer/VERSION @@ -1 +1 @@ -0.1.0+2025.09.15T18.58.19.025Z.cb6381ff.berickson.202509.extension.fixes +0.1.0+2025.09.23T15.55.02.340Z.fb5631b3.master diff --git a/learning_observer/learning_observer/rosters.py b/learning_observer/learning_observer/rosters.py index 959693c87..00ee86f7b 100644 --- a/learning_observer/learning_observer/rosters.py +++ b/learning_observer/learning_observer/rosters.py @@ -421,6 +421,10 @@ async def run_additional_module_func(request, function_name, kwargs=None): provider = user.get('lti_context', {}).get('provider') roster_source = settings.pmss_settings.source(types=['roster_data'], attributes={'domain': user_domain, 'provider': provider}) + # Do not try to run module functions for these roster sources + if roster_source in ['all', 'test', 'filesystem']: + return None + # HACK/TODO since Canvas and Schoology are launched via an LTI, # we need to pass a course to the courses - LTI applications are # provided on a course-by-course basis, so fetching the courses From dfa95f90c06f85e2cac266593b09a62d28054b0e Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Tue, 23 Sep 2025 17:20:29 -0400 Subject: [PATCH 48/88] updated extension documentation --- VERSION | 2 +- docs/how-to/extension.md | 160 ++++++++++++++++++++++++++------------- 2 files changed, 108 insertions(+), 54 deletions(-) diff --git a/VERSION b/VERSION index 0fbb6683f..bf4718cc6 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0+2025.09.23T15.55.02.340Z.fb5631b3.master +0.1.0+2025.09.23T21.20.29.794Z.eb453bf7.master diff --git a/docs/how-to/extension.md b/docs/how-to/extension.md index ea085c1ae..a89476ea0 100644 --- a/docs/how-to/extension.md +++ b/docs/how-to/extension.md @@ -1,82 +1,136 @@ # Writing Observer Extension -This is an extension which collects data from the client. +The Writing Observer browser extension collects rich event data while a writer works in Google Docs so that the Learning Observer platform can analyse the writing process. The extension code lives in [`extension/writing-process/`](../../extension/writing-process/) and is packaged with [Extension CLI](https://oss.mobilefirst.me/extension-cli/) and webpack. -## Google Churn and Breakage +## Project Layout -Google regularly changes how they do things, breaking extensions like -this one. We had major changes for the transition from -[Manifest V2 to Manifest V3](https://developer.chrome.com/docs/extensions/mv3/mv2-sunset/). -Google was trying to cripple ad blockers, which made extensions like -these much harder to write and maintain. This change, as is very -Googly, was mandated by Google before Google properly documented Manifest V3. +The directory contains a standard Extension CLI project: -2. Google changed [rendering on the -front-end](https://workspaceupdates.googleblog.com/2021/05/Google-Docs-Canvas-Based-Rendering-Update.html) -such that our code to grab text is broken. On the whole, this is less -harmful, since we never relied on this code path. We grabbed visible -on-screen text to have ground truth data for debugging how we -reconstruct documents. We can make due without that, but it'd be nice -to fix at some point. In the design of the system, we did not count on -this text to be stable (it's used for debugging). We primarily rely on -document change events and the Google Docs API. +- `src/` - extension source files. Webpack writes the bundled content scripts and background bundles back into this directory as `*.bundle.js` files. + - `writing.js` and `writing_common.js` run in the Google Docs page and capture keystrokes and context. + - `background.js` and `service_worker.js` coordinate logging, open the websocket connection to Learning Observer, and respond to browser lifecycle events. + - `pages/` stores the popup, options page, and other extension UI assets. +- `assets/` - icons used by the published extension. +- `webpack.config.js` - bundles the source code and excludes Node-specific dependencies required by the `lo-event` package. +- `test/` - placeholder for unit tests executed by the Extension CLI test runner. -## System Design +## Prerequisites -* `writing_common.js` has common utility functions -* `writing.js` sits on each page, and listens for keystrokes. It also - collects other data, such as document title, or commenting activity. - Only the keystroke logging is well-tested. This is sent onto - `background.js` -* `background.js` used to be once per browser, and maintain a - websocket connection to the server. It also listened for Google AJAX - events which contain document edits. This was changed with Manifest - V3 and still needs to be correctly documented. +- Node.js and npm (the project was last updated with Node 18.x, though later LTS versions should also work). +- Google Chrome for manual testing. +- Access to the [`lo-event`](../../modules/lo_event/) package, which provides messaging utilities that the extension uses to communicate with Learning Observer services. -The document edits are short snippets, which aggregate a small number -of keystrokes (e.g. a couple hundred milliseconds or typically 1-2 -keystrokes). These are our primary source of data. The keystroke -collection on each page is more precise (we have timestamps for each -keystroke), and helpful for some typing speed estimations, but -currently lacks a lot of context we would need to e.g. reconstruct -documents. +## Install Dependencies -Each file has more in-depth documentation. +Install everything from inside the extension directory: -## Setup information +```bash +cd extension/writing-process +npm install +``` + +The `lo-event` dependency is declared as a `file:` package in `package.json`, so it is linked from the local repository when you install. If `npm install` cannot resolve it automatically, install it explicitly: + +```bash +npm install ../../modules/lo_event +# or link it for iterative development +cd ../../modules/lo_event +npm link +cd - +npm link lo-event +``` + +## Everyday development tasks + +Most workflows are exposed through npm scripts. Run them from `extension/writing-process/`. -The extension is built as a node project. This structure helps us allow for testing and for the use of external node packages. +### Bundle source files -### Dependencies +Use webpack to generate the bundled scripts that the extension loads at runtime. -For building the extension, we rely on a handful of build tools, like webpack to bundle the code together. +```bash +npm run bundle # one-off build of background.js, writing.js, etc. +npm run bundle:watch # continuously rebuild while editing +``` + +The bundles are written to `src/*.bundle.js`. Ensure these bundles exist before packaging the extension or running tests. -The extension has a dependency on the LOEvent package, located at `/modules/lo_event`. The LOEvent package handles passing messages from Learning Observer data sources and the Learning Observer server. +### Run the extension in development -The LOEvent package has a handful of downstream dependencies that are used when it does not have access to a browser environment. These are used to mirror browser behavior in testing environments. +Extension CLI can watch the project, rebuild on changes, and output an unpacked extension in `dist/`. -When building the extension, we found that these were not always ignored when bundling the extension together. To ignore the dependencies on building the extension, we had to add them to the `externals` portion of the `webpack.config.js` on the extension itself. +```bash +npm run ext:start # Chrome/Chromium development build +``` -### Installation +Both commands keep watching the filesystem and rebuild when files change. Load the unpacked directory they produce (see [Loading the extension locally](#loading-the-extension-locally)). -To get started, run the following: +### Run automated tests ```bash -cd extention/writing-process -npm install +npm run ext:test # run the Extension CLI test suite +npm run coverage # generate an lcov coverage report via nyc +``` + +The default tests under `test/` are minimal; extend them as new functionality is added. + +### Clean build artefacts and generate docs + +```bash +npm run ext:clean # remove dist/ +npm run ext:docs # produce API docs in public/documentation/ +npm run ext:sync # refresh Extension CLI config files ``` -Since the LOEvent package is not published, you'll need to install it locally via the `npm link` command. If there are any issues with this, `npm pack`+`npm install` is more robust, but more cumbersome since it needs to be rerun whenever `lo_event` changes. See the LOEvent installation for more information about this process. +Use these scripts before packaging to ensure the release contains only fresh bundles. -### Bundling and Building +## Building for release -After all the installation finishes, you can bundle and build the extension. Running the following command will first bundle the code and then build the extension. +`npm run build` is the primary release command. It removes any existing `dist/` directory, builds the webpack bundles, and then invokes `ext:build`. ```bash -npm build +npm run build ``` -Two items will be produced. +After the command completes you will have: + +1. `dist/` – the unpacked extension directory that can be loaded directly in Chromium-based browsers. +2. `release.zip` – an archive suitable for uploading to the Chrome Web Store or distributing manually. + +You can also call the lower-level packaging commands when needed: + +```bash +npm run ext:build # Chrome release bundle only +``` + +## Loading the extension locally + +1. Run `npm run build` (or `npm run ext:start`) to populate the `dist/` directory. +2. Navigate to `chrome://extensions/` in Chrome. +3. Enable **Developer mode** and click **Load unpacked**. +4. Select the `extension/writing-process/dist` directory. + +## Deploying updates + +- Upload the generated `release.zip` to the Chrome Web Store dashboard when publishing new versions. +- Update the `version` field in `src/manifest.json` to match the release number before packaging. +- If the Learning Observer websocket endpoint changes, update `WEBSOCKET_SERVER_URL` in `src/background.js` so that the extension sends analytics to the correct server. + +## System design overview + +The extension relies on two complementary streams of information: + +- **Content scripts (`writing.js`, `writing_common.js`)** run inside Google Docs. They capture keystrokes, document metadata, and lifecycle events, and send structured messages to the background service worker. +- **Background/service worker (`service_worker.js`, `background.js`)** manages the analytics pipeline. It listens for messages from content scripts, observes Google Docs network requests (notably `save` and `bind` endpoints), and forwards data to Learning Observer via the `lo-event` logging framework. It activates only when a Google Docs tab has injected the content script to avoid unnecessary logging. + +This design protects against frequent changes in Google Docs. The network listener provides redundancy when Google modifies the document structure, while the keystroke data keeps precise typing information. + +## Maintaining compatibility with Google Docs + +Google occasionally introduces changes that can disrupt the extension, especially around Manifest V3 requirements and Google Docs rendering updates. To reduce churn: + +- Keep an eye on [Chrome extension updates](https://developer.chrome.com/docs/extensions/whatsnew/). Manifest-level changes (e.g. the MV2 → MV3 migration) often require updates to background scripts and permissions. +- When Google Docs changes its network endpoints, temporarily enable `RAW_DEBUG` in `src/background.js` to capture raw traffic and help reverse-engineer new request formats. +- Test the extension after major Google Docs updates to confirm that keystroke logging and document reconstruction still work as expected. -1. `dist/`: a directory where all the built files are copied to -2. `release.zip`: a zip of the extension +For deeper architectural details, consult the inline documentation within each source file in `extension/writing-process/src/`. From a3fa17c99e89ca1ceeae187edce86bf5253d2df0 Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Tue, 23 Sep 2025 17:21:23 -0400 Subject: [PATCH 49/88] removed unused extension files --- VERSION | 2 +- extension/extension/manifest.json | 46 --------------------------- extension/extension/wo_front_end.png | Bin 44309 -> 0 bytes 3 files changed, 1 insertion(+), 47 deletions(-) delete mode 100644 extension/extension/manifest.json delete mode 100644 extension/extension/wo_front_end.png diff --git a/VERSION b/VERSION index bf4718cc6..f19fbd1bf 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0+2025.09.23T21.20.29.794Z.eb453bf7.master +0.1.0+2025.09.23T21.21.23.699Z.dfa95f90.master diff --git a/extension/extension/manifest.json b/extension/extension/manifest.json deleted file mode 100644 index 0ebb21523..000000000 --- a/extension/extension/manifest.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "author": "Piotr Mitros", - "manifest_version": 3, - "name": "Writing Process", - "homepage_url": "https://github.com/ETS-Next-Gen/writing_observer", - "incognito": "not_allowed", - "offline_enabled": true, - "version": "1.0.0.2", - "description": "Tracks writing in Google Docs, and provides nifty insights to you and your teachers!", - "action": { - "default_title": "Writing Process", - "default_popup": "pages/settings.html", - "default_icon": { - "48": "icons/lousy-fountain-pen-48.png" - } - }, - "content_scripts": [{ - "matches": ["*://docs.google.com/document/*"], - "js": ["3rdparty/sha256.js", "writing_common.js", "writing.js"] - }], - "web_accessible_resources": [{ - "resources": ["inject.js"], - "matches": ["*://docs.google.com/*"], - "use_dynamic_url": true - }], - "background": { - "service_worker": "service_worker.js" - }, - "permissions": [ - "webRequest", - "identity", - "identity.email", - "storage", - "scripting", - "activeTab" - ], - "host_permissions": [ - "*://docs.google.com/document/*" - ], - "icons": { - "48": "icons/lousy-fountain-pen-48.png" - }, - "options_ui": { - "page": "pages/options.html" - } -} diff --git a/extension/extension/wo_front_end.png b/extension/extension/wo_front_end.png deleted file mode 100644 index 9b6e33a621c4d82c48cbae94aff8f1096eba535d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44309 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 MxR|N(klV$709C^41ONa4 From bad6fdbbda7992b7bd5461ffbd6b2576eb33ce2b Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Wed, 24 Sep 2025 20:00:42 -0400 Subject: [PATCH 50/88] added comm protocol concept doc and updated event doc --- VERSION | 2 +- autodocs/concepts.rst | 43 ++++- docs/concepts/communication_protocol.md | 114 ++++++++++++ docs/concepts/events.md | 237 +++++++++++++----------- 4 files changed, 280 insertions(+), 116 deletions(-) create mode 100644 docs/concepts/communication_protocol.md diff --git a/VERSION b/VERSION index f19fbd1bf..ca471aaa9 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0+2025.09.23T21.21.23.699Z.dfa95f90.master +0.1.0+2025.09.25T00.00.42.365Z.a3fa17c9.master diff --git a/autodocs/concepts.rst b/autodocs/concepts.rst index f6c06c98e..00aca9db6 100644 --- a/autodocs/concepts.rst +++ b/autodocs/concepts.rst @@ -2,17 +2,42 @@ Concepts ============= Explanations of key ideas, principles, and background knowledge. +Follow this recommended sequence to build context before diving into +implementation details: + +- :doc:`History ` - establishes the background and + problem space the project is addressing. +- :doc:`System Design ` - explains how the product + strategy and user needs translate into an overall system approach. +- :doc:`Architecture ` - outlines the concrete + architecture that implements the system design. +- :doc:`Technologies ` - surveys the primary tools + and platforms we rely on to realize the architecture. +- :doc:`Events ` - introduces the event model that drives + data flowing through the system. +- :doc:`Reducers ` - details how incoming events are + aggregated into the state our experiences depend on. +- :doc:`Communication Protocol ` - discusses how + the system queries data from reducers for dashboards. +- :doc:`Scaling ` - covers strategies for growing the + system once the fundamentals are in place. +- :doc:`Auth ` - describes authentication considerations + that secure access to the system. +- :doc:`Privacy ` - documents how we protect learner data + and comply with privacy expectations. .. toctree:: + :hidden: :maxdepth: 1 :titlesonly: - docs/concepts/architecture.md - docs/concepts/auth.md - docs/concepts/events.md - docs/concepts/history.md - docs/concepts/privacy.md - docs/concepts/reducers.md - docs/concepts/scaling.md - docs/concepts/system_design.md - docs/concepts/technologies.md + docs/concepts/history + docs/concepts/system_design + docs/concepts/architecture + docs/concepts/technologies + docs/concepts/events + docs/concepts/reducers + docs/concepts/communication_protocol + docs/concepts/scaling + docs/concepts/auth + docs/concepts/privacy diff --git a/docs/concepts/communication_protocol.md b/docs/concepts/communication_protocol.md new file mode 100644 index 000000000..c9ca1accf --- /dev/null +++ b/docs/concepts/communication_protocol.md @@ -0,0 +1,114 @@ +# Communication Protocol + +The communication protocol is Learning Observer's query and transport +layer. It allows dashboards, notebooks, and other clients to request +aggregated data from the key-value store and supporting services by +submitting a declarative *execution DAG* (directed acyclic graph). The +server evaluates the DAG node-by-node, resolves the required +parameters, executes reducers or helper functions, and returns the +assembled result. This document explains how that process fits +together, the core building blocks you can use in a query, and the +helper utilities that make it easier to integrate those queries into +applications. + +## Lifecycle of a Request + +1. **Query construction** - A client builds a nested query description + in Python (or another language) with the helpers in + `learning_observer.communication_protocol.query`. The helpers mirror + relational concepts such as parameters, joins, and projections and + produce JSON-serialisable dictionaries. (See: query.py L1-L123) +2. **Flattening** - Before execution, the DAG is normalised so every + node has a unique identifier and can reference other nodes via + `variable` pointers. The `flatten` utility rewrites nested + structures such as `select(keys(...))` into separate nodes to make + evaluation straightforward. (See: util.py L1-L59) +3. **Execution** - The executor walks the flattened DAG, dispatching + each node type to a registered handler. Nodes can call Python + functions, fetch keys from the key-value store, join intermediate + datasets, or map functions across collections. The executor + assembles the final payload and enforces error handling through the + `DAGExecutionException` type. (See: executor.py L1-L145, L147-L220) +4. **Exports** - Queries expose named *exports* that identify the DAG + nodes clients may request. The integration layer can bind those + exports to callables so dashboards or notebooks can invoke them as + regular async functions. (See: util.py L64-L104, integration.py L38-L102) + +This flow supports both server-defined queries and open-ended +exploration. Production deployments typically offer curated, predefined +queries while development tooling exposes the full language for +experimentation. (See: README.md L11-L36) + +## Core Node Types + +Every node in the execution DAG has a `dispatch` type that determines +how the executor evaluates it. The query helper functions generate the +correct shape for each node type. (See: query.py L19-L123) The most common nodes are: + +- **`parameter`** - Declares a runtime argument. Parameters can be + required or optional, and the executor substitutes provided values or + defaults before downstream nodes run. (See: query.py L33-L42, executor.py L114-L144) +- **`variable`** - References the output of another node in the DAG. + These indirections are automatically inserted during flattening but + can also be used explicitly when wiring complex queries. (See: query.py L45-L52, util.py L13-L61) +- **`call`** - Invokes a published Python function on the server. + Functions are registered with `publish_function`, which ensures every + callable has a unique name. Called functions may be synchronous or + asynchronous; the executor awaits results as needed. (See: query.py L55-L67, executor.py L61-L112, integration.py L21-L47) +- **`keys`** - Produces the key descriptions required to fetch reducer + outputs from the key-value store. Keys nodes typically wrap the + outputs of roster or metadata queries so downstream `select` nodes + can retrieve the associated reducer documents. (See: query.py L114-L123, util.py L72-L102) +- **`select`** - Retrieves documents from the key-value store for the + provided keys. You can request all fields or limit to specific + projections via `SelectFields` enumerations. (See: query.py L70-L83) +- **`join`** - Merges two lists of dictionaries on matching keys using + dotted-path lookups. Left rows are preserved even without a matching + right-hand record, making it straightforward to enrich reducer + outputs with roster data. (See: query.py L86-L96, executor.py L147-L220) +- **`map`** - Applies a published function to each value in a list, + optionally in parallel, returning the transformed collection. This is + useful for server-side post-processing or feature extraction before a + result is exported. (See: query.py L99-L111) + +## Building Queries Efficiently + +Writing DAGs by hand is verbose, so the protocol provides shorthands +for common access patterns. For example, +`generate_base_dag_for_student_reducer` returns an execution DAG that +retrieves the latest reducer output for every student in a course, +including roster metadata and a preconfigured export entry. Dashboards +use this helper to quickly expose reducer results without writing the +full DAG each time. (See: util.py L63-L101) + +The `integration` module can also bind exports directly to a module so +code can call `await module.student_event_counter_export(course_id=...)` +instead of manually constructing requests. This keeps the protocol's +flexibility while offering ergonomic entry points for UI +components. (See: integration.py L49-L102) + +## Tooling and Debugging + +Two exploratory tools live alongside the protocol implementation: + +- `debugger.py` - Provides an interface for submitting ad-hoc queries + and inspecting intermediate results. +- `explorer.py` - Lists predefined queries already published on the + server so you can execute them interactively. + +Because the protocol is evolving, these tools occasionally require +updates when the underlying schema changes. Keeping the communication +protocol documented and covered by tests makes it easier to spot and +fix those regressions quickly. (See: README.md L45-L72) + +## Security Considerations + +Production deployments default to predefined queries so clients can +only request vetted datasets. Open-query mode should be restricted to +trusted environmentsβ€”such as local notebooks or read replicasβ€”because +it allows arbitrary function calls and joins that may expose sensitive +information or stress backing stores. (See: README.md L11-L36) + +Understanding these concepts makes it easier to extend the protocol, +design new reducers, and reason about the performance characteristics +of dashboards built on Learning Observer. diff --git a/docs/concepts/events.md b/docs/concepts/events.md index 8eb8bfec7..e2f49301c 100644 --- a/docs/concepts/events.md +++ b/docs/concepts/events.md @@ -1,106 +1,131 @@ -Event Format Notes -================== - -Our event format is inspired in part by: - -* IMS Caliper -* xAPI/Tincan -* edX tracking log events - -None of these are _quite_ right for our application, but several are -close. They're pretty good standards! - -Limitations of industry formats -------------------------------- - -*Verbosity* Both Caliper and xAPI require a lot of cruft to be -appended to the events. For example, we have random ID GUIDs, URLs, -and all sorts of other redundancy on each event. Having things have -either *a little* bit of context (e.g. a header) or *a little* -rationale (e.g. IDs which point into a data store) is sometimes good, -but too much is a problem. With too much redundancy, events can get -massive: - -* Our speed in handling large data scales with data size. Megabytes - can be done instantly, gigabytes in minutes, and terabytes in - hours. Cutting data sizes makes working with data easier. -* Repeating oneself can lead to inconsistent data. Data formats where - data goes one place (or where redundancy is *intentional* and - *engineered* for data correction) is more robust and less bug-prone. - -*Envelopes* Caliper payloads are bundled in JSON envelopes. This is -a horrible format since: - -* It results in a lot of additional parsing... -* ... of very large JSON objects -* If there's an error or incompatibility anywhere, you can easily lose - a whole block of data -* You can't process events in realtime, for example, for formative - feedback - -Text files with one JSON event per line are more robust and more -scalable: - -* They can be processed as a stream, without loading the whole file -* Individual corrupt events don't break the entire pipeline -- you can - skip bad events -* They can be streamed over a network -* They can be preprocessed without decoding. For example, one can - filter a file for a particular type of event, student ID, or - otherwise with a plain text search. The primary goal of first-stage - preprocessing is simply to quickly cut down data size, so it doesn't - need to be reject 100% of irrelevant events. - -*Details* In many cases, the details of a format are inappropriate for -a given purpose. There are event types which are in neither -Tincan/xAPI nor Caliper, and don't fit neatly into their -frameworks. For example: - -* Formats specify timestamps with great precision, while coarse events - (such as a student graduating) don't maintain that precision. -* In one of our clients, events are generated without a user - identifier, which is then added by the server once the user is - authenticated. For these events, validation fails. -* Related to the above, fields are sometimes repeated (e.g. client-side - timestamp, server-side timestamp, and further timestamps as the event - is processed by downstream systems). Much of this fits into security; - downstream systems _should not_ trust data from upstream systems. For - example, a student shouldn't be able to fake submitting a homework - assignment earlier than they did, and a school should not be able to - backdate a state exam response. - -There are similar minor mismatches to e.g. group events, very frequent -events (such as typing), and other types of events not fully -anticipated when the standards were created. - -I'd like to emphasize that in contrast to most industry formats, these -are quite good. They're not fundamentally broken. - -How we'd like to leverage industry formats ------------------------------------------- - -Fortunately, we don't need 100% compatibility for pretty good -interoperability. Our experience is that event formats are almost -never interchangeable between systems; even with standardized formats, -the meaning changes based on the pedagogical design. This level of -compatibility is enough to give pretty interoperability, without being -constrained by details of these formats. - -Our goal is to be compatible where convenient. Pieces we'd like to -borrow: - -* Critically, the universe has converged on events as JSON lines. This - already allows for common data pipelines. -* We can borrow vocabulary -- verbs, nouns, and similar. -* We can borrow field formats, where sensible - -With this level of standardization, adapting to data differences is -typically already less work than adapting to differences in underlying -pedagogy. - -Where we are ------------- - -We have not yet done more careful engineering of our event -format. Aside from a JSON-event-per-line, the above level of -compatibility is mostly aspirational. \ No newline at end of file +# Event Format + +Our event format is inspired in part by IMS Caliper, xAPI/Tincan, and the edX +tracking log events. None of these standards are quite right for our +application, but several are close. They're pretty good standards! + +## Limitations of Industry Formats + +* **Verbosity.** Both Caliper and xAPI require a lot of cruft to be appended to + events. For example, we have random GUIDs, URLs, and other redundancy on each + event. Having a little bit of context (e.g. a header) or a little rationale + (e.g. IDs which point into a data store) is sometimes good, but too much is a + problem. With too much redundancy, events can get massive: + * Our speed in handling large data scales with data size. Megabytes can be + done instantly, gigabytes in minutes, and terabytes in hours. Cutting data + sizes makes working with data easier. + * Repeating oneself can lead to inconsistent data. Data formats where data + goes in one place (or where redundancy is intentional and engineered for + data correction) are more robust and less bug-prone. +* **Envelopes.** Caliper payloads are bundled in JSON envelopes. This is a + horrible format because: + * It results in a lot of additional parsing... + * ... of very large JSON objects. + * If there's an error or incompatibility anywhere, you can easily lose a + whole block of data. + * You can't process events in realtime, for example, for formative feedback. + +Text files with one JSON event per line are more robust and more scalable: + +* They can be processed as a stream, without loading the whole file. +* Individual corrupt events don't break the entire pipeline-you can skip bad + events. +* They can be streamed over a network. +* They can be preprocessed without decoding. For example, you can filter a file + for a particular type of event, student ID, or otherwise with a plain text + search. The primary goal of first-stage preprocessing is simply to quickly cut + down data size, so it doesn't need to reject 100% of irrelevant events. + +* **Details.** In many cases, the details of a format are inappropriate for a + given purpose. There are event types which are in neither Tincan/xAPI nor + Caliper, and don't fit neatly into their frameworks. For example: + * Formats specify timestamps with great precision, while coarse events (such + as a student graduating) don't maintain that precision. + * In one of our clients, events are generated without a user identifier, which + is then added by the server once the user is authenticated. For these + events, validation fails. + * Related to the above, fields are sometimes repeated (e.g. client-side + timestamp, server-side timestamp, and further timestamps as the event is + processed by downstream systems). Much of this fits into security; + downstream systems should not trust data from upstream systems. For example, + a student shouldn't be able to fake submitting a homework assignment earlier + than they did, and a school should not be able to backdate a state exam + response. + +There are similar minor mismatches for group events, very frequent events (such +as typing), and other types of events not fully anticipated when the standards +were created. + +I'd like to emphasize that, in contrast to most industry formats, these are +quite good. They're not fundamentally broken. + +## How We'd Like to Leverage Industry Formats + +Fortunately, we don't need 100% compatibility for pretty good interoperability. +Our experience is that event formats are almost never interchangeable between +systems; even with standardized formats, the meaning changes based on the +pedagogical design. This level of compatibility is enough to give interoperability +without being constrained by details of these formats. + +Our goal is to be compatible where convenient. Pieces we'd like to borrow: + +* Critically, the universe has converged on events as JSON lines. This already + allows for common data pipelines. +* We can borrow vocabulary-verbs, nouns, and similar. +* We can borrow field formats, where sensible. + +With this level of standardization, adapting to data differences is typically +already less work than adapting to differences in underlying pedagogy. + +## Where We Are + +We have not yet done more careful engineering of our event format. Aside from a +JSON-event-per-line, the above level of compatibility is mostly aspirational. + +## Incoming Event Flow + +Incoming events reach the Learning Observer through `/wsapi/in/`, which +establishes a long-lived websocket for each client session. The websocket stream +is processed through a series of generators that progressively enrich and +validate each message before it reaches reducers. + +1. **Initial decode and logging.** Every websocket frame is decoded by + `event_decoder_and_logger`, which writes the raw payloads to per-session log + files. When the Merkle feature flag is enabled, the same routine also commits + events to the Merkle store using the configured backend. This stage ensures + that we always have an immutable audit log of the stream. +2. **Lock fields and metadata.** Clients typically send a `lock_fields` event + first to declare metadata such as the `source`, `activity`, or other + immutable context. These fields are cached and injected into subsequent + events so downstream reducers receive consistent metadata. Server-side + metadata like IP and user agent is added separately via `compile_server_data` + and cannot be spoofed by the client. +3. **Authentication.** The pipeline buffers events until + `learning_observer.auth.events.authenticate` confirms the session. Successful + authentication attaches the derived `auth` context-containing identifiers + like the `user_id`-to each event before it continues. The websocket + acknowledges authentication so the client can react if credentials are + missing or invalid. +4. **Protection stages.** Events flow through guardrails that: + * stop processing on an explicit `terminate` event and close the associated + log files, + * block sources that appear on the runtime blacklist, notifying the client + when a block occurs, and + * handle optional blob storage interactions (`save_blob` and `fetch_blob`) + that reducers can request. +5. **Reducer preparation.** After authentication and metadata are in place we + call `handle_incoming_client_event`. This builds a pipeline from the declared + client `source`. Each source maps to a set of stream analytics modules that + expose coroutine reducers. Reducers are partially applied with metadata + (including the authenticated user) so they can maintain per-student state. +6. **Reducer execution.** Every canonicalized event passes through the prepared + reducers. Events are logged a second time-now with server metadata and + authentication context-and reducers update their internal state. If the + reducer definitions change during a session (e.g. due to a hot reload in + development) the pipeline is rebuilt on the next event. + +This staged processing allows us to maintain separate concerns for logging, +authentication, safety checks, and analytics while keeping the event format +itself lightweight. Clients only need to agree on the JSON structure of events, +while the server handles durability and routing responsibilities on their +behalf. From 1c72d9e151fcbc3c43bd44fd3cee0ce8a62de093 Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Thu, 25 Sep 2025 12:23:50 -0400 Subject: [PATCH 51/88] more descriptive table of contents plus initial communication protocol documentation how to --- VERSION | 2 +- autodocs/how-to.rst | 11 +- autodocs/reference.rst | 11 +- autodocs/tutorials.rst | 6 +- docs/concepts/communication_protocol.md | 17 ++ docs/how-to/communication_protocol.md | 210 ++++++++++++++++++++++++ 6 files changed, 253 insertions(+), 4 deletions(-) create mode 100644 docs/how-to/communication_protocol.md diff --git a/VERSION b/VERSION index ca471aaa9..08b27df3a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0+2025.09.25T00.00.42.365Z.a3fa17c9.master +0.1.0+2025.09.25T16.23.50.822Z.bad6fdbb.master diff --git a/autodocs/how-to.rst b/autodocs/how-to.rst index 534354882..deec45815 100644 --- a/autodocs/how-to.rst +++ b/autodocs/how-to.rst @@ -1,12 +1,21 @@ How-to ============= -Practical instructions to solve specific problems or achieve goals. +Practical instructions for achieving specific goals within Learning Observer. Use these guides when you know what outcome you need and want a proven recipe to follow: + +- :doc:`Communication Protocol ` - How to query data from reducers or system endpoints for dashboards. +- :doc:`Configure Learning Observer ` - Set up credentials, environment variables, and other configuration details required for a smooth deployment. +- :doc:`Build Dashboards ` - Walk through creating dashboards from reducer outputs, including layout choices and data wiring. +- :doc:`Run with Docker ` - Learn how to containerize the stack, manage images, and operate the project using Docker Compose. +- :doc:`Writing Observer Extension ` - Install, configure, and validate the Writing Observer browser extension for capturing events. +- :doc:`Interactive Environments ` - Connect Learning Observer to Jupyter and other live coding setups for iterative development. .. toctree:: + :hidden: :maxdepth: 1 :titlesonly: + docs/how-to/communication_protocol.md docs/how-to/config.md docs/how-to/dashboards.md docs/how-to/docker.md diff --git a/autodocs/reference.rst b/autodocs/reference.rst index 5df3f458d..dbc769734 100644 --- a/autodocs/reference.rst +++ b/autodocs/reference.rst @@ -1,9 +1,18 @@ Reference ============= -Detailed, structured information about APIs, configurations, and technical details. +Detailed, structured information about APIs, configurations, and technical details. Consult these resources when you need definitive answers about how the system behaves or how to integrate with it: + +- :doc:`Code Quality Standards ` - Understand our expectations for readability, style, and continuous improvement. +- :doc:`Documentation Conventions ` - Learn how we structure docs, what tools we use, and how to contribute updates. +- :doc:`Linting Rules ` - Review the automated checks that keep the codebase healthy and how to run them locally. +- :doc:`Testing Strategy ` - Explore the testing layers we rely on and guidelines for writing reliable tests. +- :doc:`Versioning and Releases ` - See how we tag releases, manage dependencies, and maintain backward compatibility. +- :doc:`Module Reference ` - Dive into the autogenerated API reference for Python modules within Learning Observer. +- :doc:`API Reference ` - Inspect the internal functionality of the system. .. toctree:: + :hidden: :maxdepth: 1 :titlesonly: diff --git a/autodocs/tutorials.rst b/autodocs/tutorials.rst index 580567507..0d6771b53 100644 --- a/autodocs/tutorials.rst +++ b/autodocs/tutorials.rst @@ -1,9 +1,13 @@ Tutorials ============= -Step-by-step guides to help you learn by doing. +Step-by-step guides that teach by doing. Follow these tutorials to get hands-on experience with core workflows: + +- :doc:`Install Learning Observer ` - Set up the development environment, install dependencies, and verify your deployment. +- :doc:`Create a Module with Cookiecutter ` - Generate a new module scaffold, customize it, and understand the key files produced by the template. .. toctree:: + :hidden: :maxdepth: 1 :titlesonly: diff --git a/docs/concepts/communication_protocol.md b/docs/concepts/communication_protocol.md index c9ca1accf..3738286a4 100644 --- a/docs/concepts/communication_protocol.md +++ b/docs/concepts/communication_protocol.md @@ -87,6 +87,23 @@ instead of manually constructing requests. This keeps the protocol's flexibility while offering ergonomic entry points for UI components. (See: integration.py L49-L102) +## WebSocket Endpoint + +Dashboards and other clients interact with the communication protocol +through a dedicated WebSocket endpoint exposed at +`/wsapi/communication_protocol`. The aiohttp application wires that path +to `websocket_dashboard_handler`, making the protocol available to +browser sessions and backend consumers alike. (See: learning_observer/routes.py L195-L213) + +When a client connects, the handler waits for a JSON payload describing +one or more queries. Each entry typically includes the flattened +`execution_dag`, a list of `target_exports` to stream, and optional +`kwargs` that provide runtime parameters. Whenever the client submits a +new payload, the server builds the requested DAG generators, executes +them, and schedules reruns based on the provided settings. Responses are +batched into arrays of `{op, path, value}` records so the client can +efficiently apply partial updates to its local state. (See: learning_observer/dashboard.py L331-L411) + ## Tooling and Debugging Two exploratory tools live alongside the protocol implementation: diff --git a/docs/how-to/communication_protocol.md b/docs/how-to/communication_protocol.md new file mode 100644 index 000000000..b72d02fb9 --- /dev/null +++ b/docs/how-to/communication_protocol.md @@ -0,0 +1,210 @@ +# How to Build and Run Communication Protocol Queries + +This guide explains the end-to-end workflow for turning a reporting idea into a runnable query on the Learning Observer communication protocol. Follow the steps in orderβ€”both humans and language models can use this as a checklist when creating or automating queries. + +## 1. Frame the Data Task + +1. Write a one-sentence description of the insight or dataset you need (e.g., *β€œReturn the latest writing sample for each student in a course”*). +2. Identify the reducers, helper functions, or key-value store documents that expose the data. Concept docs provide summaries of the executor lifecycle, node types, and utilities for transforming reducer outputs. 【F:docs/concepts/communication_protocol.md†L1-L100】 +3. Check whether an existing helper (e.g., `generate_base_dag_for_student_reducer`) already provides most of the DAG structure. Reusing helpers keeps queries consistent and concise. 【F:docs/concepts/communication_protocol.md†L62-L87】 + +## 2. Confirm Your Goal and Required Data + +1. Identify the data source(s): + + * **Reducers** - Aggregated documents stored in the key-value store. + * **Helper functions** - Python callables published with `publish_function`. + * **Roster/metadata** - Collections that need to be joined with reducer data. +2. Decide which fields must appear in the output. Note whether you need the entire document or only specific fields. +3. List all runtime values (course ID, time range, student list, etc.). These become `parameter` nodes later. + +Document these choices (in comments or metadata) so you can refer to them in later steps. + +## 3. Declare Parameters and Defaults + +Each runtime input must be expressed as a `parameter` node. Parameters can be required or optional and may include default values: + +```python +course_id = query.parameter("course_id", required=True) +student_id = query.parameter("student_id", required=False, default=None) +``` + +For each parameter, document: + +* **Name** - Identifier passed to the DAG node. +* **Type** - String, UUID, ISO date, etc. +* **Required** - Boolean flag. +* **Default** - Optional fallback value. + +> Tip: Emit parameter declarations first so later steps can reuse variables like `course_id["variable"]` consistently. + +For fixed values (e.g., reducer names or field lists), define constants once near where they are used. + +## 4. Plan the Data Flow (DAG Skeleton) + +Translate the goal into a linear sequence of operations. A typical reducer query involves: + +1. Fetching roster metadata or other context. +2. Producing keys for each entity (`keys` nodes). +3. Retrieving reducer documents with `select`. +4. Joining reducer outputs with metadata. +5. (Optional) Post-processing with `map` or `call`. + +Example outline: + +``` +roster = call("get_course_roster", course_id) +reducer_keys = keys(reducer_name, roster.students) +reducer_docs = select(reducer_keys, fields=[...]) +enriched = join(reducer_docs, roster, left_on="student.id", right_on="id") +export enriched +``` + +Verify that every step depends only on earlier outputs, and adjust until the flow is acyclic. + +## 5. Construct Nodes with Query Helpers + +Use `query.py` helpers to implement the skeleton: + +```python +from learning_observer.communication_protocol import query + +roster = query.call("get_course_roster", args={"course_id": course_id}) +reducer_keys = query.keys( + reducer="reading_fluency", + entities=query.variable(roster, "students"), +) +reducer_docs = query.select( + keys=reducer_keys, + fields=query.SelectFields.SUMMARY, +) +enriched = query.join( + left=reducer_docs, + right=query.variable(roster), + left_on="student_id", + right_on="id", +) +``` + +Guidelines: + +* Use `query.variable(node, path=None)` for downstream access to prior outputs. +* Encapsulate repeated or complex logic in functions for reuse and testing. +* Use explicit names and keyword argumentsβ€”avoid positional arguments for clarity. + +## 6. Define Exports and Integrations + +Choose which nodes should be externally accessible: + +```python +exports = { + "reading_fluency": query.export("reading_fluency", enriched) +} +``` + +If integrating with the async helper layer, pass `exports` to `learning_observer.communication_protocol.integration.bind_exports`. + +Document required parameters and defaults with the export definitions. + +## 7. Flatten, Validate, and Serialise + +1. Convert the nested DAG into executor-ready form: + + ```python + from learning_observer.communication_protocol import util + dag = util.flatten(exports) + ``` + +2. Confirm all node IDs are unique and reference earlier nodes. Inspect the flattened DAG if generated automatically. + +3. Serialise to JSON (e.g., `json.dumps(dag)`) when sending over the wire. + +4. Add automated testsβ€”at minimum a smoke test against a fixture store. + +## 8. Expose the DAG to Clients + +To make the DAG discoverable over the websocket interface: + +* Define `EXECUTION_DAG` in the module file and register it with the loader. +* On server start, the DAG will be advertised under the module’s namespace. + +Production deployments should prefer predefined DAGs for security. Open-query mode is optional and must be explicitly enabled. + +## 9. Execute the Query + +Submit the flattened DAG to the communication protocol endpoint with runtime parameters: + +```json +{ + "parameters": { + "course_id": "course-123", + "start_date": "2023-09-01" + }, + "exports": ["reading_fluency"], + "dag": { ... flattened nodes ... } +} +``` + +On success, the response includes export payloads keyed by export name. Inspect `DAGExecutionException` for error details. + +When using integration bindings, call the generated async function with the same parameters. + +## 10. Construct Websocket Requests + +Clients interact with `/wsapi/communication_protocol` via JSON messages. Each message contains: + +* `execution_dag` - Name of a predefined DAG or a full DAG object. +* `target_exports` - List of exports to run. +* `kwargs` - Runtime parameters. + +Example: + +```json +{ + "docs_request": { + "execution_dag": "writing_observer", + "target_exports": ["docs_with_roster"], + "kwargs": { "course_id": "COURSE-123" } + } +} +``` + +The server streams back updates in messages shaped like: + +```json +[ + { + "op": "update", + "path": "students.student-1", + "value": { "text": "...", "provenance": { ... } } + } +] +``` + +If `rerun_dag_delay` is set, the server automatically re-executes the DAG and pushes updates. + +## 11. Iterate and Maintain + +* Profile slow queries; large joins may need new helpers or precomputed reducers. +* Keep DAGs version-controlled. Update dependent queries when reducers or helpers change. +* Review security before exposing exports to untrusted clients. + +## 12. Test End-to-End + +* **Unit-test** reducers and helpers independently. +* **Reference** `learning_observer/learning_observer/communication_protocol/test_cases.py` for DAG tests. +* **Exercise websocket flows** manually or with automated integration tests. + +## 13. Document Parameters and Outputs + +Update module documentation with: + +* Export descriptions, parameter types, and return structures. +* Sample request payloads. +* Notes on authentication or runtime context. + +Good documentation ensures developers and tooling can invoke queries reliably. + +### Summary + +Following this workflow ensures queries are consistent, testable, and safe to expose across dashboards, notebooks, and automation tools. From d6047986b4ba0548038649c9e5699f5e56d1f16e Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Tue, 30 Sep 2025 11:53:18 -0400 Subject: [PATCH 52/88] updated documentation for modules --- VERSION | 2 +- autodocs/conf.py | 122 ++++++++++++++- autodocs/modules.rst | 2 +- docs/reference/documentation.md | 32 ++-- learning_observer/VERSION | 2 +- .../learning_observer/integrations/google.py | 4 +- modules/wo_bulk_essay_analysis/README.md | 71 +++++++++ modules/wo_bulk_essay_analysis/VERSION | 2 +- .../_images/bulk-essay-analysis-overview.png | Bin 0 -> 157818 bytes .../bulk-essay-analysis-student-tile.png | Bin 0 -> 125229 bytes .../wo_classroom_text_highlighter/README.md | 61 +++++++- modules/wo_classroom_text_highlighter/VERSION | 2 +- .../classroom-text-highlighter-options.png | Bin 0 -> 51037 bytes .../classroom-text-highlighter-overview.png | Bin 0 -> 153503 bytes .../classroom-text-highlighter-student.png | Bin 0 -> 72734 bytes modules/writing_observer/README.md | 141 +++++++++++++----- modules/writing_observer/VERSION | 2 +- 17 files changed, 371 insertions(+), 72 deletions(-) create mode 100644 modules/wo_bulk_essay_analysis/README.md create mode 100644 modules/wo_bulk_essay_analysis/_images/bulk-essay-analysis-overview.png create mode 100644 modules/wo_bulk_essay_analysis/_images/bulk-essay-analysis-student-tile.png create mode 100644 modules/wo_classroom_text_highlighter/_images/classroom-text-highlighter-options.png create mode 100644 modules/wo_classroom_text_highlighter/_images/classroom-text-highlighter-overview.png create mode 100644 modules/wo_classroom_text_highlighter/_images/classroom-text-highlighter-student.png diff --git a/VERSION b/VERSION index 08b27df3a..696424a49 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0+2025.09.25T16.23.50.822Z.bad6fdbb.master +0.1.0+2025.09.30T15.53.18.556Z.1c72d9e1.master diff --git a/autodocs/conf.py b/autodocs/conf.py index 55ec33798..3af5b92ef 100644 --- a/autodocs/conf.py +++ b/autodocs/conf.py @@ -1,8 +1,10 @@ import os import pathlib +import re import shutil import sphinx.util import sys +import unicodedata # Configuration file for the Sphinx documentation builder. # # For the full list of built-in configuration values, see the documentation: @@ -50,8 +52,110 @@ LOGGER = sphinx.util.logging.getLogger(__name__) +_MARKDOWN_IMAGE_PATTERN = re.compile(r'!\[[^\]]*\]\(([^)]+)\)') +_RST_IMAGE_PATTERNS = [ + re.compile(r'\.\.\s+image::\s+([^\s]+)'), + re.compile(r'\.\.\s+figure::\s+([^\s]+)'), +] + + +def _extract_local_assets(text): + """Return relative asset paths referenced in the provided README text.""" + + asset_paths = set() + for match in _MARKDOWN_IMAGE_PATTERN.findall(text): + asset_paths.add(match) + for pattern in _RST_IMAGE_PATTERNS: + asset_paths.update(pattern.findall(text)) + + filtered_assets = set() + for raw_path in asset_paths: + candidate = raw_path.strip() + if not candidate: + continue + # Remove optional titles ("path "optional title"") and URL fragments + candidate = candidate.split()[0] + candidate = candidate.split('#', maxsplit=1)[0] + candidate = candidate.split('?', maxsplit=1)[0] + + if candidate.startswith(('http://', 'https://', 'data:')): + continue + if candidate.startswith('#'): + continue + + filtered_assets.add(candidate) + + return sorted(filtered_assets) + + +def _copy_module_assets(readme_path, destination_dir): + """Copy image assets referenced by ``readme_path`` into ``destination_dir``.""" + + module_dir = readme_path.parent.resolve() + readme_text = readme_path.read_text(encoding='utf-8') + asset_paths = _extract_local_assets(readme_text) + for asset in asset_paths: + relative_posix_path = pathlib.PurePosixPath(asset) + if relative_posix_path.is_absolute(): + LOGGER.warning( + "Skipping absolute image path %s referenced in %s", asset, readme_path + ) + continue + + normalized_relative_path = pathlib.Path(*relative_posix_path.parts) + source_path = (module_dir / normalized_relative_path).resolve(strict=False) + + try: + source_path.relative_to(module_dir) + except ValueError: + LOGGER.warning( + "Skipping image outside module directory: %s referenced in %s", + asset, + readme_path, + ) + continue + + if not source_path.exists(): + LOGGER.warning( + "Referenced image %s in %s was not found", asset, readme_path + ) + continue + + destination_path = destination_dir / normalized_relative_path + destination_path.parent.mkdir(parents=True, exist_ok=True) + shutil.copy2(source_path, destination_path) + + +def _extract_readme_title(readme_path: pathlib.Path) -> str: + """Return the first Markdown heading in ``readme_path``. + + Defaults to the parent directory name if no heading can be found. + """ + + try: + for line in readme_path.read_text(encoding='utf-8').splitlines(): + stripped = line.strip() + if stripped.startswith('#'): + title = stripped.lstrip('#').strip() + if title: + return title + except OSError as exc: # pragma: no cover - filesystem error propagation + LOGGER.warning("unable to read %s: %s", readme_path, exc) + + return readme_path.parent.name + + +def _slugify(text: str) -> str: + """Convert ``text`` to a lowercase filename-safe slug.""" + + normalized = unicodedata.normalize('NFKD', text) + without_diacritics = ''.join(ch for ch in normalized if not unicodedata.combining(ch)) + slug = re.sub(r'[^a-z0-9]+', '-', without_diacritics.casefold()).strip('-') + return slug or 'module' + + def _copy_module_readmes(app): - """Populate ``module_readmes`` with module README files.""" + """Populate ``module_readmes`` with module README files and assets.""" docs_root = pathlib.Path(__file__).parent.resolve() modules_root = docs_root.parent / 'modules' @@ -65,11 +169,21 @@ def _copy_module_readmes(app): shutil.rmtree(destination_root) destination_root.mkdir(parents=True, exist_ok=True) - readme_paths = sorted(modules_root.glob('*/README.md')) - for readme_path in readme_paths: + readme_info = [] + for readme_path in modules_root.glob('*/README.md'): + title = _extract_readme_title(readme_path) + readme_info.append((title, readme_path)) + + readme_info.sort(key=lambda item: item[0].casefold()) + + for title, readme_path in readme_info: module_name = readme_path.parent.name - destination_path = destination_root / f"{module_name}.md" + slug = _slugify(title) + module_destination = destination_root / f'{slug}--{module_name}' + module_destination.mkdir(parents=True, exist_ok=True) + destination_path = module_destination / "README.md" shutil.copy2(readme_path, destination_path) + _copy_module_assets(readme_path, module_destination) def setup(app): diff --git a/autodocs/modules.rst b/autodocs/modules.rst index b267a6da9..a8395ea34 100644 --- a/autodocs/modules.rst +++ b/autodocs/modules.rst @@ -6,4 +6,4 @@ The module READMEs are collected automatically during the Sphinx build. :maxdepth: 1 :glob: - module_readmes/* + module_readmes/*/README diff --git a/docs/reference/documentation.md b/docs/reference/documentation.md index fd3188f15..52ce073de 100644 --- a/docs/reference/documentation.md +++ b/docs/reference/documentation.md @@ -8,36 +8,26 @@ On pushes or pull requests to the main branch, the documentation is auto-built a ## Including documentation -Since documentation is built from the code, you need to follow specific steps to include new documentation elements. +Since documentation is built from the code, every contribution should clearly state what component the documentation describes (for example a module, CLI tool, or feature page). After creating the content, make sure a reference to the source file lives in the appropriate subsection so that Sphinx can locate and render it. ### Markdown file -To include a new markdown file in the documentation, follow these steps: +To document a new page that lives in a standalone markdown file, follow these steps: -1. Place your markdown file in the `docs/` directory. Any images should be placed in `docs/_images`. -2. Add a reference to your markdown file in `autodocs/other_docs.rst`. The reference will look like this: +1. Place the markdown file under the appropriate section in `docs/`. For example, how-to guides live in `docs/how-to/` and reference material belongs in `docs/reference/`. Any images should be placed in `docs/_images`. +2. In the pull request description (and any related communication), specify which page the new file documents so reviewers understand the context. +3. Update the corresponding section index (`autodocs/how-to.rst`, `autodocs/reference.rst`, etc.) to include your new page in its `.. toctree::`. Each index file keeps its toctree organized by the section's subsections, so add a line that points to the new markdown file (for example, `docs/how-to/new_guide.md`). -```rst -.. include:: ../docs/your_markdown_file.md - :parser: myst_parser.sphinx_ -``` - -Replace `your_markdown_file.md` with the name of your markdown file. +This ensures the page is discoverable from the rendered documentation and is built automatically by Sphinx. ### Module -To include a newly added module in the documentation, follow these steps: - -1. Add a reference to the module in `autodocs/modules.rst`. The reference will look like this: - -```rst -.. autosummary:: - :recursive: - :toctree: generated/your_module_name +To document a Python package or module, follow these steps: - your_module_name -``` +1. In your change description, call out the module the documentation targets so the reviewer can verify coverage. +2. Add or update the module's `README.md` under `modules//README.md`. During the Sphinx build, `autodocs/conf.py` copies each module README into `autodocs/module_readmes/`, and `autodocs/modules.rst` automatically includes every file in that directory via a globbed toctree. +3. Make sure the module's docstrings are accurate. The `autodoc2_packages` setting in `autodocs/conf.py` lists the packages whose code documentation is generated automatically, so keeping docstrings up to date ensures the API reference stays correct. -Replace `your_module_name` with the name of your module. +Because the READMEs are gathered automatically, you only need to make sure the README exists alongside the module. The build will pick it up and render it in the Modules section. By following these steps, you can ensure that your new markdown files and modules are properly integrated into the documentation, which will then be automatically built and made available on Readthedocs. diff --git a/learning_observer/VERSION b/learning_observer/VERSION index 0fbb6683f..696424a49 100644 --- a/learning_observer/VERSION +++ b/learning_observer/VERSION @@ -1 +1 @@ -0.1.0+2025.09.23T15.55.02.340Z.fb5631b3.master +0.1.0+2025.09.30T15.53.18.556Z.1c72d9e1.master diff --git a/learning_observer/learning_observer/integrations/google.py b/learning_observer/learning_observer/integrations/google.py index de7f85c9a..95e09ff82 100644 --- a/learning_observer/learning_observer/integrations/google.py +++ b/learning_observer/learning_observer/integrations/google.py @@ -207,9 +207,9 @@ def extract_text_from_google_doc_json( length = j['body']['content'][-1]['endIndex'] elements = [a.get('paragraph', {}).get('elements', []) for a in j['body']['content']] flat = sum(elements, []) - text_chunks = [f['textRun']['content'] for f in flat] + text_chunks = [f.get('textRun', {}).get('content', '') for f in flat] if align: - lengths = [f['endIndex'] - f['startIndex'] for f in flat] + lengths = [f.get('endIndex', 0) - f.get('startIndex', 0) for f in flat] text_chunks = [_force_text_length(chunk, length) for chunk, length in zip(text_chunks, lengths)] text = ''.join(text_chunks) diff --git a/modules/wo_bulk_essay_analysis/README.md b/modules/wo_bulk_essay_analysis/README.md new file mode 100644 index 000000000..867001cdc --- /dev/null +++ b/modules/wo_bulk_essay_analysis/README.md @@ -0,0 +1,71 @@ +# Writing Observer - Classroom AI Feedback Assistant + +**Last updated:** September 30th, 2025 + +![Bulk Essay Analysis dashboard overview](_images/bulk-essay-analysis-overview.png) + +The Classroom AI Feedback Assistant dashboard helps teachers generate AI-driven feedback for an entire class of student essays in a single workspace. It combines Writing Observer's learning data with configurable prompts so you can rapidly review submissions, capture high-level trends, and craft targeted responses for each student. + +> **Who is this for?** Educators who need fast, consistent formative feedback across a large set of essays or narrative responses. + +## What you can do with this dashboard + +* **Queue AI feedback for the whole class.** Select a document source and instantly request AI-generated feedback for every student submission at once. +* **Customize prompts with placeholders.** Build prompts that reference student-specific context (e.g., `{student_text}` or custom placeholders you upload) so the AI response stays grounded in your classroom language. +* **Fine-tune the AI's voice.** Provide a system prompt that frames the tone, rubric, or learning goals you want the AI to follow. +* **Track prompt history.** Quickly re-run or iterate on past prompts without rebuilding them from scratch. +* **Monitor progress in real time.** A loading panel shows how many students have been processed so you know when the batch is complete. +* **Dive into individual students.** Expand any student tile to review their essay, the applied prompt, and the returned feedback side-by-side. +* **Adjust the layout to match your workflow.** Change tile height, students per row, and visibility of profile headers for small-group reviews or projector-friendly displays. + +## Getting started + +1. **Open the dashboard.** From the main dashboard, choose **Classroom AI Feedback Assistant** in your course navigation. The URL ends with `/wo_bulk_essay_analysis/dash/bulk-essay-analysis`. +2. **Connect to your class.** The dashboard automatically reads the course and assignment from the page URL. Use the profile sidebar to confirm you are logged in and connected. +3. **Pick a document source.** In **Settings β†’ Document Source**, choose where essays should come from (e.g., most recent document, document accessed at specific time, etc). Adjust any source-specific options if prompted. +4. **Draft your prompts.** + * **System prompt:** Sets expectations for the AI (tone, rubric, grading stance). + * **Query prompt:** Explain what you want the AI to produce. Use placeholders such as `{student_text}` to insert student work. Add your own placeholders via the **Add** button - paste text or upload `.txt`, `.md`, `.pdf`, or `.docx` files to reference rubrics or exemplars. +5. **Review placeholder tags.** Click a placeholder pill to insert it into your query. The tag manager prevents duplicate labels and shows warnings if required content is missing. +6. **Submit the batch.** Click **Submit**. The dashboard disables the button while processing and displays a progress bar with status updates. + +## Reading the results + +![Student tile and feedback panel](_images/bulk-essay-analysis-student-tile.png) + +Each student tile shows: + +* **Profile header** (optional) with avatar, name. +* **Process tags** summarizing analytics such as time on task or current activity status. +* **Student text panel** rendered with Writing Observer's rich text component. +* **Feedback card** that fills in once the AI response returns. Loading spinners indicate students still in progress. Errors surface in a dismissible alert with debug details (visible in development mode). + +Use the expand icon on any tile to open the **Individual Student** drawer for focused review, longer scrolling feedback, or to copy responses into LMS comments. + +## Managing prompts and history + +The **Prompt History** panel (right side) stores every submitted prompt for this browser session. Selecting an entry previews the exact text that was sent. + +## Advanced configuration + +Open the **Advanced** panel to: + +* **Change layout density.** Set students-per-row and tile height for flexible layouts (e.g., 1-up for conferencing, 3-up for scanning). +* **Hide/show student profiles.** Toggle headers off when projecting or sharing anonymized work samples. +* **Switch document sources on the fly.** Quickly pivot between document sources. +* **Select information overlays.** Enable additional metrics from the Classroom Text Highlighter module (e.g., time on task) to contextualize feedback. + +## Tips for effective use + +* Start with the provided sample prompts and iterate. Short, specific requests (3-5 bullet points) generate the most actionable feedback. +* Use the tag manager to maintain a consistent rubric voice. Uploading a rubric once lets you reuse it across prompts without copy/paste. +* Watch the progress bar before closing the tabβ€”students remaining in queue continue to update, and the bar helps you gauge completion. +* If no text is available for a student, the tile will note that explicitly so you can follow up with the student. + +## Troubleshooting + +* **Nothing happens when I click Submit.** Ensure the course URL includes `course_id` and `assignment_id`, and verify at least one placeholder (`{student_text}`) is present in your prompt. +* **The alert banner appears.** Hover to read the message. In development environments, open the error JSON to share with your technical support team. +* **PDF/DOCX uploads fail.** Confirm the file size is manageable and the content is mostly text (scanned images are not supported for extraction). + +Once configured, the Classroom AI Feedback Assistant becomes your hub for consistent, high-quality formative feedback at scale. diff --git a/modules/wo_bulk_essay_analysis/VERSION b/modules/wo_bulk_essay_analysis/VERSION index 4dd337e23..696424a49 100644 --- a/modules/wo_bulk_essay_analysis/VERSION +++ b/modules/wo_bulk_essay_analysis/VERSION @@ -1 +1 @@ -0.1.0+2025.04.17T11.52.09.536Z.37e99b72.berickson.202504.process.metrics +0.1.0+2025.09.30T15.53.18.556Z.1c72d9e1.master diff --git a/modules/wo_bulk_essay_analysis/_images/bulk-essay-analysis-overview.png b/modules/wo_bulk_essay_analysis/_images/bulk-essay-analysis-overview.png new file mode 100644 index 0000000000000000000000000000000000000000..b7408566551d36db75cbab6ca7001995386d391a GIT binary patch literal 157818 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 d&84(mTsmef_~9U|*oA{|7a>rquud literal 0 HcmV?d00001 diff --git a/modules/wo_bulk_essay_analysis/_images/bulk-essay-analysis-student-tile.png b/modules/wo_bulk_essay_analysis/_images/bulk-essay-analysis-student-tile.png new file mode 100644 index 0000000000000000000000000000000000000000..13c6efca47e74882cb98c639f135174c7262450a GIT binary patch literal 125229 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 **Who is this for?** Teachers who want to explore student writing through highlights, time-on-task metrics, and reusable annotation presets. + +## What you can do with this dashboard + +* **Display student writing at a glance.** See every student's document in a responsive grid, with quick navigation between document sources. +* **Highlight key language features.** Turn on NLP indicators (parts of speech, sentence structures, tone, etc.) and instantly spot trends across the class. +* **Track writing behaviors.** Add process metrics such as time on task or status badges so you know who is still working versus finished. +* **Save and reuse presets.** Build highlight collections (e.g., "Argumentative Evidence" or "Narrative Voice") and recall them with one click. +* **Surface a legend for students.** Share the color key so learners understand what each highlight represents during conferences or gallery walks. +* **Zoom into individual students.** Expand any tile for a focused view, ideal for projecting on screen or printing annotated drafts. + +## Getting started + +1. **Open the dashboard.** From the primary dashboard, select **Classroom Text Highlighter**. The dashboard loads at `/wo_classroom_text_highlighter/dash`. +2. **Confirm the connection.** The WebSocket indicator in the toolbar should be green. If not, check your login status via the profile sidebar. +3. **Pick a document source.** In **Settings β†’ Document Source**, choose where essays should come from (e.g., most recent document, document accessed at specific time, etc). Adjust any source-specific options if prompted. +4. **Select NLP options.** In the **Information Options** table, check the highlights and metrics you want to display. Group headers (e.g., *Text Information*, *Process Information*) expand to reveal individual features. +5. **Adjust the layout.** Use the view controls to set students per row, tile height, and whether student names appear. These settings make it easy to adapt the dashboard for stations, projector mode, or screen readers. + +## Working with highlights and presets + +![Highlight configuration panel](_images/classroom-text-highlighter-options.png) + +* **Legend button:** Shows the current highlight color key so you can share the meaning with students. +* **Presets panel:** Save combinations of options (e.g., "Sentence Structure") for future lessons. Name your preset, click **Add Preset**, and it stores the current configuration. Select a preset from the list to instantly apply it. +* **Deselect All preset:** Quickly clears every highlight and metric selection if you want to start fresh. +* **Custom colors:** Customize colors for each selected highlightable item. + +## Exploring student writing + +![Expanded student tile](_images/classroom-text-highlighter-student.png) + +* **Student tiles:** Each tile shows badges for selected metrics (time on task, status) followed by the highlighted text. Tiles resize automatically based on your layout settings. +* **Expand view:** Click the expand icon to open the **Individual Student** drawer. This view removes the grid so you can focus on one student, scroll without distractions, and discuss highlights during conferences. +* **Loading feedback:** A progress bar appears while new highlights are generated. The banner updates with counts so you know when all documents are ready. + +## Tips for classroom use + +* Begin mini-lessons by projecting the legend and a few anonymized tiles. Have students identify how the highlights connect to the day's learning target. +* Combine process metrics with highlights to spot students who spent little time on task but still produced strong structuresβ€”perfect for peer coaching pairs. +* Encourage student self-assessment by sharing individual tiles and asking learners to explain why certain phrases received highlights. + +## Troubleshooting + +* **No students appear.** Ensure the URL contains a `course_id` and that the selected document source has submissions. Try toggling the Options panel to refresh the selection. +* **Highlights don't change after I toggle options.** Wait for the loading banner to disappear; the dashboard batches NLP requests and updates tiles when processing finishes. +* **Colors are confusing.** Use the legend button to review the palette, or save a preset with fewer simultaneous highlights. +* **Error alert shows up.** Read the message for guidance. The dashboard records error details (visible to developers) that you can share with your support contact. + +With the Classroom Text Highlighter, you can transform raw student writing into an interactive, data-informed experience that keeps learners engaged and reflective. diff --git a/modules/wo_classroom_text_highlighter/VERSION b/modules/wo_classroom_text_highlighter/VERSION index daf8882e5..696424a49 100644 --- a/modules/wo_classroom_text_highlighter/VERSION +++ b/modules/wo_classroom_text_highlighter/VERSION @@ -1 +1 @@ -0.1.0+2025.04.16T16.38.16.396Z.ab096d59.berickson.202504.process.metrics +0.1.0+2025.09.30T15.53.18.556Z.1c72d9e1.master diff --git a/modules/wo_classroom_text_highlighter/_images/classroom-text-highlighter-options.png b/modules/wo_classroom_text_highlighter/_images/classroom-text-highlighter-options.png new file mode 100644 index 0000000000000000000000000000000000000000..0cce1f8cc3b5c7eb58cac61260ce2b89c7183e91 GIT binary patch literal 51037 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

5-|frn2p>cjEqvh3|^{)AFA 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 literal 0 HcmV?d00001 diff --git a/modules/wo_classroom_text_highlighter/_images/classroom-text-highlighter-overview.png b/modules/wo_classroom_text_highlighter/_images/classroom-text-highlighter-overview.png new file mode 100644 index 0000000000000000000000000000000000000000..1a9380e3cca8171aee6cf22799c4456ec976ed5d GIT binary patch literal 153503 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 z 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 Xm1XQa&12)AGrm8H$%vK+>w5hU6|Z-g literal 0 HcmV?d00001 diff --git a/modules/wo_classroom_text_highlighter/_images/classroom-text-highlighter-student.png b/modules/wo_classroom_text_highlighter/_images/classroom-text-highlighter-student.png new file mode 100644 index 0000000000000000000000000000000000000000..b58d22cc9e2aea5e20058d43828f9dc871a0b523 GIT binary patch literal 72734 zcmce-Wl)@55H1KIxCRXlL4&)yy99T4XK;6y;O-XO-GjTkyX)Y(lkeWUb@$iqR&6bR zrkLq--ag%Zx}ScgCtN{J91#u&4h#$oQBp$W7Z}(VVlXi92^c8QJHzhfd!U!E03k_b z7|@>=jByy~e{3gFbtfe|QzusgM-wnJTRR&QT7Z$GiHR-1+|KC=qMHvSMEg%j*wMtm z$->T-NZG>11Wei4gov4eNXf*Wh>3xTm57m%i-m)Wg_TH9mPk}YIhFE985kH5n52lH zvRmf)hNqW|*+RN$M2C_;~E_%(#=ck5t%JnRrRk-COT&U2m1cO!w@g zOyD@@7EpWA;&)DtoiGe>5Xvr^{;Z$gYcenoCYEF_|zKi+o&d-n)dYknRp>s0x{L7(ce#y4o1*|6RQ3>R+XT zIlCyEt7Y%X1-&&kU)g%ELqr_Za>!EVRh#zo9Lvl|j%z;h`?1Sdj_^X|=Q2j^VSLS{ zQ4{IWm)43;qL7cBdYGjpxm(LqHa_+G92)8xD@4K|q#xFa^imh3zDf)^%YBEH4sVb8 zsHGF;q8K1UiI>3$RVmrxAnq8jc{d)EZ&grHrws@HTT#}I~`7b zmrFS$TdY5fM|tIh)xb1I8=WTZyorafKbXWf^FEiGmC3#03Hug@st!+lx)_Zelv^~W z6scakC9uoMYoHhi`)7dY>y-bvH`W)|;yepiLSYikMZ;Zo2G$h($RTD{@{JpRsOlcN zC(lNaEuqj~#(z^YA{nWp`zX%AjaTFJC}}d}1d(8Csh_VowvC9be}5%z6y)_xr6sH) z6@+{z;?Mu#rMQ0Dt}GOWyMl-l1QtQD(?@}-D?VZc0ex$&k5A?k$V5?jxUmaX=x$d` zn=zE&KiU2`KPOr7`DBD@8%y5OI;R2(E$t)Qbs`aMBbvgCHDMeuoWJC9CL)mFFn+UK zVc)804Eh(SvQdP>$}b}VQGg%82%h92G%P`#x7cI(&V>Uu#_tasq@?&Hv_oGmX>ZQZ zrQD2JNb-el9P_M5kKFLimV9~---m`r*$Ub$W?o%LD;_&}INTU^Xtimvo_HJM=|50| z3@1#6GUH@6QsLd0b~~rs8q55fI6YI}`_UK80%rkSdNkNEgew&pOi@dZ8Unbz0rXh6 z`c_AJYVs7dWaJ^sE$@X{;{(=^m1fXFLY{N{mJG;HDl@VAa=VojgSV1g+uRXTd|6T{ zaFN=EM73Jb^d9lfIe}bvbRV=6`IdqwSndA%<<)WWwU)c~-^MzcjfYF{1dK3D;B1_m%Z5s&1`mSu(Ai?2MIi}{v|hb{Zf@hD&v{bTEL}X1;|@xF>f3& z5XT&vo%ln%dS<^}5ZrJvmU{n!gOo$vDKar~cwH5Zx7iq{Qd{sHOW9eJk)qOIF!Bn; zw@pU#s7`XFGcIuh_t|XQ{%}04C-8!bA7`-7D)nW_<(q~Dj zul%e(dH`PT|MW`Leyc#B1^*Tpc8(U5Gbo}tOTcEgDXb~Trj^{ORPf6Z0-BN33DNGN z`FFh;$=V#r_nE2yzv0ZuWctVDs}65y#=?|H2!+blb9IL7(zAMc??Pl8iFxn5uWnSS zKATf96IznPO~30t@PAo0kS&;TdS?caii73flU0~e>TtRCY$*xyW{Hh<*=clh<0omm zblm?4;a0t|%vr^8Dj(>UMtNaf+1DrjxJY|bVh*Gr;m&nsM4fre?7594D4Nckd}+q4 zBg373G+m9_`D%_oG%XwIDmny=(2!W;NT{tI&_SZ%6~<=z!`^#w_d7%E?_{(!XJX{@ zG|ncMvb!Z``+g=AS z^eu&#M+CB#IsEU##1}`TE;j3fJAmI!KICW>HMdFr54|0>nnHs#yL;eNmJiJx_Z|oq zstQ7bOh=^(cLQcHuJlwVyl6sUIHmWD#8TUo$(>q!J>MDkbvM&l{N-aHhK!yV=+d`$ z$cn)<0V_9`>toe!f zlyP4hQC{H6NAyTcV}^UKKI9AGXn%^uk)7`@X8yQ%PaQD@4!)~2psU1o9hr+k4cyqq zLyk*&F##R;(qLbDVDh2Ff%-W_UwEjoDe@nSA~&(dxlkk2+6N;2rZ@)^37LkxIwU}q(7!d zXz{@{_4*fr#nHp{V!-!+l&CVN;*_o5!5#c0`Qz4kJ5eoDv88({aSC7>8EW+hwPDga zvYI(Wf4u}`<%gzWW-D@S_Qchzr!~1Bgu>4JIKy2jx`*2W_{O~eP{=RIT_=ij%{MR7 z@L{gLiDXxD6@^x7&W*4A%4A^<%oNFXJ7CkHOt!0(Z(udQzvqa^g)ds(_p7SfIB2(I zJep4On?e`QoM`~tb*gn|L|#@uv+A5lLNi6dE!l3)n&Tm-JCI38xLX#T z?JvKd@%`*laA(@TicD2y0{eNm7*FS$Jrr-oJ@3S9DIneRJDdksMe!^!nNFCTp&{#? z9x!VgKecH3fd2%{h)mT!d`9COaqwdDg^T60r_Ulx?K$@yh;yv^t9%;Dn(|qtD;q#k zbNvbw+H@aX5!DP-+X|;iW5=t8Lt{?({u)d|OS?m#h%7)?gu&_GD!+0T*2W*={7UV9 zYuT>Y!PROFW>|uVGtf{HfVbwDspC1PbE<+av@TMi|8sc2%Zr4We9FK(oWiaA1UHS> zo%}2|W8`OzU!zN1#R8ECEVhL2XG9=Gr5y@*40z`0Rck7pO;B(}pC`v52lb0+^pDx* zc%r_Il)M5cHOKgBAs`(BHl8v6iP_MN@6Cah%Dmog*oIN}t{Y+)c}IYM$w61jI|MAn z#iyze6mx#F8JU&U36mSkyVZ$gRpZ}{NH9I|YcsY4rLjE~^r=mr_G9?2tvkNzm}6Oi zGo~RscZtp2^_qu$6=XFMx zbC)RhTfF|9A5@-{smU?Oh!ooJzh!rOE8z45QBb}qN?K(>T~!IjWPlBiFgiqc6A8dL zG_oS^(%Vh$2@(<+;nq`37{9|ZAEM!=F(_x@f>8}7XvA>WCw~onQQ;)|nC4K0(N&&YiqR+RP^?Iss{G)MF z>t(Ugo?5c>)(KME)nNk+`?H?u-n=ATdnu+IDU!Kw+U}6fL+xeYezbuwz=ax;qf;GJ z$N0VF4g=h^N5VsO58DCPepPtOILJTmjF=Jflq9A>OomVjzokcK`bY^@`(hArC5NGg z$qY8M#qi~hE}Bc)@V+i$1K2bMqERk{U~N?f_!K)}kZyy$hp6Sx?&LPpol<>Olam)@ z(n)R^OpOaP12gBvX?44FlbRMS8ax_e{1;9+naeOz&Kgm64|AW8j3=2C$fu0GJ@D#> z3W2aE4}jlxEbyCIy@7ml#H(_z~cnciOY!#i%nr>6(Svn zZH+qn8v^zTH`*vZex_jQRAY=E4UU z&d=j?Y6aZKB2}rYCC4#9xE@;qHsMwE2G)zt&&TAiL1HKsl!AZVdmDptC!715F1_loq-SeZ`{$3S zUxF=BR#U|4VR7EbL8A7g5{v^t(F^MKv>hjvC+q&?SqwPY&U~yF2@a!8jZ6)I zh?SmWmra$S%Op}^9;9xKZ?k|6$B_{@XZlqUbJCUvG1IOg5xF+(3jotZ#oj2utZ|BP}*AqnPAGHpuZI_3mDuykcP%Vc~LiQI)d;&!6TaQZN4F0i#P4 z?BPLoQUP?)nBF(|RVzWQb#6sOk@E={=bWseK^#Lat)Wa86DvH}*vedBts#`IL8ym& zPkTZbYdUGGtG=3gT$rw~MOPFzG8q1K>7s_e+JGixvrL52Kkm~llTn4%&*!6TI{4hV z&gQ@?DRec7yh)TO==G(=E}HUnNd#ez+waKx9e8EdIzlces8*%}oY<-z#bF=rwzQT)010XCIqCp_^Ol|s-`2cgG#L&F>{WR3#MX5DaZCW3$YJEW+XIr$> z_lT0ZebTsVzyK|}M!TWIgJk0DXK-GINrQCF8 z3r%8cI4T^l{A(o|z^cP67`D3-%8%%nxj%Jb@DK^V?D%)2bj2(49$0ey(zV*Ea4K97 z?kFQE8}6Oc%x5oCy&%qzKdS+cVSgpCuOO~3%yy>d8P~j9HKv8iZG` z57(>pTTvo7n!!@5>szU%HI*}auQNHfZ*Y3PKi7GI&56M5?EQwHEw}4?l2oXo^@>c} zi_9yf?vUGI%EipLc}0p+`@7K!Tgh}#8I=@NC%k@;cwL+k>aqo66JFA@n!KX|xIp@5 zg3FJah$9k4?@=9K(%%iB6~vIoIW`jLlLWP}WlHbn&`&f>1>E2bXBGj!&&owM>9D{j zPwooJgYrAGuBPf`_zbJFO%yD+y_Mus{|S$$@fSjJqu7>^ft&C-isD7XuGJU>Y@E?H z$XCTFvTu4`JGkFv)e+5%-4I`>nUTp?a#A(zGv%{=QztNZ7#l5d-hK=*8TR?S25!!> zGu`KLe0dQ=aS45oFp%2lOWsgvV4%zn;{ERL^T_5nPs-=!4Y^sLQ|x=AY!2Y!UhJIe zFmZ5ZHov##3Xka_v3X@Gw$%&SMeFDFZbS1bA>7ntFe!<1+!sfEIPdm~^3G4=X&ZM^ zXXowh3{-Hr5G2cz&nAp@xU>JORbF`cnQYW7uislgfSg-i^bSpV0R=p5AjFfGVNIy9 z{_@Vo;cIA_r1@xid!{6b`-lW<%6v0MK22W?@lJ9@QZa{rOtVr*Ao;^U9xqfLq7Znw z>K`NvU)d>slCqFbvZJF_XJSQ{xj7sqY5Kv#^ToGLGHfgMa>iZeOiN3C8be4sgJm17 zH^9{_#X~)97k47F2u>z@Bt~=mVii^hEIpLzc_ZmNT_^XTjFdLQPX|U5Rcp!5aQU51 zK;HXbM^Tn1Q68z!*vI;!r%8EO!(m|^OP@MdE6GpnF^$E;+jb+z_@*1l*(S&G7g1T0 zAT5MyyNZ1_HNw#r&U%I}mK53(FwQWUkd86+MjH+#uYL3$m{&3tePCbD z?;lpe@Ax@V@Jj@FzYT=~_vliVY}iRW#MG>En^1PSW_iLaa(t&6;4rDXAT(+$^eau> zC!B_~^N|hvarLe@j^NYq@FQ}}Mp7~@uFtq5Aai_2xC`2H%PX-VD?z=}^y6&K>h+F@ z?#Zh-y8Pc9oxe+Z)y9)b5GT89b@Lk?O~PLjK5rm%-g#GxQo=2`Me4UdyAJE!(XeNm zA#^7$4wTY8QKu==M}Vqz79(>(Di67s&B;OXS|eCMv2VJ!77ZY>1wKN*zaiMG(E!$n z>%_8UM#Y?Hw3&&TFAo75U*|GUr?Ku6%yVp1neTN<&FgKH@&(#GpWglLP7oojDX3ai zmZB~{g(W$+0e3i4p8aMZzclK24V>QlgrGk zR2vo;6bWs6%O)favKI|}d-Kxr#sYDfK`87wR@G$x(V6U!|KIVP|Bsnc^Zik&qsgqj zw`ZU_4W>1lmQIS~Ked)hp%8I$XUxqPE3hXr*sHxZ-rnBe3L+x0=pA3~EO6LuWyX?e zE$07xv$eHt)Ybn-)UJQ8P^A_X72V(8S5Q_KIa{o>SZ}GFbEZZVVq#{tSZ(;}(b+W# zM-37U!%dhmFU-vav$L}+8KcHSbG%&V#9_0h1P}Yi{aW_xGi)Co4z>c0GU=)rU^sn0 zcxbhnD(VAZK@Zv;T*?=PpUmOEa?%JE3>z>MESQi!F-`Nbo=ATdT?g<)4Knv$(}G*lez^ukD{bBl5Q| zE@X9e2}&PQN`imVX)r<|7jzq z)40Mou*cQW*=dF6hi)}n2+POEw|{V8z?NZE8uTwTThgnfPz-C;8Pa6dUDj;W{->DD zY6CGnJ+g|56`AaR&*N1igJDdQVX>I~nkA=Jesb!>W!rjyX@U5UtgjMwCJ54QvG)fa zP0h7ApK(ccJD)A?9~}X9{swbN>iu(#bHQZb?|^_WJUl%106=-d-NyR*^obKXI{Lii z%Rf#2;5y?3saL9k3~N4bn3Put{@^QzB6BOj8o zL=s0s9dQEYTiNlC@=u;Ki7J{e*BG!u|Ir*??@Vu1`jpwDKQ$aE(Q0NJxC>`J8^)yaOhb`2zu@}Z`A;{aBaR4l4 z+}-*33LT1KuO@%#en(I9Ojqs{V98bffoFSl*-D)`l0iqWmhjO#efdz8yM0Y5HvW>C zD2=Tt;coY}*Yz`a*?Q<@rCo!spyl=0(PvhN1eCRZ+7M!Tdxs?wBFEhh0KcZ-1+4rC zJJI7v_U6sGwj#ZzES1v|qSNh=C=es7R~TVVQ&w~{#q3;Ms+RN*J-4*Di-zsRuYoz>467-vuB49klu(fK3GLw<*KB}Q zmz};6?%j$i%0GWDb-us6S8kf@S=$Mv=Dl>b$7#2dj^etVtqp{Uf2A0^1u z;VfKB`U`a|f-H1h*xg-S^_)bgYYD;Kx#o`wdlnta3j+N&V8M-= zUx4^^HbX$LmP`DLy7-Z-N%kY-LE|gk`^u7e*+r)8rKE!CEPYe zVAC(od0Fjg<9b#M1p_+r0^+>;#R(_7B=Rg!^awccgZ0T)ZhCN_l;cbrkoqyehdSoc zJHPm%CYc3EjEKVmGRc2gzKCz>8G3uK%5gVTS!NNgOB=%X?FJeo@W+u?w7MiILuzqE zo62*!y~*h7PUXb;T5FEXg3RgXM`*1(iZ_I&>JxrZTtIQ$yAJ0<$#lOCgeaAAjh-Wt z&*b<}bTc>={EJSZ8+Em%lP>6f}Idlyveq=J^pZ_5Kz z8@P2|cQH(!G(2x{UD5i%z)(b1=PLijGuHT*xe8+uLED|{ioK}H0#l$qb?(*QnqGY3 zcK{~cmZV&;Lu|6&Dry^?8pEOXGn%{#fXjk&oBs~7 zIF>-ft(rjO;l#n+E8<^D&MKW-VT{OEP*Ctw)$%|Ou3F>R{FLZBXHTx*jsB{ds}BQX zw7n;6q4t)qV}-Oui#;@7nevJoRc^-U!ivHWEPFOMQ`!&NlAD{m?xcUz@Rk2VZSb66 zsH}0$ZF}1r;dm!}aw6cw7;Z$2ez5C?J>PaIcOAQT&--H_@^b~Q5}Gkj{2t2pbnTI- zZ~&^Q`x6Qf>z1!BxOC4rcvlKyW+^Xq-7E~YLSof79~l2?20r(H4%7IJ;+TQ%)Vkle zB#rx}d)NNAkCV~9p|bW5m0w4K!z=Y>SzUrl(*T4kJ3OVEl#M%{4pRTnl$PYBXW&s* zSHWN@CU5G;2bU1rXQt3g^&{t1)ZN3wkoI7H9YDR~8hpzHiCc$y^EJcZ8PKg%4$< z@`Z>I<9wBJ)yCxF8{ZtJi=?bn4En#~Q)VN}f^O2jp`a>cH9)KT_0QJ}%BbG&Hm!;2$trDlLEc(tbFRQ80J%bibrGGB#GO zh~w_=p2_2mnk6R=Je;s<{U2CO^VG{lMuUKWpry4nE+Zpy#d=Vmp=CZRzl=((;9KOt zKX6G!O9Vnw{mz$V(8OR9>8!$v=pc+pA)hT$Fq6Ar>EP-rUNEEW@me)tm0^%vWY07 zgK%;4x+6RYvL|l;8|!HCpVvb9B?H<$oflL+g~Bq1RPa-&{%xr!^Z$B?A+cY7CHWE7MPR8O06vP|6If#OX&MHb8<9`|DQhpXEzZ1mH0oziG>>U z&jb0o5-9)IcH#dA0nvi;q?t*bcFx~!L0fWJ#lx1Jnk%|MoYqzct&Bw{_{(u`@7I=x zq9|X#8)-_8FTU!hO@3;^!iX)dP<;&7?p&)KDIQ%_tHOy%sN1XR+vqSf zDvgaz65^63{QF6w@LBP^$F#Dly@(NiiS<4Qsx|+1zCe!j&X^=@fK~&y+Q#gDp-TdJ zi%a!rgAp__LPtWKkz{2^rMn}BATY}1JKdE9GM23y2%Vp=H)?(PZ8DYcXJWg~7x9ww|B-+5i6~~6%AiY(s@{)gsjVFjH zCRZ*I9JVWwTQa#_3TBMuABCUd6^=Y4pz|F%@uB|nBNy0$dpQ}xy;W`sTP-l;D_AXK z?xA*3qOxEbSIU8y-yEhk)|Z-HTkrYxy2CeKAC@e! zsb`yA<)K7^fD67|#5)4JDkJr&bMLAPG~Xv|rjJ$auN2sWPn!s>RXLV{w`P|Yd9?&A zP{Y*+Z?-`i{Gc;aIWH6+b1AKhIxsQAXG}o(H4cxi0NmfwJe*E&1Rr*pxNZe`Re ztqHhjxonutr?<-v+(n~m6^sJ{65AxVIw7jb%o>iXIKaBj4e?G5IH47)o;5)$`h#3cSJ27-a+1)R&-I=1VeGIjjO-TCsb zqs12fVsx0N2jHx7;`w6UqU&YKB_>CrKoo#XfQRC5x3*?6^EYtImw%`!YwM;jkF>-D zkM-@F7@}{`#nwkkb(8JU!4bvIN-=A2Fx3eK^OIp8W)x>@1F${t&+An|Wh@q`)3;u} zMRd6Flqy=_B@LtaU`gDP%h|~?Aq3>r)HJz}nG5e!$^Hh~Nf3bB3<{hnPP6oM@z^2b zdBRnYzt*{23G@xx6Jz+ierMXMtuC>ExJdD5L%?Znpnr9b_fGSKhN1tO0r^oL=n}cJ z2)#YcJAHslIU4Bgy1axK1FeJnq~}S=BpiyI>(2kmg>5JJ0NvPp0rEzb^!-b}vg@N3 znA>6aHy#e6FE;h22aDkyj|3n_;&ZbD(N7Ni;$*FzLN=Z9&7LwG&7wQ;HKyB6i@L&=k>bPS@>~F z4F<9iG&12%=7@;Es|!Ln4opv9;LG0CTJj#nEJxe}e92_y&m=i`WaJQ0{+{PIFTR(h zwpB!d%o+Z#1f7BtUvY%SLUS+1VuIO*`3>!OEgYerIt`Ew!B**;p)qY`d*2}56!UWq z4C##D(dv?uC(mNP46j)s>#sxf%0f>B5PgbM@>s~%AN1+Fj9*$bf3?L|XkMcS-THL3 zYFc7bpu3!@>0E6mf7X{(G}QSt_Dx-nQk6nsZhoG%hQsY0k~g0RJg^8ch#qVsy7tEl z3fA`}$CC%7Gr7JH#AlG_ce~}cLGd;Na&uL0*7JdyC;L=*gWi+d{ilxR;o4$oVZlK= z6RNKa`A(>C%7>5V3LF>?yJi2|3bXm#s{UttZ3zFzQy|FDHJ)2OaPSMiPTnkz`&ew( zQ%wlHrQ|NJ)Y{IpU9Az`9KeMVdi!of7L{_AKOtK7?Cf8qA9C{;Ff>JkcB+57A*)M4 z|I!}!pU28N0=Pgog!!q)3hmHO*T80OAVshr7xZ=z#zO^of;sVj4H?>gv^zD;05zD} zSf{Ko8fZW}Fi;)uX`vu2Q4;W6eSM9;or4AQ2yubj_9cS~A&}ydswOCP>X^$ZMa$j| z6_)gR@vTi6OayzV#k2n@AozpGA0IRV?PGOKQ+ZoAAYulew`@-2$bH* z90I`_?r*;I44ttJ9hp+NuYGRij)TqbE~Xa8(2@9kyuVEU%;n|oTAKhZ4Sjw)@cU-zXQC52GDRivq%n8)f`hM!UYpCwcwV6~ z4qF2>RTc!-bc-ShC{+KU6)<1-2gwTGH~+HLl4RW$1&{F|Z%x zRn-k0RZpB2|5+%6*hC~YxL@b;)HaJ4e|-S^UJnyt$|R_Zn}ycEwOAy8{lSF)T@K%=B6!L zkGW3$UIW3M+|z2nrcw(WlzM3IJ0nF!L02XW6oL)rm%UXRucOiMoIXFI>DfO*L7#zK zJD3XO&Z&6M*HrRC^OZS#{lw3eL+gx{O$8xyY0s<_#Y&3C?*M|5wn%pX+6yq?e z*H%%y)QGxsc!$)qToa{%zDy?_I0M&3WSu@b z<;4O)G?9&G5Sx1QNbd{OETJWCZnV%z8QH4IykvizRO=?XKQUbcA&VB+1q+l}T6H>7 z)*YvA>=2#zfccXD4N8E3()J#i5@UJa&{)Fj%Y#GRw)?B!Oom{6)x!m7RMtVb9Oz$a zB#xLO&@alTn+?K59lID`7T2HrCH{$^gGBnu=ln~_F4h|}Ai~9;K$R7{LQ{x@o!!=I zF5R{_E34U$BjE;_!N3bu1K({ZFb5F!${`Svlpk> zftdmt^erV&f}_qxqOcx^#$yGy#G%fPGVW=hAF+S1syVz~7!y9>HpQT@d|mqW`)|aJ zB0_2xwvjA3fr`q*SwLE+^x(AX@Q;V=%B#A0zWb#KnP{AzphgQdm%7Gu&K3`@zDvZu zYHuQ+i-Oed>bJEbt4N1e9;myR-J*#!=j70qa86EF&T_zM^;+7e6S{#bdQ^<_oB($F$(XtbaC#g1{b% z`;PkXz2}L8L}SeywA1*ESre+UV$1}tP~KKUQSexUF`%gk#PhSI^asX_j&ZH8AP^$6 z|3;8J`0(B@xg~$t!1Y0eprI%zW}C(gjvHAkbAVq!+??_G889z7Hn?^8r;nUc2*G^U z?PMgd-2Fk|cqa`Ngs8tfW_4jC6_H{#P=EIvcXy%NrVV06>vuKYoX~qgvE!=7fy~<+ zbj7qFg=18I!Y;`jJXL@5c>G7yfSXAi)hpr{bNr|^4FP1=;vWf&|V z5dRDzZ8D2=9&7f1$q;u|gxN`AUJxYoE|&h*ay^BPKTJTIH9ekr?jv5^e6Iil(U9P~C* zy-7f6lcT-5DCou8S+s$pW(KIJv>`Q%svP67Ma~Mp^ggwOh-yZEo(9r^53Q<0=Yd*Q zl|_n_v*(q5)b165FCOBI)}uVCSM8I7pdmLdE7(%HoSMA#EnR3;5@>ci)03^uB0y;H z`W0lZqtiW2ag{{z-0(;Tw)xKMR5G*gu1w;B^!JS}nZ5FKvGMT{8aYIRJ9zk$bKCvl zSk4B=rDZI$L9}`oZmE3y+aeb_(SfGans7&V5A$b-&Q_cxWJsg$`sO9I7qX#3CU z?1o@DTsDu?d#;8us%~K5QG#TE1KGbR%troFutCqF9OFi85TNSQ7Kk(#%?@y*;R6 zJtYa+={f3thfqaH0)GSIW40oDqJ{B3-3LaP;(3vHSr4JM*8)(S&xhAPUB<-mu-oxe z!u{g>Z6R=UXslJi>BZ~Hqh!Q90~rLgZ{V+-SMJ(`(QfNg<4iyqJFOJ+KqQpp$M?&Y z^MI7IG5cr`^{N!Py{m9&7+s(AEH4J4b9H%<;bmpO5A`OFFE-Ym+n^0x$Hh^qqKP*T zGWoulI`t}w@bf~6I?tE0C(m6}rQI=uS&}mX^o(x^>}hk6!9Ad!|I~-C+Er)a_m8=lPmb{6M#`@Rga13oHxw zdo#V^d|{(;15B=-vV7vWw^yeNW-RW`m@h(OiCz;<+wB^F@zZfVcVoJ>1N9*K3+1Li zE4A68si~Se*4TV`F*#E_>XXMADuFy}onz7-2ShL?erI;-2(t$o^kTK){a-AL-NtNo z=oRWxP<<1VjUemW$k0_JIWY!PIf|C+E#lH7q5bP-u=u^LNg+2p#>Gb3&a1qKVYPJ# za3@Y;8AJe?6qOS{qQK1fmKl!oP5+aI6FY(Kch$wcU+XPU8sliskuF{F(kl!r#4XQw zpx*FV)3n2~=W}ni@_1plxM&Gb??%4t_vQHVTDbJmd7wAxW&am1dr^(k?srT=1;(%9 zOg#O#IzCLeMvJI>?1?+P_w3@$hCV)B6Dz)wl9ElDN~xBw$20mFSAAnP;Z>0omThLgmNc$_FA5Km+m3sx$HvCw+n}adF zj&B$HuSJW^E7nbK%4_1ja81vAPcgi*W+of$)0iu*p*C+R4~|)Hj&+q|S&z$upWJ;e zKQlUno0bR>n(k9OMqG-A-MNzNq|@rKux-|K+lCH3ry`qd%&gfmDyJWSCGx9Wo7nom z3--B(i>rsJm=5;-#o8f`wWDt3Eb$$Bz12Y{_$JX#z_8+_(3-z%$TF7;uG#Bbijdak zW^2>+M(vzA{g!kb+m@~TDxYkZb{t%S+G>akY*X~x z-R+|kSN(~oXB#1zo~G_oWM`C4_RF}Uc-FjN$?MXG=NZ%G*w`7yQrt|=if^F%NYf($ zV$<_Oj>X!8)(yMtgV8GEESqPux98Z$Vj0l0n&q?3MDBf@txmee=2+b1AvW@W<~~X4 zEWh4E#hm-O=)DC=C;R;FY1!v>knJEFOzTNHhD%Gk%2T(&W{d_Hjk7pYXJP$z#>=+l zX}a3QYxMciTrHaX)c(ai)ViAgyZ+hjd`eS-Xp9s2W6d+jCuhIBe14ACc)u7Uzdrxi zlEg8YqF2d`rB*ZNuJa12G)no_6dS|Mdwnq3UNz5|G0}J?wK;mDh%Q zjBXBQgp-EBQcqRc!9a-_Wk&mF5icGM$$%+{p{2xiqF}$QX+Tz9{}IK>EP>O0);f0g zv1qo;jN<|>p7XaU)WUGDa2h54-{aNdQ=UXNgLT}B9g%gqp~OR)7=q!T9)`9iMFm@U za)p$>CF#RDC(na9g(*^Ud!hScJ!6=V)6H*(X1DLiX_Q$7VVBzLT)6`j!-N}G7I^Oq z?r09BdkTWHm0toaj%>-9=l0db^HdaI_@Zw)0943$LsQE?GKZj^bhvz=KEd8FK*QI zcQusf!dNc;`?}r&i!)I-C8j%B4jHkiJbq;J$ynRFAz*IH6XUtPjQpTNPRGd$B4?OU z8mFr~VJb5lWfFP2=eNDIe=usgV^3?yW-=~aj&YTH>+Fo4dY5|G zP7u3K5l6K#gdiidln#|?GjA}G{ftIOXID_cP*9voXV3tIVH1kUtRz81{V@scYky^Y zv5QnlsotGq2NnDISX~J5+rNo%PPA)x=PdHMLfpQW;M?ozyoJ3P;%_sD5gJy!fh!HFxq_+*Q=RHaGPLo@cyYYk0N{f^)zaTNl@?}~(5Dy;X zBApxLS}ab`38D;u#9jOr#b*Owk;?oL2=RKF*qeafWodGYUy;%f?#@o^y4_Uo*QzI< zmn{#EL&(PU>XJJLjFQj=?#mAlop0KHJ!q*ZTuCnhL0~Y0^UDqnqL4kcI|>GR781T@ zTNgtt1y>5+lbhSjU<BLsW zMa-B}v689MG8m3S$6|7!t7(;HzHOF|Zk38DxKw#fjB04;RBY1?S`H>cN3L z_lg_X66j`RcCTpWm-1oC>8=gyG#NE{*%`_q{8j7HG_dGDYxa`x>y%yKjXXg z%0FJ<4PvHUyxnMiJyvaqWe%~wM4aC}eJ-j4I*i=VT8?V!zN~37z2&>@JX5ye5Rurz zR##SDm=U$O=={t$!&rFzb-(UFSCg6v#m$~zRmWXiq1tQkV0y_6m*o*MQ|=o6$>pi$ zQKz&T!hB)}bmIb>EHrT(ED9ypU??umYl1my)lm9QfGt;j-L>vrB3|U`B85iRXg(ig zDRm%%)PT#Er8X!sQ|RwQT0Bp)8B z#0qyw3qun!WP6S>q0?amW07&7oZ=CRGIN>0^En2CS)6^qQQ^K=?WzZdov}UMxAm5i z3!P36&$1*_Wof{$IA?d|p*}+Xk-=*{D)VU|3Y9BcEt_!UeaGPLY%O8(1vM{O4W{#L z`*?AJZ1iAT2*1%ReI%Slt=*L6c%4;2|3`IhM7eH8Ho|FDnPH^G|C$9j=pR(^!by=V zugPv;egfE~`bXKR-pn-7Xa)z*sX#CAs;N5jv6&RcD;AnaAfLs_O&*H=C`D5 zQ>;8Toln|+W!l9R@Rm27c4lCH>ieSa5jTC<1IL3L4p*&Ve+u9Dh8;l-F}d8kg}k4p zib8A`A6@cC7wWC>sVKoQp6x1XL2ny@-IrW=y_XBM&-l~vk;w=4uE8Hz^IXwt1T#^h z=qQqJksqmeVa*KtOZ2!z<;_v{iD! z8YyXJ)u?HgoWGnB6i0T)@$QG?+}v3$^hv`GgyC-PS{|zh$C4EauGbe}&DLzAz!R+6%dWwz#1IVWUi#T(O>=?<DXF9LXn7%%IMg%g0#Go|u5ZG*N zAtWMisJ~Q(9s<0gHxu^9-(u+`@52_@m(@!(D>OMQZ-@~qyLw452sz`Er3mgsIO^Z(%P9pfYE z{(jv!6Hjc+#I|ia6Wg|J+cqb*ZBLvIC${a=_y68|KhI~MeLm;BJ}?9l9jO`i_OAQ2^JO;%3QMOIb2UI8K=}x=rMrW{!Y{mo#Oqa zf!?%Ye-a9>?6pRpyq^M-+@&-nElo?~^WzWaiKHeGZV9ecuCKxRouiFF)7 zbvG#sa)LQ*Bp@qARyZC7FPFzk%4p}WG7&6);h|_Dm&S-kNG|J~CP|3M^|?eT2D)-c zX35>4HZ_**{Rd<8=sYT@Oi7Hg*X_47qwEVkK)6j?*yxWDx;#bA!d4pgL-cXII610= z#ZT{Czn?iFuh1q>C>@wek~A_!Bu?mY>I>;*Ew{D~eCTW*tG=Hu`_wzZ`Pam*%6!n? zjQ~vrsQ_#NAyk|&+y3t-Cb-K_@^!sDwd@iScS2n2X`I`s?5~53!86JIuF?deN3a^e zvIL$2yeT-WJ@(HgX%YL5bSkPBTh@a36b*CCCXAqo@${p!ZLe?O9{0kk6*vjM^Q%JO zM@~KDb7+I&L|s z@jajJ?Q4NY=d1UFkDGa%SMyvuSM3=Y?`kyrlJ7;ATD6g!I;;DTDU!~AUN6Q}6@Jz# zML6xM#B8_{I>FVnVb7d%)Pc{XW;=B{bFAK0qe61#9i2@S(JQh+LuY@ngd|{NwnxPQ z_j$H^%X(@}TvUViudKTxMkiA!U+!D}B%ym;>b`8oqV4%y1$#dk?f9mcvr)2>u~xEI zE$^)d0x2YhwLe;N-{ZEm1Hrb~^u`0Asvv$Lto3@{Arra(Dk^T!n4Tp9<5mdY_n(>X#Wt6wM@u)3--@C~KNLf03|0==9OKdfH;(mAq*H3L z_U05^S3hF!5gA?E&w|^SYb641XqZzPYRZ#~($X3gu0H;rZ}Ept_f7@+%@!G(agj=t zH_?edRWdU3fB(D$s!CsO<@_vcY$zxww{-`|-kT@Zg~ta6=NXCp32LZ+HqPD{3-Xp=zB3U*1FIVBUP;VQ{``k-5ya^BUmm>L#rKk?GvK-6(5Wn#+w7lbg(Ry`Yt;L-*bDSjjbX?UdIw2w`W}~7 z_G!)nP{lcH;7oj!_Mf>yP<46)t{Li7l@!cN_ZVqspQW9>)yM0NN?2s%4V!;f#|BLBW7m?-;uz<;)*2HFe#vyte#%(va}OSN$HM;%n! zfpp62EsnXL|Ge+7bfh{Aa&&Z2RLvp%@#RxMId`5w$2r}8rAd`^xS5N?LftlQ=fXQB0zKiLykZy!^V#uFc2f{+OR3ltvHl?!NZrTboxXup=3BBeqr^Y&UZ-)-;Vg z&VZgNp7)D%+YD7{IDBE>n_mKD=uyLwEE~b|fB=&tZg0;UMU@IMf+y%<8A7rfPPcQN zjRb7Hqrad=mGmaF1!T?@^hq0ER=Jz4C*~G~50Ap%2YwiJZb>xq+IvIY-&9SC+?Ube zvSqPcCT-hfg&dx-*&;nbF6kQoc9bDOXFjh;y+z-f(bKHcg}PcinyXcP zQ2s0Pxf91suF&8mY|Emov@GGMgd^-Tt$a-?yd%3<%5=&u+eGx?;OpaxKsy7owhYYv znOpKX5{(um{P&B$tQaKR4C+$>84PUpP*LCSAr%eFSxelk(n|Gb9Pnow^Jl9!v)huhkz|uT z#GZq~{xh1ss;(4Qt@pVsJ#4MLoYb4}yl5TE{y?ifp*DTKnJPBlg>~-2BuXN!*x*&}C3`Q-+pr!>H=?XpOAADbHAC=WFrloyJb3 z>!YV4YJ4LQsr$)ue>jEa5cAJ4*nQuarRpe33c5c1vu1eKN3~wGZkp3}?%i&yaBY@IAJhi2!+kE(i$&5NQD|at47_BjbsX?Mjz17`Y{!VS%2(6otfXJl z(b!TEQ9Ic**9umy zd;aD6byvD9z$?~rO6$XT7j3tl?WtNVC;~f5WYbF!{uw!ow9NLy z4V^ojjL`QcEM2D_|1iLTd_d_qhCm5Y(Qg-!H0w1fxkUUu5y|;kkZY_=B?ZYK8aDxF zjd}Z`sLF^xqS8{kIB!W;Oyq>#+8yaZ+uzLp$j&{_45$8O2%;2~5%_NCd#j8d(5oR@ zhdGA9Ogd~@+=iCgLljV*uu$@*?jt6IX=XSK3$u2{p{hC6dlZ85Xc_30m!2lbOdjK5 zfzkq3a!xsZC_W(&YmBA%b3s!>YEF_`aa?kU${|H-YSys{>sINHB$1!O*l82bZ#>I^ zqpWXFY39{@f=lKm)I2vI?8NdRGNveh3vLWh@WB^W`O6+8z%LeZ566qk(u2WYcF8wZ zl_OZ3__&J7DnB~Yix&!Yo+*+(4GUh_36=5=LRM;puOD$_W5;=L3I_HTkDR^dhnvWA zJY4eU|?Z><-^)f>*{(QX8z2e3h;igk&<$2bP>aX zRz1n_(4#jSjdOF-j5Hcf{Uy_!+*rmCM3}!nc8FER`!G5;a#F>zoT904YNVcyHKx*@ zurLbJ+hdHv)Y?5DF(Vt zJh^+zEoS|Nlnj1jH15S~lxem$1;5&cLFwyHIBvh2mL2;qeWkJdsVkvhESQ1Kx(*1^IQt#dtvp$7Vg_2^?tAW-# z_}*eh$a|sS)3eTXu1(|;yTR8mEF&3Vl^qAJrOawJo7~{yLEIAbrjB~*(^@^LYB`&t zTu-0l*PSrvXG)syp#1g3UpL3JdQ@2M8pFx^Jt?|{lqYydI6!Q43=d%rX@U`wkC`M?DgciI$ZK03J5R9Di1$l(Dh5gg=vN|}zVqAfbI zlFT{4b%xM4hzf~#eS6HTVvDj;o6z6nrdEtnFTgCHKMt4tNvDI+n2AjMop;Sy?Oft+ zZ$smgZrPEV`6t-5g1?!_H$h}xcAk*dT{t@Yx<8+o;9Is3+)@UX-d&oU@lc=b%*+G)27)@ZaevxSjyCFt$7^5V{s`(hzlC5@;%ovfV>#MAU})GE27FgN z4}ay2S#@1>eu6l=x+Rf^Kao=q#f$FO0!>(9Qs1(=aL#mGU3i}Xel7A@Azq10wWrKS z1tvZ1cvgd_A6>M{LPGY8hZFwtGnQxRbLVTG)tO;F!jsNb^@6jSR(55vfeA&wuD%tM z(`N_aj@5m+y|8%CLoyzfxUVa^xNnkN8?|c@K;NXF?aZFF(~QIfYy4#JX|jGTxw<{{ z)Ro_my`3WLmx) zv%&Mk_VK89cz_2LCv!*fqG zQwYa%oxw~D(dFhgwMIYK(HoCqJXIVt@I03GopZUeDAXI%@k2sXNcI`r!ab&{Uv7Ue z`>r#_vYk>@)>jOuqg4<%3B+tDO~1sMIN!;LJVYn!QVl#%?U&-5hH57zr{m-coz%@) zUg2=<E1?{Ro^~cI&SDGg-Ncm*2Tj=!Yix$I)pT!N z3q(T^KUdFlzDyY<9DI|6biBiSw;3blhMY%Dx;efqoV?shohr8YWjVft!7a$y|wd!LFWq zJD2aKSmN0upPRGwVIUXjY8vuYLz2ZT@3^#%I)uYCR}E$STDNR+@? zJNM)D0Lw6RkNq5VI9G)VEzB|;bzI^r+|sI?Rsp=HH=4IBfP!AnCz?l!SakP| zPY(WSe0JJpPPPS1jGRUwP?|FX1&WRVAJPI>M*B`|N1DDWmu|$~;!6y^pq( zSsXkWjq_P!c1)oW1+2t0at>oN>uBpiQ}CCf&7rV!g5>4(1BFMgc_FbDKs+yxq8xTR z$yte^qhEr!j%#b-mer% zq-FUM4gF&PHS{R;_|B4*_^6O-D`g-T58GamjL$vs=IBWlJ5=ZG7?+z*B{SS9n+s9v zGUBy?T9oR6^f5jsCOQM&2kQmIvqn^3*r9j4eIi3%cR47`dXn$x7lKc0A`9NCHFsSh4R zZHJoL4D5_Xq@@?FX}zQ-IQ58~eqSOF$QFnI?yoI5(J@VCmgmeGFURs18yI-QLMkk> z>81?CeHn49w~iy{y;}1(B`Xc0H=N#_4H^Oc!Uu~d3}X#Wkn7?VBAGNXHD5P)o#?_t zZXSJot%GeNjA4*xN8mYz?bA|W!a^r+V>%j>55rF^da>+#N5K?bjKSFPQqySo6$V>e zdz+v-#6*z6$b9&bHR_6?#Tz#J{>|+PhqnBO)!PG^SYS{NBgV+f))>-j#N{*%Qsx5(JT(3-Jv(SQsscbqSk0ncPwe9bo~7pispCpj zu}ROXKn_t0Vt5o%Z+0Z;Xt!P3@5XjUto?W%8_NR^0WC0B0i0&Q;PZV~Zdurv514g+ zNFPaLbUM)GgY zR6e4Sn57}pX3Zur$(%M>+`2c?6(O3SjIQw;9mMSec7~%SSi*dFUR_I7_OeICaAxE?F7lX4NXi`swntOG zcu8&vH3~;+eHbLn<4Hb?n$Uf zDbAI)kDe_;Bbv>@5jSuPYwZ09<9aZJ)O$8!;FdsgPIzSV-wmib)Qwf<@N5-zRE ziHKV%lF5KtUbwomu!ms&oC}kT9DnY4X4&#I}Y71k|uvO7vb+fu>u`e4$subqJ+ zq}>q@ly>zU_Uv$nX~?*!p);77m2Z!)r1Y#fb{t=(d|sx-*IoRpe7}brlR%Xt$wov9 z{cuD}Fp?QFhU9vm%%0~ayTU$6Jc|BLsmGPZVgalWJ9j{N7rdMvn9lDzN;rFY@t%Sz zb*sYN6eTua`q;#0Krl2#{nCoTnT-SYk&_G!WyANb(q!V3L6NU*`X_y5M`cb?a$1TY zupFa5>n4j6mWLtb8;Nr|Uby~gI63^;bdzR&5>Qr0+ueaAki*RKy>T0G4-`jbC?#MO z+=(8co7TK5!Du-`$&;qyDg6dzznjwUHbEie)dHGBw|{9Sn9_|fvkR>e7rPEk(xXuv(woFGyYO#&6%THr;=`^#zc%(%{McjO4Ec@k(*ZOk% ziM4GGv8}Lt?g1{VOZKH}eE2MGN}|V_c~USW2EmmRX@Dyr)JPFENI=zch^C}+-1R%# zmV|NWC>eIPZ){mK!6dfQ^eyo`qj6AjYr^o9-!-LmP?;q5y z=3HAsgYtaNT!h=6Xjk9Sb+=~3r^n~|%Y|#54i-n7z8Vn;2aROj(#qNsK44a+X7h9R zg37>nO!^dhkpEk} zLO^`8gpy@V0w{2+*}oUx5w{)@jFp)+*sCm2NnTd|;Zc-PLrSF-rwQgAQw7MKxmc06 z_cp`vvhm*!lOvR>>ov>MRS`>6j!r(WBDS2ne*3a5a4eE>dtLDF_?^+om&JPh)gqBU$RO)fHmX<2;jFKmspo%+N@C%P1P?&iXWUrniVgX-~0JGnmP@|=j?_Pf47 zK$^(;Sm;U|38-3LA^&o_|N1jxD=B?jDr3q1IJPTWCn@dNnsDv%#_?{Bf%}x~)CF@r z-(WAz#xIY(CP>%|Y=zIAS?qS7+XbJ0R#zn?B*q3)k0t|(Vis_9jW^Lz#Su%9+Ku^< ztLm;Y&5=XP8o?(=THBa712wpFO&hEPXpVCSmxnD>L=K${v*x;=LcLAulv7$G)8wFT zH5F6uu-XNP+>cjhOd9ovwSqrRWp#0Fy+m3RnjAs7ZF{F83I)aeA@^nuCevCG0&xGQ z3jhP(MFevD zz-4&>Iu*n0ZG-pSsykzi8i+Ph8@|kW`?0lNV-w(jSw^2tFe(!of$x+?#x&0!#|5{a z&hhzQu`S^8{2%VY$KPIEu;j(yHJ9%Yi~wZd2FOpYELYNDMKNoGfG!n7P(Na6H;hK# zggR7UVA6&;(oHKYGZM+_1<&Q;J&;#o$+|6%780r;3bG`hcKE^-fs(i=EkK7J&- zHfQ5gqcDVG{ei;%CQDtS0cHJ=Y%OilGUuTKM7d?vd-QR@&xIdme~qcwBxJKcX!RRD z(;IYWvhV6a;1lDjR;N)VxoP709u|G!Nj&}7XOP2uw(jvxQ5hYpL20d9Dc&Gx|2N1- zxw5(^nKvx+Pf3r=Z93oG=XYRyXRZYWiK#LrJ83+F%G98TrObPty`3~ZvP%{ydvYFm zJ#zRB@00obpZsdZyC^4~J5RmM^>+|CYTn-EY&C3;631FOtzVSsFTC21S{V$)8_F~2 zfDQA~;;MxGVwUH`o+_P|uIUQPZ*r46m?s}S=IgqHfLqVvV;SFeT2b#lX<+gNtP3x& z0`x&PTP=F%h#|2U;O~&9?O<<@Xw~tzAaEAJ`Ee+;%v*xU#`;ut{DGkwJ?P$P@P>u$ zj?^QehuxfAMY>BkbEEHM-j_VI8wg&jIloH3*OzlTb0ebSjXZh9M|H|2==yg=+nnmd z+3{YtO}}vdk8;E}h4puKO>=e6bY|e-;#qyQmXeozQ+9>7JN5NWn?kR42Z#}T!_&d{ zehu1o93kC4;P>7Un?7GmHNI04f`26fUki6nWQwY|i1=l{T0sk#?odH(j4?0|fdU_6@CirN8jeI-mem#cJouG9aL2HZG znmYF(4FMoSH3=I0n=qWLQ(;R&0R~R8S=8_czR60I{Z(4;i*yCeOYw0*Z+GF;t47HhX)Z^8z;KNf23K&8Z zR$P26Z$P?Xf!XMwv39G`rFP?W+SkHBF(}{U7S$^?NG^OQ^uG}$yI{sx!^V=0%1)}j27<2_0u@qa8>N|)K;B=3? zQ;idw#c4i0jm)4^VInut+P4^fUm_IQ#0~nF$uQoRg8XeE+98!d&np{gNhg4dI2ye2 zo&xkGNJzEpK=D8Um2X?5tyjeA0p_62l#ygn3V2Ay z&~FeeeDWyiid%UiPFBQmP^EYrNzfL!7hoq%M>L{ZSF^NW#Z-!Kk6hOs?n=!M{cOlm zQk?kT;c$Y0&|!-j);Kn$c?T|MhaaIAyGfA;ah!2AOCUT`bBGo&DiXJ!m^^24zPa4- zc710+t)Pf25kvt_sd7?AI+Wm~eBj|w!854X9bd5+)~H6Efw2vziE(74KDzp(+g+_y zq{ZVBYS#A%a5F&3cz}={fQWmo-T(&hZDQ`raz}6(e5d{6+ zhVafa?2i(!5@7fX7>dha?Gd6DrcV`Zdm(OS_l$qcq#(6B?mO`u@yu4?bCG$0&r^bJ4r)I0rV^6EOd|Aq2J z>j2Y&>#Xl7^kop>))reE+C=p0% zwgenO{AHXhpd_=T0C%Mk<-clXC%pX0m_i<~KMcXFS@#do7XI4p_RS#3g_-zb@m)lX z^J>ct!c`oj36EWx?>R3!s(#Vs6=Iq-a?ay|{Rb#hD_6Bd6{m=EXxWfzoMkhFB?^9) zr{lk*P#hgyOQ$u)fcZC!dJ&>3N|76Y8|B{5uO!khoju0%dpJ150`B_OHTLm;1 zq}`k9>9_h-XSY5|Sb(>=^i;%f`T#vwIiNL@{N#hB8)@WSJ4m4Vm+@~<`fOgklr zOrH|oQ^i=Lesq0*k>b>9Y*EA(lE=oc^41UeJ+`=yVYjLPwLdcJA$HlRJ1W z9}JH~6dfvpg$Rr=6*3gsf1-6>CEt7M1}Osv`+E@dCFIA$*C}VUNqec?Ib%l1m3|vH z`}b|1L9i%RtLe%wEN&{d+>aoN&Cv1cfu2tDQCjp_72PMJ z*+T#y`3qmDTm=6^q*L>hg8xQr+mZHD;j*jK=7uxEZ?D=gxZo-(fu>R%E|PdU_rZmt_|adkmve>o25(oCcO~C zLT~!H*pId**@l75l9R_BQ@XHjgQ2}oZw=#n&T~Dl>)~Io$XDpthp!1f$$XyqaQ(=^ zkn7Ezjtx58lGIc3uVRX|Pa^*hr(DVHM$jUckRpxSsKp@RXOWi}TTb1{Arkm+T=l4+ zut*td+aU7#SCNv?l1nAfLMTn&(A!s3+JpV0$kEC$AdaR2gn0%|O^S)8#m3ip530r* zP+16oLDYj}WM(J0_C^G))?ld;yt?L}9u*3*0imwA4SS&3P7dp2=PY{ADr{U_oyU{_ z{7s&wj(@ZnVMDO&?~qA72je88n9j{e@F?92u2H?5Xzt$9{l(+D?i6^=t!^>#OU9)( zk@}8+PHn)axWZ7;*H)2?Q2*Dps()G}Hop&|-nQV(Oj6tqXmtGDw=ovHY17RfDHeyy z)n*F#7~{RoK$=HgY+<%UloHIpDHxVJWB^O_CYKM0!_2Hwi^2avx`=BQS&_m8SYIB%CVZnnZmS{Q${ zU~O%-XI1mBfPkAl>dG0gLD41B=Q!6%=DJZ&+OPU86B^+;oh8R6@zGoClw5f^a5wg>3z-miu8O` zY${yrmU}G)x7-Mje>YTV|7#^81eK-3YeF5YAAHvoTU6P!Q3#>RP zR{5BmPK~f-gOR7d%|xSp7khR#?=}?0HYB0y*zVQvR2ftiCnHSwk&R8MiDfL@pMg{QVmNAL?yGgCujVso4CeHkEhUL@1d`Kac@jtn{(-u6lGp zqRZ2GWm@rn#Pip_g1JN^C87BrwWhbIi+?fGBoREBjii1%|5BA)$d$kT2#KX+>@PR# z8BWQ|DXzSw>>_mI`zfa+XR`K`XBdg;ewy2Nq=lgVRg@TKNobM?&NJ{y;9-j@Iw^Z@ zF25r&9x7;;d@oXhC}7FnNM8Hhq6gHZHKfr6-^gpaV<2vIk>xOVveaA_4?V$Q==7Jw z#S)9N(Dk?02{z{TEGBbzRY*i!1T(3Mhe#u}MBjZ$i3+omNQhXIb0bM9)bTmXm83@U z%qu#Bld2vfvHvq9nV0>)^CVQ2L5xcgSna+w(ULBeWU={Qs7^u2-4{3Sy~W>giGG~O zR>^xrEroPyF!V;RrZYJA{L%J7s?}FJmDIH;G?rzoL_G_SL3GZgsIU-5PaB5aTUFIm zVU7vrSDiTN*0Aob5_9yI502n8YJh<;F{kQmSxmZ@;^W>5-?^q@cfRvK10!NbznhN& zWT)-AU#JtneTesp18Xb-G}QYM6?tz}zqC5al=mk;?}->{|1_ipHlEojKi?`h57XPO zBbdANDe>B61I`UFigfGjh^r`uXQGQ@0sM|747vjqr-Nwz{yPB{z%L!LTN9 z3bH+3;5`_sa72 z<-`9$At)8&3_bV`bC3$Jm2*>(J08`J!71o=1TS5oiC}PS0>B^{ zcwc#jJ8tYz^)fA!aaGjYU^Qb9?dIvqYi!3?L}m9F@ifGU;1ZV-B6 ztCljSw+B?b?Tis!T%wnPg)o%!E6&@pU8dKeeC@zuHgT_Ue9jS?~ZCBD8RMKTzuWDiNIwNC5s?zC? zUZim30RR7g;iw4QwTW4X&x%OY zwE)Hca!UmmKdn39+al?%7#^+IP?Y@LfkFZz(UKckR? z*F@`nXvWmjv>4VpMI@aTqh#B;Aft}PZ~thh8DZ3EMKOE-K*C`F9>w%eRZ{D&u&}*g zAaTN{Em;QDrZ`adK{+#vVbO{e!AzatS8yB|>taPx8mpy3wC#O@S=qV~f3QQPo!lI| zaLJ~k754p!jvY^?Nrc&OU?cZS*j^K{R|2WT4c@$V$J&Ee$(gKoBQ4OkNiq~qZyj0Bx$z_Ca%lBVrHH>nK;T!JN z&W`n{I=%gUG^9y6a?aw=N$Y8--U#Y90ydI*H?>5$ zW-*Ao!SxjKpph59Xk(*aSfZ$vHZC5^4r-ZX6E=;~>D3nND^oQJ=e%W%3^~K{Vg1m& z^Q5E~?R?}DR-$+wJFw(ltwyI5@=P1L zCL{$2_vdX)XML-Hrml7B6m!QVbA5fWZ+_6}zY3_XhcZwAoy%6wIK1W2l?r!A#YUb@ zv?*b^?;Qsnv%%9_FrtRS*k9s4Nc=BibNseCxQuL^l~Mh*YE;QMRA%bbWuYJ1A{|nt zg(qLW<KcH8~c@}kG9g}7P|1BANEGkIiU?as^lHk)c+j9fx1`6KGrB9$l4j@+G20+ z$vN-JS8l?ZpFMH3LQhdlWEDgcP?)WDXpRCZ>UgN0MpU8?HSnDUI|`O|8U;==Rn+C@ zQH}AmXtZ#`dD;?#_7lfIkJr}os%YDje{eaSkDp_G=dv@dO}bR3dkqjm!tmz8 z(ZY`v!2kX8#I7tt7`i&goZU6w^A>#dzp>I$oc2#gJ_|8yDJ?@GG{UPVjLT58)~=x^ zp&SzMmgD!(no!5b7xT82&CAiFJs>rb=Q8s< zwn|GfUo1*6Ycw0w!sBg?Y>!&(wvb}dBWq=hE;(~@U!I(RPFqhuJ>c~2=@DDDlO)|x z3lvX2E|vc4Xw4$_&$RAuozgGWku96vT|frs6aq+m1?}2EM<7?AvaV@bL6jTPQR7}% zhJ>gG4Fb+P){cPhl@ZOC3*7dX8CjecxckmW8yFY=(ZaOdoC08Y|4&)^;r1}*p}fVb zUvK`~SClyBPIB-6nbJiyXtm!qx;@`#!fw_QEAYZ>JST_JV%B@|4J%{M?f@K9sKd5* ze0VY*q(BH@SGu=v#XEms5$Zuonm{8u_@-f+X=i=B-&IB(DRv1>Ny2tAS5ior65=7_V0?S47s8! zto15Nmb4`V524tv{*`I$u^;hgsfE zBV@xx8npj9-(aMw?~GNFvG88hT$5Q|V5o-vjt4n_r$vd$2l=%D<(q3L03-3cZ!V86 ztfC0jE)bIh`4V~+hf_!MUnN+3@^EDmaI?3Wow}=DZ-TB&dvH!U^%t6gvw^czkB?4s z;u(O{s=!Db(hRVWxv<_(6g`kx*%bVx!8pplnAAlP(heJ6+aoJ^V%swFZLg22yHSq# z^nQ!51iES6XvP&+XLgW5g^+M4{giQygIJ4Vd`;kzlNC517Ik&RhB`h3%2s8i>(Avza@`hv?tT^#R%tjkjax}l1=N8=VrJV7q$k;xNF|a~F zqCp=(z4|MYK=>BUlEhe(KKrk`Er#-gT^;AzNA=_Xg9A*sddi`pEtTR2HeTT%ffygS zC;=P)Wh(Az3VNmoFJQ^29IrPe@B91bHGAc^%LsP73EwqnY5lU(=3g`bwYXx|2Kxk* zGcVIvvl7FUD9$>et13McN>TD^5mIvGO5)Xqv=WGxvj-MU z6U{SR3zy(RMgI#+-k2O;k&t#(SCcSKrzr;j6NT=_XW?%}s6QmzpOJ}4_E|iF+^mokHT@y3)jo=kO z4u9hM55(17l{Er@3;JVFV63)wkG(Jsp~@5ZsZ7LuNhe-7-yM~~a9|RPz~hrz6`Y%L zhoTiiBat)rKd=^bBma(*ztykAx3HWxa||HY~mRA9**Wh zJ;CI6s@iEmj zq5M#xU2Ta}A*YZA%X}Ey!zL}=8Ct7ul)rX0|U%^)^CbVV}-+j=uqs_doh$stSBT3jg^6n0B^nDf$L`0En;Dnh)@O5S> z6Pcp-?-59#IZQIjv}KeWu+i0IkHIcf_OD8dWoI*k05$3r?t$o&8n>p5WvRpQJU$BO zSytt?Pr!AUIMj#SCVCuo62&JDRqmuqoE)y1f8P6sVo~kp>eR2`8}KE|*1X_}9f2w8RXTP+u|vqwuWT342wjfg88JBi%D4UqGJUSH{50nED-MW=7sx9$aIyDv zdvE3wyC^H4js_%H@yAcH*;fo=;slqJ<&35DIC({C484qJ<2V9&X-K+1*^X!@VGtd4 zTnBykOHt1~iU@vbt8Rl!nV~{jVKf%i#YYC?tzdyA&I|0G8ob^p%oK4)lkK6gHVVB{ zaHt9A&_c#Yl{VMJnhMxf70j6m?NKjyw`Z!-gYO8+Wx?xajz4Z4G8T5(8WsU$gfkPa zXS~wwMiU&iViPPlwB)LVC%XVO{Tk>a?-KmhDJCjSZDKMeM6*yH3*Izg_#ubTp25=C zSdq4o_>;emUhbDCO)c$S8Oa@7w^8f!0?fekUxK(aH)+=$qGBh`oIT!{ZDx$R5jZ7L z`;wdpO5S|n@Y(S;+Ff&I`J5RJ2=n@%#tl@-fl=c27b0N>ui_UEhRu-i(ZdYAdF^MQ zBoyreJg@F=@}wZ2>t`!yJ#Q{4_YUsoumr3kvCp&wIA$D3T95F6&*t9+=;cEd+-7`^H{ZN<8DfFf_!)dJP$6>+-pThtQT){ zsmfh+o``s-OLW;dNhwsh0+)5u+9(@Yi)vOy!waLCV(;UBw}EYv5Kc$;LI#k8MX)9w zx#)Z>-Dc*qbyuIk7ucX4O zBb3Xm;>*fV9+IU8fvWp#SOJga;o>?iiB+?Nu4bz3dQ7(7SV4|O^7#@9D}Bhr31x8H zJ=#80V%WZRPm+nie*@n3hTZ6Zi#-Iy&qpO2*XwkbI}x{xcE>FSXUW*7 z$RnN={~_)3jp9}DeV{ua?x;PE?7i1xW_~vqiN0w;be^Ep%VkMbguWW8pI-p4x4QBg zuemQLd^+lUW(X5vWU}-CeW)Su<96^J3`W}WR*Kx{67;xFl2N?+R|wqSzbaWVq-gl| zlL@+a6Hpxwq%?6Hy$i$bu2WqKUU{&Bm2KB!Q7h6MY;*B_crSJH}_otPgc;}8D zB>=2ySFSYAI+ve1A5SesRr77_m|nYu31A~0>4tGi#fdEN^i1x%i7lX5i|o_lM}&2< zq76No$km73K{r|^GJg_J=4%5ay^m*p{8(xlgJozV2=T+?p9T!nyt|EQB=ym4GaUNT zr`)46n%9R>4{V~-L@r^5&aK_|??`ZO%Ns>k;JM+Nk!_WB%LcSCElYpdp{8lsv;-|x-nuPN$|Y!b*P(#1Itm4;q| zb{e0c-Icj=Q-H`n#*=BMho-P$ij)c>L0}AWmvk>j+izKizwq-hfO^~u3wLve|52C? zmttzX?J1|{5qYhKdfk@LpW#GiOo&=m>s<6C;%u%DkMBz|k?w}KE`Cw8}B=$cCgL^o{} z49~U8LfXoW^ezhw+mf>Ojt^ml;i9ich>`Y=-CZaMnZ1za&o+dqDyfh&r<(g|D)qKK z^JQq0R`3N@<~E2ThgRmq=3jMZqZw$eA@$bEOiqC$$)P9>4HK_}=FT)SlcokzRHJYW z;}g_|MH;gD1~@-+ytP94ep}pwRWFjUv4!Q}GWc%M7gB z8MkR8&v9GTyvfK03_&i=(8sCGW1X^C*moS3O92HYZr!*uy%^APh{Vl)Jt9%EM&9|c zwL8-Ncbc@_><5Y@>D9fldQI`Qdr)B8VJ?(Er0{tsm|)!HXyn8~iUD~7SvD@cEj)^s z9G^WkGjQ7zV-$OYq?YJ$fR>R`;` zRtOH>zh+MZHXzotM($d?djkkv>K|r)c*tk)#L>e~{v|0%fV1%z5JTmb>&;^j5i^8d zUng=iF^^vrw1#`QqAaH<5`Me^-(Ya$_ZzCs%bTLv#J>fXK^ZU_^f0Oe)HJ7?v>y!GFIkVGc$6o$eH6Aa||p^{{w3A z72g8Xd}wkJAfw8qs3ErEvB!l!LOThtR zW(X8%ArzqAaB6CkW_>{W= zd*X@@+GL5IB2n^Up%~XAk&PdJ`Bh{0T5`%5S5rXeCXAY|{f3LA&uX3XqUtQL!2M~Cg)ez%Y)0N*c#{j+b%jULzat1&5MqubNTYeV3JUOhLm!nbgrd$@^O@@P zlxUqNH%X$MKNQ>%5plzn6^HW+|ByVr26;`?%B9T&RTK1&3Ox-OuL7nnztdLvpG*plM`GO7-b*bhgIHEI7L% zoScUlUSS+D?n*64^~GuT^Nm~EfEOd}o(GFNVH8|Iygy~ee;CQs) z7&qWmgN!9J#poh~&^5XqY3F65hO_mAZDOV`fSUL2jX4=o)x#M{fzaq(79i5A>mY@#`@Iay-07b!X*hitg2Hz24c#C6yZMlVOmC~$9pcB% z@lAv4JHpz#%Z6_EhqvbS({6ybe5STFHwM%IsMVFplTYb2@D>lvNly0hG*AiJlpPcz z|49qZNw$nuEuF*T2lC!=Gsk-uVy|;a!T-&g<<1|gt(53K3w}V2n#NVn6{Eq`Dz7-0 zj;Aa1LCTPMBvnIDiyy82N>KDU;m2vbW3Z@4-J0%yH4KKHW%DfqPF zMyWCDX-hI@?&@WZ`WY|Ft1BpaJEoAshYhhC>tGGbNNk0D;pCUcqQq?mkt+ET0yeH5 zFRs#J1WIb31uFkhup|U{)oH(X65F59cZ9aI>y;@Ktk086 zEAp%Ox0ilE$+fg`!evFTRC}>|MX2$I!;7uBs8=T*LY<|iAaRWF@jaZcvBtJezfUz5 zY4|(gdBZ&?bJDv(JE0G4L?1(Z-=b7SH`X`9hKA@tUkvdVW+qm0sxqaAstFy2l0?jy zDBmL`&r!|_gg>0oBH8E$uV5+3#c{cs0((`DBAhu|{B%fbrld&;uA`JNdX5cnm&`R3 z#@BK8F(KEosd?2A_qr52ypotXw?mn#F_&YcLgDpK{t+Gre7Z^T$6>+3T!}NZdY1`E zXxE#PABT!ZQ*}8pobwTA8iJz$$IpmTyKzy^!O^k=l2(UC z$pjXaix))whG7WBIJLoXO>XNr=Me|Tk!IAUYBqodJ`5Rn$oPv*&y|XW4}rBAp(R0o zS(gGti$)O4OdkuQr3qB2_HV%P}Gt*cxl3RnMC}kkHBP+@vT*@G~{=@P)9h&$o6RLxgTi8@Iqf&;vc{ z2fefsd7wud|o3KU&JZ5xY>bE7}vxUedp@hJ>ves^u?Sk@IRG+j+$ zS+}Xfcz@ZiF+J40CfM2#TIyqhGt}-Y>4_iB_{&JOSQzcA{Rd}FO;SpLi5tl(qNLQ{ zn0lY1DL+iRSd3`zDx)9bv?5%+Jzyo4iStW`b3B@HLT1_s78HlSE?F(3x&v^HfERIE zYGV?j08)D_X0rOAQ%!csQgrFs-R%t#2~8|-jq56gf`5Pvg%$Dq$w5yD4BIu?z7^1J zx!%r_j9+GnySuU+Nn*>w?zw+mFtJ;h$wv5P+C-rVom>u-fqrcntDihUbXEuFo4~KA zbxYKrV5ZgovP26?4z;dkkZtPyEDYjVm>8oA-)rcjWol~byWu&nyE|uQW~P>lKNgpo z+lA+hh6mwp5gXqbEk|pOYNeSZIWgWH$9>>Nf~x|VTsDhmRIAf1-p|iZzsI-QX4ZM{ zKtGRVS$=bOe4|yb6liZS9GM(GuJ!^=n_MO++vF+oc) z2f}Y44R`Bmd%5g?PDy+pGLQM^!M{d8%90HG*Ng4<4&rx|g|ayQ{ZuSSDbjzuhQpnN zBN^eJ3;sMcO1zt&6Z6{cbuu3Aokbq{?}MK=k86{pIW+i5Tm1Di!qIEhllA3MuFcFVS8l7z-gbFoo17P&Ua{>2L2b-7 z8slhc5nLA?o>K8nGqrHd{TWv`bl@|5cKg_1DBTXthKZJcQfx8$nxi}It`!7;m9ftcd zE%7m9BWW~#4kbnr(nar4Kc~mn{Jv8{4%?QGcs*}!wyGqcrpy`QRDoAF!MDY$L7H2P zIi398X>8o7-{^(bgpu|h4YXHpou8e=4HhrJ@iKODwtx3v|K^l5b;oeNRavYha?PV? zhMrDRmA%>=8}34d_whSnGGt1}Z*Oqa{i|X$2`!dtWICgU^;Zr`4BaW!m;So=cejkE z5-@FcY!S$_c&u~>jpO?6_*RYp4U~f;Z#3?huv*CB8d3w>C64nR9;4Q@A&Jx`Qv=Pm zZ}uZaQ(A_ZZ$HX|f7FUjMEWopvcq%X=yKs+{#($-YbWHrsC;Q>n9m;;f_n3h$0tC;K(wlc-K@{fqDX`H5Fw`ok4w{i?p3nR(v=yZA8mtlJ-Y`JpIKqvx%cU*r!V6>LR)WT4g3=mpC4b~ ztWFOZZL}>f29CC`@Hg)V@a7aftKVhrh^QtHzGh z?ov%^FrFRn-c#FH_13RsCq=&yoc+dh|2UeOWx*|7*ENWIMlg@!-<9&_&aE1k%4SMw z;NnVuWCB~KLEVK{Pb{rV!$7ua#R;0%5cn!VH4aquU?a`{?O|!4_j|q2Q6(SFgNG6` zmC!SHp>zhm%hRGZeNItE8^o;A4}j@Zw7N%Ekj9yoCFSG7(U#}XhcrLPwZ%p71YaCV z?@Z8(^i}1FY(Y`jwA^oTlGpA`bfy$D!S0_01ue`?oER=}m_jjaa7+0E^M*lO`K$`V zhSQVcg*3bJ> zUnix>DaJHbZA|aFTl+Qd)g2Z+jtl_}D~4`Cz(<-m0Q;#qc2_R_FKb=m;&C65k~P%0 z%4^nm6N7XX9``9uvZicq2*nE1ZA;u=zG@J!P2=b!1=LQ9uTrY^?%C9q^{WAcGc989 zRAp$-vorBE<$a%yR^*eSHocoyM=-1$PcvFVRfUa9gp=+5sqtt+O+NWq%ZevWOv9pI z7aKJv@P!mOX=JMR*oT<3&=G{Kol^Cwf6DU%thO|GeW}AH4h!^{dJf816k2#OKCe65 zcd0>za#VRWQu$oVVLqxewaC1 z2}{fE9-kY;Df@-e6Vkz=*t$n&z1gJ{n=)^1P83I0yLEJ|dF!?*_ns|i(W!rgrP-47 zi_a*!2SEc5q>WM1(TNX|HqyxYfi?jm0R^A+S67YK5|X^Kx`4Pkz6s!IY;OlpOc-m# zomi?}W+@*PLmf1dUQkUP{H#*9*7XGD=cj__h~RXW5}PQ}+o*z59K|Voh?5QQfqxC7 z5Gg}Nmr8PUQ)dbca2JJ^1NzGY%JBlQ4r)(p8Io-ETeMTst&TRA?{;=c#)9RwMOEQn z9khlS9xkc4Wsa?=)cN}g40vW5&Xj?PlcyfveaeUz!pSK^qkZ z*Qp;3l@wKUCN}Gm4+XL0A&UUaVRo&srYIUnawqq}F-4oO6<5A0Vp8sa?pmt$nmrMF zByO;?rrh{VLOkPjbwp*bLEZoyc#mtTC>9jyh4Vm~mh8TmXUIbKFX5sCv4c_~kf35S z!XO5@=BJrik3XEfJJWg+{UA0#9x(%(Rx{0e7`NekGG` zwmnJuYk4yG1GZ|5967TgM-3Px8r`x_iP=GO-I6}Dfbhdm!ebQPUub-l7-wpIvi>l0G|wV;|@ zRdS|k3q41)x_$JuMW)FcFN#a=AuG&|pJyY}?7JwNdlqTGZss){bI6-0XZ}Jatfs8T zH18VdIn~R~obphvRNAR~FA$u)^~;y0y=$;bO{Yu+pWwtA;(U2F;W=Tq)mankzw9e>Sg(x|_>^D=kqdtJ%{@nQH1mXz zeEZi^vMj02Q3R~!vZSSwVJxQhZdIuQaYfn6TeeMBcLNkK6_1I&O-?W;Q8&5 zE-QA{28sFZ0~i+sLbp->M9xw_eu{a<20_0=bI_e@G=AgG5m$H3|15BaEG1V|5~MFO z{E@rTp@ZA1s2LFd?Be=ZED!=5N{*H}5S{&ad>N#*1KzduAIilPRg>R3CXbx&qViZV zb?2MEi&~@jio*+hfxbTT> zd)ji;A=B_<#MhA=;F1)c6`}lrQKgeptrcsJ=N946-pb-LEy~z|7aYsW1KSd?UM)~r z8#2o5_$z@d!Sf->>>oW6ya2hoVnwoFh5_qtm$pY$|`RMj@4p{Y2@yyLHX6LTqtP)9t-|htJwI zKF&P7l+DRT(a)&P1)rfTb!D2V4hlEht!uk70MTcrWC^{6)BbGLoGK(VJA9atS`r)1 zIK|Ze+;4c>*Fn-5VIC1s#kWTgrBcdarWu2k81H|>r+n00lU8NM{9_c@3Bp8->kJ29`fCNHPkFLSgM zDrB6T+IAnUbvHa7iE6{kN_(p_uqifou+CyjmuU|g*{hC{mavz+yawPODGXKch0mU+ zoSChi8GNSh-PShq+u&IMrQsFl-6%4v0vxcek5;$L_ne9$5wXOpY%Y*B$A=s$xVzF4 zIo7!d0Np^DH_@@^7iSFUt#|#Gwt;6UiDTo~cd=!Ia`T3aN;b)CGE4`;;{?7oq6`4F zv}UE1_jk*8%NdV9=vt+MK&13+dKZHK#7w0l%p*%=ZLRNEG%*9sY?$S}t;sW|_P)Q` zb#ha$WZz1`EpyqCxR_-OAh^9tn$a-P;=&JS>zZ?oi~oW0w-+mTBeP&7gpLw{KsFt- zjN?9kEh_-bmm^^8)Lkjo5JLl_T=3S~vTZ*1ABJ~npev2&UC_OYJbdehoJHg`FR#dx zy(MO&+ogE@RaKcQfIJ@BL2QW%$rHoI ziYjWFGrIzlqGm-+{;S9lN%SeUpETZ~o|d&E*(1xk z3!+0DY6A7@%9vn3)aOK-y4Xu1`lMC7-4eTxj z2dH)5lgbgU=V2MC(jLF>ZmQ)(#jb$zXKxnLij@S-6XR*CQA)8W@ZQvI#S{P{!}mRz zMmeGKG0sfAFh5&4#?P9Zl@AM}53EHM{9(>t}wsr)F*4wO7F*U%9 zC1($YUg03A%0yNH1pZO@U(9n80{A12g%(#Ce}a^{C5(Bpnc%0n2z*tNq#CtDtUv;o zts%54q&d&RpHYeykOr83U98S^k_9(86pCDURgz>fWGEof6a^0eROMgLKL42uz(Gb1 z^_M9kk~-rSE)}3yMohsxuBh)1RVN9LGL*x*^;59au}{++swPz~+|GgJ&oR zD$=DklyFy8kS>X%Q;*Sho8<2Vtv0(Fyx0?1l4jO8C52y+h;&)Zgb!lUW)96Wuiiq2^AcGTejBGquu3s2(LA^%H3 z6Q=pnv6GW0!w^H?)SzYl{Wf(Oo!PLpECs#SIyU>V&DxWi-J;13&hb%9{(z(8<4APOnWZ+3(gEFxE4#1Ny~LFRPj z2!sNLk=v}OJ?!So?4E`83-s7DV;P@i7PU<#vUhgN1zRCmu;eF>03|{5oMs$^>zjLe zEh6jytsU4bDfA5Wv7|=#dyg`c@sS%F;8WADnU!K?BvJjWai(xT%x-Bx4!0ncdv^4XyN(pR;Sq$KSm6J%N{$m$0K@W5N2PLA=|55q?apYw36NdBJ~R+v36w=x-; zRYh@48#a!-(7~_=mS<;LYIia#qv|e1lGG?=4d!d{)@-rq%T!d?@&DqrP7E2h3?q5O zJ*`Q?$&v5gLQ~0hFA)TG)=`UOHtQ*9{ju(~$9sZQmC!9WjdqpyTPF@cTl4+15e_lv z`1xF1!3f}F$k#U*L@bA1PA7&~h40=dG09n?DJJ)db&Hg$ch90uY<6E2lZm`M0GzsG>ykecW6zAudLX-EzaZ zq!=<`yPvzs)XE{@;Kh=XlwNdD@0TzyuIRW_xAP#QiI+;lgh4^w*e%0;EIR5+6oaZc3WP&n{9S0l6<0cR@pR~g?i*Ymeny~w7BjcokkMl>O=P12Cj88~~O zwyhn>Bo~9kWOoM&&}_P>6q&ya&8!Tb;%|cIFsSH%Da$}n4VO~17)-QDH$zfJ7( z`atG`P9IvZb!U)03^^X3&i}d;tw{|WvG_(L(?YFLB4fCsm(iN6)Zb_!+cRhzP4&gv z8P88~GkJB?`o1;(YCR+KVZ>7{9(gn;qlX-v)M+YAc06;G{?zq7itTxeyN~w&?7*gS z<DqI50hd-hy-J+g-=R2ae;LGoJRr0-Nm*yGq=@NR znJn0Hj{_4^yh=x?NkwrPl(5j4P!G@&J^4mbpDMBzGab9=H1c`qniTo`e^R;a=cU#; z`9W%WQwI|-AWzBR6F&|#WgQV(ZNY_l>1JA~BQK%vi247((T?W-(J*n>d6B1I_GG9> zTn>@)2lG&&oKYYDy1sH%+{_|VNOCmO$D-%{GTe=;KHZ&uBsjHEP1!N!qEGA%BWA}B;Wvg?=iI+)f02z_sC08JLJGo#OjlVGwk!Re32zHKCC`I4#y7EF0CYWo!a7F~ER3Y)9Q2IHzyI z^Ov~3AZUXvQ>O6Z5^lB`zk`dKe^T#}#5=iEP{;o*>ViOZPImc4Va&YUK`&BzHG9jb zCWB3+AnOW9Vpg^C;1!2>>*w}I;R-@p0lGWE%eJ$2t>NwX+Raq>a&iK=RJ%3JvJ*D5 zgfGFYj*lz3=I^ouczwv}A6>^UME6QHsr<3qIPh93J4~1EUWRn{rBLT0HokYNj6S#LCf;)>U?y&qTX)Hd^kLVDtWD0>PR3SUL2La*_~K>}18h zi9PP^0o3p3FBeIy^fvj^dweIBNi}8rPHs4@ebr{TI&fUWTcr96+wSr5uQ<-4tRJV! zuspo}w>C?rF_at@L@Bn$jss{8l29DL63mI|ocsXy#3ou&Opd- zMm`D$BQ;UdE~!79j>f|0V`e&hQ!v%;%vaE#cSq!+glFq%(m>l2^x**D&qfo|3%c|T zj>TTLnIyGa4z!fq^h3D1xQM$xOVuBqBCS#qIlXSD-Fx;FwBAgfTZl%VESY=#KaouVg)^|QpQh(|1WPu`%iD|CL#vf)3Bk@ z*Vcywr?0`N30iTd8>&A3zEW*uG^8h&vIJyB{Dpb3qIbc}4e3kFPmkbcJDOKll?3gF zlHQ*awJfIk!QzqfNs=7<#h04p4Jl$3gDEOoH#QgI>Nd@v8EuHL#Ycd;0&c?5z=9Gx&00J9Q~@*N4m8Q+K;NbtO2z##2g>9&w-yi zK`+w_x6)P~nTbCpuAu8L{Se}=IjyIJ7Z2j=pR{9=2W}5IQKmW*Ppp<_oZXwu@{1I} zUC}(gC`fnvgFwMQ;o8}7IBlE@v|<8deVR5V_Cxu26O%& zkd^8Fw`(E~4Mn0uLSTH+Mw6S6A9Vi^r~8z&nkBa`Ud_=Pc@59|bhj*M5j=Eu&u7py zzw>UnDf!JdExS4YV(+68gZbzE^AYiXFzQBd4<2bzWj4J7|G}BD9V9HtN$x;8)A^QM zqSs}e`pIiU_R}443pMfikuftjTwgir=J>k1WEm*-iYGd z@_WS&lmCJ~tsv!R^?!jaPQy=y^ikKQ()z0rKxj@*J{im97>h0`3|YBvpnhZWid>wR zKF$;#E30Q%e%dGg6T-V8BW_fJD)UTu+gnC(eAQ9y!3pI1sedTX5&68tg>DWX9%>Pz zKzC_;>*`S6%>38v$W-Y`HDI$c5jQHkz0mzYO@hXg*ZqcdJ=;W@4D(b0#zvBwe0%f| z1G1>L$yc;B5nLcvD6%~KX?MH(^?Yzn@Nu?f)p@5StDK2Y6C^=aZs7yvr>ByzDtZS@!+6*7Xmx9g=4{aMncVA1-kKDY#jrY~QS>XY(N7 zb<2f8oAo0}K;gTI=2LIwFP&FjXWK}l_7uMa`MX#_Vj! zN6|nR-n-)nvMRVTAT^!JjMMdAXv2<~!F2B^_+FALt`0FIEvA?*g86A6$|#hPD>L*l zJFp2`+?};I=aGO`diRpDpg^s>$TwS&d`)aeK-NjpK>{At2?p`7CHq)E~(m(WKx z)L*qe!yV)~L1%liT9TCu+r~YO&f>5$wspgwT;m~bjTRojfhs*G!M+nDSBH{kcSp!t zk#!$<8M5z)Ne9!P`Ebs-v>n*bNK%sEXn?|Pqlp^)^C!hxN*q7CcdtnrY8vi_w!X=M zj6WKbad(M+wHBQdTxOpq3Hb{%^uP`!jaa+L0PeBv7#t>$bl6c?Iv&#H` z&=b3yCI>xG2R};ImVl2nb>DP+rHgC}t9GmR;xHY@=A=cHO6Kswr>h0#^AN9+SBX!z zx1IDh=rnLPc&L%3Flo5v>-gV>N5;4I8@|2H$VC?`C*su$5j&L}0SzmPZb86nEbc?+ zpRvq84{`FVlS&*MRG)^CfBMg~WwB7YQmTPF7q?Z(HjN6X^GGp|Recd_!{SVBj#1N( zAfuT`-5@ZBMImm4 zZT8U%GrcV#s<`~yrk(L0n5^>9WuVX+m#x=00B*R#G^jf`H+Cz-cw$nlxUryH0^KI| z5W)iY{JGQ@d;#M0lSo^uUp<}m#fQx{^0)mX0`SotT>~?~iz@M74Nz9k4E<;@tv*}X zzdGL9DPRxi8CQnMfh*yk#ykEc!+oyhYLz&2*`{^Tf?{lF%LXlH>r|}<8`GoLx<{(6 z-_x!Zba$a;z{+10+1yU^x3;JsjHm2bS#7z<_1BJk_e{9Hr|{cKN+Ze3a0*}i|0I{x zmbq0YGy_h*7R2xmUmp+tgY;ZurjKan#rdAH_anyF_wTmqxyket#U8%Qyujxr?_@_C z@a19rz2WKL6mndnrYbF9V0riIr09vhhSA^H*Gg^o%_i&8EL3q6(QGE$yVOV{9lsWyzl4gZtYcJpy!?(O5AtC4*$9_6-|SI8cNWfq=p_j zxRhd6H0J>pGY-b1smHL&kfV@H1aROSviXDNO_Zl1tnNxObzmU$C8Z$3%Nh;JB zKR+w|<*X$L9*Re~3N2|25grmnIgcU{^&tHJqBidiUIPy(eqD6KN66b7QlpOwvC^j9TiA{u<)^fby&zHu}CDKjTM+rracJvkhA7+X2n@W;`$AXNC zGF=_k51mlQW$$|$Ztxyj!gm#YR$10Z2a*4+UZl6DhG?*>CCm8puxVM<8uh@7Kl$Ug zpq>*@xFAy6l;Q^Kok_D+&iFGst{_Qge~g;9LXC<7IfNF)6o48%m694vJ+xgrJOIHlgPju=qvy5t3%5q60DwCFWS?e^!aRQwbKC z-u@d|GPA#U_(xwV1R0gQ%OQ7v@OsInrGr(3cDuh)hv|5mK99kZSMIfYI_6c!H8pt_ zgG%ZrB+lEvn+4PPc-_G10YVO$jPA&8W$YdYrVKBzWq1uET3+>8m1lbh)MpLHn1`yR)knSo7pO-^o(wPwWk7cg(FCYRzWmP6yXI zG1qWx&lEEh^l+optG*J5(VEX6Os!XFq*hj`>l^HBXt(nwAh>zkOGx|8eE3s}#s`qf zLsrGX{Jk_P6TKgLoZN0?K`6B{(;4(qV(ZP$>qG2BBRzf?=a--44mwW}Zl>hx=yQvM zg9Vc%v(oKV#c}7ahSBr5Tl-GweR5PB{*3WC4`y3*4!)eUai-UIR#KMscf{m4=`fIp z;h(J-D9A&Y=cAm5BmIk*ohQGg?6-avHcu$vcWQ|Ca@jYP$Gg;oyv>U;Pk!4${+FS5 z>Ltj28F9v%D_ie0Q+_3@e~0fJ&J=6}-Xg6R^aMA8jbqB>#?8{&Zr<^j%Bx zQx#BbqL-Xgg@Rv7FdLtq`Pjr2{5z(#)!mis?CIL@a6%91HTt*VzzC_(dC6a)rr1pv zGWtvG3B`nA89eX*;8;~dK84QWXs|zsVxEfFT{lYM92#nXT98u|Hi~$ zc;Ev5WCU4dC-E{ZTYX?aqM(bZ(iF2EK^win@!RmRZ+t;&Fv-Pzt+ZX3zMs&qw25UkN$&efyvm{fmA_A z>s$sP9wkxV8zRf@8SkDl&#MzxKkRtcQsNx*qS{jxA#hHDs_y7alW}1DCfZwleA!uo1JblFEd8cIuAcLokP4j!R^}fwwIdDa7 zo%8_I&M?Zh!Xmdf9WtLVHYu69Gu~L_I@_`tKGfVM;P~X+(9)042Xa--Dk*;e<1?XPQI_+ zU+T|SVKVblHltbX-IF1`@Yt4EpyD-Xx()gr@tiz&lGvN$`jeHy-KxfdxA99>gIyuh zub6xw>)~dSk#1>mavc;D=j8Kw3hd}LhG7B%zUd1uOcwJM z@QTVDs2>7n+y68p`&wW9cR8m@xP~Et`PSdc)|4>^y1zOT8Z7Hny+8lKk)q?xCQLa! z>|w+MSpN%Y?`C61o@#(lk)y4fy;TSi5%K&^Smv38j3I4xNoJ$ls@EA4rAx!-kEO#- zAxUJfaZ?t|OhNM6W(6Mq?5-!o5Z^o{&!`*y=4uy*VJ)kssa{Z4CX(8viEvsF*53K_ z;@HYYy_k9-4Yd@XZgjQ7@V~-MlWwTa0oh{ipKWiNpWEOQVlqr!Oo=>HcJ=t#N$dyX z{+}-FP|HOB5hgQTc=2g9haQ}tMQyM?27+cwJy7L0=adp_feN%ACGz^vT|{Q&9}l6w zC+!Z~P@D{1I%TkgArXaLS9#2zc|H6}sFv)gUsSE*8Xl(yIRTs2egW=|0I_XH^pRK7 z5P}B%7Zz1n^IkPb0FRUWINtlluu9`H8D+fa}RFgd= z$r(z8oXQ&hb!Zg1x3^1od|Cc|v9%V9=kU$ui%+pO)C@Aggt9IRKcjd=57Nj&M~zLp zG|qP7uO;qJ%(@65&jy{)@QZB5 zBa2&^1NBbw-VCW#8$?qq&qwI;POje8cAA?d+N!-OhO`xd5{>gOrI!KT?~&@M>VwlI zBXJKFSeD1J1ffFejZ3@&e9ART+a8xg&$N}Qme_WBppvILINZ;%$<$}f`@s)IcQ3W|GZ|9cN>Y?)pw(S~+kLGJ($RxPAn zOkZk0vdY4Cm^hc(ugIU4Df^bMqhC-K6Spra|9HKjY@IV{8H?Ia(G0MT7`6w>G$7F1 z1lBRe@iDWqAQe^H?Mc&71+eA+i*8+}(A%F}H0 z0r{Smj5tJ|^S_`jhlXS?4dH@n6%Igd740>;(ko^E7%ccZ)8RL?=~)ZUa2$JN;C_pt zY1IF=S@;ubsxGy2hL2c&LS{)c^6+Xue9rj1(dS7zu(^9pb-10guL}E4dEG=z(AW0v z9rZrUTL|S0&ZNP{_9rN=g6hXttputORM=!Y`Z zrPFLFU{F+x7o|Hy^KQMMv~YBPLH>hgm;+-A4tI`?)lUNdxNLNN?)dEQ%Ks4!kL+^( zYws%dM*hz_vG*1pkm8T1l~-Yym^RR9Eja?A;C{GPGivmlLEzdMB@Yj1Q+gFtTxsB@ z&bwWRBn*RI#;Pr`lvx%)%fUm}&T4gN4o#-eB*v zkjw}s#{6dJlD0_Bp-)0hY=SW=COiy{%{s9R&8@J3L4ZX-6)kK=l&{%$HgoYD3)Th! zBqox5?~%hJXH{{QRl((argW@0blq%o>@-4lt*Q!yS7V{&@GL~@Z*wKaMt^gz<0Dpf zdtDu@u^Tdiqg%9B>7`Qf^Uc#%Bj<|kALAt2-@}t89WLy$QKyyW-Ve)|m)grgrdrzA z0vNv-0rh{TyLBg=MJd)?z7Sh))r0Ua+n2g-2t$Vhp(i7$CAn2)#4P1qY_Zg04N15P zZ40(VMW_Lkh_NDtAwgYP+&LI-vJ>YC>RKL!-452265b*jTjBG}R3 z(^l#ct_6{__QWkPz7q24YN=c64vgv~j0tw9fd0?`?t#ZKGBj{mQ!yl&YIn_%stVBR zqRE+N-#ea*aI>nyD!>S=Ac%l$+&oFhw)~*Kaj3i*&pi(sw)5 z1{c_QkLn=sCoj|L@`94Z$h5`sDi+T9yB4&BcwN4mY{fE$Agnm3h|x{ooHVuQK+m2i zAyN4atS#lV^p>CBJudEN7iYMtDi`h)0H|I<$I33}UhcuL9#6(2kMO*(zOD(`8fK;1 znY{T8cUhy#sG_U44(ZQDstkZX)Dqo@6HrNiDX4ZH65`ekg~s?OcCxBS`R4q7=M&K| z0m%p=g2SoQ?L4K$#lCJbGM~j22fe~f%4j4kNc>In1o_pa04~lrPaX@*$?3LSp5ELK zKM4}9f!wf?m^u^&MXC=;IAyqQm4DX&d-`s4T{@HGU94bXvJQy!cg#_b~d4wKigv!}@BnVe;j<3_?~&OLC%|8tydE$7B~ zFGGGf2DEN~l2pyo!$#Df$%qNCR(O04DWi7Hd;nI>6_jQDwRsiL5bXJm{3nku%Bl8H zH4sRp6@ILvx<8sS}^QUkukIU>i!>L8z-j1Mh(3B9~N4L6-r!V$Jia}tN%7E2vp(+#Bk zg2$z#SP3%^EHr`AesluI36J!{l)Qm}JpCh@p1D6kI%=uEypu*&-8g_NWp3JVQ3{l1 zBU$ocNa%=XA4l7}9j&BV#wypjkdQ_ww$qVbbFK=+9q0wAk;k85n(o(c>c87+Q9Nu2 z*NjrK5o=BS#0Bfy*n8f6|)9%q%2B6%oJKO zI91dmoSi5Hk-^q|HUw9FHJrQd)h#|gr>Ry6^j z=u5sC{L{Uok`pTL)t`j8N{cCc+zA@H=Gs5S9n`u8V?2PFiDBFf^BW#u$f>Xdtd07t zcoTjT`MBe}Qs1lB=VhhG`%JAe9%Ijl6vrDmZNacMB5UBiGHNytA$}^L=sM zd^|Wx(tDKA6Rv~W$VRmvp1RKqHx?H0ET$=+iB=*)?-Cy7w$RBc@E^X<`jqU(J>C9p zaB$5S73&v6w=*)u%A_-k$Js&rLi*Lk?&nepXJoHP1HN=+pdiG08;{#ql62xz7dE8y zjuG`TXf`}`*PEpjDswnKA78j;I|G%pFTQ&xLz~(-X7BF8QWJyi_1X66GA_1$Sef>H z4p!rX*Gh@I!J`r_gBR=?hfPOe2mRP*JuO;eMW4Sa2MGsS0Xog#qI|7!I%d2_LiTlu z{xRKwZ{IxJ)8z~%-@5Pm z4IR;Q9Hz5hZllh7YaEk?l(e1ca<~;Onc?Ses#3FF{o*@yK;tWg&bAqi&?n8@GT|^? zJgF~BcN$GAlUpnP@;HAc3quk5ejt<1@vY(k*ZQRJ2xRJ=&`Fq9>rp)^>D7aZpxE*>j z%80F&rIE7I@4_I?E1G1oVOtmh=p}9C#@76%QTnc>BrY~cY+EPK2A>pFzxK-Dx{PJ% zl44Jfb*IME$AZh2#Oa}@96~c;EaWWO2<5;IYerf|&w_b|6(YV`o$u4HC6dW@`g(a% zR{tL97~#bXzPRHBaW8Ka#NGecsKF9`%@o)g}z8PM`2*h z_sG>d$s9+z?$VUJID`+Cz_J})Vf8jc{Pv0H51N*=z|@F%o{IUXU|!Jt_>DB@|uk?H4+ zE0^h0C03?wppe=UC|~zi3GE$V(c}c?9_G9Xd$v&mEj?+0?xa(>C^{gMGDf#|MtkeP z05S_$J{?L!?M>QQ6jcDqK|`gtM{BKgz>h$`-iEj(G}#*tKZ)S>tjaY%J-Dce!R{Aq zz&pCSJZ%9K{uGh>e2R@NIVW+V3fg<@X?j}HDk8G5N91*CtDB1d0e?mkoSh9aR?|yI zUFZCNxO>MiOS)$7v&-s2mu=g&ZQJg$?dr1G1qneX#$ z@7%fbx-wSgS`q*Ftq4n%e6y)j7xl#cgw8VBa_6S{V^B~SZ|VMDpTGs@I>xwvNR;-3 z>%bh$n$OwCI00&bnmFH^Kc=$^oBii_{{F410%ILv3*f%3D!H`a-s8MeUgi&e^-VS% zGzlOa#i25Jjm0b(&o&?40q-hMq6SujtbwiB@aIPN?b`C*l%nO z9$0SM>4CH*`e|1x2@Db`B+tQQ;*{XJlR~^S%5qs&5HlH}Bg}2(sCN}u%3@|#)ZZgPq zW_4$;Y~M|tzx@h6Ggw@!k#97=gbVCn4eYvR3Oa89?jx+(NdpyEi8JaUFuht=a!pLR z7NvE8=v5~IIcV6PiC@v(=6QGY_4pY6_b+u&a4><3A3pU`(z;f92f@V|#CfbYXQ8B! z2PGFaXWbEmekbnmr@L~S|GJb%UZSl%f;&1s?CM`vL`t%Zx(RUfBkL6(!n3MsM$A(m z)g!WcMilj{GY_(%>sE(dP(MF+c0B=H1{!ZnP3e&-s$#2#oDv_>O!`!Z9YK&36yy~{ zWRPEk-^b)wEXqxTKH0JTe*{4H1p{i7T=UVXo)7ukJqaL*p{*!5bV|!O`y*sL7c@;t z73hS1P@Vhzv;wSOfD2(_YA~47DNqbOTgsxKh78Ar7%b3qFxiQ3&xa9!7VwB8feRsp z5G`uBPl#ND@Cg0$^%FUQQ1oBNjfjGf{&grU6j~Vg3-EgA#Lz$f`DCQ|>)%ewR{(Y~ zk2!4jkMcGGYsMEcTY74m~!kFyQk3j<4#bk_ipca@Yb831cS}md18QZab zwE%>&NHUzU7>uH~r(nk8DafIN2zh~f{bR}|7Z19+QqNhik4WuJmpCzvW{5`*7jzp9 zI~TvmA;s)=26!@X$x|1l`1pOynvEN!5y_9SKyT%6)L)kJZW(kY7FaEh-x$YL@yCeU zMf>-bkq4gm{;?30wPY~T>N``o3=yR#j(`Hp$vgOaCvOHKr4XFOm*U8-BT>b`%1gWO zV8cVGY6U@oOL!)Ce9D`&mghEy?Xd1G(aUi3z{Xe1KUV3}gST>z_US~A5(&)}it-pi z|Hq>F<@tChd*2Qq5vo}85glS-GSajH8ylNL&8#Dtwa{&d8G8cwc{9bTF%(x_zl%Dv zL9r;OTfmAD7!sPQ<5rue89KUb$3AFMvw&uwoS3k1S)@>nN0Tt}X1qVDW4=f;@fau= z8AV6*2!g!^C`Q92j&hn`=2Q;d9aJe8inlm>2PV|g%1Ti@$@XV^D<<^!8G_BOPdW3f zH-=XH`cB5Z?_;4cmI9iJ)lP3wNGp&n#w2cmUCg<3sNrHK!k*Mln%wh@OKoreXZQw| zo!LZ)`HUH^%=R*o!iMR0&QAgKUh)Ezc`nV(LY0!L0J8+VankrrKud<-EsUlBjiHJ% zEm0ukJ&1Tg5;NE4N={lEa@{foGs!?ks-qC>YVPW#Y;kP}9TOyIn#-m^4X(@}rsdwE zh&#SF&~&@AEcXVpYNhE-)G3EV*rbAdN^uL?e#6G81sCuAW4zY9U>R6Afu{WZ2_35F zT83W?dv^X1*Qt7w4P-c4mu83O-dqQEI{nbwPae)y%NIYTRFGd@%EtON-(rJbb;~xbf>=$aYv>} z32b zVM#ib;LZ_#&m1QkQ0^F6Ur#Q!0A1G{Y8zZ`g^WzNqJ?%GXt-<8GtaH9REZ)5-1g=w zqhiQ(oJfg%8PjvR0w*`(&q(|e&FOl;1*o;pzQis4xlM#*U%`guo{7k-wgid zVtk;~z}QHih@xs>mtdwgK9#UA!YXqMQAHy2<8Z$Hh_@(4e?m7`R@qyTv)kmOje1Ib z`lN3pgbrdfL>%-M zK3HSHt&YEmi!{kp7_O!cIvV^zp24N3D__l`vZ$SNn`mj>uwe!{(o^;!d`)20?=u*7 zZkhLPN`>B-@SNTg7-Oa6VFZmRwy8O_TF!29HkLu%K_dIik>=dA23Z&CTx9+S=wkZ_ zR?!{aV=#X*Ic5t5w1?FQ!aF^9j(M*-0=Rbw@P=8pd(ZP>^=d$3HUwh^`P0#$k`=6U zp&?ztB$(${v{X0w8Wi7{$OOeE1Ap3%QWY2HTJdMjP!@Y;ayd3Eyj;eIImxR9PFacq z{GMDPfb*zCKbR%VN$$o*K%<&)p_2R6mcw!$7EDTvx+&C1Dso+OaqYh1Fuc65uz04g z|1D9gG_Z!R8CHv!O5t~E_^n_}wKJd|AxV9tTLw{Xk5)n-L+Ij3i@!Y2`4NFt9+#n# zc7CiMElAOL)&4!D9T|FKkwpYMGt=z){PXT&zv#lR1F|8Gglq@>mJmRVNSd3-AIZVY zUPtnV$E-e7*Uu5cB_M15pp3%p&KG5{9h;TXpHn${aJ9ffM;^FH1rzeH1X>7V=GKA% zrGV~u*_MzOMV73?HW7V|5}XCRKYSEKF(l$v?|iqETFr_PhqobW4xO3}ppM3d44<{q z6c$~`$l^%CApT0UNegh-X?AWi5!rUNM|Ei}i|Y}g3*6)`#c4Xgk(RAVq$5CcoEn!c z!yOsBhVGEz<8&xUch*i=0WVOQ~74ZHtfv-PO-RWz&5tZOcsA`~Us-9>QfyerGT zngepTyBK@n_kL92{8onY6&hb>obF*(_3$;70nK>LEokww>D3tc2}F`&1lN!Fa7!>S z6LT^bQO$_vowh=FS%RAOaW8zt;G@HW@rVchy0V9RJy+L>Ok~#XWUM;bbo6>6a1fHP zJ-f2{eDOYRM@W+k;OyNnY&R#WdbO@_^#6xa1+2o2%iP>H;W8I8%rsmb|Gs?~*ob~5 zsgmYmMyp{hR475xX7SI>OZB6Klr}uu?A+Ia#=a9~aH)Vs7~o5Mj`6C~Qd*e*$#r&{ zc!6Y9vLeM&+xaWZAs?Z8kAQVm>ggB>i9PN7NQ2d}-b`Z!9W_I8wK1N`N+Sf^hde+V zhvU)=A^AKlZz?3u{DNa(9zvFZSsAM0sg+eT2EJ*=r)Nq#|3_6mD=j_s^xQ2$SS~~= z6t^rOFZpc3VUotDA6bVB4dU82@hq#f2X0soM}fO~E1*q7O%2dHtG}M;h)aS(R*_FXYkA=G+_$ zYCsi#pyv+#w&5uTSZCC8XQv!EnA%g1pJq)jwFLk%XonuoBYLz`-aQhUY@VaCGdOlhY4DLG4601m)Yk;eN9{q+O9q7a%M!!6rNv)i zi2)@8jp#p1JCy{&zt-0Y{{H<7jey`c^Wu4|rC?`abnDkp zjZwpMnIrr=pXm3f9@cPiTj=?;&b)&AXrV|I-2uf{e3;oNzZ=ErRlbZt3Cq zFhRV*cNUd>{13!gw%=Ut#h}g?o?F{V!!X94HdWAq|BN*UF?1_j>#DHaAeOxDSm6*c ziKjJ8q@>Z4jZ8Mf88$rv&r+E66kau9_gFL`sNx@PxyUqp_$D zJ6MP!K>`~Z>pKT8(H}NmoZ&S1(7`%XdvWRa_1mumBb~kI(MCp^&DVzQPL1to(u~M6 z8_8~nWuV2EkLqKK-N!wtkQBs+$IGt97Nzgaap`hwc(x`LQDz^HXfB~;hEssW0wbI_ z6LDjN6vVvuu|e7L{c!V~e(&(%_NJwBh_PcRyvZXl2ItcHm`r{@%&+X}5a9?3BFw)W?Jm#x|j zMtRvUdG&v$K{@Jol@&UjcxP=I<-0;VSqSL&<>m;C9p36Q&)wkBr&MF1 zIKK$QF(OH0%?53&I_*|AR5nA5rASb>^Rgj<8?-&JKlCTt1!YZ{uR0LvA$&d6jTBT)dCXN2hK97G_gH~;YPRr@7?fN<%;&eYuQ8PtJOnd~H zR>3f;=FRKp$w`8O!zTi!qc()c8*v&C#9Bddn}B0wR#F*PxA>t?tl~)-b&d=lWgtT+ z$;ayHrTJY8iTEW$^Rq6~3u>pv>GA_~ic2`Xn-J9=M$v=GwGQ@^SCtFX_*78+lE%zz zxLb@ARB#>&2WRh2iU4Bo_Dl!Q$C<7KpMacLWaO?<$)sfRx zy-YJR7NFNU%Bd(^1hD`@uKrV=%<8X3_3z{}>5<6^;wMEIw#hu0XycdZIjPJ7;Vxxa zH+hiZ>11WMB9PTtolN$zTb9uk=j8b`?D$3PKI7CnPghW(oT>K%`rP4Eo$6~LJ7V{k zW;}S@)n`ze$nPDVgi#&_-!hytU}(qYZ||O%Gp^2L?>3s!G&S=MC*NuJ*cNepXp)){ zQB~9_3LoK3j|v*!Z3=$Kd6NV`)PpknW%LIoEo$OXzsmvlZRGT^PV3(q+2=dj!=!Xq z4?d=VDeh&kChhp&_NwjX9X^-ndX*;*{Y76>ExgXU`wmk6j#cJVwWY2yzPRnzyPT_8 zv)C|mq?ng=-v4Y$;x%7Nf5PhcK(KvC{Y{$HegOG>gR*A3=T;qN5m-~`&c@;4&TJ2o zL?Nt&YuUzbul$R$@KP18X`G{mBlzrjHu~kwft=yC_ra|{GI(iECF|2)p6lxbV`5=D zBC zYpUZp68lPO=z&@sMD5=ZCbe0sg9^=;hoWL&btO5B!J=j?;16nsxc^KF ze(&C0)?ZR7s4~Yv}ad!BzFK`414O4MA*Ur-1vmc_yHQ_VK zc4a@kW1MsG+-#u)4y~2&mZIbueD-_HIv{fi`1&SXX<@D(*BP=x&?b*9r=I#pnB&zK6W%lS~%?2p$f^G^NfGY5WWB1rvxaMKn+@hXR%6FkW}j(uW52dzE_s&_c$&W6Lu9^g z$mEuBTTWR^b6SLG>^cGUfX+XKzn0KyXzO}jzLcSHV4Wm04R;54x`|EfXD`|;grvED z@82Vuo$U(p;tE0Mu?#U$2*M-40bt=)UtX)znLTlw*Jy6)HdOT|=_x38(Y9DuxEeX4 z=)O9BP;n#;^1bC8THkVX35%;yMrqiaM7D=c)3feeT+&w7wWhy!5dSF!N1sl8l|+db z;-JY$H-$ON0ChVkxnr;vc&uh)F16zZ3KkAvr%&Zn?Y3YBXq>dF>gfqMX8h%~Oxqd| zb)j|B)(PYTKZ+fzyvD1{*AioBpX!_*J8_+wD~&>~ygRs-;jH0aP6SGvjEzCd=aO#h zs`e_h=HFt5jY@7R<0v-m@V&Mgzx_>{=*${N7QfzMhHc6r#;#qY>#FT{Rzb z3+`>w+Xe|Z$Qtxm?f@Seju~uNl~N$F=$Q!qqlX+aN-tmVnX!?IyJ6&mzm^POU?Ik& z>;k~n+e;|do$(g?6m$I6g!Zq17{UXUt{)#tWiF`ga@n?l)c%T8w81keJ!ibYOJcQK zGND{fRCjHClOq!*wCqxW3xsN+V(U}gv~OYp1KMp;$~qUvu9=&CbiV=US-@CmjB~e7 zhFCjw{Jr>*K=sgVi7`SknQ_A1N;ay8J)mvaV*w;ZDLiIAgQ1?X9dr~JhTr5Li*vX0 zJHr^=1n~zUgxZFb4-@z`B=+WCJ&-62RD3e&U@B(Z|;1Q5iNr~k^W6caA~ zoPffeknHDiAiyDBCz-YXDXB1N@&MN***Iuwad?yDzqs>+V^j{QfUrq;*HVeAl=km-#TGNPDl@O67`mU+$YW~w zvT}})w2%8_SznH1beDzK(HYU!-y6y#cBw%oms?+x3RV5~1g);k-dXTln)-g#!uH6| z>kueqI^(59@JIW~M|3u%RyPq0+U1TvrA7}88kL+}i*^0QoqPt67bmlUjki z;M{y;e`Yc(g+y=?g_|pV`G!WTi8IGXX<4yn8w!7wE~-xF2KoJN|5A4T+Q%u^Dkw5< z5NCCx)+hOgs~)g6b5xluJ2Lx7{pC-_J%$v;#|$hkW~5Q=$<`?L9{E)a^aE%#WfL@u za=~Vx59R*tU#h0PiC;)+@iVGCMw&?kJJDiRNy98KTbLx3dA1Sv;`?V?{fyEpM zoJ7T+JtIMnhHKrm>O+Ezj@v)TT~2}pJu78QN!ppesQ6wAl$%;U6sIu98}dtQ3T_O) zYNy-ZC#7u;$8xB-IgB#71>$7~Yfg)$K~3+ElRIA3^e24S;*_*Wha>7w83iRB54_yu zM}9YmmQaD1uKjH`6H6U0Seb;-h~2IW2=&0IWt379ABXmEBZ}KYV)mjUr#_ZIgHIji z`L->RH!2LL2dMKEjX-khn2+vXA`~E#i>B2+5p-N!S-&=iCcL?qJC7L{)Cg)wxvFpZ0i3k-kRz=eB;u$Cep9I@O}d$&VPh6(>4`QaIMPwoiK75d-f?!=;T8!fsj&1 z-M8vg5aE$A`2&we^v8j<`K}Iu^IAtTp#Vu4cp zQ7)oMw4NQM!9&~1=6Ee=@@_A9hJFJb_)iS#plHs_2?50>aXAQA&Z}dCoK*a zo^VnBKAK1eL}r4muOzDQ=26x2&36PJ1lk2b&B@U$d1Ho90|BSv(6KqEQ#`{;`uvF> zBNIqyDHhV%-D)X{UMeD*Elp3U=#8l*9i*AqbQ_?59EmU&;BiM~O)QkQ=V3t(6ed|$ zH!xYgMlV62XWS}ds~Na4xz4C~huz-)in2XX@&3VK&FRgis3Ms; zWM*kH=jh0}yYR8T!H%)!ZAdndxU^AyO`k0tqKUV$yT0&E7b4S<8P(5~?!q0P_HPX1 z>T-`GzoLvP8qBKqVmf=FH=EU)4r+m3NM*ron{n`;dJw#I%1>u5$6(nvcVXY%Qn?J@r_0AyyeID;aFP!LI-v zyf{bL0~@;LxTx|e97G1oN}CgBb{JTWp^j@*{77I?(goH4Eh$)10eEF+JdLKb+r>c; z+29k$skJIC%L-coeiQD;S@^h7y9T`~;F{Gcs{$XL#gs3DvFuCixkFGII_*|(r2_(Z z&GCAb^yJhQ_KMdfBZU~L`#v%fqh1jUd7Bh-@TAK*6SU#X+1Z{mSZyycHD)xuWP0lk zM8=YOPLo5{H*wt+P%PP<4=P=nI_9wkfGGgBA@6mnMuPh2t^<4?g~bO;*4$*}D;r}q z7YKn(72#Gg?28xW(?y~QX3kunLtyUkUx@^X03sqHl1aPi=@81Tk(H6ybN3NVtZ{^G zq@#Dnj71r_Yz|34s^!~|{O>td08)=JBpV6256&z5B05He!)BX8u6@3wf>; z3-sg(Qk64Y6NX|_QOOLSFrV2|>^w4oSgcxC6|6AZP~UP2f_T@#y!Vsn9XxHjOzDP> zjLaL~iWtbx3$bfAKE|!eG@H%d=50=3T}l@Tu(Z2ZI~7W$$5I2`j*VxV(BLKe*JyGF zM~Jp10$2y7pnk_Eo2|nLB}%m!)otdyr+P{f5vB?a2iI+}F=YDN(B8Bg0a&xWdi+j- zm?ut)pLCNAYrE!CMDhps8!b57O`P0uyQTRa?g5cChwCq`Ht6<%qPlDM_3OASiMdnM zJus;C)8}gs%3vf^J$rcMMC}-CG^6r)E#@MLue5N6PoRb#!rD79R!N?i7&SVzafCa) z`}Z(db^hC2GEkh%)^JbBi}K3g$VC*R(BewT9-10|YEz*OG8f&+HkjB=Dms==zd_KG z(G0=XTjGz+r4qYyWILS^wa7cL+!<(MGsO+fh`{k=nnOG>=jvW>%bwSZjrkvvDF8u1 z7$DP&A%Rla+3|gNa1u`=6wEugo~>{{$kxWHcMH`2BLq3EtBPt+FmoQYr8K#vV8CS2 zr6KIH7vZnc)KUh2PLp@>lb)nCNx1=sqxG2^`^P@E2gRM6vyZ`vD@|06HEB53xNAlg ziP0e>oE!p7BLEuGw+iZS+L^HR&mJ#lr1fWdY?+mt>XQSGuo@3v(0WkTkwpmGRb?0d zrYqPdld5*zgyFX3yVBX=y4Oa{K2}9%+9WwoJOvc%g-gTAvLb!R$-E(yO@f~o47V>! zQ4O-0A$~>pz}bO{{&4OJYJQ`{@Zi1#^X^RGR?P@6^IE{9-kWiY^Y1d@-VuQ`<#<{D zPjyBG0YJ>}Ir;q@Wcp>!wBu4JZ5(eKGR5mc9UJn7NR&y`QZ8KAe7~T#=;r+t0CGe9zjCG>7Zz{~X2Wu#NHQ4;e zyksq6STG({5gIPJqJA1kc~MnRO{)v=@SWAz?9^eNw!$1 zzg=W~`~8o5MYrMm13cq_?ai=OePYVKk%kfaFS%Lb&7yD8D6$nkeA#~lORL#c1oF~^ zR%=z&NW3#OQ!<5pk|V5&v1I+n_N%5R=b$gWvde-C(@#u}9OMz8{w{`yJ?Cd2^;W>h z;ZBe58HOg-Xc0|iFw}*PjkEu`qNv|(1V2iTp2+&qi(0+qM3|qE*>l99l73`Y_&1H7 z>>nyO*-r5*e5xc5fqvf?lSQj{a^Jv(KgDR+?HDfeK*qcdN2?Fyk zQya7t_#J}oRQQP6iD*{lVN=od2cjMPW(Fx>S5{i|Gj{}$3O2Qc2CpCW+$ky3zthpJ zjMZBRPGg06mB$w>I(F(oZxs-xe`o4c`-(*M!(XL{lk)?jN@g*t731jZ866-u5TFnw z0Nr|PSBK3y&_k~~C|r@Ldkm#~$`a*MX-p{IHjs{nUv&{p6h0X(BRAmzelHV3yT-3 za$7u>4vEOj0u~Iqmh)sH*7RT|)iX2STCf+qjw;rsjyUNS9yaw}DLpuKfUDj6QSIpq zXzC$({k8NPE%vuijCud`o{5}~*Cp87Cp3~cc+nD&7v&!kRg%vP?3@P4OAKV#l?=)z zOgzla@kiK-Kmu5QYr!4jEL3qHIFhQ3B}1%ToL267b(d0#&l&yg_PnAZHP zum3lZX`a}uFHYW^(t+DEWcrktzCn&w{k8q!W%{We-{kWlOle}`C90pl) z*=hb`eiVm(l8xY|^Xak;`sSzhzccTa%u53Q4Xth;R{oYO6+!l7X$%UjHmfzujp$^6 zLZI}N5{Oe{K&x@kpq|Bm%eadMW?4_P)0^09oy`{wfzNJYj#unpy?Z_V6IyCQ->oD( z+pyD$qkh=0t>xK&!nd{n5}x@L7on(wZ*kXYlBb1%H9B6G+Ah?bANuG#VR`xR`OH^CIoK9q@kBRIk0edQ)KerSkrB0|8wY~Hky=BpTPls=z_6az|v6RD~%9D5jH!--CV55P) z@NU%o98{8Kknb~z?rYtgDVfP6XL8)d$eQF5lQLt=Bm|tN583QwARlCLJ>hMOuet52 z@Ne7p_v?U6ay-8xx;BJ{?XUAoKF97an)tqrI!&latNIl>xNhX#{0d#7V2$>23V@Jh z!`>#4E4VDdp!^snAChq4<)y-8+P7*Tvbzp}+;sBq`dpUgkMoZcn7$n}=}26PT7JtZ z@P>l|0rQnz;BqqohrHc1wS;nhF#RNJu%L00NI`x71>By4)fK9CkcNV|7jq5X;)KRtb%) z5tGUA2a`e0bG!f%7kO=&RGW%@{@c4%1 zOL7un(4A1{x;`>J38D%|MZ_*#Ptt{yeL zhvBZ_TvEou!jY)eLhzzcjAL=YJ3;(!w(j*u?Fis$0<-t@1b z2aJ-^&+7tb`f-n3J};VEgXc+2>0!`fb0_amxl9;hR}1uiRZ1CwiWh9DSAPsbJ$@0toV!hpZ;AY=@HQBG0^ju_pT-R~J3R0Fz28LC)X;i+dl9(> z`j9-|1;j25H=$B!Cqfp1>d1SZIUld>X`}}mf9QNd25PZ{Z^zI*bx z-LC8=^GQx|rNF{D2jiDNRB5uv%V+g^$uYlpo!VI#4;+N1UAyz;OW`asG4zMy zN?Ds?6zcnJ_NH?j&FVyg`=eviBHR}YFc@}t5B|Wd4Xg1o3V{~Hw?N?G5U8Emm>F1x z8gF?lOQiVPe7b=X3>nUW77iH>0qri;@G#kk)^F>@S3$seNE_$6k>YCAupPP7e-=QQ z468s*g#0LA+)bF<7|_TdaC{+dfqX-`zRHhA^qJmZ;2#LE;glwe{6|OZGpM9y9_@D$P-#Kc|zT zX@#pfZSZr=QQ>(n6wxG_Da9~yXA{zRe3oFoh7;SWOyRH3o6o80?#Cl0K7rIdy$X+B zQNsWbUqMPZcQs!AUd`VzCKUdo0=OUP<-XZ0-gMmzUct|W1WGS>0;J?Fb*NXygF&%Q zXp_ZB>=hQwT9|0VsVq&O91a5im49DV+-kvPm$x7_jFu+)nlo{J*Z}))M*3sB?cu=f zXi|E|f~?bjSF4KNP%JsZQzt5%656W<-v?=qTE)Jq=tPOWKZeXUk{KycS1<-F9*47* zCdIW~8vg_5Y2U%mE4~`_cK#s)*NU-*lP!irWw~ZxVOm3v05fAuB%{KcuX-D2Bx#lqbnkJoE?aDZXZ(qL>E7?smhWB`XF%dll+wPtd zz;f}@hooSJ^Kb|wKIEc0y|$$ew$H5^o*u&W_m9Q49*Eq=VMS=IPA6{rOy!?er=NX+ zLHeFh`)aYe(?nfz<;Im=Ib1xdRncNxx|3%g3^^fSpc2Q)oPVkryAsj>>m4ZEyj-GE z7gsX;G5%1bAl-=h6KCQswc%i~-}S=;_^@V0H%uW7&C?8%s^qzFsw|Sh=0Q|dpV(vWGX;p zM6!%D(es1Q)2XDhxYKO@WAY~cAIS8oB$)w~IpA!RHk#=NrllZ(B`G&o5{X6RN-OhP zhJIcGmP}inRY#X`Jfe6Hk{>cq9Pis?;xD~|YC_1!RK5O;`ckhq0f%6w zWY@^@(Pr#cJYe+QO2A$!Bdhex(T;gKHs8wd;IpRWjdIF- z`BXrwk8-YbkJ@e9yI=ELGlMhAXuXNnJkP@FNQo<4^oYM&4TDMH)b`YP9#Xn*M~&V0V_*t21D%J*BzL zxTymvlAk(-q)||hh=v$m)SmB(_AHnZusMhD-!T8DUHhlr_n+>vCNNlES+Z`SA^xVc zaG75hb!yuQby-$YD zrRO`d`kC&R4$hp)o#sangYr+NRV~l=0|U0_k0Z074Iy@nq+E??)RdmRRbwjfD9F9(zLY6zzv zg@uccl)2dIuqH9g4^{)Gw#UY6|H6krcyK{Qb~Po_QT#kE7H9I$O1U)=GbPP<>Au+X zRaCX;+PSH2hL02C3Xvr+PQ4^r+}zg{EXi{%7P8QBgfe(Uez4;)g%Yur%z)MEn8lG+ z?_@p=Er&s85mBd6b`r5}NyKKKc<$>Qs@aEgjbTnI<$)|6{e~l=!^DacMvjfxXZ~dg zP8t9&jG4V%GA8h6dfYkZE(`EJbq5@!RUBKHj_~RtNN2w==mU#MLBGe>7Dc_u&+KaE zr}a?UI8eq?hG`QOD=^0shFd3Fjw^ibp3AQ$JT-mUuuXZVpp4r!Zu+JYlM9`ahds|s zS$1|((fscWDKEdNxNVFU;a+IJTm88egkk953>GPx-d}i~Q(kqqQ$f8CTC76Mbwlfa zeUBkMgO!DqvDI|&%u;uLl}MqNQOuZ3Jm_-p?gVV{`1 zKKT?h1Pd$fEJ;>iH|+k~-B;@355#^0z5&T1#G-HCa4@7qg;Y8E*jjwoeJLZacyN9C z!8bp}W4~@NtHZ?V-*}WJu-mey8E#L@zDDi)(d+0>pnM7eDyIU=&^VX1rVjWOQWmYF zc^91~s+SW5PEAp)3gq%hLT9Vg^5--IyI#Rr&y0Cda0320u4FNkzoXP_Sbe(74=w%3 z`3r-VmxuF)Ixh%Mq5Q#yY5sC@@T4@{qoP9o1jIQuQ09)LbF4O5?yR-0H}Vwhj;U?V zga(a32^u-Dum(14xK!{>;__?ueO5wfZqt%aE@|M)?pz<#)3GrYOKBO;%4Ul^TFj_x zh`l%xNLkrv70hZ}sa+=t@9%vvHvdEzNgjcJgNH3E7B#mr+Jh+Pcds9Wlv9+@2M{{c@_0TJD&&>+-YJl#T=$otr=k=A~YX3m%^JBdyz{*4olEn150da$zsoRTSLvAx;$?8O<{z z7rEeWyC$KsU2_px1X&ug^X(EI7{e*tz;E zCs9DLcgr%L^-bcOo@U?0nrNJ4sQKq%_dsv^BI39C-pq7)c{^V0o)paJ23~pG?vXH2 z_Z@p7-FA|rQHMk85oU457e<_4(nKYMMgE;+O?FHCPv|A|uafmzc2_?pa|Gvz*9B{S zeuo!+OVn>PQ#qkfW3r^QVAw*oGQUre}9;)CgLJ z?Fe+%y4puSPP9>z#f)~!_*$jf`CCf(AApHs$d9zzY`ta;8OMCRyPBe=)M8o;dF(P? zO}VpkRef)k+(RYz;`|4Pc-L`Ea5V*-7*+g=0=Bh&o5PntXL$^aNdJj8FtX&X=4icO zP?IVSm7L~`g6;KD{+hH0X9>@p4jry;zX$_d;Gf3&ZN3t>pK4YHL-_z)+I&a-*@{Wa zc=PM>BAa6GUZ6I}!~7_{EnW%E{;Uoa*WXnq^0}ul@d^)-;rrPQsE7+)8uSHz>XA%< zFlI#PAw`*cSX~_Jh@WtxoR^;Que~R6Zg~`Ehu+aX3v`IyS@c-Icf3J`&f|f>yDs=c zy0G5*IdjB9z=-~JqtD`tQ*f^3CMy;|RJ%9Mk*?k8Rviz$VJF(!f5DQiAKp)24SkZ{ z!6W^dW>eN}NACHs7dWzoJHA0_`H>gM4t5`cnt1OY&`wv$6htpz2kbi#RNF0wb<`v2@3|Zndd!ek`Ll zkSW26IOTj1mR)za-Ml`_VvOtf1|Q#!YbRoq8+^};>McUd$X8nKh?rsZT=&n8a{`=@ z!(8=g+h*m(d49ZMN!PQu%VSnbz^>}4^SqvcepA#`Vb=P|QYzr>_<=>{vOZp&i9QNbs4^+vmQ&p2u9!+g?w(}cD z{r(^Ep)HM3DO?jn)<0QLRTvJmcN;$#zqrx|QsaNSyuw4zD(<)RV{xqA;LNBd%FB+% z6Qsxk;K97w%LH^MF85%kHth2UfRkpi%M8D^C)^fb8m1rqZm^6g|*5^WeWl0SuwA`;4Mz9S}Xv`bY z+I+{7o>`?x&iEaSk&*lT*?YZg!byqCWfzkm4CEN5Lf7zU;DtN$-L4|drq!Wee9f5W z^wH6Mp@xbpRZfK6=;GQ4$=N>Q$Q-zOBlpLBL6+J}{NOkK-2A@BN!wrLd4(^`$BOs4 zP4|3T-W_In=izv>?R0#LH70*~I8$NSB>;fp#d%tuK={i>=r9FFQ9pg(W9Cx7+;dC= z6(%LJd^JI6wgkh0^~@876M#I?6li!FzMGjQN3FPJAIp1bWekyv*!4t1=~a|ICZqRZ zRfLg+l8nrz0>S~dimm#R9q{oT&6>mnpdSrp_HL|zp7QpSv{I77!i0M&K^r6m>uevk z-yN+R~ax%QLc#R zOU~;0%;W{t*5|9S7Kr8Y`hl4~oLWqD=tHQ`;O(`O^*sqQWP*@aSSXxhrVWXyNkk*x zoE2gzWqq}A&CghZFDtd@xrLI0mU5A>B0iumTTLHbk(D?q;Y)F*g0kf&Ze>g^^ zeD~gdilzIBH+xAn5uJ1fU8(m(1(31roWs5;=VXg%2!2~LF0qb>h_?CcqlYOife3%@ z9|jXGFCdPHv>V8*=(;SqXWoltiG^b*B#Jg;w znm>5EDHhH7HC9Ij$(o{_Fe+4Ju9&v=J2niHJWJ5{dIBXp9qRY)+}|WeFVT6iBs*tq zR9o!X5*7>xSLXh4uDPG7b=#ejH*jb!(;EZwZ-po|zM+vbikGUiU&<*TtH&4Hp7nFke z`!0`wc0ukP_xnMsF&P~La>!Y>*@4zRgleu+@0mv^)VlNpQZ5{BMO8VR?a)(SKPf=% z#j}|;_k-Z!r@%rq`)1GAssr1AX4R-6ghm=A=*jKNySr>HyWR7(UidXM9g+`N`b5QA zO4P%$=lOOfMDenx+2pfM#oL`Jv(D?V)oq)JtkD4ZK!M$Y(&thUQ}?PI&_jq0Yl`!+ zywfG6!@jro``!lXZ^Kf_XRdy5kkC0KH%H7d3U_XaqE!3b$*Ko`8xu}0=0S=1Gq9vy zX;R+wFo-cTHu)whNuO9Rg42a(lsf(3^;w_Gmxl_2Ww@s@>0B!rF@< zZ<mQ&p{2K9osdqj58D=0vg zidAA=RE3w;z<3z`+hV9Gsm)6IEmG37zZ_~L_Q}yjp_i|-NPRMv%#NKkyA1eVPobs{ z0KSE?kww8TVo*_!4)~5uCPKIo%kfWn!he6$Q(rb7(aar7*g#$?YL0I@ew?HMKl9g? z81i8`y`dzw0$lhGa>8?{o6V>$M}O&`9pl8Df7ON;Akvy9t8H>=TCRvJI+Cw3J`xza zcjdM~2T-c>ro}p2YDA36&P9se?=NPt490hkgfUa?p;I7h>Gwj=npVg{|6i3|XEa=0 z+a`L8{)lLqh#I4f`h*A~h!(wuj9v%Pdm9N6Eo#*0y|-YHAlgWD$zUSNAea#ZVGQ3% z-aqg9zHhzndd{D{&e````#$%%_r9)upX;nM-cR(i?6pG27Hv0;q6l7xnkpIZB4@6i z#yD`+FMub1nBy&~bHAZ}qpvR;*-i8Fok;EpIbCXQy&a)EN&kpn4ddVL@OBS-{xXy6 zSOC>n{=x~_VixjY;6UEuvPtZ`;k!sDT91Pfxy{0X;q#kygIW8lADP~oFMDl2INPiw z%eCB%jDt{w>#ywBe$sfIlDedf!D@W&86AJ+$Gw`A)wp(Qb&lfZ>|v7+eRJJr?;aZx zovaFdoD$d88cjGQllnOIMi$;#AYwwtK6~N}k1;#4ydXqe=3Gu)2!d?-Ylr_j81VkN z?;l{jF``~5aeGz5F&DPqbaEwPH!S_qd7hFTzwxZhU9WjX-MAJ!YKP*fHowS!SDWHv z3KEiqh|9qcN3IM7Bb&sFn#o4 z$3^u}wP+FRd|@$mV*D*oh|5XbFa8AxD^H(+VnsA|Me!xyN=8JENn3b%VH@|6k^75LvV6EGMywqbSbtpT=A*#=HO4MY6hPGmU zqizl3fo172M&j6RU(q{c{A+b}%^QwZHFh=FN`*4G@=I1$1w-WuN9`<;{x7P>FrLzU`ElwmgyN-zw7dh7c#i2>FN}^|2E}pj&KK{y(EHAJo(@6nV`#!JG z8&mk2&yxN}@O|Pt4{HSL$R3%k8G0!MIrV-ZY#QZxN=| z-$wg{Ehbtz@&^@PzY;yZnCg7k@(cA9=FejKDBY5TA97V|Ik@9cG;VE@&8g}3A~K02 z3SpoxIxi4#m=QnSvXP3h7k~<(~U>ZG9W!~C=4prk#*dp8sbOK`GQzu!^HM8-m zl9vO6R}Fk&!Yh-tq3ydhE6y|wu6D-O#FyE3=fh8maMnts`9rDo=z@11V5^y7UvQF`pg^k~*C#H8Vy~&6*f%ZH3qq2z16LiNlV9e;ytz4C6xTRht^pj#8DM*R$=;3SZ~N}db@}pk7sRXY(DU|Z@1yvHwR(OLOABN? zzRdfqq=Q-+b;#<-K4@`puXY_KY8B-81n(7k8!0VasLG$u6*49$Rzt=zul8LelsY@? z#BF*@`kik#@<&Z=0%SBhqJS`GoM*ZDUS9S~a4_VuS?MMClrOIyM%wRHjyZga6QA+H zSITp@d(=93giGLEt_>aBRSuT14uco*LD z&GcO1NNlC9GR-tR=4*ua-)=}he=hk+S+xM+Yf{GcP3_m7epR^l%tP37DsAM4K>-#z z+}4ILZVC$BQ)iDsW{KH#M6}JR^>XnJ%AG1zu72wadJSz_qdc1VX${U$7cDAdX+Br3 zdwUVxN+^nIkGn4;5-3i>Ak@dZg)KfkFrGzhQgkKlg#gTi{t-%nWQSQFeqxOwK!z)8 zWBG!(SNY|M*b_-XDlgNfHp(tS>5h1^z8&5MVy?*^yKY~W^rcl8#{=ojA5+?GZn3u# z6FWZ?zy#c)4jD^0Ut3Gy$qU`W4lRG}k2Op*k_p*oes)(Nvo*SBJ6TC2zh9hiDKDOV;c4N>smpQ>L z1bhMQHlaNiI5gvAHv5!mF&?{!2K8V$!$!G&;qyqXYFY&UmM=}%v;gB}gS|cs+a`E7 zxra$qG$byiL#=8`eBJmH*6`D9!MtTWzH|%LtP`T!T1bTNLnWz5(e2wbfiiPkas^~a zv+kbgv!uoEyT!dv#c~TYKeWB^M9+oDe`XxK_!Ic?(g?K^$BqZnkj0=+yPE`?41Mdj zui5qcRxuf2g~KvY18EcOMl{rqGR>rBek>aJK!P07IMAeUbw=5=3m9h&5InUVbN@B;bVg|E$_a5kid{o0iy*l$pCK=w`Y1D=%XcyhfJYuEt^;FWa^>}K z3%`so<+Iq*UL5IMAP+?@k>Uu{zsoSi-C??BMK?Wei9CgJv6NmT;)hV_dDvS%ywSM- z?*%Yt#onyq9N4RN942G@UHGdKD0WC#c=_;~m20n$Dc48D@)x!B7uquRZzm&-3l^1* z$w2=qS=|l%AigTu-m-HXVQA(2S;4)ku1p9S+p`T34GdAdrk8GH0^c*Bm0ck^D z-VmT22T1lE)tsJt8c<}r9tUzlx_?-I@89yMD8k+BrIghJb5H_5PNnt7lw36L)x_9J z-|^XmPX(8zpdH#;lLCmUsfVv~C3WG3l@?_9T_b+am@s<-OO=;ye(AVTr{(?3UXC6W z=$lO44Qpi`Q$gly&xn^wTLvPNuFLQZH@l29&tK&smk4O@hcAqL>112G$KLlPO$`&y zp?Q0H6qTK1=wvU8$P28CM0?VbGL4Zw!(CT=zlJUZ=BPJMF5*UMNB&G~u23*Vraf%4 zO3i=vh&;U0No=$CRdlz)S+aBnKRFpIgz_^Bd|^~{#1=Z3pMQ6Z2{{P@5eYBo9COR= zw73d^MQLARvtcV{)+)3^lpt(>H^Hbgr0SfEO>J#}U3Kw~`S`CvkT6 zzq1#QS|u5@&+-@%l;aGpQ(}9GTTnGphE7k@rYh9Q@l|mZllx5DvDj4usHpHsWoq0sacleD@H@)#gcF6 zo+$4pPD)OrK!qAW5l>6!dY)+1Yo4%gMr*?LlHN_uTWw_|j?X}-pY4_{xb8_rII)h8 z)D|7Ie$(6*tY4XAam<=kWHMBI6bMwgf;#*Qx_28Ad``LNGeY8#^{aJ>5&`V*ZUb># z;^7yglrT3dFuW(@kSCWu-17NK9r?Vxgj`=4gybGd1CaZkfn$v_vxuRCLGGnTv{WjT z1E<>Dix%yMM#e+(_X-O?opX?~!WsUJtK6JqvXf-)7}tI}A&%q-5A9PLdY@wp>w7Wy zFJPzlMT>m01tj%c6x9IzVa3>nN!A{$HzO^zPf?OG#%*>)7U^_O(PSR2BhXm_yK|}| zOa4|DoCnBoZqAu{o4yN-my(pMEX4CGqT!Ara2X!>&Z;+*y&HXz(T8+f1lIC@f+GJF zX8y53JY!6X%S`#y%*>%*`5#cC&n_t7F4xJG+4bZl2ekO0B}9i<}HqN_{BKo5#D5l{yNK!U+Zal{9x#x8}|x3 z7!_FR@=H+X4zmBUT=BRsoPbO<>Gj+Bt1$dbh{& The module is designed so that any feature can be toggled on or off. This lets you reuse only the pieces that are available in your deployment environment. + +## Configuration (`creds.yaml`) + +Below is a representative configuration block. Any unspecified key falls back to the documented default value. ## Config (`creds.yaml`) ```yaml modules: - writing_observer: - use_nlp: false - use_google_documents: false - use_languagetool: false - languagetool_host: http://localhost - languagetool_port: 8081 - verbose: false - gpt_responders: - ollama: - model: llama2 - host: http://localhost:11434 - openai: - model: gpt-3.5-turbo-16k - api_key: your-secret-key + writing_observer: + use_nlp: false + use_google_documents: false + use_languagetool: false + languagetool_host: http://localhost + languagetool_port: 8081 + verbose: false + gpt_responders: + ollama: + model: llama2 + host: http://localhost:11434 + openai: + model: gpt-3.5-turbo-16k + api_key: your-secret-key ``` -- `use_nlp` (bool, default false): determines whether or not we should use the AWE libraries or not to compute NLP indicators. -- `use_google_documents` (bool, default false): determines if we should attempt to update the reconstruct reducer with the ground truth (Google docs API). -- `use_languagetool` (bool, default false): enable LanguageTool information to be processed. - - `languagetool_host` (str, default `localhost`): host that is running LanguageTool - - `languagetool_port` (int, default 8081): which port LanguageTool is running on -- `verbose` (bool, default false): print state whenever reducers update -- `gpt_responders` (obj, {}): how you wish to connect to GPTs. See dedicated section below for more details. +### Top-level flags + +| Key | Type | Default | Description | +| --- | --- | --- | --- | +| `use_nlp` | bool | `false` | Enable AWE NLP indicators such as parts of speech, main idea detection, and academic language metrics. | +| `use_google_documents` | bool | `false` | Sync reconstructed reducer state with a Google Doc using the Google Docs API. | +| `use_languagetool` | bool | `false` | Run LanguageTool analysis on submitted text. | +| `verbose` | bool | `false` | Log reducer state whenever it updates (useful when debugging pipelines). | + +### LanguageTool options + +| Key | Type | Default | Description | +| --- | --- | --- | --- | +| `languagetool_host` | str | `http://localhost` | Host URL for the LanguageTool server. | +| `languagetool_port` | int | `8081` | Port on which LanguageTool is available. | + +Set `use_languagetool` to `true` only when LanguageTool is reachable at the configured host and port. + +### GPT responders + +The `gpt_responders` object declares one or more providers. The system iterates over the responders in the order they appear and selects the first one that is correctly configured. This allows you to offer fallbacks (for example, try a local Ollama instance before hitting OpenAI). + +Currently supported responders: + +#### Ollama + +| Key | Type | Default | Description | +| --- | --- | --- | --- | +| `model` | str | `llama2` | Model name served by the Ollama runtime. | +| `host` | str | `http://localhost:11434` | Base URL of the Ollama server. You can also supply this via the `OLLAMA_HOST` environment variable. | + +#### OpenAI + +| Key | Type | Default | Description | +| --- | --- | --- | --- | +| `model` | str | `gpt-3.5-turbo-16k` | Chat completion model to call. | +| `api_key` | str | _(required)_ | Secret API key for your OpenAI account. You can also provide it through the `OPENAI_API_KEY` environment variable. | + +## Feature-by-feature guidance + +### NLP indicators (AWE) + +1. Set `use_nlp: true` in the configuration. +2. Ensure the AWE libraries are installed and accessible from the runtime environment. +3. Call the relevant reducers or service functions to compute indicators such as parts-of-speech counts or academic language percentages. + +These indicators are useful for automated writing evaluation and can be combined with LanguageTool or GPT feedback. + +### Google Docs synchronization + +Enable `use_google_documents` when you want the module to keep a Google Doc up to date with reconstructed reducer state. You must supply Google API credentials in the broader system configuration (outside the scope of this document). + +### LanguageTool error detection -## Available tools +1. Run or connect to a LanguageTool server. +2. Configure `languagetool_host` and `languagetool_port`. +3. Set `use_languagetool: true`. -There are 3 primary tools we have available to us. +The module will then enrich reducer output with spelling and usage error information, which can be displayed directly to writers or incorporated into other feedback flows. -1. NLP indicators to suppoprt automated writing evaulation. These indicators provide information about the text (parts of speech, main ideas, % of academic langauge, etc.). -2. LanguageTool to detect specific errors being made in text. These errors include spelling and incorrect word usage. -3. Ability to interface with LLMs. +### GPT-based feedback -## GPT Responders +1. Add one or more entries to `gpt_responders` (see tables above). +2. Provide any required credentials via configuration or environment variables. +3. Enable the module feature or reducer that consumes GPT responses (for instance, conversational feedback or revision suggestions). -We allow users to define different ways of interacting with GPTs. The system will iterate over each listed responder and return the first successfully configured one. The current supported responders are: +## Tips for integration -### Ollama +* Toggle features gradually during development to keep dependencies manageable. +* Use the `verbose` flag while integrating new reducers to observe state transitions and diagnose issues. +* Keep secrets (such as API keys) out of version control by relying on environment variables or secret management tooling. +* When deploying in containers, expose the LanguageTool and Ollama ports to the module runtime. -- `model` (str, default `llama2`): the model you wish to use. -- `host` (str, default `http://localhost:11434`): the host running Ollama. You may also set the `OLLAMA_HOST` environment variable. +## Further resources -### OpenAI +* [LanguageTool server documentation](https://dev.languagetool.org/http-server) +* [Ollama documentation](https://docs.ollama.ai/) +* [OpenAI API reference](https://platform.openai.com/docs/) -- `model` (str, default `gpt-3.5-turbo-16k`): the model you wish to use. -- `api_key` (str): your secret openai API key. You may also set this with the `OPENAI_API_KEY` environment variable. +These resources provide additional setup instructions and troubleshooting guides for the third-party services referenced above. diff --git a/modules/writing_observer/VERSION b/modules/writing_observer/VERSION index c98f3678a..696424a49 100644 --- a/modules/writing_observer/VERSION +++ b/modules/writing_observer/VERSION @@ -1 +1 @@ -0.1.0+2025.08.07T19.58.28.937Z.ed90597d.berickson.202507.new.lti.updates +0.1.0+2025.09.30T15.53.18.556Z.1c72d9e1.master From 8b205d0c2750fa8bcaa2c0a4837b3f533b82f895 Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Wed, 1 Oct 2025 13:50:57 -0400 Subject: [PATCH 53/88] updated system settings documentation --- VERSION | 2 +- autodocs/concepts.rst | 3 + autodocs/extension.rst | 6 - autodocs/reference.rst | 2 + docs/concepts/system_settings.md | 115 ++++++++ docs/how-to/config.md | 279 ------------------ docs/reference/system_settings.md | 152 ++++++++++ learning_observer/VERSION | 2 +- .../learning_observer/auth/auth.md | 2 +- 9 files changed, 275 insertions(+), 288 deletions(-) delete mode 100644 autodocs/extension.rst create mode 100644 docs/concepts/system_settings.md delete mode 100644 docs/how-to/config.md create mode 100644 docs/reference/system_settings.md diff --git a/VERSION b/VERSION index 696424a49..0aa6c860e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0+2025.09.30T15.53.18.556Z.1c72d9e1.master +0.1.0+2025.10.01T17.50.57.101Z.d6047986.master diff --git a/autodocs/concepts.rst b/autodocs/concepts.rst index 00aca9db6..58ec6c63c 100644 --- a/autodocs/concepts.rst +++ b/autodocs/concepts.rst @@ -13,6 +13,8 @@ implementation details: architecture that implements the system design. - :doc:`Technologies ` - surveys the primary tools and platforms we rely on to realize the architecture. +- :doc:`System Settings ` - describes how the + system loads global and cascading settings. - :doc:`Events ` - introduces the event model that drives data flowing through the system. - :doc:`Reducers ` - details how incoming events are @@ -35,6 +37,7 @@ implementation details: docs/concepts/system_design docs/concepts/architecture docs/concepts/technologies + docs/concepts/system_settings docs/concepts/events docs/concepts/reducers docs/concepts/communication_protocol diff --git a/autodocs/extension.rst b/autodocs/extension.rst deleted file mode 100644 index 8bc9e1ca3..000000000 --- a/autodocs/extension.rst +++ /dev/null @@ -1,6 +0,0 @@ -.. _extension: - -Extension ---------- -.. include:: ../docs/extension.md - :parser: myst_parser.sphinx_ diff --git a/autodocs/reference.rst b/autodocs/reference.rst index dbc769734..ba358f17b 100644 --- a/autodocs/reference.rst +++ b/autodocs/reference.rst @@ -4,6 +4,7 @@ Reference Detailed, structured information about APIs, configurations, and technical details. Consult these resources when you need definitive answers about how the system behaves or how to integrate with it: - :doc:`Code Quality Standards ` - Understand our expectations for readability, style, and continuous improvement. +- :doc:`System Settings ` - Review what each system setting does and where it is used. - :doc:`Documentation Conventions ` - Learn how we structure docs, what tools we use, and how to contribute updates. - :doc:`Linting Rules ` - Review the automated checks that keep the codebase healthy and how to run them locally. - :doc:`Testing Strategy ` - Explore the testing layers we rely on and guidelines for writing reliable tests. @@ -17,6 +18,7 @@ Detailed, structured information about APIs, configurations, and technical detai :titlesonly: docs/reference/code_quality.md + docs/reference/system_settings.md docs/reference/documentation.md docs/reference/linting.md docs/reference/testing.md diff --git a/docs/concepts/system_settings.md b/docs/concepts/system_settings.md new file mode 100644 index 000000000..fc7d0ae93 --- /dev/null +++ b/docs/concepts/system_settings.md @@ -0,0 +1,115 @@ +# System settings + +Learning Observer depends on a single source of truth for everything from +server ports to which pieces of modules are enabled. We rely on the +[PMSS](https://github.com/ETS-Next-Gen/pmss) registry because it gives us a +predictable, type-checked way to describe those concerns once and reuse them +across the whole stack. This article explains why the settings layer exists, +how `creds.yaml` fits into the picture, and why we support cascading +``*.pmss`` files that behave a bit like CSS for configuration. + +To view all settings and what they do, checkout the [System Settings Reference](../reference/system_settings.md). + +## Why centralize configuration? + +* **Shared vocabulary.** Modules, reducers, and integrations all speak the same + language when they ask for `hostname`, `redis_connection.port`, or + `modules.writing_observer.use_nlp`. PMSS enforces the field definitions so we + can freely move code between services without wondering what the knobs are + called. +* **Type safety and validation.** Every field is registered with a type and + optional parser. PMSS refuses to start if a value is missing or malformed, + surfacing errors during boot instead of in the middle of a request. +* **Operational portability.** Teams deploy Learning Observer to wildly + different environments. A single registry allows a site to describe network + boundaries, third-party credentials, or feature flags in one place and keep + those choices under version control. + +## The role of `creds.yaml` + +Most installations load configuration from `creds.yaml`. When the process +starts, `learning_observer.settings` initializes PMSS with a +`YAMLFileRuleset`, parses that file, and registers the run mode and other core +fields. The YAML mirrors the namespace hierarchy, so options live exactly where +operators expect to see them: + +```yaml +server: + port: 5000 +modules: + writing_observer: + use_nlp: true +``` + +`creds.yaml` gives us a stable default: it travels with the deployment, can be +checked into private infrastructure repositories, and is easy to audit during +reviews. Even when we introduce additional sources, the YAML baseline remains +the anchor that documents the "intent" of the environment. + +## Cascading ``*.pmss`` overlays + +While one file covers global defaults, we often need tweaks that depend on +**who** is asking for data or **where** a request originates. PMSS supports +multiple rule sets, so we extend the base YAML with optional ``*.pmss`` +overlays. Each overlay is a small PMSS file whose contents look just like the +YAML fragment they augment, but they add selectors that encode *specificity*. + +Think of these selectors like CSS. We start with the low-specificity default +rule and then layer on increasingly precise matches: + +```pmss +roster_data { + source: all; +} + +roster_data[domain="learning-observer.org"] { + source: google; +} +``` + +When `learning_observer/learning_observer/rosters.py` asks for `roster_data` +it supplies attributes such as the caller's email domain or provider. PMSS +walks the rule set, finds the most specific block that matches the request, and +returns that value. In the example above a teacher from +`learning-observer.org` would receive the `google` roster source, while any +other user would keep the global `all` default. Additional selectors can layer +on top for providers, schools, or classrooms with each more specific rule +overriding the broader ones. + +At runtime we still assemble a deterministic cascade: + +1. Load the global `creds.yaml` defaults. +2. Apply any environment overlays (for example, a `district.pmss` file that + swaps OAuth credentials for that customer). +3. Resolve request-scoped overlays that match the supplied selectors, letting + the most specific rule win. + +PMSS tracks the provenance of each value so developers can inspect which file +supplied the final answer when troubleshooting. Because overlays reuse the +same registered fields, we retain all of the type checking that protects the +base configuration. + +## How code consumes the cascade + +Once the cascade is assembled, code does not care whether a value came from the +YAML baseline or an overlay. Components call +`learning_observer.settings.pmss_settings.()` (optionally via +`module_setting()` helpers) and PMSS resolves the field using the active rule +stack. That means a request handled for an instructor can pick up +instructor-specific defaults while a system job, using the same accessor, still +observes the site-wide configuration. + +## Extending the system settings surface + +Adding a new capability follows a consistent pattern: + +1. Register the field with PMSS, giving it a name, type, description, and + default if appropriate. +2. Update `creds.yaml` (or the reference documentation) to teach operators what + the new setting does. +3. Optionally create overlay files where the value should vary by tenant, user, + or integration partner. + +By keeping configuration declarative and cascading, we get the flexibility to +serve many partners without branching the codebase, all while preserving the +predictability administrators expect from a single system settings registry. diff --git a/docs/how-to/config.md b/docs/how-to/config.md deleted file mode 100644 index 84f9ea673..000000000 --- a/docs/how-to/config.md +++ /dev/null @@ -1,279 +0,0 @@ -# Config (creds.yaml) - -The Learning Observer project can be configured on a per-system basis using a configuration file. This configuration file is essential for setting various options such as the environment mode (development or production), authentication, and module-specific settings. - -## Settings - -The settings are loaded in via the `learning_observer/learning_observer/settings.py` file. You see them referred to in the code with - -```python -import learning_observer.settings -item = learning_observer.settings.settings['kvs'] -``` - -### Hostname and Protocol - -The `hostname` and `protocol` sections define the address and protocol used for accessing the Learning Observer application. - -- `hostname`: The address of the application server. -- `protocol`: The protocol used for communication (http or https). - -Example: - -```yaml -hostname: localhost:8888 -protocol: http -``` - -### XMPP - -The `xmpp` section defines the credentials and configurations for XMPP connections, including sinks, sources, and stream components. - -- `sink`: Configuration for XMPP sinks that receive messages. - - `jid`: The Jabber ID for the sink. - - `password`: The password for the sink. -- `source`: Configuration for XMPP sources that send messages. - - `jid`: The Jabber ID for the source. - - `password`: The password for the source. -- `stream`: Configuration for XMPP streams used for debugging. - - `jid`: The Jabber ID for the stream. - - `password`: The password for the stream. - -Example: - -```yaml -xmpp: - sink: - jid: sink@localhost - password: {xmpp-sink-password} - source: - jid: source@localhost - password: {xmpp-source-password} - stream: - jid: stream@localhost - password: {xmpp-stream-password} -``` - -### Authentication - -The `auth` section is responsible for configuring various authentication methods. - -- `google_oauth`: Configuration for Google OAuth authentication. - - `web`: A sub-configuration containing Google OAuth settings. - - `client_id`: The Google OAuth client ID. - - `project_id`: The Google OAuth project ID. - - `auth_uri`: The Google OAuth authorization URI. - - `token_uri`: The Google OAuth token URI. - - `auth_provider_x509_cert_url`: The Google OAuth provider x509 certificate URL. - - `client_secret`: The Google OAuth client secret. - - `javascript_origins`: The list of allowed JavaScript origins. -- `password_file`: The path to the password file used for authentication, created using the `lo_passwd.py` script. -- `http_basic_auth`: Configuration for HTTP basic authentication. - - `mode`: The mode of HTTP basic authentication, either 'nginx' or 'password-file'. - - `password_file`: The path to the password file used for authentication, or 'null' if authentication is done by nginx. - -Example: - -```yaml -auth: - google_oauth: - web: - client_id: {google-oauth-client-id} - project_id: {google-oauth-project-id} - auth_uri: https://accounts.google.com/o/oauth2/auth - token_uri: https://oauth2.googleapis.com/token - auth_provider_x509_cert_url: https://www.googleapis.com/oauth2/v1/certs - client_secret: {google-client-secret} - javascript_origins: ["{url}"] - password_file: passwd.lo - http_basic_auth: - mode: remove-this - password_file: passwd.lo -``` - -### PubSub - -The `pubsub` section configures the pubsub system type used for the application. - -- `type`: The type of pubsub system, either 'stub' for in-memory debugging or 'redis' for small-scale production. - -Example: - -```yaml -pubsub: - type: stub -``` - -### Redis Connection - -This determines how we should be connecting to Redis. If these are not -set, the connection will default to using `localhost:6389` without a -password. - -```yaml -redis_connection: - redis_host: localhost - redis_port: 6389 - redis_password: yoursupersecurepassword -``` - -### KVS - -The `kvs` section configures the key-value store used in the application. - -- `default`: The default configuration for the key-value store. - - `type`: The type of key-value store, such as 'stub', 'redis', or 'redis_ephemeral'. - - `expiry`: The expiry time for objects in the key-value store (in seconds). - -Example: - -```yaml -kvs: - default: - type: stub - expiry: 6000 -``` - -### Roster Data - -The `roster_data` section configures the source of roster data. - -- `source`: The source of roster data, such as 'filesystem', 'google_api', 'all', or 'test'. - -Example: - -```yaml -roster_data: - source: filesystem -``` - -### AIO - -The `aio` section configures user session settings. - -- `session_secret`: The unique secret key for your deployment. -- `session_max_age`: The maximum age of a session, in seconds. - -Example: - -```yaml -aio: - session_secret: {unique-aio-session-key} - session_max_age: 3600 -``` - -### Config - -The `config` section configures the run mode and debug settings. - -- `run_mode`: The run mode, either 'dev' or 'deploy'. -- `debug`: A list of debug options, such as "tracemalloc". - -Example: - -```yaml -config: - run_mode: dev - debug: [] -``` - -### Logging - -The `logging` section configures the logging settings. - -- `debug_log_level`: The log level, either 'NONE', 'SIMPLE', or 'EXTENDED'. -- `debug_log_destinations`: A list of log destinations, such as 'CONSOLE' and 'FILE'. - -Example: - -```yaml -logging: - debug_log_level: SIMPLE - debug_log_destinations: - - CONSOLE - - FILE -``` - -### Theme - -The `theme` section configures the appearance and messages of the application. - -- `server_name`: The name of the server. -- `front_page_pitch`: The message displayed on the front page. -- `logo_big`: The URL of the logo. - -Example: - -```yaml -theme: - server_name: Learning Observer - front_page_pitch: Learning Observer is an experimental dashboard. If you'd like to be part of the experiment, please contact us. If you're already part of the experiment, log in! - logo_big: /static/media/logo-clean.jpg -``` - -### Event Auth - -The `event_auth` section configures authentication settings for events. - -- `local_storage`: Configuration for local storage authentication. - - `userfile`: The file containing user information. - - `allow_guest`: A boolean indicating whether guest access is allowed. -- `chromebook`: (optional) Configuration for Chromebook authentication. - - `allow_guest`: A boolean indicating whether guest access is allowed on Chromebooks. - -Example: - -```yaml -event_auth: - local_storage: - userfile: students.yaml - allow_guest: true -``` - -### Feature Flags - -The `feature_flags` section allows you to enable or disable specific features in development. - A dictionary with the feature names as keys and boolean values to indicate whether the feature is enabled or disabled. - -Example: - -```yaml -feature_flags: {} -``` - -### Server - -The `server` section configures the server settings. - -- `port`: The port number on which the server will listen. - -Example: - -```yaml -server: - port: 8888 -``` - -### Modules - -The `modules` section configures the settings for each module installed on the system. Different systems may define different items depending on which modules they wish to have installed on their system. An error will be raised if an expected setting is missing from a given module. Here is an example for the `writing_observer` module. - -- `writing_observer`: Settings specific to the Writing Observer module. - - `use_nlp`: A boolean indicating whether to use natural language processing or a stub function. (default: `false`) - - `use_google_documents`: A boolean indicating whether to use Google Documents. (default: `false`) - - `languagetool_port`: The port the LanguageTool server is running on (default: `8081`) - - `languagetool_host`: The host the LanguageTool server is running on (default: `localhost`) - - `use_languagetool`: A boolean indicating whether to use LanguageTool or a stub function. (default: `false`) - -Example: - -```yaml -modules: - writing_observer: - use_nlp: true - use_google_documents: false - languagetool_port: 8081 - use_languagetool: true -``` - -Note: This is just an example of how to configure the settings for a specific module. Each module may have its own unique settings, and you should consult the module's documentation for information on which settings are required and how they should be configured. diff --git a/docs/reference/system_settings.md b/docs/reference/system_settings.md new file mode 100644 index 000000000..ce097d30e --- /dev/null +++ b/docs/reference/system_settings.md @@ -0,0 +1,152 @@ +# System Settings + +This page lists every field registered with PMSS for the **Learning Observer +base application**, grouped by the namespace you use in `creds.yaml`. Each +entry includes the YAML path, purpose, default (if any), and the code that +relies on it. Module-specific settings (such as the Writing Observer module) +should be documented alongside their module guides in the reference section. + +Refer back to the [system settings concept](../concepts/system_settings.md) +concept guide for details on how these values are loaded and consumed by the +runtime. + +## Global application keys + +| YAML path | Description | Default | Used in | +| --- | --- | --- | --- | +| `run_mode` | Selects `dev`, `deploy`, or `interactive` runtime profiles to control logging and startup behaviour. | required | [`learning_observer/learning_observer/settings.py`](../../learning_observer/learning_observer/settings.py), [`learning_observer/learning_observer/main.py`](../../learning_observer/learning_observer/main.py) | +| `hostname` | Public host name for OAuth callbacks and secure cookie scopes. Include the port when not using 80/443. | required | [`learning_observer/learning_observer/auth/social_sso.py`](../../learning_observer/learning_observer/auth/social_sso.py), [`learning_observer/learning_observer/webapp_helpers.py`](../../learning_observer/learning_observer/webapp_helpers.py) | +| `protocol` | Protocol (`http` or `https`) that governs cookie security and OAuth redirect URLs. | required | [`learning_observer/learning_observer/auth/social_sso.py`](../../learning_observer/learning_observer/auth/social_sso.py), [`learning_observer/learning_observer/webapp_helpers.py`](../../learning_observer/learning_observer/webapp_helpers.py) | +| `clone_module_git_repos` | Controls whether module git repositories are cloned automatically (`y`), skipped (`n`), or prompted (`prompt`). | `prompt` | [`learning_observer/learning_observer/module_loader.py`](../../learning_observer/learning_observer/module_loader.py) | +| `dangerously_allow_insecure_dags` | Enables uploading arbitrary dashboard DAG definitions for development experiments. Leave disabled in production. | `false` | [`learning_observer/learning_observer/dashboard.py`](../../learning_observer/learning_observer/dashboard.py) | +| `fetch_additional_info_from_teacher_on_login` | Starts a background job after Google login to fetch extra teacher documents immediately. | `false` | [`learning_observer/learning_observer/auth/social_sso.py`](../../learning_observer/learning_observer/auth/social_sso.py) | + +### `config` namespace + +| YAML path | Description | Default | Used in | +| --- | --- | --- | --- | +| `config.run_mode` | Mirror of the root `run_mode` so runtime helpers can resolve the value within the `config` namespace. | required | [`learning_observer/learning_observer/main.py`](../../learning_observer/learning_observer/main.py) | +| `config.debug` | List of diagnostic toggles (for example `"tracemalloc"`) that enable extra debugging helpers during development. | `[]` | [`learning_observer/learning_observer/routes.py`](../../learning_observer/learning_observer/routes.py) | + +### `server` namespace + +| YAML path | Description | Default | Used in | +| --- | --- | --- | --- | +| `server.port` | Port that the aiohttp web server binds to. Falling back to an open port in development remains TODO. | `8888` | [`learning_observer/learning_observer/main.py`](../../learning_observer/learning_observer/main.py) | + +### Session management (`aio` namespace) + +| YAML path | Description | Default | Used in | +| --- | --- | --- | --- | +| `aio.session_secret` | Secret used to encrypt and sign aiohttp session cookies. Generate a unique value per deployment. | required | [`learning_observer/learning_observer/webapp_helpers.py`](../../learning_observer/learning_observer/webapp_helpers.py) | +| `aio.session_max_age` | Session lifetime in seconds. | required | [`learning_observer/learning_observer/webapp_helpers.py`](../../learning_observer/learning_observer/webapp_helpers.py) | + +### Redis connection (`redis_connection` namespace) + +| YAML path | Description | Default | Used in | +| --- | --- | --- | --- | +| `redis_connection.redis_host` | Hostname for Redis. | `localhost` | [`learning_observer/learning_observer/redis_connection.py`](../../learning_observer/learning_observer/redis_connection.py) | +| `redis_connection.redis_port` | Port for Redis. | `6379` | [`learning_observer/learning_observer/redis_connection.py`](../../learning_observer/learning_observer/redis_connection.py) | +| `redis_connection.redis_password` | Password for Redis (set to `null` when unused). | `null` | [`learning_observer/learning_observer/redis_connection.py`](../../learning_observer/learning_observer/redis_connection.py) | + +### Logging (`logging` namespace) + +| YAML path | Description | Default | Used in | +| --- | --- | --- | --- | +| `logging.debug_log_level` | Chooses how verbose diagnostic logging should be (`NONE`, `SIMPLE`, or `EXTENDED`). | inherits environment default | [`learning_observer/learning_observer/log_event.py`](../../learning_observer/learning_observer/log_event.py) | +| `logging.debug_log_destinations` | Ordered list of destinations that should receive debug logs (`CONSOLE`, `FILE`). | `['CONSOLE', 'FILE']` in development | [`learning_observer/learning_observer/log_event.py`](../../learning_observer/learning_observer/log_event.py) | + +### Key-value stores (`kvs` namespace) + +| YAML path | Description | Default | Used in | +| --- | --- | --- | --- | +| `kvs.default.type` | Backend used for the default key-value store (`stub`, `redis`, `redis_ephemeral`, or `filesystem`). | required | [`learning_observer/learning_observer/kvs.py`](../../learning_observer/learning_observer/kvs.py) | +| `kvs.default.expiry` | Expiration (seconds) required when `type` is `redis_ephemeral`. | required for `redis_ephemeral` | [`learning_observer/learning_observer/kvs.py`](../../learning_observer/learning_observer/kvs.py) | +| `kvs.default.path` | Filesystem directory for persisted values when `type` is `filesystem`; supports optional `subdirs`. | required for `filesystem` | [`learning_observer/learning_observer/kvs.py`](../../learning_observer/learning_observer/kvs.py) | +| `kvs..type` | Additional named KVS pools that modules can request by name. Same accepted values as `kvs.default.type`. | optional | [`learning_observer/learning_observer/kvs.py`](../../learning_observer/learning_observer/kvs.py) | +| `kvs..expiry` | Per-store expiration when the named pool uses the `redis_ephemeral` backend. | required for `redis_ephemeral` pools | [`learning_observer/learning_observer/kvs.py`](../../learning_observer/learning_observer/kvs.py) | +| `kvs..path` | Filesystem location (and optional `subdirs`) for the named store when using the `filesystem` backend. | required for `filesystem` pools | [`learning_observer/learning_observer/kvs.py`](../../learning_observer/learning_observer/kvs.py) | + +### Roster ingestion (`roster_data` namespace) + +| YAML path | Description | Default | Used in | +| --- | --- | --- | --- | +| `roster_data.source` | Selects the roster provider: `filesystem`, `google`, `schoology`, `x-canvas`, `all`, or `test`. | required | [`learning_observer/learning_observer/rosters.py`](../../learning_observer/learning_observer/rosters.py) | + +### Core authentication flags (`auth`) + +| YAML path | Description | Default | Used in | +| --- | --- | --- | --- | +| `auth.password_file` | Enables password authentication using the specified credentials file (generated with `lo_passwd.py`). | disabled | [`learning_observer/learning_observer/routes.py`](../../learning_observer/learning_observer/routes.py) | +| `auth.test_case_insecure` | When `true` or configured with overrides, short-circuits authentication for automated tests. | `false` | [`learning_observer/learning_observer/auth/handlers.py`](../../learning_observer/learning_observer/auth/handlers.py) | +| `auth.demo_insecure` | Demo mode that fabricates user sessions for walkthroughs; may be a boolean or a mapping with a fixed `name`. | `false` | [`learning_observer/learning_observer/auth/handlers.py`](../../learning_observer/learning_observer/auth/handlers.py) | + +#### Google OAuth (`auth.google_oauth.web`) + +| YAML path | Description | Default | Used in | +| --- | --- | --- | --- | +| `auth.google_oauth.web.client_id` | Google OAuth client ID. | required | [`learning_observer/learning_observer/auth/social_sso.py`](../../learning_observer/learning_observer/auth/social_sso.py) | +| `auth.google_oauth.web.client_secret` | Google OAuth client secret. | required | [`learning_observer/learning_observer/auth/social_sso.py`](../../learning_observer/learning_observer/auth/social_sso.py) | + +#### LTI providers (`auth.lti.`) + +| YAML path | Description | Default | Used in | +| --- | --- | --- | --- | +| `auth.lti..auth_uri` | LMS endpoint that initiates the OIDC login flow. | required | [`learning_observer/learning_observer/auth/lti_sso.py`](../../learning_observer/learning_observer/auth/lti_sso.py) | +| `auth.lti..jwks_uri` | JWKS endpoint used to validate LMS ID tokens. | required | [`learning_observer/learning_observer/auth/lti_sso.py`](../../learning_observer/learning_observer/auth/lti_sso.py) | +| `auth.lti..token_uri` | OAuth token endpoint for the LMS. | required | [`learning_observer/learning_observer/auth/lti_sso.py`](../../learning_observer/learning_observer/auth/lti_sso.py) | +| `auth.lti..redirect_uri` | Callback URL inside Learning Observer. | required | [`learning_observer/learning_observer/auth/lti_sso.py`](../../learning_observer/learning_observer/auth/lti_sso.py) | +| `auth.lti..private_key_path` | Location of the private key used to sign LTI messages. | required | [`learning_observer/learning_observer/auth/lti_sso.py`](../../learning_observer/learning_observer/auth/lti_sso.py) | +| `auth.lti..api_domain` | Base Canvas API domain for roster and assignment cleaners (Canvas-specific). | required | [`learning_observer/learning_observer/integrations/canvas.py`](../../learning_observer/learning_observer/integrations/canvas.py) | + +#### HTTP Basic authentication (`auth.http_basic`) + +| YAML path | Description | Default | Used in | +| --- | --- | --- | --- | +| `auth.http_basic.password_file` | Credential store used when Learning Observer verifies HTTP basic logins directly. Set to `null` when nginx handles auth. | required when present | [`learning_observer/learning_observer/routes.py`](../../learning_observer/learning_observer/routes.py), [`learning_observer/learning_observer/auth/http_basic.py`](../../learning_observer/learning_observer/auth/http_basic.py) | +| `auth.http_basic.login_page_enabled` | Enables the built-in login endpoint that proxies browser-provided credentials into a session. | `false` | [`learning_observer/learning_observer/auth/http_basic.py`](../../learning_observer/learning_observer/auth/http_basic.py) | +| `auth.http_basic.full_site_auth` | Marks that nginx protects every route so middleware should expect auth headers on each request. | `false` | [`learning_observer/learning_observer/auth/http_basic.py`](../../learning_observer/learning_observer/auth/http_basic.py) | +| `auth.http_basic.delegate_nginx_auth` | Indicates that nginx performs the credential check; Learning Observer only trusts forwarded headers. | `false` | [`learning_observer/learning_observer/auth/http_basic.py`](../../learning_observer/learning_observer/auth/http_basic.py) | + +### Background services and feature flags + +| YAML path | Description | Default | Used in | +| --- | --- | --- | --- | +| `feature_flags.*` | Optional feature toggles (`uvloop`, `watchdog`, `canvas_routes`, etc.). `True` enables a flag and allows nested configuration. | varies | [`learning_observer/learning_observer/settings.py`](../../learning_observer/learning_observer/settings.py) | + +### Event stream authentication (`event_auth` namespace) + +| YAML path | Description | Default | Used in | +| --- | --- | --- | --- | +| `event_auth.local_storage.userfile` | Path (under the data directory) listing device tokens that should be treated as authenticated. | required for `local_storage` | [`learning_observer/learning_observer/auth/events.py`](../../learning_observer/learning_observer/auth/events.py) | +| `event_auth.local_storage.allow_guest` | Permits unauthenticated fallbacks when the Chromebook or extension token is unknown. | `false` | [`learning_observer/learning_observer/auth/events.py`](../../learning_observer/learning_observer/auth/events.py) | +| `event_auth.http_basic` | Presence enables nginx-backed HTTP basic auth for event streams. No additional keys are required. | disabled | [`learning_observer/learning_observer/auth/events.py`](../../learning_observer/learning_observer/auth/events.py) | +| `event_auth.guest` | Enables guest sessions that mint random IDs for browsers without credentials. | disabled | [`learning_observer/learning_observer/auth/events.py`](../../learning_observer/learning_observer/auth/events.py) | +| `event_auth.hash_identify` | Enables hash-based identity hints (e.g., `/page#user=alice`) for one-off experiments. | disabled | [`learning_observer/learning_observer/auth/events.py`](../../learning_observer/learning_observer/auth/events.py) | +| `event_auth.testcase_auth` | Allows automated tests to tag events with deterministic user IDs. | disabled | [`learning_observer/learning_observer/auth/events.py`](../../learning_observer/learning_observer/auth/events.py) | + +## Modules + +Modules can define their own PMSS namespaces under `modules.`. +Consult each module's reference guide for those settings - for example, the +Writing Observer module documents its configuration alongside the rest of its +module documentation. + +## Example snippet + +```yaml +run_mode: dev +hostname: localhost:8888 +protocol: http +config: + run_mode: dev +server: + port: 8888 +aio: + session_secret: "replace-me" + session_max_age: 3600 +redis_connection: + redis_host: localhost + redis_port: 6379 + redis_password: null +``` diff --git a/learning_observer/VERSION b/learning_observer/VERSION index 696424a49..0aa6c860e 100644 --- a/learning_observer/VERSION +++ b/learning_observer/VERSION @@ -1 +1 @@ -0.1.0+2025.09.30T15.53.18.556Z.1c72d9e1.master +0.1.0+2025.10.01T17.50.57.101Z.d6047986.master diff --git a/learning_observer/learning_observer/auth/auth.md b/learning_observer/learning_observer/auth/auth.md index f38fd6889..c62b4818a 120000 --- a/learning_observer/learning_observer/auth/auth.md +++ b/learning_observer/learning_observer/auth/auth.md @@ -1 +1 @@ -../../../docs/auth.md \ No newline at end of file +../../../docs/concepts/auth.md \ No newline at end of file From f12616b4c95ee5224360da9ace49d99f0483a089 Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Wed, 1 Oct 2025 17:16:15 -0400 Subject: [PATCH 54/88] added documentation for serving as lti --- VERSION | 2 +- autodocs/concepts.rst | 3 + autodocs/how-to.rst | 2 + docs/concepts/student_identity_mapping.md | 36 ++++++++++ docs/how-to/lti.md | 87 +++++++++++++++++++++++ 5 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 docs/concepts/student_identity_mapping.md create mode 100644 docs/how-to/lti.md diff --git a/VERSION b/VERSION index 0aa6c860e..39acef61a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0+2025.10.01T17.50.57.101Z.d6047986.master +0.1.0+2025.10.01T21.16.15.146Z.8b205d0c.master diff --git a/autodocs/concepts.rst b/autodocs/concepts.rst index 58ec6c63c..8c0002830 100644 --- a/autodocs/concepts.rst +++ b/autodocs/concepts.rst @@ -21,6 +21,8 @@ implementation details: aggregated into the state our experiences depend on. - :doc:`Communication Protocol ` - discusses how the system queries data from reducers for dashboards. +- :doc:`Student Identity Mapping ` - explain + how learners information is mapped across integrations. - :doc:`Scaling ` - covers strategies for growing the system once the fundamentals are in place. - :doc:`Auth ` - describes authentication considerations @@ -41,6 +43,7 @@ implementation details: docs/concepts/events docs/concepts/reducers docs/concepts/communication_protocol + docs/concepts/student_identity_mapping docs/concepts/scaling docs/concepts/auth docs/concepts/privacy diff --git a/autodocs/how-to.rst b/autodocs/how-to.rst index deec45815..7bf21ad7d 100644 --- a/autodocs/how-to.rst +++ b/autodocs/how-to.rst @@ -6,6 +6,7 @@ Practical instructions for achieving specific goals within Learning Observer. Us - :doc:`Communication Protocol ` - How to query data from reducers or system endpoints for dashboards. - :doc:`Configure Learning Observer ` - Set up credentials, environment variables, and other configuration details required for a smooth deployment. - :doc:`Build Dashboards ` - Walk through creating dashboards from reducer outputs, including layout choices and data wiring. +- :doc:`LTI ` - Cover how to install Learning Observer as an LTI application. - :doc:`Run with Docker ` - Learn how to containerize the stack, manage images, and operate the project using Docker Compose. - :doc:`Writing Observer Extension ` - Install, configure, and validate the Writing Observer browser extension for capturing events. - :doc:`Interactive Environments ` - Connect Learning Observer to Jupyter and other live coding setups for iterative development. @@ -18,6 +19,7 @@ Practical instructions for achieving specific goals within Learning Observer. Us docs/how-to/communication_protocol.md docs/how-to/config.md docs/how-to/dashboards.md + docs/how-to/lti.md docs/how-to/docker.md docs/how-to/extension.md docs/how-to/interactive_environments.md diff --git a/docs/concepts/student_identity_mapping.md b/docs/concepts/student_identity_mapping.md new file mode 100644 index 000000000..e902c6a2d --- /dev/null +++ b/docs/concepts/student_identity_mapping.md @@ -0,0 +1,36 @@ +# Student Identity Mapping + +This document describes the current approach for reconciling a student's identity across the Google Workspace context used by Writing Observer and external platforms that only surface an email address (for example when the application is launched as an LTI tool). + +## Why the mapping exists + +When Writing Observer runs inside Google Workspace we naturally have access to the Google user identifier that shows up in event payloads. However, when the product is embedded as an LTI application we receive the learner's email address but do not receive the Google identifier. Many downstream reducers and dashboards expect to look students up by the Google identifier that is emitted by Google Docs events. Without an explicit bridge between those two identifiers we would be unable to join activity data with roster or profile information for LTI launches. + +## Data sources involved + +Two pieces of infrastructure cooperate to keep an email-to-Google-ID lookup table available: + +1. **`student_profile` reducer** – The `student_profile` KVS pipeline in `writing_analysis.py` stores the latest email address and Google identifier (`safe_user_id`) observed for each student. The reducer only updates its state when either value changes. The resulting records live in the reducer's internal key-value namespace and therefore need to be copied to a place where other services can access them. 【F:modules/writing_observer/writing_observer/writing_analysis.py†L233-L253】 +2. **`map_emails_to_ids_in_kvs.py` script** – This maintenance script scans the reducer's internal keys, extracts any records that contain both `email` and `google_id`, and writes a dedicated `email-studentID-mapping:{email}` entry to the key-value store. The explicit mapping gives any service that only knows the email address a way to recover the Google identifier. 【F:scripts/map_emails_to_ids_in_kvs.py†L1-L29】 + +This flow is intentionally simple: the reducer captures whatever the client reports, and the script copies the data to keys that other components already know how to query. + +## Operating the script + +The email mapping script is normally run in the same environment as other KVS maintenance tasks. It requires access to the same credentials file that reducers use. A manual run looks like this: + +```bash +python scripts/map_emails_to_ids_in_kvs.py +``` + +The script performs a full scan every time it runs, so it is safe to execute multiple times or to schedule as a recurring job. + +## Limitations and future direction + +The current reducer-plus-script approach fills an immediate gap but remains a stopgap solution: + +* **Tight coupling to Google identity** – The reducer only records the Google identifier surfaced by Google Docs. If we ingest events from another platform, there is no canonical place to persist its identifiers. +* **No user object abstraction** – Each consumer must know which KVS keys to query. A shared user object (or identity service) would allow the system to attach multiple external identifiers, roles, and profile attributes to a learner and to expose them through a stable API. +* **Operational overhead** – Because the mapping lives in the KVS, we must remember to run the maintenance script anywhere we expect the lookup table to be fresh. + +In the future we plan to introduce a formal user object that encapsulates identifiers, roles, and cross-system metadata. That abstraction would make this lookup process unnecessary by giving every component a single source of truth for student identity. Until then, this document serves as a reference for the current mapping workflow. diff --git a/docs/how-to/lti.md b/docs/how-to/lti.md new file mode 100644 index 000000000..b87ea3661 --- /dev/null +++ b/docs/how-to/lti.md @@ -0,0 +1,87 @@ +# Serve Learning Observer as an LTI 1.3 tool + +Learning Observer ships with an IMS LTI 1.3 implementation that relies on the platform's OpenID Connect (OIDC) handshake to authenticate users and obtain scoped API access tokens from the learning management system (LMS). Use this guide to register the tool in an LMS and connect it to a course shell. + +## Prerequisites + +* A deployment of Learning Observer reachable by the LMS (typically via HTTPS). +* An LMS that supports LTI 1.3 with dynamic registration or manual developer keys (e.g., Canvas or Schoology). +* Administrative access in that LMS to create a developer key / external tool. + +## 1. Generate and share a signing key + +Learning Observer signs client assertions with an RSA private key when it exchanges OIDC launch data for an LMS access token. Generate a keypair, store the private key somewhere on the application host, and upload the public key to the LMS when you create the developer key. + +```bash +# create a 4096-bit RSA keypair +openssl genrsa -out secrets/lti-tool-private.pem 4096 +openssl rsa -in secrets/lti-tool-private.pem -pubout > secrets/lti-tool-public.pem +``` + +Record the filesystem path to the private key; you'll reference it in the application configuration in the next step. + +## 2. Configure `creds.yaml` + +Enable the LTI provider in `learning_observer/creds.yaml` by adding an entry under `auth.lti.`. Each provider requires the LMS endpoints, the Learning Observer redirect URL, and the private key location. A Canvas example looks like this: + +```yaml +auth: + lti: + sample-canvas: + client_id: "10000000000000" + auth_uri: "https://canvas.example.edu/api/lti/authorize_redirect" + jwks_uri: "https://canvas.example.edu/api/lti/security/jwks" + token_uri: "https://canvas.example.edu/login/oauth2/token" + redirect_uri: "https://lo.example.edu/lti/sample-canvas/launch" + private_key_path: "secrets/lti-tool-private.pem" + api_domain: "https://canvas.example.edu" # Canvas-specific +``` + +Set `redirect_uri` to the public URL that will receive the POST launch request (`/lti//launch`). The login initiation URL for the LMS is the matching `/lti//login` route. + +Restart the application after updating configuration so the new provider registration is loaded. + +## 3. Enable LMS API routes + +Learning Observer only exposes the IMS Names & Roles (NRPS) and Assignment & Grade Service (AGS) proxy routes when the matching feature flag is turned on. Add the appropriate flag to `creds.yaml` so the application registers the routes during startup: + +```yaml +feature_flags: + canvas_routes: true # Canvas NRPS/AGS proxy endpoints + # schoology_routes: true # Schoology NRPS/AGS proxy endpoints +``` + +## 4. Map the roster source with PMSS + +LTI launches identify the LMS provider in the session so roster lookups can decide which backend to call. Create a PMSS overlay that maps the provider to the correct roster source (see [System settings](../concepts/system_settings.md) for more on PMSS). For Canvas, create a file such as `config/roster_source.pmss` alongside `creds.yaml` with the following contents: + +```pmss +roster_data[provider="sample-canvas"] { + source: sample-canvas; +} +``` + +If you support multiple LMS tenants, add additional selector blocks for their email domains or provider names and point them at `schoology`, `filesystem`, or any other supported roster backend. + +## 5. Register the tool in the LMS + +When you create the LTI developer key/external tool inside the LMS: + +1. Supply the **OIDC login initiation URL** as `https:///lti//login`. +2. Supply the **redirect/launch URL** as `https:///lti//launch`. +3. Paste the **public key** generated earlier so the LMS can validate the signed client assertions. +4. Copy the LMS-issued **client ID** and the platform endpoints (authorize, JWKS, token) into `creds.yaml` if you have not done so already. + +Publish the developer key and install the tool in the desired course/context. The LMS will send the context identifiers in the launch claims so Learning Observer can associate sessions with the right course. + +## 6. Verify the launch + +* Add the external tool link to a module or assignment inside the LMS course. +* Launch the tool from within the LMS. Learning Observer should redirect the browser to the LMS's authorize endpoint, validate the launch state and nonce, and then create a session for the user when the LMS returns. +* Successful launches land on the root of the application with LMS-specific authorization headers stored in the session for follow-up roster and grade sync operations. + +If the launch fails, inspect the Learning Observer logs for messages beginning with `LTI Launch`β€”they include detailed context whenever state validation, token exchange, or JWT verification fails. Once the LMS recognizes the tool, you can remove or hide other authentication methods; Learning Observer will automatically expose the LTI login routes whenever `auth.lti` providers are configured. + +## 7. Plan student identity mapping + +LTI launch data only includes the learner's email address, while Writing Observer's Google Workspace integrations emit a Google-specific user identifier. To keep downstream reducers and dashboards working for LTI cohorts, plan to run the maintenance workflow that maps emails to Google IDs. Refer to the [Student Identity Mapping guide](../concepts/student_identity_mapping.md) for an overview of how the reducer and maintenance script cooperate and how to operate the sync in production. From 7706b5a8b01070fba037b0947cec3b3f25842edb Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Mon, 13 Oct 2025 15:31:35 -0400 Subject: [PATCH 55/88] updated dashboard documentation --- VERSION | 2 +- autodocs/how-to.rst | 2 +- docs/how-to/dashboards.md | 87 ++++++++++++++++++++++++------ modules/writing_observer/README.md | 2 +- modules/writing_observer/VERSION | 2 +- 5 files changed, 76 insertions(+), 19 deletions(-) diff --git a/VERSION b/VERSION index 39acef61a..de21e3a82 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0+2025.10.01T21.16.15.146Z.8b205d0c.master +0.1.0+2025.10.13T19.31.35.177Z.f12616b4.master diff --git a/autodocs/how-to.rst b/autodocs/how-to.rst index 7bf21ad7d..872902c42 100644 --- a/autodocs/how-to.rst +++ b/autodocs/how-to.rst @@ -6,7 +6,7 @@ Practical instructions for achieving specific goals within Learning Observer. Us - :doc:`Communication Protocol ` - How to query data from reducers or system endpoints for dashboards. - :doc:`Configure Learning Observer ` - Set up credentials, environment variables, and other configuration details required for a smooth deployment. - :doc:`Build Dashboards ` - Walk through creating dashboards from reducer outputs, including layout choices and data wiring. -- :doc:`LTI ` - Cover how to install Learning Observer as an LTI application. +- :doc:`Serve as LTI application` - Cover how to install Learning Observer as an LTI application. - :doc:`Run with Docker ` - Learn how to containerize the stack, manage images, and operate the project using Docker Compose. - :doc:`Writing Observer Extension ` - Install, configure, and validate the Writing Observer browser extension for capturing events. - :doc:`Interactive Environments ` - Connect Learning Observer to Jupyter and other live coding setups for iterative development. diff --git a/docs/how-to/dashboards.md b/docs/how-to/dashboards.md index b003941ea..8655fddab 100644 --- a/docs/how-to/dashboards.md +++ b/docs/how-to/dashboards.md @@ -189,29 +189,86 @@ NextJS is web framework for building React-based web applications along with add Follow the [Getting Started Guide](https://nextjs.org/docs/app/getting-started) in the official NextJS documentation. -### NextJS in the Learning Observer +### Serving NextJS in the Learning Observer -Before add a NextJS application can be built and added to the system, a few configurations changes need to be made. The built application will not access the server-side code. Any server-side API endpoints need to be implemented in Python. The code that calls these endpoints will need to be updated to point to the correct path. +Before NextJS application can be built and added to the system, a few configuration changes need to be made. The built application will not access the server-side code. Any server-side API endpoints need to be implemented in Python. The code that calls these endpoints will need to be updated to point to the correct path. -Additionally, we need to add a `basePath` to our `next.config.js` file. When building the application, this replaces prefixes all paths with the defined base path. This allows links to function appropriately while being served from Learning Observer. +Additionally, we need to add a `basePath` to our `next.config.js` file. When building the application, this prefixes all paths with the defined base path. This allows links to function appropriately while being served from Learning Observer. Using a base path is especially important when multiple modules serve NextJS dashboards, because it prevents routing conflicts by ensuring that each module's assets are namespaced by the module name. ```js const nextConfig = { // ... the rest of your config - basePath: '/_next/moduleName/pathOfBuiltApplication' + basePath: '/_next//', + output: 'export', +} ``` -To add a NextJS project to a module, first the code needs to be built with `npm run build`. A directory named `out` will be created and the built application will be placed there. Copy this directoy to a module within Learning Observer. +With this configuration: -Next, add the path to the built application to the module's `module.py` file +* Without a base path, multiple modules exporting dashboards to `/` will conflict with one another and with Learning Observer's own root path. +* With a base path, each module is served from `/_next///`, which avoids those conflicts by including the module name in the URL path. +* Avoid absolute paths inside the application (for example, `href="/students"`). Absolute paths ignore the configured `basePath`, which breaks routing when the dashboard is served from Learning Observer. Prefer relative links or the [`next/link`](https://nextjs.org/docs/pages/api-reference/components/link) component's `basePath`-aware helpers. -```python -# module.py -# ...other definitions -''' -Next js dashboards -''' -NEXTJS_PAGES = [ - {'path': 'pathOfBuiltApplication/'} -] +Use query parameters instead of dynamic path segments for any routing that needs to work after static export. For example, prefer `students/compare?id=123` instead of `/students/[id]/compare` so that the static export can generate the page. + +During development it can be helpful to mock the data that will normally arrive via the Learning Observer websocket. A simple placeholder object keeps the dashboard usable before the websocket connection is wired up: + +```js +const data = { + students: { + martha: { + documents: { + history_essay: { + text: 'this is my history essay' + } + } + } + } +}; +``` + +To add a NextJS project to a module: + +1. Build the project with `npm run build`. The static export requires the `output: 'export'` setting shown above. A directory named `out` will be created and the built application will be placed there. +2. Copy the contents of `out` to `modules///`. +3. Add the path to the built application to the module's `module.py` file + + ```python + # module.py + # ...other definitions + ''' + Next js dashboards + ''' + NEXTJS_PAGES = [ + {'path': '/'} + ] + ``` + +4. Install the module in editable mode with `pip install -e modules/`. +5. Start Learning Observer with `make run` to serve the dashboard. + +### Connecting to the Communication Protocol + +When you are ready to connect to Learning Observer's Communication Protocol, install the `lo_event` module to gain access to the shared websocket utilities. + +Installing `lo_event` makes the `LOConnectionDataManager` React hook available to your NextJS project. The hook manages websocket connection state and incoming messages so that your components can focus on rendering data. + +Adding `lo_event` may surface build-time errors if your environment lacks Node polyfills. For example, some systems report `fs` being unavailable. You can instruct Webpack to ignore the `fs` module when bundling the client by extending `next.config.js`: + +```js +const nextConfig = { + // ...existing config + webpack: ( + config, + { buildId, dev, isServer, defaultLoaders, nextRuntime, webpack } + ) => { + config.resolve.fallback = { + ...config.resolve.fallback, + fs: false, // tells Webpack to ignore fs in client bundle + }; + return config; + }, +}; + +module.exports = nextConfig; ``` diff --git a/modules/writing_observer/README.md b/modules/writing_observer/README.md index ea658cb29..1aa811ea4 100644 --- a/modules/writing_observer/README.md +++ b/modules/writing_observer/README.md @@ -4,7 +4,7 @@ The Writing Observer module wraps the NLP and feedback services that power Writing Observer features. It exposes a consistent interface for: -* generating writing indicators from the Analytics for Writing Evaluation (AWE) stack +* generating writing indicators from the Analytics for Writing Evaluation (AWE) stack, see [AWE Workbench](https://github.com/ETS-Next-Gen/AWE_Workbench) * surfacing grammar and usage issues through LanguageTool * forwarding communication protocol queries to GPT-based responders diff --git a/modules/writing_observer/VERSION b/modules/writing_observer/VERSION index 696424a49..de21e3a82 100644 --- a/modules/writing_observer/VERSION +++ b/modules/writing_observer/VERSION @@ -1 +1 @@ -0.1.0+2025.09.30T15.53.18.556Z.1c72d9e1.master +0.1.0+2025.10.13T19.31.35.177Z.f12616b4.master From ec3193cb69d9aede01774f5d2481ea40d04be433 Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Mon, 13 Oct 2025 15:46:25 -0400 Subject: [PATCH 56/88] Small communication protocol fixes * added more logging to comm protocol * updated documentation * added check for target exports --- VERSION | 2 +- docs/how-to/communication_protocol.md | 33 ++++++++ learning_observer/VERSION | 2 +- .../communication_protocol/executor.py | 45 ++++++++-- .../learning_observer/dashboard.py | 55 +++++++++++- scripts/generic_websocket_dashboard.js | 83 +++++++++---------- scripts/generic_websocket_dashboard.py | 61 ++++++-------- 7 files changed, 194 insertions(+), 87 deletions(-) diff --git a/VERSION b/VERSION index de21e3a82..52d5c0c20 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0+2025.10.13T19.31.35.177Z.f12616b4.master +0.1.0+2025.10.13T19.40.55.998Z.5fbaf513.berickson.2025.10.13.comm.protocol diff --git a/docs/how-to/communication_protocol.md b/docs/how-to/communication_protocol.md index b72d02fb9..9159dd478 100644 --- a/docs/how-to/communication_protocol.md +++ b/docs/how-to/communication_protocol.md @@ -147,6 +147,12 @@ Submit the flattened DAG to the communication protocol endpoint with runtime par On success, the response includes export payloads keyed by export name. Inspect `DAGExecutionException` for error details. +The executor validates each requested export before any DAG work begins. If an +export name is unknown - or if its declared `returns` node cannot be found - the +server responds with a `DAGExecutionException` describing the missing export or +node. Surfacing these errors in logs or UI telemetry helps diagnose typos and +stale configuration quickly. + When using integration bindings, call the generated async function with the same parameters. ## 10. Construct Websocket Requests @@ -183,6 +189,33 @@ The server streams back updates in messages shaped like: If `rerun_dag_delay` is set, the server automatically re-executes the DAG and pushes updates. +### Manual testing with the generic websocket dashboards + +Two helper scripts live in `scripts/` for exercising websocket flows without running a full dashboard UI: + +* `generic_websocket_dashboard.py` (Python + `aiohttp`) +* `generic_websocket_dashboard.js` (Node.js + `ws`) + +Both scripts ship with a template payload under the `REQUEST` constant. Update the payload to target the exports and parameters you want to testβ€”for example, changing `execution_dag`, `target_exports`, or `kwargs.course_id`. + +To run the Python version: + +```bash +python scripts/generic_websocket_dashboard.py +``` + +The script opens a websocket to `/wsapi/communication_protocol`, sends the JSON request, and pretty-prints any responses. Install dependencies with `pip install aiohttp` if needed. + +The Node.js version follows the same pattern. After adjusting `REQUEST`, run: + +```bash +node scripts/generic_websocket_dashboard.js +``` + +If you copy the script into a browser console, delete the `require('ws')` line so the native `WebSocket` implementation is used. + +Use these scripts to confirm executor behaviour during developmentβ€”for example, to observe partial updates or to verify that query parameters are wired correctly before embedding a request in a Dash dashboard. + ## 11. Iterate and Maintain * Profile slow queries; large joins may need new helpers or precomputed reducers. diff --git a/learning_observer/VERSION b/learning_observer/VERSION index 0aa6c860e..d0dc8086c 100644 --- a/learning_observer/VERSION +++ b/learning_observer/VERSION @@ -1 +1 @@ -0.1.0+2025.10.01T17.50.57.101Z.d6047986.master +0.1.0+2025.10.13T19.17.03.311Z.f12616b4.berickson.2025.10.13.comm.protocol diff --git a/learning_observer/learning_observer/communication_protocol/executor.py b/learning_observer/learning_observer/communication_protocol/executor.py index 4ce83511a..a57588139 100644 --- a/learning_observer/learning_observer/communication_protocol/executor.py +++ b/learning_observer/learning_observer/communication_protocol/executor.py @@ -446,7 +446,7 @@ async def handle_select(keys, fields=learning_observer.communication_protocol.qu # Determine fields to keep based on the current resulting_value if fields is All if fields == learning_observer.communication_protocol.query.SelectFields.All: - current_fields_to_keep = {key: key for key in resulting_value.keys() if key != 'provenance'} + current_fields_to_keep = {key: key for key in resulting_value.keys() if key != 'provenance'} if resulting_value else {} else: current_fields_to_keep = fields_to_keep @@ -666,10 +666,38 @@ async def execute_dag(endpoint, parameters, functions, target_exports): See `learning_observer/communication_protocol/test_cases.py` for usage examples. """ - target_nodes = [endpoint['exports'][key]['returns'] for key in target_exports] - + exports = endpoint.get('exports', {}) + nodes = endpoint.get('execution_dag', {}) visited = set() - nodes = endpoint['execution_dag'] + + # --- Resolve targets and collect any obvious errors early --- + target_nodes = [] + target_errors = {} # maps target node name -> error dict + + for key in target_exports: + if key not in exports: + # Unknown export requested + target_name = f'__missing_export__:{key}' + target_nodes.append(target_name) + target_errors[target_name] = DAGExecutionException( + f'Export `{key}` not found in endpoint.exports.', + inspect.currentframe().f_code.co_name, + {'requested_export': key, 'available_exports': list(exports.keys())} + ).to_dict() + continue + + target_node = exports[key].get('returns') + if target_node not in nodes: + # Export exists, but its `returns` node is missing from the DAG + target_nodes.append(target_node) + target_errors[target_node] = DAGExecutionException( + f'Target DAG node `{target_node}` not found in execution_dag.', + inspect.currentframe().f_code.co_name, + {'target_node': target_node, 'available_nodes': list(nodes.keys())} + ).to_dict() + continue + + target_nodes.append(target_node) async def dispatch_node(node): """ @@ -743,10 +771,17 @@ async def visit(node_name): visited.add(node_name) return nodes[node_name] + out = {} + for e in target_nodes: + if e in target_errors: + out[e] = _clean_json_via_generator(target_errors[e]) + else: + out[e] = _clean_json_via_generator(await visit(e)) + return out + # Include execution history in output if operating in development settings if learning_observer.settings.RUN_MODE == learning_observer.settings.RUN_MODES.DEV: return {e: _clean_json_via_generator(await visit(e)) for e in target_nodes} - # HACK currently `dashboard.py` relies on the provenance to tell users which # items need updating, such as John Doe's history essay. This ought to be # handled by the communication protocol during execution. Once that occurs, diff --git a/learning_observer/learning_observer/dashboard.py b/learning_observer/learning_observer/dashboard.py index 7799773b4..1722cf752 100644 --- a/learning_observer/learning_observer/dashboard.py +++ b/learning_observer/learning_observer/dashboard.py @@ -587,6 +587,53 @@ async def websocket_dashboard_handler(request): Returns: aiohttp web response. ''' + active_user = await learning_observer.auth.get_active_user(request) + user_context = { + 'user_id': (active_user or {}).get('user_id'), + 'user_email': (active_user or {}).get('email'), + 'user_role': (active_user or {}).get('role'), + } + + def _log_protocol_event(event, **extra): + ''' + Emit structured debug logs describing websocket activity. + ''' + payload = { + 'event': event, + 'user_id': user_context.get('user_id'), + 'user_email': user_context.get('user_email'), + 'user_role': user_context.get('user_role'), + 'remote': request.remote, + 'forwarded_for': request.headers.get('X-Forwarded-For'), + 'request_path': str(request.rel_url), + } + payload.update(extra) + try: + debug_log('communication_protocol_event', json.dumps(payload, sort_keys=True)) + except TypeError: + # Fall back to logging the raw payload if serialization fails. + debug_log('communication_protocol_event', payload) + + def _summarize_query(query): + ''' + Provide a compact description of the query for log aggregation. + ''' + summary = {} + for key, value in (query or {}).items(): + if not isinstance(value, dict): + summary[key] = {'non_dict_value': repr(value)} + continue + execution_dag = value.get('execution_dag') + summary[key] = { + 'target_exports': value.get('target_exports', []), + 'execution_dag_type': type(execution_dag).__name__, + 'execution_dag_name': execution_dag if isinstance(execution_dag, str) else None, + 'kwargs': value.get('kwargs', {}), + } + return summary + + _log_protocol_event('connection_opened') + ws = aiohttp.web.WebSocketResponse(receive_timeout=0.3) await ws.prepare(request) client_query = None @@ -661,11 +708,15 @@ async def _drive_generator(generator, dag_kwargs, target=None): try: received_params = await ws.receive_json() client_query = received_params + _log_protocol_event( + 'query_received', + query_summary=_summarize_query(client_query), + ) # TODO we should validate the client_query structure except (TypeError, ValueError): # these Errors may signal a close if (await ws.receive()).type == aiohttp.WSMsgType.CLOSE: - debug_log("Socket closed!") + _log_protocol_event('connection_closed', reason='client_close_frame') return aiohttp.web.Response() except asyncio.exceptions.TimeoutError: # this is the normal path of the code @@ -674,7 +725,7 @@ async def _drive_generator(generator, dag_kwargs, target=None): continue if ws.closed: - debug_log("Socket closed.") + _log_protocol_event('connection_closed', reason='websocket_closed_flag') return aiohttp.web.Response() if client_query != previous_client_query: diff --git a/scripts/generic_websocket_dashboard.js b/scripts/generic_websocket_dashboard.js index fbce5797a..018616a9e 100644 --- a/scripts/generic_websocket_dashboard.js +++ b/scripts/generic_websocket_dashboard.js @@ -1,53 +1,48 @@ /* - This is test code for our new generic dashboard framework. + Simple Node.js websocket client for exercising the communication protocol. - It runs with node.js. We're developing it in node so that we can use - this as a starting point for thinking about a front-end framework, and - perhaps share test code. - - We don't have a clean plan for where we'll go (e.g. reuse code versus - prototypes). It's starting as a clone of the Python code with the same - filename. + Update the REQUEST payload to point at the execution DAG and exports you want + to test. */ -// var d3 = require('d3'); -// d3.text("https://www.google.com", function(d) {console.log(d);}); +// Remove this line when running within a browser terminal (i.e. non-Node.js environment) const WebSocket = require('ws'); -server = 'ws://localhost:8888/wsapi/generic_dashboard' - -messages = [ - { - "action": "subscribe", - "keys": [{ - "source" : "da_timeline.visualize.handle_event", - "KeyField.STUDENT": "guest-225d890e93a6b04c0aefe515b9d2dac9" - }], - "refresh": [0.5, "seconds"] - }, - { - "action": "subscribe", - "keys": [{ - "source" : "da_timeline.visualize.handle_event", - "KeyField.STUDENT": "INVALID-STUDENT" - }], - "refresh": [2, "seconds"] - }, - { - "action": "start" - } -] - -socket = new WebSocket(server); -socket.on('message', msg => console.log(msg.toString())); +const SERVER = 'ws://localhost:8888/wsapi/communication_protocol'; -socket.onopen = function() { - console.log("Open"); - for(var i=0; i { + console.log('Open'); + socket.send(JSON.stringify(REQUEST)); +}); + +socket.on('message', (msg) => { + try { + const parsed = JSON.parse(msg.toString()); + console.log(parsed); + } catch (error) { + console.log(msg.toString()); + } +}); + +socket.on('error', (err) => { + console.log('Error'); + console.log(err); +}); + +socket.on('close', (event) => { + console.log('Close'); + if (event) { + console.log(event); + } +}); diff --git a/scripts/generic_websocket_dashboard.py b/scripts/generic_websocket_dashboard.py index 8109b0295..0615b6d9f 100644 --- a/scripts/generic_websocket_dashboard.py +++ b/scripts/generic_websocket_dashboard.py @@ -1,48 +1,41 @@ -''' -This is a test script for a web socket interaction. -''' +"""Simple websocket client for manual communication protocol testing.""" import aiohttp import asyncio +import json -messages = [ - { - "action": "subscribe", - "keys": [{ - "source": "da_timeline.visualize.handle_event", - "KeyField.STUDENT": "guest-225d890e93a6b04c0aefe515b9d2dac9" - }], - "refresh": [0.5, "seconds"] - }, - { - "action": "subscribe", - "keys": [{ - "source": "da_timeline.visualize.handle_event", - "KeyField.STUDENT": "INVALID-STUDENT" - }], - "refresh": [2, "seconds"] - }, - { - "action": "start" +# Example request payload for the communication protocol websocket. Adjust the +# execution_dag, target_exports, and kwargs to match the reducers you want to +# exercise. +REQUEST = { + "docs_request": { + "execution_dag": "writing_observer", + "target_exports": ["docs_with_roster"], + "kwargs": { + "course_id": 12345678901 + }, } -] +} async def main(): async with aiohttp.ClientSession() as session: - async with session.ws_connect('http://localhost:8888/wsapi/generic_dashboard', timeout=0.5) as ws: - for message in messages: - await ws.send_json(message) - + async with session.ws_connect( + "http://localhost:8888/wsapi/communication_protocol", + timeout=5, + ) as ws: + await ws.send_json(REQUEST) async for msg in ws: - print(msg.type) if msg.type == aiohttp.WSMsgType.TEXT: - print("Message") - print(msg.data) + try: + parsed = json.loads(msg.data) + except json.JSONDecodeError: + parsed = msg.data + print(json.dumps(parsed, indent=2)) elif msg.type == aiohttp.WSMsgType.ERROR: - print("Error") - print(msg) + print("Error received from websocket:", msg) break - return True -asyncio.run(main()) + +if __name__ == '__main__': + asyncio.run(main()) From c3469ce8ec348dff2227396b8efae6f2f4cff68c Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Tue, 14 Oct 2025 09:37:19 -0400 Subject: [PATCH 57/88] added missing timestamp info --- VERSION | 2 +- learning_observer/VERSION | 2 +- learning_observer/learning_observer/dashboard.py | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index 52d5c0c20..25828c70f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0+2025.10.13T19.40.55.998Z.5fbaf513.berickson.2025.10.13.comm.protocol +0.1.0+2025.10.14T13.37.19.878Z.ec3193cb.master diff --git a/learning_observer/VERSION b/learning_observer/VERSION index d0dc8086c..25828c70f 100644 --- a/learning_observer/VERSION +++ b/learning_observer/VERSION @@ -1 +1 @@ -0.1.0+2025.10.13T19.17.03.311Z.f12616b4.berickson.2025.10.13.comm.protocol +0.1.0+2025.10.14T13.37.19.878Z.ec3193cb.master diff --git a/learning_observer/learning_observer/dashboard.py b/learning_observer/learning_observer/dashboard.py index 1722cf752..01bd0cc00 100644 --- a/learning_observer/learning_observer/dashboard.py +++ b/learning_observer/learning_observer/dashboard.py @@ -9,6 +9,7 @@ import asyncio import copy +import datetime import inspect import json import aiohttp.client_exceptions @@ -606,6 +607,7 @@ def _log_protocol_event(event, **extra): 'remote': request.remote, 'forwarded_for': request.headers.get('X-Forwarded-For'), 'request_path': str(request.rel_url), + 'timestamp': str(datetime.datetime.now()), } payload.update(extra) try: From faff12b3dd97332143e71c60aea075fc99d34290 Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Wed, 15 Oct 2025 10:41:46 -0400 Subject: [PATCH 58/88] added basic service worker config to writing extension --- VERSION | 2 +- extension/writing-process/src/background.js | 13 +++---------- .../writing-process/src/service_worker_config.js | 7 +++++++ 3 files changed, 11 insertions(+), 11 deletions(-) create mode 100644 extension/writing-process/src/service_worker_config.js diff --git a/VERSION b/VERSION index 25828c70f..3ad8911cb 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0+2025.10.14T13.37.19.878Z.ec3193cb.master +0.1.0+2025.10.15T14.41.46.897Z.c3469ce8.master diff --git a/extension/writing-process/src/background.js b/extension/writing-process/src/background.js index 236a91d3e..d5544d1a7 100644 --- a/extension/writing-process/src/background.js +++ b/extension/writing-process/src/background.js @@ -2,15 +2,7 @@ Background script. This works across all of Google Chrome. */ -// Do not save debug requests. We flip this frequently. Perhaps this -// should be a cookie or browser.storage? -var RAW_DEBUG = false; - -/* This variable must be manually updated to specify the server that - * the data will be sent to. -*/ -var WEBSOCKET_SERVER_URL = "wss://learning-observer.org/wsapi/in/"; - +import { CONFIG } from "./service_worker_config.js"; import { googledocs_id_from_url } from './writing_common'; import * as loEvent from 'lo_event/lo_event/lo_event.js'; @@ -21,6 +13,8 @@ import { browserInfo } from 'lo_event/lo_event/metadata/browserinfo.js'; import { chromeAuth } from 'lo_event/lo_event/metadata/chromeauth.js'; import { localStorageInfo, sessionStorageInfo } from 'lo_event/lo_event/metadata/storage.js'; +const { RAW_DEBUG, WEBSOCKET_SERVER_URL } = CONFIG; + // We would like to support fetching the websocket server from storage // callback = new Promise((resolve, reject) => { @@ -35,7 +29,6 @@ let loEventActive = false; let loggers = []; const manifestVersion = chrome.runtime.getManifest().version; - // We are not sure if this should be done within `websocketLogger()`'s `init` // or one level up. function startLogger () { diff --git a/extension/writing-process/src/service_worker_config.js b/extension/writing-process/src/service_worker_config.js new file mode 100644 index 000000000..464af0659 --- /dev/null +++ b/extension/writing-process/src/service_worker_config.js @@ -0,0 +1,7 @@ +// service_worker_config.js +export const CONFIG = { + // Flag for logging `raw_http_request` events + RAW_DEBUG: false, + // Learning Observer websocket connection endpoint + WEBSOCKET_SERVER_URL: "wss://learning-observer.org/wsapi/in/", +}; From 1073452b1f0d250f390c588377a9f0fc9e987432 Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Mon, 20 Oct 2025 10:32:09 -0400 Subject: [PATCH 59/88] added more context to documentation and added multiple roster source how to --- README.md | 14 +++- VERSION | 2 +- autodocs/how-to.rst | 4 +- autodocs/index.rst | 6 ++ docs/concepts/architecture.md | 13 +++- docs/concepts/reducers.md | 9 +++ docs/how-to/dashboards.md | 7 ++ docs/how-to/multiple_roster_sources.md | 101 +++++++++++++++++++++++++ docs/tutorials/cookiecutter-module.md | 23 +++++- docs/tutorials/install.md | 7 +- 10 files changed, 175 insertions(+), 11 deletions(-) create mode 100644 docs/how-to/multiple_roster_sources.md diff --git a/README.md b/README.md index 589843d63..8c56ebba8 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,14 @@ per-student writing data, and aggegators to make dashboards. We've tested this in math and writing, but our focus is on writing process data. +At a high level, Learning Observer functions as an application platform. +The primary `learning_observer` module bootstraps the system: it loads +configuration, connects to storage and messaging back ends, and brokers +communication with data sources. Other modules plug into that +infrastructure to define the specific reducers, dashboards, and other +items that users interact with, letting teams experiment with new +features without having to reimplement the platform core. + It's not finished, but it's moving along quickly. ## Writing Observer @@ -54,9 +62,9 @@ that the core approach and APIs are correct. ## Getting Started -We have a short guide to [getting started](docs/workshop.md). Getting -the base system working is pretty easy. The guide is pretty -comprehensive (including how to develop your first module). +We have a short guide to [installing the system](docs/tutorials/install.md). +Getting the base system working is pretty easy. To create a new module +for the system to use, check out our [cookiecutter module guide](docs/tutorials/cookiecutter-module.md). ### System requirements diff --git a/VERSION b/VERSION index 3ad8911cb..1e529147e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0+2025.10.15T14.41.46.897Z.c3469ce8.master +0.1.0+2025.10.20T14.32.09.486Z.faff12b3.master diff --git a/autodocs/how-to.rst b/autodocs/how-to.rst index 872902c42..55b2336f3 100644 --- a/autodocs/how-to.rst +++ b/autodocs/how-to.rst @@ -4,9 +4,9 @@ How-to Practical instructions for achieving specific goals within Learning Observer. Use these guides when you know what outcome you need and want a proven recipe to follow: - :doc:`Communication Protocol ` - How to query data from reducers or system endpoints for dashboards. -- :doc:`Configure Learning Observer ` - Set up credentials, environment variables, and other configuration details required for a smooth deployment. - :doc:`Build Dashboards ` - Walk through creating dashboards from reducer outputs, including layout choices and data wiring. - :doc:`Serve as LTI application` - Cover how to install Learning Observer as an LTI application. +- :doc:`Configure Multiple Roster Sources` - Allow the system to dynamically choose a roster source given a user's context. - :doc:`Run with Docker ` - Learn how to containerize the stack, manage images, and operate the project using Docker Compose. - :doc:`Writing Observer Extension ` - Install, configure, and validate the Writing Observer browser extension for capturing events. - :doc:`Interactive Environments ` - Connect Learning Observer to Jupyter and other live coding setups for iterative development. @@ -17,9 +17,9 @@ Practical instructions for achieving specific goals within Learning Observer. Us :titlesonly: docs/how-to/communication_protocol.md - docs/how-to/config.md docs/how-to/dashboards.md docs/how-to/lti.md + docs/how-to/multiple_roster_sources.md docs/how-to/docker.md docs/how-to/extension.md docs/how-to/interactive_environments.md diff --git a/autodocs/index.rst b/autodocs/index.rst index d4509ecf0..10bedee6f 100644 --- a/autodocs/index.rst +++ b/autodocs/index.rst @@ -12,6 +12,12 @@ per-student writing data, and aggegators to make dashboards. We've tested this in math and writing, but our focus is on writing process data. +At a high level, Learning Observer operates as an application platform: +the core :mod:`learning_observer` package boots the system, loads +configured modules, and manages shared data services, while each module +provides the specific dashboards, reducers, and other artifacts that +users interact with. + Our documentation is organized into four main categories, each serving a different purpose. You can explore them below: - :doc:`Tutorials ` - Step-by-step guides to help you learn by doing. diff --git a/docs/concepts/architecture.md b/docs/concepts/architecture.md index cdfead31e..084a65170 100644 --- a/docs/concepts/architecture.md +++ b/docs/concepts/architecture.md @@ -5,7 +5,7 @@ Piotr Mitros ## Introduction Like all such documents, this document should be taken with a grain of -salt. It my be out-of-date, or not fully implemented. +salt. It my be out-of-date, or not fully implemented. ## Overview @@ -19,6 +19,17 @@ salt. It my be out-of-date, or not fully implemented. 5. Consumers can aggregate these notifications, inspect the external state, and make a dashboard. +## Application platform structure + +Learning Observer acts as the shared platform that hosts and coordinates +modules. The core `learning_observer` package owns the boot process: it +loads configuration, establishes connections to databases and pub/sub +systems, and exposes the APIs modules use to register reducers, +dashboards, and other artifacts. Individual modules focus on defining +those artifacts, relying on the platform to handle data ingestion and +communication so new functionality can be added without duplicating the +runtime infrastructure. + ### Technology choices 1. Generic student information (e.g. names, auth, etc.) cn live in diff --git a/docs/concepts/reducers.md b/docs/concepts/reducers.md index 36cb2f588..2139974dd 100644 --- a/docs/concepts/reducers.md +++ b/docs/concepts/reducers.md @@ -82,4 +82,13 @@ REDUCERS = [ ] ``` +The `context` value must match the `source` string attached to incoming +events. When an event arrives on the websocket it advertises a `source` +identifier (for example `org.mitros.writing_analytics`). The stream +analytics loader uses that identifier to look up which reducers to +execute. If the reducer `context` does not match the event `source`, the +event will never reach your reducer. Double-check that any new event +emitters (such as browser extensions or test scripts) populate the same +`source` string that your module registers here. + NOTE: the `default` defined in the `module.py` file is for handling defaults when queries are made, while the `null_state` defined in the reducer decorator is used for initializing state of a new incoming event stream (e.g. a new student started sending events). diff --git a/docs/how-to/dashboards.md b/docs/how-to/dashboards.md index 8655fddab..f0f767fc5 100644 --- a/docs/how-to/dashboards.md +++ b/docs/how-to/dashboards.md @@ -157,6 +157,13 @@ window.dash_clientside.learning_observer_template = { } ``` +The websocket helper always reads its connection details from the page +hash. Without a hash (or without a `course_id` entry in the decoded +parameters) the client returns `no_update`, meaning the dashboard never +sends a query to the communication protocol. When you add links to a +dashboard, ensure they preserve whatever hash parameters the module +expectsβ€”typically at least `course_id` and sometimes other filter values. + To add a dashboard to a module, add the following to the module's `module.py` file ```python diff --git a/docs/how-to/multiple_roster_sources.md b/docs/how-to/multiple_roster_sources.md new file mode 100644 index 000000000..fe0823342 --- /dev/null +++ b/docs/how-to/multiple_roster_sources.md @@ -0,0 +1,101 @@ +# Configuring Multiple Roster Sources + +This guide explains how to configure Learning Observer to load rosters from +multiple providers using [PMSS (Preference Management Style Sheets)](https://github.com/ETS-Next-Gen/pmss). + +## 1. Define roster sources in PMSS + +Create a PMSS file (for example `rosters.pmss`) that declares the roster sources +and the provider/domain specific overrides. The default rule selects `all` +roster data, while provider- and domain-specific rules replace that value when a +match is found. + +```pmss +roster_data { + source: all; +} + +roster_data[provider="example-schoology"] { + source: schoology; +} + +/* Note that Canvas requires a slightly different source format */ +roster_data[provider="example-canvas"] { + source: example-canvas; +} + +roster_data[domain="learning-observer.org"] { + source: google; +} +``` + +Save the file alongside your configuration so that it can be referenced by the +application. + +## 2. Load the PMSS ruleset in `settings.py` + +Add the new PMSS file to the `pmss.init` call in `settings.py`. This loads both +the standard YAML configuration and the roster rules you just defined. + +```python +# settings.py +pmss_settings = pmss.init( + prog=__name__, + description="A system for monitoring", + epilog="For more information, see PMSS documentation.", + rulesets=[ + pmss.YAMLFileRuleset(filename=learning_observer.paths.config_file()), + pmss.PMSSFileRuleset(filename="rosters.pmss"), + ], +) +``` + +> **Note:** The PMSS file path is currently hard-coded. Future work will make +> this discovery automatic. + +## 3. Ensure roster sources are supported + +Each roster source requires the associated integration to be configured: + +- `google` requires Google OAuth credentials and callback URLs to be set up. +- `example-canvas` (and other Canvas instances) may need additional PMSS options to + expose Canvas-specific configuration, such as API tokens and instance URLs. +- `schoology` requires the Schoology integration to be enabled and configured. + +Verify that the necessary credentials and configuration options exist before +enabling a source. + +## 4. Enable feature flags for roster routes + +In the system settings, confirm that the relevant feature flags are enabled so the +system exposes the API routes for each provider. Flags follow the +`_routes` naming pattern, for example: + +```yml +# creds.yaml +feature_flags: {google_routes, canvas_routes, schoology_routes} +``` + +These flags allow the application to access the provider-specific endpoints and +retrieve roster data. + +## 5. Test with multiple contexts + +Access the system through each supported context to verify that rosters appear +as expected: + +- Sign in with Google to check the `google` roster data. +- Launch from Canvas via LTI to verify the `example-canvas` roster. +- Launch from Schoology to validate the `example-schoology` roster. + +The user's session context (provider and domain) is evaluated in +`learning_observer/rosters.py` to choose the appropriate PMSS setting. As you +switch between providers, confirm that the roster displayed matches the source +specified in your PMSS configuration. + +## 6. PMSS as a user-specific configuration tool + +This setup demonstrates how PMSS can tailor settings per user or institution by +matching on provider and domain. While roster selection is the primary use case +now, the same approach can be extended to differentiate other settings for +specific audiences in the future. diff --git a/docs/tutorials/cookiecutter-module.md b/docs/tutorials/cookiecutter-module.md index 97245f6e5..f84970be5 100644 --- a/docs/tutorials/cookiecutter-module.md +++ b/docs/tutorials/cookiecutter-module.md @@ -39,6 +39,15 @@ The template scaffolds all of the pieces Learning Observer expects, including a 2. Review `reducers.py` to understand how the template reducer counts events and where to extend it for your own analytics. You can find it next to `module.py` in the generated package directory. 3. Open `dash_dashboard.py` to learn how the generated layout publishes the reducer output on a Dash page. Use this as a starting point for your own visualizations. + While you examine `module.py`, note that each reducer entry includes a + `context` string. That value must match the `source` identifier the + event producer sends on every message (for example the Google Docs + extension reports `org.mitros.writing_analytics`). The stream + analytics loader uses the pairing to dispatch events to the correct + reducers. If you change the `context` in your module, be sure to + update the emitting client so its `source` field matchesβ€”otherwise the + reducer will never receive the events you expect. + ## 4. Install the module in editable mode Installing the module registers its entry point so Learning Observer can discover it. From the repository root run: @@ -60,6 +69,14 @@ Replace `` with the directory created in step 2 (for exam 3. Wait for the services to come up, then open `http://localhost:8888/` in a browser. Your new module should appear on the home screen because the template registers it as a course dashboard card by default inside `module.py`. + The generated dashboard expects URL hash parameters (for example + `#course_id=`) so the client-side websocket helper + knows which course to query. Navigating through the home page card + fills these values in automatically. If you copy or bookmark the + dashboard URL, make sure to keep the hash segment - without it the + websocket connection will never send a query and the page will stay + blank. + ## 6. Stream sample data to exercise the module To see live data, send synthetic writing events using the helper script. @@ -76,8 +93,8 @@ To see live data, send synthetic writing events using the helper script. ## 7. Next steps -* Customize the reducer in `reducers.py` to compute the metrics your dashboard requires. -* Expand the Dash layout to visualize your new metrics. -* Add additional reducers, exports, or pages to `module.py` as your module grows. +* Customize the reducer in `reducers.py` to compute the metrics your dashboard requires. See the [reducers concept overview](../concepts/reducers.md) for guidance. +* Expand the Dash layout to visualize your new metrics. The [dashboard how-to](../how-to/dashboards.md) walks through available UI patterns. +* Add additional reducers, exports, or pages to `module.py` as your module grows. Refer back to the [communication protocol concepts](../concepts/communication_protocol.md) when defining new DAG exports. With these steps you have a working, template-based module running end-to-end inside Learning Observer. From here you can iterate on analytics and UI changes quickly by editing the generated files and reloading the server. diff --git a/docs/tutorials/install.md b/docs/tutorials/install.md index 7b08e81c6..412765c04 100644 --- a/docs/tutorials/install.md +++ b/docs/tutorials/install.md @@ -2,11 +2,13 @@ Use this tutorial to get the Learning Observer running on your machine. It walks you through the exact commands to run and the order to run them in, so you can follow along step by step. +As you set things up, keep in mind that Learning Observer functions as an application platform: the core `learning_observer` package starts the services and loads modules, and each module contributes dashboards, reducers, or other features that run on top of that core. + ## Before you begin Make sure your computer meets these requirements: -1. **Operating system** – A Unix-style system works best. We regularly test on Ubuntu. macOS generally works as well. Windows users should install the project inside [Windows Subsystem for Linux (WSL)](workshop/wsl-install.md) before continuing. +1. **Operating system** – A Unix-style system works best. We regularly test on Ubuntu. macOS generally works as well. Windows users should install the project inside [Windows Subsystem for Linux (WSL)](../workshop/wsl-install.md) before continuing. 2. **Python** – Install Python 3.10 or 3.11 (any version newer than 3.9 is expected to work). 3. **Package manager (recommended)** – Have a virtual environment tool ready. We prefer [`virtualenvwrapper`](https://pypi.org/project/virtualenvwrapper/), but you can use `python -m venv`, Conda, or another tool you like. 4. **Optional tools** – @@ -118,3 +120,6 @@ Once the server is running, open a browser and go to one of the following URLs: You should see the Learning Observer dashboard with a list of courses and analytics modules. Because the workshop configuration disables authentication, you can immediately click around and start experimenting. ## Next steps + +- Explore the [cookiecutter module tutorial](cookiecutter-module.md) to scaffold a package from the template. +- Review [system settings concepts](../concepts/system_settings.md) before deploying beyond a simple setup. From 87861f3a3a35576c848d313b473832372af236a1 Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Thu, 23 Oct 2025 09:46:43 -0400 Subject: [PATCH 60/88] Change Popover trigger from 'click' to 'focus' --- .../wo_classroom_text_highlighter/dash_dashboard.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/dash_dashboard.py b/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/dash_dashboard.py index 6a86b6d6b..1ac0f18b0 100644 --- a/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/dash_dashboard.py +++ b/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/dash_dashboard.py @@ -121,7 +121,7 @@ id=_legend_button, color='primary'), dbc.Popover( id=_legend_children, target=_legend_button, - trigger='click', body=True, placement='bottom'), + trigger='focus', body=True, placement='bottom'), lodrc.ProfileSidebarAIO(class_name='rounded-0 rounded-end', color='secondary'), ], class_name='align-items-center') From 3ff5ed7afc4593e7a64a2857da466c53c78f20e3 Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Tue, 28 Oct 2025 09:41:31 -0400 Subject: [PATCH 61/88] added kvs documentation and referenced based on tutorial feedback --- VERSION | 2 +- docs/concepts/key_value_store.md | 107 ++++++++++++++++++++++++++ docs/tutorials/cookiecutter-module.md | 2 + 3 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 docs/concepts/key_value_store.md diff --git a/VERSION b/VERSION index 1e529147e..af0c81e4e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0+2025.10.20T14.32.09.486Z.faff12b3.master +0.1.0+2025.10.28T13.41.31.134Z.87861f3a.master diff --git a/docs/concepts/key_value_store.md b/docs/concepts/key_value_store.md new file mode 100644 index 000000000..5d994724f --- /dev/null +++ b/docs/concepts/key_value_store.md @@ -0,0 +1,107 @@ +# Key-value store + +Learning Observer reducers and dashboards communicate through a key-value store (KVS). +Reducers write internal state and dashboard-facing summaries to the store, while +queries and presentation layers read those JSON blobs back. The +[`learning_observer.kvs` module](../../learning_observer/learning_observer/kvs.py) +wraps the different storage backends behind a common async API. + +## Router and lifecycle + +The `KVSRouter` constructed during startup owns every configured backend. When +`learning_observer.prestartup` runs it reads the `kvs` section from system +settings, instantiates the requested implementations, and exposes them through +the module-level `KVS` callable. Most code imports `learning_observer.kvs.KVS` +and invokes it to obtain the default backend: + +```python +from learning_observer import kvs + +store = kvs.KVS() # returns the default backend configured in settings +value = await store[key] +``` + +Reducers obtain the store implicitly through decorators such as +`kvs_pipeline` and `student_event_reducer`. Those helpers capture the module +context, derive the reducer keys, and persist the reducer's internal and +external state back to the configured KVS. + +The router also exposes named backends as attributes. If a module needs to +store data in a non-default backend it can call `kvs.KVS.()` where +`` matches the identifier from configuration. + +## Configuring backends + +The `kvs` block in `creds.yaml` (or an equivalent PMSS overlay) declares each +backend. Settings accept either a mapping or an array of key/value tuples. +Every entry must provide a `type` that matches one of the built-in +implementations: + +```yaml +kvs: + default: + type: filesystem + path: .lo_kvs + redis_cache: + type: redis_ephemeral + expiry: 30 +``` + +During startup the router validates the configuration and raises a +`StartupCheck` error if a backend is missing required parameters or references +an unknown type. Once the process finishes booting, the resulting callable is +available to the rest of the system as `learning_observer.kvs.KVS`. + +### Supported types + +| Type | Class | Persistence behavior | Required settings | +|------------------|-----------------------------|------------------------------------------------------------------------|-----------------------------------------------| +| `stub` | `InMemoryKVS` | Data lives only in process memory and disappears on restart. | None | +| `redis_ephemeral`| `EphemeralRedisKVS` | Uses Redis with a per-key TTL for temporary caches. | `expiry` (seconds) | +| `redis` | `PersistentRedisKVS` | Stores data in Redis without an expiry; persistence depends on Redis. | Redis connection parameters in system config. | +| `filesystem` | `FilesystemKVS` | Serializes each key to JSON on disk for simple local persistence. | `path`; optional `subdirs` boolean | + +All backends share the async API: `await store[key]`, `await store.set(key, value)`, +`await store.keys()`, and `await store.dump()` for debugging. + +### Filesystem layout + +The filesystem implementation writes JSON documents under the configured path. +If `subdirs` is true it mirrors slash-separated key prefixes into nested +folders while prefixing directory names with underscores to avoid collisions. +This backend is convenient for workshops or debugging because state survives +restarts as long as the directory remains intact, but it is not designed for +large-scale deployments. + +### Redis variants + +Both Redis implementations rely on the shared connection utilities in +`learning_observer.redis_connection`. The ephemeral variant requires an +`expiry` value so it can set a TTL when calling `SET`, making it suitable for +integration tests or scratch environments. The persistent variant omits the TTL +so keys remain until explicitly deleted or until the Redis server evicts them. +Ensure the Redis instance has persistence enabled (`appendonly` or RDB +snapshots) if the deployment expects reducer state to survive reboots. + +### In-memory stub + +The stub backend keeps a Python dictionary in memory. It is useful for unit +tests or prototype scripts but should not be used when the process restarts or +scales across workers. The module exposes a `clear()` helper to wipe the store +between tests. + +## Working with reducer state + +Reducer keys follow the pattern `,,` where the scope +captures whether the state is internal or external, the module identifies the +producer, and the selector encodes the entity (for example, a student ID). When +reducers process new events they read the previous state from the KVS, compute +an updated value, and call `set()` to write it back. Dashboards and protocol +adapters then fetch the external state by constructing the same key or by using +higher-level query helpers that wrap the KVS API. + +If a dashboard appears empty after restarting the server, confirm which backend +is active. In-memory and ephemeral Redis stores start empty on boot, so the +system needs a fresh stream of events to repopulate reducer state. Filesystem +and persistent Redis backends retain data unless their underlying storage was +cleared. diff --git a/docs/tutorials/cookiecutter-module.md b/docs/tutorials/cookiecutter-module.md index f84970be5..ad2639cf9 100644 --- a/docs/tutorials/cookiecutter-module.md +++ b/docs/tutorials/cookiecutter-module.md @@ -91,6 +91,8 @@ To see live data, send synthetic writing events using the helper script. This sends five concurrent simulated students worth of Google Docs events to the default local endpoint using the helper found at `scripts/stream_writing.py`. 3. Refresh your browser. The default reducer counts incoming events, so you should see the totals increase on the Dash page included with the template. Both the reducer and the dashboard live alongside `module.py` in your generated package. +A common cause for missing data is having 0 reducer output available in storage. Check your [Key Value Store](../concepts/key_value_store.md) settings to assess how the reducer output is being stored. + ## 7. Next steps * Customize the reducer in `reducers.py` to compute the metrics your dashboard requires. See the [reducers concept overview](../concepts/reducers.md) for guidance. From c84a34840faf0a00386425da75f657a6d3c5a2fa Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Mon, 3 Nov 2025 10:44:31 -0500 Subject: [PATCH 62/88] Added communication protocol endpoing logging ability * added detailed logging to communication protocol endpoint * added todo about stripping provenance * added user domain to dashboard log pmss setting * added dashboard_settings.logging_enabled to the reference * added more context to system settings documation * added a few more comments --- VERSION | 2 +- docs/concepts/system_settings.md | 25 ++ docs/reference/system_settings.md | 6 + learning_observer/VERSION | 2 +- .../learning_observer/dashboard.py | 261 +++++++++++++----- learning_observer/learning_observer/util.py | 2 + 6 files changed, 231 insertions(+), 67 deletions(-) diff --git a/VERSION b/VERSION index af0c81e4e..f4b4217cd 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0+2025.10.28T13.41.31.134Z.87861f3a.master +0.1.0+2025.11.03T15.31.30.155Z.0dd51980.berickson.logging diff --git a/docs/concepts/system_settings.md b/docs/concepts/system_settings.md index fc7d0ae93..b24009c46 100644 --- a/docs/concepts/system_settings.md +++ b/docs/concepts/system_settings.md @@ -99,6 +99,31 @@ stack. That means a request handled for an instructor can pick up instructor-specific defaults while a system job, using the same accessor, still observes the site-wide configuration. +### What context we pass today + +Every call into `pmss_settings` names the setting through the `types` list. We +build that list from the canonical namespace of the settingβ€”`['server']` for the +public port, `['redis_connection']` for Redis, `['modules', module_name]` for +module flags, and so on. Because the list mirrors the hierarchy defined in +`creds.yaml`, we get deterministic lookups even when overlays layer additional +rules on top. + +Selectors (the `attributes` argument) are rarer. Only features that genuinely +vary per request provide them today. For example, roster resolution passes the +requesting user's email domain and the LTI provider so the `roster_data` +configuration can pick the correct backend, and the dashboard logging toggle +adds the user's domain to honour tenant-specific overrides. Most other settings +still rely solely on the namespace lookup. + +### Where we want to go + +We want every lookup that depends on request context to assemble the same +attribute payload in the same place. Rather than sprinkling ad-hoc conditionals +around the codebase, helpers should gather the domain, provider, role, or other +selectors once and pass them through every relevant PMSS call. This keeps the +setting definitions declarative, makes it obvious which selectors operators can +target in overlays, and avoids drift between different parts of the system. + ## Extending the system settings surface Adding a new capability follows a consistent pattern: diff --git a/docs/reference/system_settings.md b/docs/reference/system_settings.md index ce097d30e..88c53e2c4 100644 --- a/docs/reference/system_settings.md +++ b/docs/reference/system_settings.md @@ -41,6 +41,12 @@ runtime. | `aio.session_secret` | Secret used to encrypt and sign aiohttp session cookies. Generate a unique value per deployment. | required | [`learning_observer/learning_observer/webapp_helpers.py`](../../learning_observer/learning_observer/webapp_helpers.py) | | `aio.session_max_age` | Session lifetime in seconds. | required | [`learning_observer/learning_observer/webapp_helpers.py`](../../learning_observer/learning_observer/webapp_helpers.py) | +### Dashboard Settings (`dashboard_settings` namespace) + +| YAML path | Description | Default | Used in | +| --- | --- | --- | --- | +| `dashboard_settings.logging_enabled` | Determine if we should log dashboard sessions. | `false` | [`learning_observer/learning_observer/dashboard.py`](../../learning_observer/learning_observer/dashboard.py) | + ### Redis connection (`redis_connection` namespace) | YAML path | Description | Default | Used in | diff --git a/learning_observer/VERSION b/learning_observer/VERSION index 25828c70f..f4b4217cd 100644 --- a/learning_observer/VERSION +++ b/learning_observer/VERSION @@ -1 +1 @@ -0.1.0+2025.10.14T13.37.19.878Z.ec3193cb.master +0.1.0+2025.11.03T15.31.30.155Z.0dd51980.berickson.logging diff --git a/learning_observer/learning_observer/dashboard.py b/learning_observer/learning_observer/dashboard.py index 01bd0cc00..0199a3176 100644 --- a/learning_observer/learning_observer/dashboard.py +++ b/learning_observer/learning_observer/dashboard.py @@ -15,6 +15,7 @@ import aiohttp.client_exceptions import jsonschema import numbers +import os import pmss import queue import time @@ -34,10 +35,12 @@ import learning_observer.auth import learning_observer.constants as constants import learning_observer.rosters as rosters +import learning_observer.util -from learning_observer.log_event import debug_log +from learning_observer.log_event import debug_log, log_event, close_logfile import learning_observer.module_loader +import learning_observer.communication_protocol.executor import learning_observer.communication_protocol.integration import learning_observer.communication_protocol.query import learning_observer.communication_protocol.schema @@ -53,6 +56,13 @@ default=False ) +pmss.register_field( + name='logging_enabled', + type=pmss.pmsstypes.TYPES.boolean, + description='Log dashboard sessions to .dashboard.log files', + default=False +) + def timelist_to_seconds(timelist): ''' @@ -457,7 +467,8 @@ async def dispatch_named_execution_dag(dag_name): except KeyError: debug_log(await dag_not_found(dag_name)) finally: - return query + pass + return query async def dispatch_defined_execution_dag(dag): @@ -476,7 +487,8 @@ async def dispatch_defined_execution_dag(dag): debug_log(await dag_incorrect_format(e)) return query finally: - return query + pass + return query DAG_DISPATCH = {dict: dispatch_defined_execution_dag, str: dispatch_named_execution_dag} @@ -562,20 +574,26 @@ def _find_student_or_resource(d): output = [] if 'STUDENT' in provenance: output.append('students') - output.append(provenance['STUDENT']['user_id']) + output.append(str(provenance['STUDENT']['user_id'])) if 'RESOURCE' in provenance: if 'doc_id' in provenance['RESOURCE']: output.append('documents') - output.append(provenance['RESOURCE']['doc_id']) + output.append(str(provenance['RESOURCE']['doc_id'])) if 'assignment_id' in provenance['RESOURCE']: output.append('assignments') - output.append(provenance['RESOURCE']['assignment_id']) + output.append(str(provenance['RESOURCE']['assignment_id'])) if output: return output return _find_student_or_resource(provenance) return [] +# We track protocol log creation per-process so that concurrent websocket +# connections produce unique filenames even when they open at the same time. +DASHBOARD_PROTOCOL_LOG_COUNTER = 0 +DASHBOARD_PROTOCOL_LOG_LOCK = asyncio.Lock() + + @learning_observer.auth.teacher async def websocket_dashboard_handler(request): ''' @@ -594,29 +612,88 @@ async def websocket_dashboard_handler(request): 'user_email': (active_user or {}).get('email'), 'user_role': (active_user or {}).get('role'), } + # Fetch PMSS setting flag for dashboard logging, scoped by user's email domain + user_domain = learning_observer.util.get_domain_from_email(user_context['user_email']) + dashboard_protocol_logging_enabled = learning_observer.settings.pmss_settings.logging_enabled(types=['dashboard_settings'], attributes={'domain': user_domain}) + + global DASHBOARD_PROTOCOL_LOG_COUNTER + async with DASHBOARD_PROTOCOL_LOG_LOCK: + current_counter = DASHBOARD_PROTOCOL_LOG_COUNTER + DASHBOARD_PROTOCOL_LOG_COUNTER += 1 + + # TODO this is similar to our incoming student event log file names + # this ought to be abstracted to a helper function + # The combination of timestamp / user / process / counter creates + # unique filenames for each dashboard session + protocol_log_filename = "{timestamp}-{user:-<15}-{remote:-<15}-{counter:0>10}-{pid}.dashboard".format( + timestamp=datetime.datetime.utcnow().isoformat(), + user=(user_context.get('user_id') or 'UNKNOWN')[:15], + remote=(request.remote or '')[:15], + counter=current_counter, + pid=os.getpid(), + ) + + is_protocol_log_closed = False + + def close_protocol_log(): + '''Close the structured protocol log if logging is enabled.''' + if not dashboard_protocol_logging_enabled: + return + nonlocal is_protocol_log_closed + if not is_protocol_log_closed: + try: + close_logfile(protocol_log_filename) + finally: + is_protocol_log_closed = True + + has_logged_connection_closure = False + lock_field_event = { + 'event': 'lock_fields', + 'fields': { + 'user_id': user_context.get('user_id'), + 'user_email': user_context.get('user_email'), + 'user_role': user_context.get('user_role'), + 'remote': request.remote, + 'request_path': str(request.rel_url) + } + } + if dashboard_protocol_logging_enabled: + log_event(lock_field_event, filename=protocol_log_filename) def _log_protocol_event(event, **extra): ''' - Emit structured debug logs describing websocket activity. + Emit structured logs describing websocket activity. ''' + if not dashboard_protocol_logging_enabled: + return + nonlocal has_logged_connection_closure # ensure we mutate the outer flag payload = { 'event': event, - 'user_id': user_context.get('user_id'), - 'user_email': user_context.get('user_email'), - 'user_role': user_context.get('user_role'), - 'remote': request.remote, - 'forwarded_for': request.headers.get('X-Forwarded-For'), - 'request_path': str(request.rel_url), 'timestamp': str(datetime.datetime.now()), } payload.update(extra) try: - debug_log('communication_protocol_event', json.dumps(payload, sort_keys=True)) + log_event(payload, filename=protocol_log_filename) except TypeError: # Fall back to logging the raw payload if serialization fails. - debug_log('communication_protocol_event', payload) - - def _summarize_query(query): + log_event({ + 'event_type': 'communication_protocol_event', + 'event': event, + 'serialization_failed': True, + 'payload_repr': repr(payload), + }, filename=protocol_log_filename) + if event == 'connection_closed': + has_logged_connection_closure = True + + def _close_connection_and_cleanup(reason: str): + """ + Idempotently log connection closure and close the protocol log file. + """ + if not has_logged_connection_closure: + _log_protocol_event('connection_closed', reason=reason) + close_protocol_log() + + def _create_query_summary_for_logging(query): ''' Provide a compact description of the query for log aggregation. ''' @@ -640,26 +717,26 @@ def _summarize_query(query): await ws.prepare(request) client_query = None previous_client_query = None - batch = [] - lock = asyncio.Lock() + pending_updates = [] + pending_updates_lock = asyncio.Lock() background_tasks = set() - async def _send_update(update): + async def _queue_update(update): '''Send an update to our batch ''' - async with lock: - batch.append(update) + async with pending_updates_lock: + pending_updates.append(update) - async def _batch_send(): - '''If our batch has any items, send them to the client - then wait before checking again. + async def _send_pending_updates_to_client(): + '''If our queue has any items, send them to the client, clear + the queue, then wait before checking again. ''' while True: - async with lock: - if batch: + async with pending_updates_lock: + if pending_updates: try: - await ws.send_json(batch) - batch.clear() + await ws.send_json(pending_updates) + pending_updates.clear() except aiohttp.web_ws.WebSocketError: break except aiohttp.client_exceptions.ClientConnectionResetError: @@ -683,7 +760,7 @@ async def _execute_dag(dag_query, target, params): await _drive_generator(generator, dag_query['kwargs'], target=target) # Handle rescheduling the execution of the DAG for fresh data - # TODO add some way to specific specific endpoint delays + # TODO add some way to specify specific endpoint delays dag_delay = dag_query['kwargs'].get('rerun_dag_delay', 10) if dag_delay < 0: # if dag_delay is negative, we skip repeated execution @@ -700,47 +777,101 @@ async def _drive_generator(generator, dag_kwargs, target=None): update_path = ".".join(scope) if 'option_hash' in dag_kwargs and target is not None: item[f'option_hash_{target}'] = dag_kwargs['option_hash'] - await _send_update({'op': 'update', 'path': update_path, 'value': item}) + # TODO this ought to be flag - we might want to see the provenance in some settings + item_without_provenance = learning_observer.communication_protocol.executor.strip_provenance(item) + update_payload = {'op': 'update', 'path': update_path, 'value': item_without_provenance} + _log_protocol_event( + 'update_enqueued', + payload=update_payload, + target_export=target + ) + await _queue_update(update_payload) - send_batches_task = asyncio.create_task(_batch_send()) + send_batches_task = asyncio.create_task(_send_pending_updates_to_client()) background_tasks.add(send_batches_task) send_batches_task.add_done_callback(background_tasks.discard) - while True: - try: - received_params = await ws.receive_json() - client_query = received_params - _log_protocol_event( - 'query_received', - query_summary=_summarize_query(client_query), - ) - # TODO we should validate the client_query structure - except (TypeError, ValueError): - # these Errors may signal a close - if (await ws.receive()).type == aiohttp.WSMsgType.CLOSE: - _log_protocol_event('connection_closed', reason='client_close_frame') - return aiohttp.web.Response() - except asyncio.exceptions.TimeoutError: - # this is the normal path of the code - # if the client_query hasn't been set, keep waiting for it - if client_query is None: + try: + while True: + try: + received_params = await ws.receive_json() + client_query = received_params + _log_protocol_event( + 'query_received', + query_summary=_create_query_summary_for_logging(client_query), + ) + # TODO we should validate the client_query structure + except aiohttp.client_exceptions.WSMessageTypeError as e: + # Check if this was a close message + if ws.closed: + _close_connection_and_cleanup('websocket_closed_with_message_error') + break + # Log the unexpected message type and continue + _log_protocol_event('unexpected_message_type', error=str(e)) + continue + except (TypeError, ValueError) as e: + _log_protocol_event( + 'json_parse_error', + error_type=type(e).__name__, + error_message=str(e) + ) + if ws.closed: + _close_connection_and_cleanup('websocket_closed_during_json_parse') + break continue + except asyncio.exceptions.TimeoutError: + # this is the normal path of the code + # if the client_query hasn't been set, keep waiting for it + if client_query is None: + continue - if ws.closed: - _log_protocol_event('connection_closed', reason='websocket_closed_flag') - return aiohttp.web.Response() - - if client_query != previous_client_query: - previous_client_query = copy.deepcopy(client_query) - # HACK even though we can specificy multiple targets for a - # single DAG, this creates a new DAG for each. This eventually - # allows us to specify different parameters (such as the - # reschedule timeout). - for k, v in client_query.items(): - for target in v.get('target_exports', []): - execute_dag_task = asyncio.create_task(_execute_dag(v, target, client_query)) - background_tasks.add(execute_dag_task) - execute_dag_task.add_done_callback(background_tasks.discard) + if ws.closed: + _close_connection_and_cleanup('websocket_closed_flag') + break + + if client_query != previous_client_query: + previous_client_query = copy.deepcopy(client_query) + # HACK even though we can specify multiple targets for a + # single DAG, this creates a new DAG for each. This eventually + # allows us to specify different parameters (such as the + # reschedule timeout). + for k, v in client_query.items(): + for target in v.get('target_exports', []): + execute_dag_task = asyncio.create_task(_execute_dag(v, target, client_query)) + background_tasks.add(execute_dag_task) + execute_dag_task.add_done_callback(background_tasks.discard) + + # Various ways we might encounter an exception + except asyncio.CancelledError: + _close_connection_and_cleanup('server_cancelled') + except (aiohttp.web_ws.WebSocketError, + aiohttp.client_exceptions.ClientConnectionResetError, + ConnectionResetError) as e: + _log_protocol_event( + 'connection_closed_gracefully', + exception_type=type(e).__name__, + detail=str(e)) + _close_connection_and_cleanup('client_disconnected') + except Exception as e: + _log_protocol_event( + 'handler_exception', + exception_type=type(e).__name__, + detail=repr(e)) + _close_connection_and_cleanup('server_exception') + finally: + # Ensure all background tasks are stopped cleanly + for t in list(background_tasks): + t.cancel() + if background_tasks: + await asyncio.gather(*background_tasks, return_exceptions=True) + + # Close WebSocket gracefully if not already closed + if not ws.closed: + try: + await ws.close() + except Exception: + pass + return aiohttp.web.Response() # Obsolete code -- we should put this back in after our refactor. Allows us to use diff --git a/learning_observer/learning_observer/util.py b/learning_observer/learning_observer/util.py index 4b0db2c8e..525d2c9a8 100644 --- a/learning_observer/learning_observer/util.py +++ b/learning_observer/learning_observer/util.py @@ -302,6 +302,8 @@ async def async_generator_to_list(gen): def get_domain_from_email(email): '''Helper function to extract the domain from an email address ''' + if email is None: + return None if '@' in email: return email.split('@')[1] return None From b8ce7074312780c0c02fd5a32b877f9f7ba90709 Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Wed, 19 Nov 2025 08:01:52 -0500 Subject: [PATCH 63/88] fixed static json endpoint for modules --- VERSION | 2 +- learning_observer/VERSION | 2 +- learning_observer/learning_observer/routes.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/VERSION b/VERSION index f4b4217cd..a0653bbc1 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0+2025.11.03T15.31.30.155Z.0dd51980.berickson.logging +0.1.0+2025.11.19T13.01.52.758Z.c84a3484.master diff --git a/learning_observer/VERSION b/learning_observer/VERSION index f4b4217cd..a0653bbc1 100644 --- a/learning_observer/VERSION +++ b/learning_observer/VERSION @@ -1 +1 @@ -0.1.0+2025.11.03T15.31.30.155Z.0dd51980.berickson.logging +0.1.0+2025.11.19T13.01.52.758Z.c84a3484.master diff --git a/learning_observer/learning_observer/routes.py b/learning_observer/learning_observer/routes.py index 8811f668c..fbe4e1a7a 100644 --- a/learning_observer/learning_observer/routes.py +++ b/learning_observer/learning_observer/routes.py @@ -457,7 +457,7 @@ def register_extra_views(app): if 'static_json' in view: views.append(aiohttp.web.get( f'/views/{view["module"]}/{view["suburl"]}/', - lambda x: aiohttp.web.json_response(view['static_json']) + lambda request, data=view['static_json']: aiohttp.web.json_response(data) )) elif 'method' in view and 'handler' in view: views.append(HTTP_METHOD_MAPPING[view['method']]( From fa3a15fced0d3401a444f30ddb4670914df1bc74 Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Wed, 19 Nov 2025 08:06:35 -0500 Subject: [PATCH 64/88] added note to student id mapping about where to run the command from --- VERSION | 2 +- docs/concepts/student_identity_mapping.md | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/VERSION b/VERSION index a0653bbc1..6e4b3cbdc 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0+2025.11.19T13.01.52.758Z.c84a3484.master +0.1.0+2025.11.19T13.06.35.864Z.b8ce7074.master diff --git a/docs/concepts/student_identity_mapping.md b/docs/concepts/student_identity_mapping.md index e902c6a2d..73d4fc123 100644 --- a/docs/concepts/student_identity_mapping.md +++ b/docs/concepts/student_identity_mapping.md @@ -17,10 +17,11 @@ This flow is intentionally simple: the reducer captures whatever the client repo ## Operating the script -The email mapping script is normally run in the same environment as other KVS maintenance tasks. It requires access to the same credentials file that reducers use. A manual run looks like this: +The email mapping script is normally run in the same environment as other KVS maintenance tasks. It requires access to the same credentials file that the main server use. Thus, we need to run the script from the `learning_observer` directory. A manual run looks like this: ```bash -python scripts/map_emails_to_ids_in_kvs.py +cd learning_observer/ +python ../scripts/map_emails_to_ids_in_kvs.py ``` The script performs a full scan every time it runs, so it is safe to execute multiple times or to schedule as a recurring job. From c1eabcb36578f096c32f74b9a58a401843666f84 Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Tue, 16 Dec 2025 14:51:07 -0500 Subject: [PATCH 65/88] Ability to serve LO Blocks via LO while signing in through Canvas (#246) Added demo module for uploading grades to an LTI service. --- VERSION | 2 +- autodocs/how-to.rst | 2 + docs/how-to/connect_lo_blocks_to_canvas.md | 364 ++++++++++++++++++ docs/how-to/lti.md | 93 +++++ learning_observer/VERSION | 2 +- .../learning_observer/auth/events.py | 24 ++ .../learning_observer/auth/lti_sso.py | 7 +- .../learning_observer/integrations/canvas.py | 40 +- .../learning_observer/integrations/util.py | 75 +++- modules/lo_lti_grade_demo/README.md | 18 + .../lo_lti_grade_demo/__init__.py | 0 .../lo_lti_grade_demo/module.py | 48 +++ .../lo_lti_grade_demo/views.py | 315 +++++++++++++++ modules/lo_lti_grade_demo/pyproject.toml | 3 + modules/lo_lti_grade_demo/setup.cfg | 12 + scripts/generate_jwks.py | 82 ++++ 16 files changed, 1059 insertions(+), 28 deletions(-) create mode 100644 docs/how-to/connect_lo_blocks_to_canvas.md create mode 100644 modules/lo_lti_grade_demo/README.md create mode 100644 modules/lo_lti_grade_demo/lo_lti_grade_demo/__init__.py create mode 100644 modules/lo_lti_grade_demo/lo_lti_grade_demo/module.py create mode 100644 modules/lo_lti_grade_demo/lo_lti_grade_demo/views.py create mode 100644 modules/lo_lti_grade_demo/pyproject.toml create mode 100644 modules/lo_lti_grade_demo/setup.cfg create mode 100644 scripts/generate_jwks.py diff --git a/VERSION b/VERSION index 6e4b3cbdc..2259bd00a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0+2025.11.19T13.06.35.864Z.b8ce7074.master +0.1.0+2025.12.16T16.39.56.199Z.46876dc.berickson.20251215.lo.blocks.through.canvas diff --git a/autodocs/how-to.rst b/autodocs/how-to.rst index 55b2336f3..79f7fb512 100644 --- a/autodocs/how-to.rst +++ b/autodocs/how-to.rst @@ -6,6 +6,7 @@ Practical instructions for achieving specific goals within Learning Observer. Us - :doc:`Communication Protocol ` - How to query data from reducers or system endpoints for dashboards. - :doc:`Build Dashboards ` - Walk through creating dashboards from reducer outputs, including layout choices and data wiring. - :doc:`Serve as LTI application` - Cover how to install Learning Observer as an LTI application. +- :doc:`Connect LO Blocks to Canvas via Learning Observer` - Show how to connect launch LO Blocks through Learning Observer from within Canvas. - :doc:`Configure Multiple Roster Sources` - Allow the system to dynamically choose a roster source given a user's context. - :doc:`Run with Docker ` - Learn how to containerize the stack, manage images, and operate the project using Docker Compose. - :doc:`Writing Observer Extension ` - Install, configure, and validate the Writing Observer browser extension for capturing events. @@ -19,6 +20,7 @@ Practical instructions for achieving specific goals within Learning Observer. Us docs/how-to/communication_protocol.md docs/how-to/dashboards.md docs/how-to/lti.md + docs/how-to/connect_lo_blocks_to_canvas.md docs/how-to/multiple_roster_sources.md docs/how-to/docker.md docs/how-to/extension.md diff --git a/docs/how-to/connect_lo_blocks_to_canvas.md b/docs/how-to/connect_lo_blocks_to_canvas.md new file mode 100644 index 000000000..ef99b2f8a --- /dev/null +++ b/docs/how-to/connect_lo_blocks_to_canvas.md @@ -0,0 +1,364 @@ +# Setting up LO Blocks with Canvas Integration + +This guide walks you through integrating LO Blocks with Learning Observer and Canvas via LTI. This setup allows Canvas users to access LO Blocks dashboards while maintaining proper authentication and data flow between all three systems. + +## Prerequisites + +You'll need access to the following systems: + +- **Learning Observer** - Base platform installation +- **LO Blocks** - Dashboard application +- **Canvas** - LMS instance with administrative rights + +## Part 1: Canvas Configuration + +### Initial Setup + +1. **Sign in to Canvas** with administrative privileges + +2. **Create test environment** (recommended for initial setup): + - Create a sample course + - Add sample students + + > **Note for local testing**: If running Canvas locally via Docker Compose: + > - No email server is configured by default + > - All "sent" emails print to the console + > - You must find confirmation email URLs in the console when adding users + +### Configure LTI Application + +Follow the [detailed LTI configuration guide](https://learning-observer.readthedocs.io/en/latest/docs/how-to/lti.html) in our documentation. + +Within Canvas, you'll want to: + +1. Navigate to the Admin portal +2. Click `Developer Keys`, then click `+ Developer Key` +3. Select `LTI Key` +4. Populate the configuration +5. Save the key +6. Enable the key for use + +> **Note**: You may need to revisit these settings after completing the Learning Observer configuration (see "Putting it all together" section below). + +## Part 2: Learning Observer Configuration + +### Base Installation + +1. **Install Learning Observer** base platform using the [Tutorial: Install](../tutorials/install.md) + +### Module Setup + +2. **Create a module** to connect to a reducer: + - [Tutorial: Build and Run a Module from the Cookiecutter Template](../tutorials/cookiecutter-module.md) + - Match the `context` in your module to the `source` in your LO Event (see LO Blocks) + - Add an endpoint in `COURSE_DASHBOARDS` defining the connection to the LO Blocks server + +3. **Configure authentication**: + - Set up password file login using `scripts/lo_passwd.py` (place the outputted file within the `learning_observer/` directory) + +### Canvas Integration Settings + +4. **Modify roster source settings** + + Edit `learning_observer/rosters.py` to update available PMSS values for `roster_source`. + + ```python + pmss.parser('roster_source', parent='string', choices=['google', 'demo-canvas', 'schoology', 'all', 'test', 'filesystem'], transform=None) + ``` + +5. **Update core settings** in your configuration: + + ```yaml + auth: + lti: + demo-canvas: # Allows users to sign in via LTI + + event_auth: + lti_session: # Allows websocket events from LTI-authenticated users + + feature_flags: + canvas_routes: true # Enables Canvas LTI API calls + + roster_data: # See roster PMSS configuration below + ``` + + > **Important**: Replace `demo-canvas` with an identifier specific to your Canvas instance (e.g., `middleton-canvas`, `easttownhigh-canvas`) + +6. **Create roster PMSS file** (`rosters.pmss`): + + ```pmss + roster_data { + source: all; + } + + roster_data[provider="demo-canvas"] { + source: demo-canvas; + } + ``` + + Any user with the provider `demo-canvas` will use the roster source `demo-canvas` whereas the rest of the users will use rouster source equal to `all`. + +7. **Register PMSS file** in `learning_observer/settings.py`: + + ```python + pmss_settings = pmss.init( + prog=__name__, + description="A system for monitoring", + epilog="For more information, see PMSS documentation.", + rulesets=[ + pmss.YAMLFileRuleset(filename=learning_observer.paths.config_file()), + pmss.PMSSFileRuleset(filename='rosters.pmss') + ] + ) + ``` + +**TODO**: Document any other settings needed "for data to flow properly" (referenced but incomplete in original document) + +## Part 3: LO Blocks Configuration + +1. **Configure websocket connection** + + Inside of `src/lib/state/store.ts` check the following: + + - `WEBSOCKET_URL` points to the Learning Observer instance + - `websocketLogger` is included in our list of available `loggers` + - Ensure the `lo_event.init` function uses the same source as defined in the reducer created earlier + +2. **Build the application**: + + ```bash + npx next build + ``` + +3. **Start the application**: + + ```bash + npx next start + ``` + +## Part 4: Putting It All Together + +### Understanding the Architecture + +LO Blocks is a Next.js application with both client and server-side components. This means: + +- We cannot serve it directly from Learning Observer (which requires static builds) +- We must run both applications side-by-side on the same machine +- We need a reverse proxy to route traffic between them + +### Nginx Reverse Proxy Configuration + +We'll use Nginx to route traffic between Learning Observer and LO Blocks. + +#### Routing Strategy + +- **Default traffic** β†’ Learning Observer +- **`/lo-blocks` path** β†’ LO Blocks application +- Users navigate to LO Blocks via a course dashboard link in Learning Observer + +#### Step 1: Configure Next.js Base Path + +Edit your `next.config.js` to set the base path: + +```javascript +const nextConfig = { + basePath: '/lo-blocks' +}; + +export default nextConfig; +``` + +Then rebuild LO Blocks: + +```bash +npx next build +``` + +> **Important**: The `basePath` setting only affects `next/link` and `next/router`. It does NOT affect `fetch()` calls made by the application. + +#### Step 2: Configure Nginx + +Create an Nginx configuration to handle both applications: + +``` +upstream learning_observer { + server localhost:8002; +} + +upstream lo_blocks { + server localhost:3000; +} + +proxy_cache_path /var/cache/nginx/auth levels=1:2 keys_zone=auth_cache:10m max_size=100m inactive=60m; + +map $http_upgrade $connection_upgrade { + default upgrade; + '' close; +} + +server { + listen 8001; + server_name localhost; + + # Default: primary service + location / { + proxy_pass http://learning_observer; + + # WebSocket bits + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + + # Common headers + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Cookie $http_cookie; + } + + # 1) Next.js app mounted at /special-route + # (this is essentially your original block; I only added a slash to the prefix + # for clarity in matching deeper paths). + location /special-route { + # auth for anything under /special-route + auth_request /auth-check; + + auth_request_set $user_id $upstream_http_x_user_id; + auth_request_set $user_email $upstream_http_x_user_email; + auth_request_set $user_name $upstream_http_x_user_name; + + proxy_pass http://lo_blocks; # passes /special-route[...] as-is to Next + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + proxy_set_header X-User-ID $user_id; + proxy_set_header X-User-Email $user_email; + proxy_set_header X-User-Name $user_name; + + proxy_set_header Authorization $http_authorization; + } + + # 2) NEW: route /api/* -> Next's /special-route/api/* + # + # This is the key bit: we don't use 'rewrite' here, we let proxy_pass + # do the path mapping via its trailing slash behavior. + location /api/ { + # Apply the same auth behavior as /special-route + auth_request /auth-check; + + auth_request_set $user_id $upstream_http_x_user_id; + auth_request_set $user_email $upstream_http_x_user_email; + auth_request_set $user_name $upstream_http_x_user_name; + + # Map: + # /api/content -> /special-route/api/content + # /api/foo/bar?x=1 -> /special-route/api/foo/bar?x=1 + proxy_pass http://lo_blocks/special-route/api/; + + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + proxy_set_header X-User-ID $user_id; + proxy_set_header X-User-Email $user_email; + proxy_set_header X-User-Name $user_name; + + proxy_set_header Authorization $http_authorization; + } + + # Internal location for auth checking + location = /auth-check { + internal; + + proxy_pass http://learning_observer/auth/userinfo; + proxy_pass_request_body off; + proxy_set_header Content-Length ""; + proxy_set_header X-Original-URI $request_uri; + + proxy_set_header Authorization $http_authorization; + proxy_set_header Cookie $http_cookie; + + proxy_cache auth_cache; + proxy_cache_valid 200 5m; + proxy_cache_key "$http_authorization$cookie_session"; + } + + location /auth/userinfo { + proxy_pass http://learning_observer/auth/userinfo; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + + error_page 401 403 = @auth_error; + + location @auth_error { + return 401 '{"error": "Authentication required or user does not exist"}'; + add_header Content-Type application/json; + } +} +``` + +The configuration should include: + +- Proxy pass rules for Learning Observer (default) +- Proxy pass rules for `/lo-blocks` β†’ LO Blocks +- **API fetch() workaround**: Rewrite `/api/*` requests to include the `/lo-blocks` prefix + - This is necessary because Next.js `fetch()` calls don't respect `basePath` + - Safe because Learning Observer doesn't use `/api` routes + +> **Assumption**: LO Blocks runs on port 3000, Learning Observer runs on port 8002 +> To change the Learning Observer port: Add `--port 8002` to the Makefile run command or adjust in `creds.yaml` + +### HTTPS Configuration for Canvas + +Canvas requires HTTPS for LTI integrations. For local development: + +#### Option 1: Cloudflare Tunnel (Recommended for local testing) + +1. **Create a secure tunnel**: + + ```bash + cloudflared tunnel --url http://localhost:8001 + ``` + + This creates a tunnel between your local port and a public HTTPS URL. + +2. **Update configurations** with the tunnel URL: + + **In `creds.yaml`**: + + ```yaml + hostname: your-tunnel-url.trycloudflare.com # Without https:// + + auth: + lti: + demo-canvas: + redirect_uri: https://your-tunnel-url.trycloudflare.com/auth/lti/callback + ``` + + **In Canvas LTI configuration**: + - Update all URL fields with the proper domain `https://your-tunnel-url.trycloudflare.com` + - `target_link_uri: domain/lti/demo-canvas/login` + - `oidc_initiation_url: domain/lti/demo-canvas/login` + - `redirect_uris: domain/lti/demo-canvas/launch` + +## Verification and Testing + +Once everything is configured: + +1. **Canvas β†’ Learning Observer connection**: + - Users should be able to launch the LTI tool from Canvas + - Authentication should work via LTI + +2. **Navigation to LO Blocks**: + - Users should see the dashboard link in Learning Observer + - Clicking should navigate to LO Blocks interface + +3. **Data persistence**: + - User progress in LO Blocks should save properly + - Data flows through websocket connection to Learning Observer diff --git a/docs/how-to/lti.md b/docs/how-to/lti.md index b87ea3661..e2af168b4 100644 --- a/docs/how-to/lti.md +++ b/docs/how-to/lti.md @@ -85,3 +85,96 @@ If the launch fails, inspect the Learning Observer logs for messages beginning w ## 7. Plan student identity mapping LTI launch data only includes the learner's email address, while Writing Observer's Google Workspace integrations emit a Google-specific user identifier. To keep downstream reducers and dashboards working for LTI cohorts, plan to run the maintenance workflow that maps emails to Google IDs. Refer to the [Student Identity Mapping guide](../concepts/student_identity_mapping.md) for an overview of how the reducer and maintenance script cooperate and how to operate the sync in production. + +## 8. Submit grades through the AGS proxy + +Once the feature flag for your LMS is enabled (for example `canvas_routes: true`), Learning Observer registers proxy endpoints that forward IMS Assignment & Grade Service (AGS) calls using the authorization headers captured during the LTI launch. You can submit grades for a Canvas course line item with a simple POST to the proxy URL: + +```bash +curl -X POST \ + -H "Content-Type: application/json" \ + -b cookies.txt \ # session created by an LTI launch + https://lo.example.edu/sample-canvas/lineitem_scores/12345/67890 \ + -d '{ + "userId": "student-lti-id", + "scoreGiven": 8, + "scoreMaximum": 10, + "activityProgress": "Completed", + "gradingProgress": "FullyGraded", + "timestamp": "2024-06-30T15:04:05Z" + }' +``` + +* Replace `sample-canvas` with your configured provider name. +* `12345` is the Canvas course ID and `67890` is the AGS line item ID returned by the `/course_lineitems` endpoint. +* The JSON body follows the [IMS AGS score](https://www.imsglobal.org/spec/lti-ags/v2p0#score-media-type) format and is forwarded verbatim to the LMS. + +For server-side use, the corresponding local function accepts the same parameters via `json_body`: + +```python +await canvas_functions['raw_lineitem_scores']( + runtime, + courseId='12345', + lineItemId='67890', + json_body={ + 'userId': 'student-lti-id', + 'scoreGiven': 8, + 'scoreMaximum': 10, + 'activityProgress': 'Completed', + 'gradingProgress': 'FullyGraded', + 'timestamp': '2024-06-30T15:04:05Z' + } +) +``` + +`canvas_functions` is the dictionary returned by `setup_canvas_provider(...)(app)` during app initialization. Both the HTTP route and the local call reuse the LTI access token stored in the user's session, so no additional authentication headers are required. + +### Try it with the sample LTI Grade Demo module + +The repository ships with a sample module you can use to poke at the LTI launch context and post grades through a browser form. Install the module (e.g., `pip install -e modules/wo_lti_grade_demo`) and visit `/views/wo_lti_grade_demo/lti-grade-demo/` after launching via LTI. The page shows: + +- A session summary pulled from the launch claims (provider, course context IDs, and the current `userId`). +- A minimal grade submission form that forwards to `/views/wo_lti_grade_demo/submit-score/`, which in turn calls the AGS score endpoint registered for the provider detected in the session. +- A "Load line items" helper that calls `/views/wo_lti_grade_demo/line-items/` for the current course (or a manually entered `courseId`) and fills a datalist so you can pick a valid AGS line item ID. + +The helper endpoints `/views/wo_lti_grade_demo/session-summary/` and `/views/wo_lti_grade_demo/line-items/` are also available if you prefer to pull the launch metadata and line items directly and post scores with your own client. + +### Getting Canvas line items to appear + +Canvas only exposes LTI Assignment & Grade Service (AGS) line items for assignments that are tied to an external tool launch (or that were created through the AGS API). A plain Canvas assignment without an LTI tool configured will not show up in `/course_lineitems`. To create a testable line item: + +1. In Canvas, open the course and click **Assignments β†’ + Assignment**. +2. Set a name and points, then under **Submission Type** choose **External Tool** and pick your installed LTI tool. Save and publish the assignment. +3. Launch the tool from the assignment at least once so Canvas associates the line item with the tool. +4. Re-run the demo module's "Load line items" helper or call `/course_lineitems` and you should see the new line item label and ID. + +If you prefer to create a line item programmatically, POST an AGS line item object to `/api/lti/courses/{courseId}/line_items` (or the proxy route registered by `canvas_routes`). A minimal body looks like: + +```json +{ + "scoreMaximum": 10, + "label": "My LTI Practice Assignment", + "resourceId": "practice-1" +} +``` + +Canvas returns the new `id` URL in the response, which is the `lineItemId` you can plug into the grade submission examples above. + +### Managing multiple assignments from one LTI tool + +If your Writing Observer deployment hosts several learning experiences (for example, multiple dashboards or writing tasks) but you want to keep using a single LTI tool installation, create **one Canvas assignment per experience** and route inside the tool based on the launch claims: + +1. In Canvas, add a separate **External Tool** assignment for each activity. All of them can point to the same LTI launch URL; Canvas will still generate distinct AGS line items and a unique `resource_link_id` per assignment. +2. (Optional but recommended) Add a Canvas *Custom Field* on each assignment such as `module_slug=revision` or `activity_id=essay-1`. These become `custom` claims in the LTI launch payload and let you steer users to the right part of your app. +3. In your request handler, read the launch session and route to the correct module using the custom field or `resource_link_id`. A minimal example: + + ```python + launch = request.session['lti_launch'] + custom = launch.get('custom', {}) + target = custom.get('module_slug') or MODULE_BY_RESOURCE_LINK.get(launch['resource_link_id']) + return RedirectResponse(f"/views/{target}/") + ``` + +4. When posting grades, reuse the AGS line item associated with the launch. You can pull it from `/views/wo_lti_grade_demo/line-items/` (filtered by `courseId` and matched via `label`/`resourceId`) or store the returned `lineItem` claim from the launch for the active assignment. + +This approach keeps a single LTI registration while giving instructors separate Canvas assignments and gradebook columns for each experience in your app. diff --git a/learning_observer/VERSION b/learning_observer/VERSION index a0653bbc1..2259bd00a 100644 --- a/learning_observer/VERSION +++ b/learning_observer/VERSION @@ -1 +1 @@ -0.1.0+2025.11.19T13.01.52.758Z.c84a3484.master +0.1.0+2025.12.16T16.39.56.199Z.46876dc.berickson.20251215.lo.blocks.through.canvas diff --git a/learning_observer/learning_observer/auth/events.py b/learning_observer/learning_observer/auth/events.py index ba20dfdc2..e650992aa 100644 --- a/learning_observer/learning_observer/auth/events.py +++ b/learning_observer/learning_observer/auth/events.py @@ -220,6 +220,30 @@ async def chromebook_auth(request, event, source): } +@register_event_auth('lti_session') +async def lti_session_auth(request, event, source): + """Authenticate websocket events using the existing LTI session. + + When a dashboard is launched through LTI, the launch flow stores the + verified user information (including the subject identifier and course + context) in the aiohttp session via :func:`update_session_user_info`. If a + websocket request reuses that session, we can surface the same metadata for + incoming events so reducers can attribute them correctly. + """ + + session = await aiohttp_session.get_session(request) + user = session.get(constants.USER) + if not user: + return False + + return { + 'sec': 'authenticated', + constants.USER_ID: user[constants.USER_ID], + 'providence': 'lti', + 'lti_context': user.get('lti_context') + } + + @register_event_auth("hash_identify") async def hash_identify(request, event, source): ''' diff --git a/learning_observer/learning_observer/auth/lti_sso.py b/learning_observer/learning_observer/auth/lti_sso.py index e6bcd3ba3..c9ff4104b 100644 --- a/learning_observer/learning_observer/auth/lti_sso.py +++ b/learning_observer/learning_observer/auth/lti_sso.py @@ -223,7 +223,12 @@ def create_user_from_claims(claims: dict, provider: str) -> dict: 'http://purl.imsglobal.org/vocab/lis/v2/membership#Instructor', 'http://purl.imsglobal.org/vocab/lis/v2/system/person#SysAdmin' ] + learner_roles = [ + 'http://purl.imsglobal.org/vocab/lis/v2/institution/person#Learner', + 'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner' + ] is_instructor = any(r in roles for r in instructor_roles) + is_learner = any(r in roles for r in learner_roles) # Include the LTI Launch Context # HACK in Canvas, each course has 2 IDs: @@ -246,7 +251,7 @@ def create_user_from_claims(claims: dict, provider: str) -> dict: 'family_name': claims.get('family_name', ''), 'picture': claims.get('picture', ''), 'role': learning_observer.auth.ROLES.TEACHER if is_instructor else learning_observer.auth.ROLES.STUDENT, - 'authorized': is_instructor, + 'authorized': is_instructor or is_learner, 'lti_context': lti_context # TODO figure out backto. With google sso, we had a state we could store things in # 'back_to': request.query.get('state') diff --git a/learning_observer/learning_observer/integrations/canvas.py b/learning_observer/learning_observer/integrations/canvas.py index fe6dbaa0f..aa8da9342 100644 --- a/learning_observer/learning_observer/integrations/canvas.py +++ b/learning_observer/learning_observer/integrations/canvas.py @@ -1,7 +1,6 @@ import pmss import re -import learning_observer.kvs import learning_observer.constants as constants import learning_observer.settings as settings @@ -18,11 +17,18 @@ def setup_canvas_provider(provider): base_url = settings.pmss_settings.api_domain(types=['auth', 'lti', provider]) - ENDPOINTS = list(map(lambda x: util.Endpoint(*x, api_name=provider), [ - ('course_list', base_url + '/api/lti/courses/{courseId}/names_and_roles'), - ('course_roster', base_url + '/api/lti/courses/{courseId}/names_and_roles'), - ('course_lineitems', base_url + '/api/lti/courses/{courseId}/line_items'), - ])) + ENDPOINTS = [ + util.Endpoint('course_list', base_url + '/api/lti/courses/{courseId}/names_and_roles', api_name=provider), + util.Endpoint('course_roster', base_url + '/api/lti/courses/{courseId}/names_and_roles', api_name=provider), + util.Endpoint('course_lineitems', base_url + '/api/lti/courses/{courseId}/line_items', api_name=provider), + util.Endpoint( + 'lineitem_scores', + base_url + '/api/lti/courses/{courseId}/line_items/{lineItemId}/scores', + api_name=provider, + headers={'Content-Type': 'application/vnd.ims.lis.v1.score+json'}, + method='post' + ) + ] register_cleaner = util.make_cleaner_registrar(ENDPOINTS) @@ -129,11 +135,27 @@ def clean_course_assignments(canvas_json): if not isinstance(line_items, list): # If it's not already a list, check for lineItems property that might contain the list line_items = canvas_json.get('lineItems', []) - + normalized_line_items = [] + for item in line_items: + if not isinstance(item, dict): + continue + + raw_id = item.get('id') + line_item_id = raw_id + if isinstance(raw_id, str): + raw_id = raw_id.rstrip('/') + # Canvas returns the full line item URL; extract the trailing ID segment + line_item_id = raw_id.rsplit('/', 1)[-1] + + normalized = dict(item) + normalized['lti_id'] = raw_id + normalized['id'] = line_item_id + normalized_line_items.append(normalized) # Sort by due date if available, otherwise by title - line_items.sort( + normalized_line_items.sort( key=lambda x: x.get('endDateTime', x.get('label', 'ZZ')), ) - return line_items + print('Canvas', normalized_line_items) + return normalized_line_items return register_canvas_endpoints diff --git a/learning_observer/learning_observer/integrations/util.py b/learning_observer/learning_observer/integrations/util.py index 4058b228c..53a159890 100644 --- a/learning_observer/learning_observer/integrations/util.py +++ b/learning_observer/learning_observer/integrations/util.py @@ -29,7 +29,11 @@ import learning_observer.util -class Endpoint(recordclass.make_dataclass("Endpoint", ["name", "remote_url", "doc", "cleaners", "api_name", "headers"], defaults=["", None, None, None])): +class Endpoint(recordclass.make_dataclass( + "Endpoint", + ["name", "remote_url", "doc", "cleaners", "api_name", "headers", "method"], + defaults=["", None, None, None, "get"] +)): def arguments(self): return extract_parameters_from_format_string(self.remote_url) @@ -68,7 +72,18 @@ def extract_parameters_from_format_string(format_string): return [f[1] for f in string.Formatter().parse(format_string) if f[1] is not None] -async def raw_api_ajax(runtime, target_url, key_translator=None, cache=None, cache_key_prefix=None, headers=None, **kwargs): +async def raw_api_ajax( + runtime, + target_url, + key_translator=None, + cache=None, + cache_key_prefix=None, + headers=None, + method='get', + json_body=None, + data=None, + **kwargs +): ''' Make an AJAX call to an API, managing auth + auth. @@ -90,7 +105,8 @@ async def raw_api_ajax(runtime, target_url, key_translator=None, cache=None, cac headers = {} headers.update(request.get(constants.AUTH_HEADERS, {})) - cache_available = cache is not None and cache_key_prefix is not None + method = method.lower() + cache_available = method == 'get' and cache is not None and cache_key_prefix is not None if cache_available: cache_key = f"{cache_key_prefix}/{learning_observer.auth.encode_id('session', user[constants.USER_ID])}/{learning_observer.util.url_pathname(url)}" @@ -103,20 +119,38 @@ async def raw_api_ajax(runtime, target_url, key_translator=None, cache=None, cac return response_data async with aiohttp.ClientSession(loop=request.app.loop) as client: - async with client.get(url, headers=headers) as resp: - response = await resp.json() + request_kwargs = {'headers': headers} + if json_body is not None: + request_kwargs['json'] = json_body + if data is not None: + request_kwargs['data'] = data + + async with client.request(method.upper(), url, **request_kwargs) as resp: + content_type = resp.headers.get('Content-Type', '') + + # Many LTI-compliant endpoints return vendor-specific JSON media types + # (e.g., application/vnd.ims.lti-nrps.v2.membershipcontainer+json). + # Treat any content type containing "json" as JSON, but fall back to + # text if parsing fails. + if 'json' in content_type.lower(): + try: + response = await resp.json() + except Exception: + response = await resp.text() + else: + response = await resp.text() learning_observer.log_event.log_ajax(target_url, response, request) if cache_available: if settings.feature_flag('use_clean_ajax') is not None: await cache.set(cache_key, json.dumps(response, indent=2)) - if key_translator: + if key_translator and isinstance(response, (dict, list)): return learning_observer.util.translate_json_keys(response, key_translator) return response -def raw_access_partial(remote_url, key_translator=None, cache=None, cache_key_prefix=None, name=None, headers=None): +def raw_access_partial(remote_url, key_translator=None, cache=None, cache_key_prefix=None, name=None, headers=None, method='get'): ''' This is a helper which allows us to create a function which calls specific API endpoints. @@ -125,13 +159,18 @@ async def caller(runtime, **kwargs): ''' Make an AJAX request to the API ''' + json_body = kwargs.pop('json_body', None) + data = kwargs.pop('data', None) return await raw_api_ajax( - runtime, - remote_url, - key_translator, - cache, + runtime, + remote_url, + key_translator, + cache, cache_key_prefix, headers, + method, + json_body=json_body, + data=data, **kwargs ) @@ -177,7 +216,7 @@ async def api_docs_handler(request): aiohttp.web.get(f"/{api_name}", api_docs_handler) ]) - def make_ajax_raw_handler(remote_url): + def make_ajax_raw_handler(remote_url, method): ''' Creates a handler to forward API requests to the client. ''' @@ -190,6 +229,8 @@ async def ajax_passthrough(request): key_translator, cache, cache_key_prefix, + method=method, + json_body=await request.json() if method != 'get' else None, **request.match_info ) return aiohttp.web.json_response(response) @@ -232,12 +273,13 @@ async def cleaner_local(runtime, **kwargs): for e in endpoints: function_name = f"raw_{e.name}" raw_function = raw_access_partial( - remote_url=e.remote_url, + remote_url=e.remote_url, key_translator=key_translator, cache=cache, cache_key_prefix=cache_key_prefix, name=e.name, - headers=e.headers + headers=e.headers, + method=e.method ) result_functions[function_name] = raw_function cleaners = e._cleaners() @@ -258,10 +300,11 @@ async def cleaner_local(runtime, **kwargs): name=cleaners[c]['name'] ) + route_factory = getattr(aiohttp.web, e.method.lower()) app.add_routes([ - aiohttp.web.get( + route_factory( e._local_url(), - make_ajax_raw_handler(e.remote_url) + make_ajax_raw_handler(e.remote_url, e.method) ) ]) diff --git a/modules/lo_lti_grade_demo/README.md b/modules/lo_lti_grade_demo/README.md new file mode 100644 index 000000000..6e838cb73 --- /dev/null +++ b/modules/lo_lti_grade_demo/README.md @@ -0,0 +1,18 @@ +# LTI Grade Demo + +A minimal example dashboard that uses the Assignment & Grade Service (AGS) proxy +routes registered during an LTI launch. It surfaces routes under +`/views/wo_lti_grade_demo/`: + +- `/lti-grade-demo/` renders a simple HTML dashboard for experimenting with grade + submissions. +- `/session-summary/` dumps the current LTI launch metadata (provider, course + context, and active user). +- `/line-items/` lists cleaned AGS line items for the current course (or a + specified courseId) to help pick a target assignment. +- `/submit-score/` proxies an AGS score payload to the LMS for the current LTI + session. + +The module depends on an active LTI session with `canvas_routes` (or the +relevant LMS routes) enabled so the AGS endpoints are registered in +`learning_observer.integrations.INTEGRATIONS`. diff --git a/modules/lo_lti_grade_demo/lo_lti_grade_demo/__init__.py b/modules/lo_lti_grade_demo/lo_lti_grade_demo/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/modules/lo_lti_grade_demo/lo_lti_grade_demo/module.py b/modules/lo_lti_grade_demo/lo_lti_grade_demo/module.py new file mode 100644 index 000000000..d3ec4a965 --- /dev/null +++ b/modules/lo_lti_grade_demo/lo_lti_grade_demo/module.py @@ -0,0 +1,48 @@ +""" +Basic LTI grade submission demo module. + +This module exposes a minimal HTML dashboard (served via the `EXTRA_VIEWS` +mechanism) that reads the active LTI session and posts an AGS score using the +existing Canvas proxy wiring. It is intended as a copy/paste example for new +integrations rather than a production UI. +""" + +from . import views + +NAME = "LTI Grade Demo" + +EXTRA_VIEWS = [ + { + "name": "LTI Grade Demo Dashboard", + "suburl": "lti-grade-demo", + "method": "GET", + "handler": views.render_dashboard, + }, + { + "name": "LTI session summary", + "suburl": "session-summary", + "method": "GET", + "handler": views.session_summary, + }, + { + "name": "Course line items", + "suburl": "line-items", + "method": "GET", + "handler": views.course_line_items, + }, + { + "name": "Submit LTI grade", + "suburl": "submit-score", + "method": "POST", + "handler": views.submit_score, + }, +] + +COURSE_DASHBOARDS = [{ + 'name': NAME, + 'url': "/views/lo_lti_grade_demo/lti-grade-demo/", + "icon": { + "type": "fas", + "icon": "fa-star" + } +}] diff --git a/modules/lo_lti_grade_demo/lo_lti_grade_demo/views.py b/modules/lo_lti_grade_demo/lo_lti_grade_demo/views.py new file mode 100644 index 000000000..ae382a3b9 --- /dev/null +++ b/modules/lo_lti_grade_demo/lo_lti_grade_demo/views.py @@ -0,0 +1,315 @@ +import datetime + +import aiohttp_session +from aiohttp import web + +import learning_observer.constants as constants +import learning_observer.integrations +import learning_observer.runtime + + +DASHBOARD_HTML = """ + + + + + LTI Grade Demo + + + +

LTI Grade Demo

+

This sample page reads the LTI launch context from your session and posts a score through the Assignment & Grade Service (AGS) proxy routes.

+ +
+

Session info

+

Loading...

+

+  
+ +
+

Submit a grade

+

Fill the fields and submit. Defaults use the current LTI user and course.

+
+
+ + +
+
+ + + + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ + +
+
+

+ +
+ +
+

Last response

+
No submission yet.
+
+ + + + +""" + + +def _current_user_and_context(session): + user = session.get(constants.USER, {}) + context = user.get("lti_context", {}) if isinstance(user, dict) else {} + return user, context + + +def _validate_lti_session(session): + user, context = _current_user_and_context(session) + if not user: + raise web.HTTPUnauthorized(text="No active LTI session") + if constants.AUTH_HEADERS not in session: + raise web.HTTPUnauthorized(text="Missing LTI authorization headers") + if not context: + raise web.HTTPBadRequest(text="Missing LTI launch context") + provider = context.get("provider") + if not provider: + raise web.HTTPBadRequest(text="Missing LTI provider on session") + return user, context, provider + + +async def render_dashboard(request): + await aiohttp_session.get_session(request) # ensure session cookie exists + return web.Response(text=DASHBOARD_HTML, content_type="text/html") + + +async def session_summary(request): + session = await aiohttp_session.get_session(request) + try: + user, context, provider = _validate_lti_session(session) + except web.HTTPException as exc: + raise exc + + response = { + "userId": user.get(constants.USER_ID), + "email": user.get("email"), + "name": user.get("name"), + "provider": provider, + "ltiContext": context, + "hasAuthHeaders": constants.AUTH_HEADERS in session, + } + return web.json_response(response) + + +async def course_line_items(request): + session = await aiohttp_session.get_session(request) + _, context, provider = _validate_lti_session(session) + + integrations = learning_observer.integrations.INTEGRATIONS.get(provider) + if not integrations or "assignments" not in integrations: + raise web.HTTPServiceUnavailable(text="Line item endpoint is not registered for this provider") + + course_id = request.query.get("courseId") or context.get("api_id") + if not course_id: + raise web.HTTPBadRequest(text="courseId is required") + + runtime = learning_observer.runtime.Runtime(request) + line_items = await integrations["assignments"](runtime, courseId=str(course_id)) + return web.json_response(line_items) + + +async def submit_score(request): + session = await aiohttp_session.get_session(request) + user, context, provider = _validate_lti_session(session) + + integrations = learning_observer.integrations.INTEGRATIONS.get(provider) + if not integrations or "raw_lineitem_scores" not in integrations: + raise web.HTTPServiceUnavailable(text="Grade submission endpoint is not registered for this provider") + + try: + data = await request.json() + except Exception as exc: # pragma: no cover - aiohttp provides clear message already + raise web.HTTPBadRequest(text=f"Invalid JSON body: {exc}") + + line_item_id = data.get("lineItemId") + if not line_item_id: + raise web.HTTPBadRequest(text="lineItemId is required") + + course_id = data.get("courseId") or context.get("api_id") + if not course_id: + raise web.HTTPBadRequest(text="courseId is required") + + timestamp = data.get("timestamp") or datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).isoformat() + + score_payload = { + "userId": data.get("userId") or user.get(constants.USER_ID), + "scoreGiven": data.get("scoreGiven"), + "scoreMaximum": data.get("scoreMaximum"), + "activityProgress": data.get("activityProgress", "Completed"), + "gradingProgress": data.get("gradingProgress", "FullyGraded"), + "timestamp": timestamp, + } + + runtime = learning_observer.runtime.Runtime(request) + result = await integrations["raw_lineitem_scores"]( + runtime, + courseId=str(course_id), + lineItemId=str(line_item_id), + json_body={k: v for k, v in score_payload.items() if v is not None}, + ) + + return web.json_response({ + "provider": provider, + "courseId": str(course_id), + "lineItemId": str(line_item_id), + "submittedPayload": score_payload, + "lmsResponse": result, + }) diff --git a/modules/lo_lti_grade_demo/pyproject.toml b/modules/lo_lti_grade_demo/pyproject.toml new file mode 100644 index 000000000..8fe2f47af --- /dev/null +++ b/modules/lo_lti_grade_demo/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["setuptools>=42", "wheel"] +build-backend = "setuptools.build_meta" diff --git a/modules/lo_lti_grade_demo/setup.cfg b/modules/lo_lti_grade_demo/setup.cfg new file mode 100644 index 000000000..c38499e9e --- /dev/null +++ b/modules/lo_lti_grade_demo/setup.cfg @@ -0,0 +1,12 @@ +[metadata] +name = LTI Grade Demo +version = 0.1 +description = Basic dashboard to inspect LTI context and post AGS scores + +[options] +packages = find: +include_package_data = true + +[options.entry_points] +lo_modules = + lo_lti_grade_demo = lo_lti_grade_demo.module diff --git a/scripts/generate_jwks.py b/scripts/generate_jwks.py new file mode 100644 index 000000000..095dd2676 --- /dev/null +++ b/scripts/generate_jwks.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python3 +""" +Generate JWKS from your public key for Canvas LTI configuration +Usage: python generate_jwks.py [kid] +""" + +import sys +import json +import base64 +import hashlib +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.backends import default_backend + +def generate_jwks(public_key_path, kid=None): + try: + # Load the public key + with open(public_key_path, 'rb') as f: + public_key_pem = f.read() + public_key = serialization.load_pem_public_key( + public_key_pem, + backend=default_backend() + ) + + # Extract the public numbers + numbers = public_key.public_numbers() + + # Helper function to convert int to base64url + def to_base64url(num): + byte_len = (num.bit_length() + 7) // 8 + num_bytes = num.to_bytes(byte_len, byteorder='big') + encoded = base64.urlsafe_b64encode(num_bytes).decode('utf-8') + return encoded.rstrip('=') # Remove padding + + # Generate kid if not provided + # Using SHA256 hash of the public key (common practice) + if kid is None: + key_hash = hashlib.sha256(public_key_pem).digest() + kid = base64.urlsafe_b64encode(key_hash[:16]).decode('utf-8').rstrip('=') + + # Build the JWK + jwk = { + "kty": "RSA", + "e": to_base64url(numbers.e), + "n": to_base64url(numbers.n), + "alg": "RS256", + "kid": kid, + "use": "sig" + } + + return jwk + + except FileNotFoundError: + print(f"Error: Could not find file '{public_key_path}'") + sys.exit(1) + except Exception as e: + print(f"Error: {e}") + sys.exit(1) + +if __name__ == "__main__": + if len(sys.argv) < 2 or len(sys.argv) > 3: + print("Usage: python generate_jwks.py [kid]") + print("\nExamples:") + print(" python generate_jwks.py public_key.pem") + print(" python generate_jwks.py public_key.pem my-custom-key-id") + sys.exit(1) + + public_key_file = sys.argv[1] + custom_kid = sys.argv[2] if len(sys.argv) == 3 else None + + jwks = generate_jwks(public_key_file, custom_kid) + + print("\n" + "="*60) + print("Copy this JSON and paste it into Canvas public_jwk field:") + print("="*60 + "\n") + print(json.dumps(jwks, indent=2)) + print("\n" + "="*60) + print(f"\nβœ“ Key ID (kid): {jwks['kid']}") + if custom_kid is None: + print(" (auto-generated from key fingerprint)") + print("\n IMPORTANT: If you specify a 'kid' when signing JWTs,") + print(" it must match this value!") + print("="*60) From 65cc64d50cfc3f9f302eaa40a0c79347164b6b9f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 14:51:31 -0500 Subject: [PATCH 66/88] Bump http-proxy-middleware in /modules/lo_dash_react_components (#222) Bumps [http-proxy-middleware](https://github.com/chimurai/http-proxy-middleware) from 2.0.7 to 2.0.9. - [Release notes](https://github.com/chimurai/http-proxy-middleware/releases) - [Changelog](https://github.com/chimurai/http-proxy-middleware/blob/v2.0.9/CHANGELOG.md) - [Commits](https://github.com/chimurai/http-proxy-middleware/compare/v2.0.7...v2.0.9) --- updated-dependencies: - dependency-name: http-proxy-middleware dependency-version: 2.0.9 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/lo_dash_react_components/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/lo_dash_react_components/package-lock.json b/modules/lo_dash_react_components/package-lock.json index eb2735ec8..7270bbd1f 100644 --- a/modules/lo_dash_react_components/package-lock.json +++ b/modules/lo_dash_react_components/package-lock.json @@ -10028,9 +10028,9 @@ } }, "node_modules/http-proxy-middleware": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.7.tgz", - "integrity": "sha512-fgVY8AV7qU7z/MmXJ/rxwbrtQH4jBQ9m7kp3llF0liB7glmFeVZFBepQb32T3y8n8k2+AEYuMPCpinYW+/CuRA==", + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz", + "integrity": "sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q==", "license": "MIT", "dependencies": { "@types/http-proxy": "^1.17.8", From c8383aa0a757f237929ebc87674cacf278ab3338 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 14:51:48 -0500 Subject: [PATCH 67/88] Bump webpack-dev-server in /modules/lo_dash_react_components (#225) Bumps [webpack-dev-server](https://github.com/webpack/webpack-dev-server) from 5.1.0 to 5.2.1. - [Release notes](https://github.com/webpack/webpack-dev-server/releases) - [Changelog](https://github.com/webpack/webpack-dev-server/blob/master/CHANGELOG.md) - [Commits](https://github.com/webpack/webpack-dev-server/compare/v5.1.0...v5.2.1) --- updated-dependencies: - dependency-name: webpack-dev-server dependency-version: 5.2.1 dependency-type: direct:development ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .../package-lock.json | 27 ++++++++++++++----- modules/lo_dash_react_components/package.json | 2 +- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/modules/lo_dash_react_components/package-lock.json b/modules/lo_dash_react_components/package-lock.json index 7270bbd1f..d33923a12 100644 --- a/modules/lo_dash_react_components/package-lock.json +++ b/modules/lo_dash_react_components/package-lock.json @@ -45,7 +45,7 @@ "tailwindcss": "^3.4.14", "webpack": "^5.96.1", "webpack-cli": "^5.1.4", - "webpack-dev-server": "^5.1.0" + "webpack-dev-server": "^5.2.1" }, "engines": { "node": ">=22.0.0" @@ -19369,15 +19369,16 @@ } }, "node_modules/webpack-dev-server": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.1.0.tgz", - "integrity": "sha512-aQpaN81X6tXie1FoOB7xlMfCsN19pSvRAeYUHOdFWOlhpQ/LlbfTqYwwmEDFV0h8GGuqmCmKmT+pxcUV/Nt2gQ==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.2.1.tgz", + "integrity": "sha512-ml/0HIj9NLpVKOMq+SuBPLHcmbG+TGIjXRHsYfZwocUBIqEvws8NnS/V9AFQ5FKP+tgn5adwVwRrTEpGL33QFQ==", "devOptional": true, "license": "MIT", "dependencies": { "@types/bonjour": "^3.5.13", "@types/connect-history-api-fallback": "^1.5.4", "@types/express": "^4.17.21", + "@types/express-serve-static-core": "^4.17.21", "@types/serve-index": "^1.9.4", "@types/serve-static": "^1.15.5", "@types/sockjs": "^0.3.36", @@ -19388,10 +19389,9 @@ "colorette": "^2.0.10", "compression": "^1.7.4", "connect-history-api-fallback": "^2.0.0", - "express": "^4.19.2", + "express": "^4.21.2", "graceful-fs": "^4.2.6", - "html-entities": "^2.4.0", - "http-proxy-middleware": "^2.0.3", + "http-proxy-middleware": "^2.0.7", "ipaddr.js": "^2.1.0", "launch-editor": "^2.6.1", "open": "^10.0.3", @@ -19426,6 +19426,19 @@ } } }, + "node_modules/webpack-dev-server/node_modules/@types/express-serve-static-core": { + "version": "4.19.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", + "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, "node_modules/webpack-dev-server/node_modules/define-lazy-prop": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", diff --git a/modules/lo_dash_react_components/package.json b/modules/lo_dash_react_components/package.json index 9679e22d4..fc12ef115 100644 --- a/modules/lo_dash_react_components/package.json +++ b/modules/lo_dash_react_components/package.json @@ -58,7 +58,7 @@ "tailwindcss": "^3.4.14", "webpack": "^5.96.1", "webpack-cli": "^5.1.4", - "webpack-dev-server": "^5.1.0" + "webpack-dev-server": "^5.2.1" }, "browserslist": { "production": [ From 08cc2b2264f0e5863a603ab8a8d71779d712dfc4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 14:52:28 -0500 Subject: [PATCH 68/88] Bump on-headers and compression in /modules/lo_dash_react_components (#231) --- updated-dependencies: - dependency-name: on-headers dependency-version: 1.1.0 dependency-type: indirect - dependency-name: compression dependency-version: 1.8.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- modules/lo_dash_react_components/package-lock.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/lo_dash_react_components/package-lock.json b/modules/lo_dash_react_components/package-lock.json index d33923a12..81da15d91 100644 --- a/modules/lo_dash_react_components/package-lock.json +++ b/modules/lo_dash_react_components/package-lock.json @@ -6389,16 +6389,16 @@ } }, "node_modules/compression": { - "version": "1.7.5", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.5.tgz", - "integrity": "sha512-bQJ0YRck5ak3LgtnpKkiabX5pNF7tMUh1BSy2ZBOTh0Dim0BUu6aPPwByIns6/A5Prh8PufSPerMDUklpzes2Q==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", + "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", "license": "MIT", "dependencies": { "bytes": "3.1.2", "compressible": "~2.0.18", "debug": "2.6.9", "negotiator": "~0.6.4", - "on-headers": "~1.0.2", + "on-headers": "~1.1.0", "safe-buffer": "5.2.1", "vary": "~1.1.2" }, @@ -13257,9 +13257,9 @@ } }, "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", "license": "MIT", "engines": { "node": ">= 0.8" From 18f04ee30edf3fefb1a8fa4e18d5954dc5a93d55 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 14:52:47 -0500 Subject: [PATCH 69/88] Bump form-data from 3.0.2 to 3.0.4 in /modules/lo_dash_react_components (#232) Bumps [form-data](https://github.com/form-data/form-data) from 3.0.2 to 3.0.4. - [Release notes](https://github.com/form-data/form-data/releases) - [Changelog](https://github.com/form-data/form-data/blob/v3.0.4/CHANGELOG.md) - [Commits](https://github.com/form-data/form-data/compare/v3.0.2...v3.0.4) --- updated-dependencies: - dependency-name: form-data dependency-version: 3.0.4 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .../package-lock.json | 118 +++++++++++++----- 1 file changed, 86 insertions(+), 32 deletions(-) diff --git a/modules/lo_dash_react_components/package-lock.json b/modules/lo_dash_react_components/package-lock.json index 81da15d91..956f52eef 100644 --- a/modules/lo_dash_react_components/package-lock.json +++ b/modules/lo_dash_react_components/package-lock.json @@ -5958,6 +5958,19 @@ "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==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -7566,6 +7579,20 @@ "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==", "license": "BSD-2-Clause" }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/duplexer": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", @@ -7761,13 +7788,10 @@ "license": "MIT" }, "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.2.4" - }, "engines": { "node": ">= 0.4" } @@ -7814,9 +7838,9 @@ "license": "MIT" }, "node_modules/es-object-atoms": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", - "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -7826,14 +7850,15 @@ } }, "node_modules/es-set-tostringtag": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", - "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", + "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==", "license": "MIT", "dependencies": { - "get-intrinsic": "^1.2.4", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", - "hasown": "^2.0.1" + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -9328,14 +9353,16 @@ } }, "node_modules/form-data": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.2.tgz", - "integrity": "sha512-sJe+TQb2vIaIyO783qN6BlMYWMw3WBOHA1Ay2qxsnjuafEOQFJ2JakedOQirT6D5XPRxDvS7AHYyem9fTpb4LQ==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.4.tgz", + "integrity": "sha512-f0cRzm6dkyVYV3nPoooP8XlccPQukegwhAnpoLcXy+X+A8KfpGOoXwDr9FLZd3wzgLaBGQBE3lY93Zm/i1JvIQ==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.35" }, "engines": { "node": ">= 6" @@ -9453,16 +9480,21 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "license": "MIT", "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "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" @@ -9486,6 +9518,19 @@ "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==", + "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", @@ -9652,12 +9697,12 @@ } }, "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.1.3" + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -9745,9 +9790,9 @@ } }, "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==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -12437,6 +12482,15 @@ "tmpl": "1.0.5" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/mdn-data": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", From 07ead87e7493507adaf9d12e123f9fb5282e2107 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 14:53:06 -0500 Subject: [PATCH 70/88] Bump js-yaml from 3.14.1 to 3.14.2 in /modules/lo_dash_react_components (#243) Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 3.14.1 to 3.14.2. - [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md) - [Commits](https://github.com/nodeca/js-yaml/compare/3.14.1...3.14.2) --- updated-dependencies: - dependency-name: js-yaml dependency-version: 3.14.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .../lo_dash_react_components/package-lock.json | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/modules/lo_dash_react_components/package-lock.json b/modules/lo_dash_react_components/package-lock.json index 956f52eef..d6d6a7c0d 100644 --- a/modules/lo_dash_react_components/package-lock.json +++ b/modules/lo_dash_react_components/package-lock.json @@ -2703,9 +2703,9 @@ } }, "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==", + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", "license": "MIT", "dependencies": { "argparse": "^1.0.7", @@ -12015,9 +12015,9 @@ "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==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -18308,9 +18308,9 @@ } }, "node_modules/svgo/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==", + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", "license": "MIT", "dependencies": { "argparse": "^1.0.7", From 10031218dd55623afa644610087e36fb95c15a71 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 14:53:22 -0500 Subject: [PATCH 71/88] Bump node-forge from 1.3.1 to 1.3.2 in /modules/lo_dash_react_components (#244) Bumps [node-forge](https://github.com/digitalbazaar/forge) from 1.3.1 to 1.3.2. - [Changelog](https://github.com/digitalbazaar/forge/blob/main/CHANGELOG.md) - [Commits](https://github.com/digitalbazaar/forge/compare/v1.3.1...v1.3.2) --- updated-dependencies: - dependency-name: node-forge dependency-version: 1.3.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .../package-lock.json | 293 +++++++++++++++++- 1 file changed, 277 insertions(+), 16 deletions(-) diff --git a/modules/lo_dash_react_components/package-lock.json b/modules/lo_dash_react_components/package-lock.json index d6d6a7c0d..00e70911d 100644 --- a/modules/lo_dash_react_components/package-lock.json +++ b/modules/lo_dash_react_components/package-lock.json @@ -104,6 +104,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", "license": "MIT", + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.26.0", @@ -746,6 +747,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.26.0.tgz", "integrity": "sha512-B+O2DnPc0iG+YXFqOxv2WNuNU97ToWjOomUQ78DouOENWUaM5sVrmet9mcomUGQFwpJd//gvUagXBSdzO1fRKg==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1612,6 +1614,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.9.tgz", "integrity": "sha512-s5XwpQYCqGerXl+Pu6VDL3x0j2d82eiV77UJ8a2mDHAW7j9SWRqQ2y1fNo1Z74CdcYipl5Z41zvjj4Nfzq36rw==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-module-imports": "^7.25.9", @@ -2128,6 +2131,7 @@ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "license": "MIT", + "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -2255,6 +2259,7 @@ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "license": "MIT", + "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -3310,6 +3315,174 @@ "@parcel/watcher-win32-x64": "2.5.0" } }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.0.tgz", + "integrity": "sha512-qlX4eS28bUcQCdribHkg/herLe+0A9RyYC+mm2PXpncit8z5b3nSqGVzMNR3CmtAOgRutiZ02eIJJgP/b1iEFQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.0.tgz", + "integrity": "sha512-hyZ3TANnzGfLpRA2s/4U1kbw2ZI4qGxaRJbBH2DCSREFfubMswheh8TeiC1sGZ3z2jUf3s37P0BBlrD3sjVTUw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.0.tgz", + "integrity": "sha512-9rhlwd78saKf18fT869/poydQK8YqlU26TMiNg7AIu7eBp9adqbJZqmdFOsbZ5cnLp5XvRo9wcFmNHgHdWaGYA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.0.tgz", + "integrity": "sha512-syvfhZzyM8kErg3VF0xpV8dixJ+RzbUaaGaeb7uDuz0D3FK97/mZ5AJQ3XNnDsXX7KkFNtyQyFrXZzQIcN49Tw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.0.tgz", + "integrity": "sha512-0VQY1K35DQET3dVYWpOaPFecqOT9dbuCfzjxoQyif1Wc574t3kOSkKevULddcR9znz1TcklCE7Ht6NIxjvTqLA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.0.tgz", + "integrity": "sha512-6uHywSIzz8+vi2lAzFeltnYbdHsDm3iIB57d4g5oaB9vKwjb6N6dRIgZMujw4nm5r6v9/BQH0noq6DzHrqr2pA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.0.tgz", + "integrity": "sha512-BfNjXwZKxBy4WibDb/LDCriWSKLz+jJRL3cM/DllnHH5QUyoiUNEp3GmL80ZqxeumoADfCCP19+qiYiC8gUBjA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.0.tgz", + "integrity": "sha512-S1qARKOphxfiBEkwLUbHjCY9BWPdWnW9j7f7Hb2jPplu8UZ3nes7zpPOW9bkLbHRvWM0WDTsjdOTUgW0xLBN1Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/@parcel/watcher-linux-x64-glibc": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.0.tgz", @@ -3352,6 +3525,69 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.0.tgz", + "integrity": "sha512-twtft1d+JRNkM5YbmexfcH/N4znDtjgysFaV9zvZmmJezQsKpkfLYJ+JFV3uygugK6AtIM2oADPkB2AdhBrNig==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.0.tgz", + "integrity": "sha512-+rgpsNRKwo8A53elqbbHXdOMtY/tAtTzManTWShB5Kk54N8Q9mzNWV7tV+IbGueCbcj826MfWGU3mprWtuf1TA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.0.tgz", + "integrity": "sha512-lPrxve92zEHdgeff3aiu4gDOIt4u7sJYha6wbdEZDCDUhtjTsOMiaJzG5lMY4GkWH8p0fMmO2Ppq5G5XXG+DQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -3422,6 +3658,7 @@ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", "license": "MIT", + "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/popperjs" @@ -4422,6 +4659,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", "license": "BSD-2-Clause", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "5.62.0", "@typescript-eslint/types": "5.62.0", @@ -4862,6 +5100,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -4948,6 +5187,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -5822,6 +6062,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001669", "electron-to-chromium": "^1.5.41", @@ -5868,7 +6109,6 @@ "integrity": "sha512-SW9lzGTLvWTP1AY8xeAMZimqDrIaSdLQUcVr9DMef51niJ022Ri87SwRRKYm4A6iHfkPaiVUu/Duw2Wc4J7kKg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "semver": "^7.0.0" } @@ -5879,7 +6119,6 @@ "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, "license": "ISC", - "peer": true, "bin": { "semver": "bin/semver.js" }, @@ -7954,6 +8193,7 @@ "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -8010,7 +8250,6 @@ "integrity": "sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "semver": "^7.5.4" }, @@ -8027,7 +8266,6 @@ "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, "license": "ISC", - "peer": true, "bin": { "semver": "bin/semver.js" }, @@ -8182,7 +8420,6 @@ "https://opencollective.com/eslint" ], "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.1.2", "@eslint-community/regexpp": "^4.11.0", @@ -8218,6 +8455,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz", "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==", "license": "MIT", + "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.8", @@ -8326,7 +8564,6 @@ "integrity": "sha512-6TyDmZ1HXoFQXnhCTUjVFULReoBPOAjpuiKELMkeP40yffI/1ZRO+d9ug/VC6fqISo2WkuIBk3cvuRPALaWlOQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "builtins": "^5.0.1", @@ -8356,7 +8593,6 @@ "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "type-fest": "^0.20.2" }, @@ -8373,7 +8609,6 @@ "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, "license": "ISC", - "peer": true, "bin": { "semver": "bin/semver.js" }, @@ -8387,7 +8622,6 @@ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, "license": "(MIT OR CC0-1.0)", - "peer": true, "engines": { "node": ">=10" }, @@ -8422,6 +8656,7 @@ "integrity": "sha512-57Zzfw8G6+Gq7axm2Pdo3gW/Rx3h9Yywgn61uE/3elTCOePEHVrn2i5CdfBwA1BLK0Q0WqctICIUSqXZW/VprQ==", "dev": true, "license": "ISC", + "peer": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -9425,6 +9660,20 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "license": "ISC" }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "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", @@ -9566,7 +9815,6 @@ "integrity": "sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "resolve-pkg-maps": "^1.0.0" }, @@ -10468,7 +10716,6 @@ "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "builtin-modules": "^3.3.0" }, @@ -11095,6 +11342,7 @@ "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", "integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==", "license": "MIT", + "peer": true, "dependencies": { "@jest/core": "^27.5.1", "import-local": "^3.0.2", @@ -12806,9 +13054,9 @@ } }, "node_modules/node-forge": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", - "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.2.tgz", + "integrity": "sha512-6xKiQ+cph9KImrRh0VsjH2d8/GXA4FIMlgU4B757iI1ApvcyA9VlouP0yZJha01V+huImO+kKMU7ih+2+E14fw==", "license": "(BSD-3-Clause OR GPL-2.0)", "engines": { "node": ">= 6.13.0" @@ -13835,6 +14083,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.7", "picocolors": "^1.1.1", @@ -14725,6 +14974,7 @@ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "license": "MIT", + "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -15561,6 +15811,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -15695,6 +15946,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -15726,6 +15978,7 @@ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", "integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -16605,7 +16858,6 @@ "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", "dev": true, "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } @@ -16725,6 +16977,7 @@ "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz", "integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==", "license": "MIT", + "peer": true, "bin": { "rollup": "dist/bin/rollup" }, @@ -16883,6 +17136,7 @@ "integrity": "sha512-Q4fOxRfhmv3sqCLoGfvrC9pRV8btc0UtqL9mN6Yrv6Qi9ScL55CVH1vlPP863ISLEEMNLLuu9P+enCeGHlnzhA==", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.0.2", @@ -17017,6 +17271,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -18742,7 +18997,8 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "peer": true }, "node_modules/tsutils": { "version": "3.21.0", @@ -18791,6 +19047,7 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "license": "(MIT OR CC0-1.0)", + "peer": true, "engines": { "node": ">=10" }, @@ -19275,6 +19532,7 @@ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.96.1.tgz", "integrity": "sha512-l2LlBSvVZGhL4ZrPwyr8+37AunkcYj5qh8o6u2/2rzoPc8gxFJkLj1WxNgooi9pnoc06jh0BjuXnamM4qlujZA==", "license": "MIT", + "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.6", @@ -19322,6 +19580,7 @@ "integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@discoveryjs/json-ext": "^0.5.0", "@webpack-cli/configtest": "^2.1.1", @@ -19428,6 +19687,7 @@ "integrity": "sha512-ml/0HIj9NLpVKOMq+SuBPLHcmbG+TGIjXRHsYfZwocUBIqEvws8NnS/V9AFQ5FKP+tgn5adwVwRrTEpGL33QFQ==", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "@types/bonjour": "^3.5.13", "@types/connect-history-api-fallback": "^1.5.4", @@ -19934,6 +20194,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", From 0a37769b70ce68d20d72fddafc8486c79a177a48 Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Mon, 5 Jan 2026 09:45:58 -0500 Subject: [PATCH 72/88] various small comments that I missed in a previous commit --- VERSION | 2 +- autodocs/how-to.rst | 2 ++ docs/how-to/impersonation.md | 31 +++++++++++++++++++ learning_observer/VERSION | 2 +- .../communication_protocol/executor.py | 2 ++ .../learning_observer/integrations/util.py | 1 - 6 files changed, 37 insertions(+), 3 deletions(-) create mode 100644 docs/how-to/impersonation.md diff --git a/VERSION b/VERSION index 2259bd00a..322bc6416 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0+2025.12.16T16.39.56.199Z.46876dc.berickson.20251215.lo.blocks.through.canvas +0.1.0+2026.01.05T14.45.58.857Z.10031218.master diff --git a/autodocs/how-to.rst b/autodocs/how-to.rst index 79f7fb512..827e7cdce 100644 --- a/autodocs/how-to.rst +++ b/autodocs/how-to.rst @@ -11,6 +11,7 @@ Practical instructions for achieving specific goals within Learning Observer. Us - :doc:`Run with Docker ` - Learn how to containerize the stack, manage images, and operate the project using Docker Compose. - :doc:`Writing Observer Extension ` - Install, configure, and validate the Writing Observer browser extension for capturing events. - :doc:`Interactive Environments ` - Connect Learning Observer to Jupyter and other live coding setups for iterative development. +- :doc:`Impersonate Users ` - Start and stop acting as another user while keeping dashboards informed. .. toctree:: :hidden: @@ -25,3 +26,4 @@ Practical instructions for achieving specific goals within Learning Observer. Us docs/how-to/docker.md docs/how-to/extension.md docs/how-to/interactive_environments.md + docs/how-to/impersonation.md diff --git a/docs/how-to/impersonation.md b/docs/how-to/impersonation.md new file mode 100644 index 000000000..149fbe5ca --- /dev/null +++ b/docs/how-to/impersonation.md @@ -0,0 +1,31 @@ +# Impersonating users + +Learning Observer includes a lightweight impersonation flow so administrators can review dashboards and data as another user. The implementation relies on two HTTP routes that adjust the authenticated session and a Dash banner that surfaces the active impersonation. + +## Start impersonating + +The `start_impersonation` handler is protected by the `@learning_observer.auth.admin` decorator, so only administrators can trigger the flow. It accepts the target user ID as a path parameter and stores that identifier in the encrypted session under the `impersonating_as` key. + +``` +GET /start-impersonation/{user_id} +``` + +Once the route runs, the session entry looks like `{ "user_id": }`, and any request that reads the active user from the session will treat this impersonated identity as the current user. + +## Stop impersonating + +The stop route clears the impersonation entry from the session. + +``` +GET /stop-impersonation +``` + +If no impersonation was active, the handler returns a message indicating that nothing changed. + +## How impersonation affects user lookup + +Whenever user information is needed during a request, `learning_observer.auth.utils.get_active_user` checks for the `impersonating_as` session entry first. When present, it returns the impersonated profile instead of the authenticated user stored under `user`. This makes downstream code unaware of whether a request is genuine or impersonated. + +## Dash banner for impersonation state + +Dash pages include a small banner at the top of the layout. On page load, `update_impersonation_header` reads the session from the underlying `aiohttp` request. When an impersonated user is present, the banner renders a label showing the impersonated identity and a β€œStop” button that links to `/stop-impersonation`. diff --git a/learning_observer/VERSION b/learning_observer/VERSION index 2259bd00a..322bc6416 100644 --- a/learning_observer/VERSION +++ b/learning_observer/VERSION @@ -1 +1 @@ -0.1.0+2025.12.16T16.39.56.199Z.46876dc.berickson.20251215.lo.blocks.through.canvas +0.1.0+2026.01.05T14.45.58.857Z.10031218.master diff --git a/learning_observer/learning_observer/communication_protocol/executor.py b/learning_observer/learning_observer/communication_protocol/executor.py index a57588139..8f609f2c8 100644 --- a/learning_observer/learning_observer/communication_protocol/executor.py +++ b/learning_observer/learning_observer/communication_protocol/executor.py @@ -494,6 +494,7 @@ async def _extract_fields_with_provenance_for_students(students, student_path): The provenance is the current history of the communication protocol for each item. ''' async for s in ensure_async_generator(students): + # TODO if the item is just a string, we should use it instead of trying to get a nested item s_field = get_nested_dict_value(s, student_path, '') field = { learning_observer.stream_analytics.fields.KeyField.STUDENT: s_field @@ -513,6 +514,7 @@ async def _extract_fields_with_provenance_for_students_and_resources(students, s The provenance is the current history of the communication protocol for each item. ''' async for s, r in async_zip(students, resources): + # TODO if the item is just a string, we should use it instead of trying to get a nested item s_field = get_nested_dict_value(s, student_path, '') r_field = get_nested_dict_value(r, resources_path, '') fields = { diff --git a/learning_observer/learning_observer/integrations/util.py b/learning_observer/learning_observer/integrations/util.py index 53a159890..d5296ece6 100644 --- a/learning_observer/learning_observer/integrations/util.py +++ b/learning_observer/learning_observer/integrations/util.py @@ -97,7 +97,6 @@ async def raw_api_ajax( request = runtime.get_request() url = target_url.format(**kwargs) user = await learning_observer.auth.get_active_user(request) - if constants.AUTH_HEADERS not in request or user is None: raise aiohttp.web.HTTPUnauthorized(text="Please log in") From b80005f909391a9827e214ed1f980d4c2120cf5f Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Tue, 6 Jan 2026 09:44:23 -0500 Subject: [PATCH 73/88] documented roster and courselist format --- VERSION | 2 +- learning_observer/VERSION | 2 +- .../learning_observer/integrations/util.py | 15 +++++++ .../learning_observer/rosters.py | 39 +++++++++++++++++-- 4 files changed, 53 insertions(+), 5 deletions(-) diff --git a/VERSION b/VERSION index 322bc6416..684b37fcb 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0+2026.01.05T14.45.58.857Z.10031218.master +0.1.0+2026.01.06T14.44.23.358Z.0a37769b.master diff --git a/learning_observer/VERSION b/learning_observer/VERSION index 322bc6416..684b37fcb 100644 --- a/learning_observer/VERSION +++ b/learning_observer/VERSION @@ -1 +1 @@ -0.1.0+2026.01.05T14.45.58.857Z.10031218.master +0.1.0+2026.01.06T14.44.23.358Z.0a37769b.master diff --git a/learning_observer/learning_observer/integrations/util.py b/learning_observer/learning_observer/integrations/util.py index d5296ece6..d526e5635 100644 --- a/learning_observer/learning_observer/integrations/util.py +++ b/learning_observer/learning_observer/integrations/util.py @@ -314,6 +314,21 @@ def make_cleaner_registrar(endpoints): ''' Creates a register_cleaner function specific to a list of endpoints. + Cleaners are pure post-processing functions that reshape provider JSON + responses into Learning Observer's shared integration format. They should: + + * Accept a raw provider payload (already key-translated when a + ``key_translator`` is provided to :func:`register_endpoints`). + * Return JSON-serializable Python objects (dicts/lists) without + aiohttp-specific types so the same function works as both a web handler + and an in-process helper. + * Normalize identifiers and key casing consistently across providers (for + example, Google/Canvas/Schoology rosters all emit ``user_id`` and keep + nested ``profile`` structures) and apply deterministic sorting so callers + can rely on stable ordering. In practice this means matching each + integrator's native payload into the common roster/course list formats + documented in the cleaners themselves. + Returns: A function that can be used as a decorator to register cleaners. ''' diff --git a/learning_observer/learning_observer/rosters.py b/learning_observer/learning_observer/rosters.py index 00ee86f7b..ce87966e4 100644 --- a/learning_observer/learning_observer/rosters.py +++ b/learning_observer/learning_observer/rosters.py @@ -414,7 +414,7 @@ async def run_additional_module_func(request, function_name, kwargs=None): if not kwargs: kwargs = {} - user = await auth.get_active_user(request) + user = await auth.get_active_user(request) or {} # Grab roster source based on user user_domain = learning_observer.util.get_domain_from_email(user.get('email')) @@ -450,13 +450,25 @@ async def run_additional_module_func(request, function_name, kwargs=None): if inspect.isawaitable(result): result = await result return result - debug_log(f'No result from `{roster_source}.{function_name}`') + debug_log(f'No result returned from `{roster_source}.{function_name}`\nkwargs:{kwargs}') return None async def courselist(request): ''' List all of the courses a teacher manages: Helper + + Returns a list of course dictionaries. Each course has the following structure: + + { + 'id': str, # Unique course identifier + 'name': str, # Course name/label + 'description_heading': str, # Course description + } + + Note: Different integrations (Google Classroom, Canvas, Schoology, etc.) + may return data in different formats. Each integration should register + cleaners to transform their data into this expected format. ''' course_list = await run_additional_module_func(request, 'courses') if course_list is not None: @@ -464,6 +476,7 @@ async def courselist(request): # TODO if course_list is falsey, the following code may fail if there if ajax is not defined. # Legacy code + debug_log('Falling back to ajax call for course list retrieval.') course_list = await ajax( request, url=COURSE_URL, @@ -503,7 +516,26 @@ async def course_roster_memoization_layer(c): async def courseroster(request, course_id): ''' - List all of the students in a course: Helper + List all of the students in a course. + + Returns a list of user dictionaries. Each user has the following structure: + + { + 'profile': { + 'name': { + 'given_name': str, # User's first name + 'family_name': str, # User's last name + 'full_name': str # User's full name + }, + 'email_address': str, # User's email address + 'photo_url': str # URL to user's profile photo (optional) + }, + constants.USER_ID: str # Unique user identifier (local to our system) + } + + Note: Different integrations (Google Classroom, Canvas, Schoology, etc.) + may return data in different formats. Each integration should register + cleaners to transform their data into this expected format. ''' roster = await run_additional_module_func(request, 'roster', kwargs={'courseId': course_id}) if roster is not None: @@ -511,6 +543,7 @@ async def courseroster(request, course_id): if not ajax: return [] + debug_log(f'Falling back to ajax roster call for course: `{course_id}`.') roster = await ajax( request, url=ROSTER_URL, From 0984e08f44b1d7b99508fdfe80f47e9aad9b37c7 Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Tue, 6 Jan 2026 11:20:35 -0500 Subject: [PATCH 74/88] updated and documented offline reducer replay --- VERSION | 2 +- autodocs/how-to.rst | 2 + docs/how-to/offline_replay.md | 79 ++++++++++++++++++ learning_observer/VERSION | 2 +- learning_observer/learning_observer/kvs.py | 27 ++++++ .../learning_observer/offline.py | 83 ++++++++++++------- 6 files changed, 163 insertions(+), 32 deletions(-) create mode 100644 docs/how-to/offline_replay.md diff --git a/VERSION b/VERSION index 684b37fcb..55d096bfb 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0+2026.01.06T14.44.23.358Z.0a37769b.master +0.1.0+2026.01.06T16.20.35.371Z.b80005f9.master diff --git a/autodocs/how-to.rst b/autodocs/how-to.rst index 827e7cdce..9b97f0992 100644 --- a/autodocs/how-to.rst +++ b/autodocs/how-to.rst @@ -5,6 +5,7 @@ Practical instructions for achieving specific goals within Learning Observer. Us - :doc:`Communication Protocol ` - How to query data from reducers or system endpoints for dashboards. - :doc:`Build Dashboards ` - Walk through creating dashboards from reducer outputs, including layout choices and data wiring. +- :doc:`Offline Reducer Replay ` - Explain how to repopulate reducer content with study logs. - :doc:`Serve as LTI application` - Cover how to install Learning Observer as an LTI application. - :doc:`Connect LO Blocks to Canvas via Learning Observer` - Show how to connect launch LO Blocks through Learning Observer from within Canvas. - :doc:`Configure Multiple Roster Sources` - Allow the system to dynamically choose a roster source given a user's context. @@ -20,6 +21,7 @@ Practical instructions for achieving specific goals within Learning Observer. Us docs/how-to/communication_protocol.md docs/how-to/dashboards.md + docs/how-to/offline_replay.md docs/how-to/lti.md docs/how-to/connect_lo_blocks_to_canvas.md docs/how-to/multiple_roster_sources.md diff --git a/docs/how-to/offline_replay.md b/docs/how-to/offline_replay.md new file mode 100644 index 000000000..f0339cffb --- /dev/null +++ b/docs/how-to/offline_replay.md @@ -0,0 +1,79 @@ +# Offline replay with study logs + +This guide covers how to replay Learning Observer study logs for offline analysis using `learning_observer/offline.py`. + +## Prerequisites + +- Install dependencies so the Learning Observer package and its modules are importable +- Change into the Learning Observer directory for proper discoverability of files + +```bash +pip install -e learning_observer/ +cd learning_observer/ +``` + +- Collect the study logs you want to replay. Study logs are produced with a `.study.log` suffix (optionally `.gz` when compressed). They include replay metadata that the offline pipeline expects; do **not** use the raw `.log` files. + +## Processing a single study log file + +Use the helper functions in `learning_observer/offline.py` to initialize the environment and process a file. The examples below assume you are running them from the repository root. + +```bash +python - <<'PY' +import asyncio +from learning_observer import offline + +# Prepare the in-memory KVS and reducers for offline replay +offline.init('creds.yaml') + +# Replace this path with the study log you want to replay +log_path = "/path/to/your/session.study.log" + +async def main(): + processed, source, user = await offline.process_file(file_path=log_path) + print(f"Processed {processed} events from {source} as user {user}") + +asyncio.run(main()) +PY +``` + +- `process_file` only accepts study logs ending in `.study.log` or `.study.log.gz` and will reject other log types. +- If you do not provide a `userid`, a random safe username is generated to avoid inadvertently reusing PII from the log. + +## Processing all study logs in a directory + +To replay multiple study logs, point `process_dir` at a directory. It automatically filters to `*.study.log` and `*.study.log.gz` files. + +```bash +python - <<'PY' +import asyncio +from learning_observer import offline + +offline.init('creds.yaml') + +async def main(): + files, events = await offline.process_dir("/path/to/study/logs") + print(f"Processed {events} events across {files} study logs") + +asyncio.run(main()) +PY +``` + +## Resetting state between runs + +Offline processing stores data in the in-memory key-value store (KVS). To clear previously replayed events before another run, call `offline.reset()`: + +```bash +python - <<'PY' +import asyncio +from learning_observer import offline + +offline.init('creds.yaml') + +async def main(): + await offline.reset() + print("KVS cleared") + +asyncio.run(main()) +PY +``` diff --git a/learning_observer/VERSION b/learning_observer/VERSION index 684b37fcb..55d096bfb 100644 --- a/learning_observer/VERSION +++ b/learning_observer/VERSION @@ -1 +1 @@ -0.1.0+2026.01.06T14.44.23.358Z.0a37769b.master +0.1.0+2026.01.06T16.20.35.371Z.b80005f9.master diff --git a/learning_observer/learning_observer/kvs.py b/learning_observer/learning_observer/kvs.py index 325c7b8d8..c996d9c90 100644 --- a/learning_observer/learning_observer/kvs.py +++ b/learning_observer/learning_observer/kvs.py @@ -181,6 +181,16 @@ async def keys(self): await self.connect() return await learning_observer.redis_connection.keys() + async def clear(self): + ''' + Remove all keys from the redis-backed KVS. + + This uses ``FLUSHDB`` on the configured Redis connection, which clears + the entire database. Use cautiously in shared environments. + ''' + await self.connect() + return await (await learning_observer.redis_connection.connection()).flushdb() + async def remove(self, key): ''' Remove item from the KVS. @@ -295,6 +305,23 @@ async def keys(self): keys.append(self.safe_filename_to_key(f)) return keys + async def clear(self): + ''' + Remove all entries stored in the filesystem-backed KVS. + + The base directory is preserved, but all contained files (and + directories when ``subdirs`` is enabled) are removed. + ''' + if self.subdirs: + for root, dirs, files in os.walk(self.path, topdown=False): + for f in files: + os.remove(os.path.join(root, f)) + for d in dirs: + os.rmdir(os.path.join(root, d)) + else: + for f in os.listdir(self.path): + os.remove(os.path.join(self.path, f)) + # TODO change the keys to variables KVS_MAP = { diff --git a/learning_observer/learning_observer/offline.py b/learning_observer/learning_observer/offline.py index b2af7a158..6b94aa8b7 100644 --- a/learning_observer/learning_observer/offline.py +++ b/learning_observer/learning_observer/offline.py @@ -4,11 +4,10 @@ Helpers to support the use of Learning Observer in scripts, in other applications, and in Jupyter notebooks. ''' -import argparse import asyncio -from cgi import print_arguments +import gzip +import itertools import json -import sys import os import names @@ -75,10 +74,11 @@ async def process_file( pipeline=None ): ''' - Process a single log file. + Process a single study log file. Args: - file_path (str): The path to the log file to process. + file_path (str): The path to the study log file to process + (``*.study.log`` or ``*.study.log.gz``). source (str): The source of events (e.g. org.mitros.dynamic_assessment) If not specified, the source will be inferred from the events. @@ -89,7 +89,8 @@ async def process_file( Number of events processed, source, and userid If `source` is not specified, the source will be inferred from the - log file. + log file. Only study logs should be used for replay to ensure the + appended timestamp and metadata are handled correctly. If `userid` is not specified, a username will be generated with `names.get_first_name()`. We do this because we don't want to @@ -104,25 +105,41 @@ async def process_file( # Opener returns an iterator of events. It handles diverse sources: # lists, log files, and compressed log files def opener(): - return events_list - - if file_path is not None: - if file_path.endswith('.log'): - def file_opener(): - return open(file_path) - elif file_path.endswith('.log.gz'): - def file_opener(): - return gzip.open(file_path) + if events_list is not None: + return iter(events_list) + + if file_path is None: + raise ValueError("Either events_list or file_path must be provided") + + def decode_event(line): + # Study logs append a timestamp separated by a tab; strip it off before decoding + line = line.decode("utf-8") if isinstance(line, bytes) else line + if "\t" in line: + payload, suffix = line.rsplit("\t", 1) + line = payload + return json.loads(line) + + if file_path.endswith('.study.log'): + file_handle = open(file_path, 'r') + elif file_path.endswith('.study.log.gz'): + file_handle = gzip.open(file_path, 'rt') else: - raise ValueError("Unknown file type: " + file_path) + raise ValueError("Unknown study log type (expected .study.log or .study.log.gz): " + file_path) - def opener(): - return (json.loads(line) for line in file_opener().readlines()) + with file_handle as fp: + for line in fp: + yield decode_event(line) + + # We need to inspect the events to determine source, but also reuse them for processing + events_iter, events_for_processing = itertools.tee(opener()) if source is None: - for event in opener: - source = event['client']['source'] - break + try: + first_event = next(events_iter) + except StopIteration: + raise ValueError("No events available to infer source") + source = first_event['client']['source'] + events_for_processing = itertools.chain([first_event], events_for_processing) # In most cases, for development, a dummy name is good. if userid is None: @@ -140,9 +157,9 @@ def opener(): pipeline = await learning_observer.incoming_student_event.student_event_pipeline(metadata) else: pipeline = await pipeline(metadata) - print(pipeline) + n = 0 # Number of events processed - for event in opener(): + for event in events_for_processing: try: await pipeline(event) n += 1 @@ -155,15 +172,15 @@ def opener(): async def process_files(files): ''' - Process a list of log files. + Process a list of study log files. Args: - files (list): A list of log files to process. + files (list): A list of study log files to process. Returns: Total number of events processed - This function will process each file in the list, and print the + This function will process each study log file in the list, and print the results. ''' total = 0 @@ -177,7 +194,7 @@ async def process_files(files): async def process_dir(path=os.getcwd()): ''' - Process all log files in a directory. + Process all study log files in a directory. Args: path (str): The path to the directory to process. @@ -185,10 +202,16 @@ async def process_dir(path=os.getcwd()): Returns: Number of files processed, total number of events processed - This function will process all log files in the directory, and - print the results. + This function will process all study log files (``*.study.log`` or + ``*.study.log.gz``) in the directory, and print the results. These logs + include replay metadata and should be used instead of raw ``.log`` files + when re-running offline analyses. ''' - files = [os.path.join(path, f) for f in os.listdir(path) if f.endswith('.log')] + files = [ + os.path.join(path, f) + for f in os.listdir(path) + if f.endswith('.study.log') or f.endswith('.study.log.gz') + ] events_processed = await process_files(files) return len(files), events_processed From 17387e3e35696a7bde6a15af59450093d287abb0 Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Wed, 14 Jan 2026 09:12:03 -0500 Subject: [PATCH 75/88] abstracted time on task reducers to common area to allow for more modules to use (#252) --- VERSION | 2 +- learning_observer/VERSION | 2 +- .../stream_analytics/__init__.py | 2 + .../stream_analytics/time_on_task.py | 103 +++++++++++++++++ modules/writing_observer/VERSION | 2 +- .../writing_observer/writing_analysis.py | 106 ++---------------- 6 files changed, 120 insertions(+), 97 deletions(-) create mode 100644 learning_observer/learning_observer/stream_analytics/time_on_task.py diff --git a/VERSION b/VERSION index 55d096bfb..32e666dbe 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0+2026.01.06T16.20.35.371Z.b80005f9.master +0.1.0+2026.01.13T18.33.25.519Z.0984e08f.berickson.20260113.abstract.time.on.task.reducers diff --git a/learning_observer/VERSION b/learning_observer/VERSION index 55d096bfb..32e666dbe 100644 --- a/learning_observer/VERSION +++ b/learning_observer/VERSION @@ -1 +1 @@ -0.1.0+2026.01.06T16.20.35.371Z.b80005f9.master +0.1.0+2026.01.13T18.33.25.519Z.0984e08f.berickson.20260113.abstract.time.on.task.reducers diff --git a/learning_observer/learning_observer/stream_analytics/__init__.py b/learning_observer/learning_observer/stream_analytics/__init__.py index e52cf138c..36a809ea3 100644 --- a/learning_observer/learning_observer/stream_analytics/__init__.py +++ b/learning_observer/learning_observer/stream_analytics/__init__.py @@ -20,6 +20,8 @@ from learning_observer.log_event import debug_log +import learning_observer.stream_analytics.time_on_task + REDUCER_MODULES = None LAST_UPDATED = None diff --git a/learning_observer/learning_observer/stream_analytics/time_on_task.py b/learning_observer/learning_observer/stream_analytics/time_on_task.py new file mode 100644 index 000000000..a31391f88 --- /dev/null +++ b/learning_observer/learning_observer/stream_analytics/time_on_task.py @@ -0,0 +1,103 @@ +''' +Helpers for time-on-task reducers. +''' +import pmss + +pmss.register_field( + name='time_on_task_threshold', + type=pmss.pmsstypes.TYPES.integer, + description='Maximum time to pass before marking a session as over. '\ + 'Should be 60-300 seconds in production, but 5 seconds is nice for '\ + 'debugging in a local deployment.', + default=60 +) +pmss.register_field( + name='binned_time_on_task_bin_size', + type=pmss.pmsstypes.TYPES.integer, + description='How large (in seconds) to make timestamp bins when '\ + 'recording binned time on task.', + default=600 +) + + +def default_time_on_task_state(): + return { + 'saved_ts': None, + 'total_time_on_task': 0 + } + + +def apply_time_on_task(internal_state, current_timestamp, time_delta_threshold): + if internal_state is None: + internal_state = default_time_on_task_state() + last_ts = internal_state['saved_ts'] + internal_state['saved_ts'] = current_timestamp + + if last_ts is None: + last_ts = internal_state['saved_ts'] + if last_ts is not None: + delta_t = min( + time_delta_threshold, + internal_state['saved_ts'] - last_ts + ) + internal_state['total_time_on_task'] += delta_t + return internal_state + + +def default_binned_time_on_task_state(): + return { + 'saved_ts': None, + 'binned_time_on_task': {}, + 'current_bin': None + } + + +def get_time_bin(timestamp, bin_size): + b = (timestamp // bin_size) * bin_size + return int(b) + + +def update_binned_time_on_task(internal_state, current_bin, last_timestamp, delta_time, bin_size): + '''Handle updating the internal state for binned time on task.''' + next_bin = current_bin + bin_size + next_bin_str = str(next_bin) + + current_bin_str = str(current_bin) + if current_bin_str not in internal_state['binned_time_on_task']: + internal_state['binned_time_on_task'][current_bin_str] = 0 + + if last_timestamp + delta_time >= next_bin: + internal_state['binned_time_on_task'][current_bin_str] += next_bin - last_timestamp + if next_bin_str not in internal_state['binned_time_on_task']: + internal_state['binned_time_on_task'][next_bin_str] = 0 + internal_state['binned_time_on_task'][next_bin_str] += last_timestamp + delta_time - next_bin + else: + internal_state['binned_time_on_task'][current_bin_str] += delta_time + + +def apply_binned_time_on_task( + internal_state, + current_timestamp, + time_delta_threshold, + bin_size +): + if internal_state is None: + internal_state = default_binned_time_on_task_state() + last_timestamp = internal_state['saved_ts'] + current_bin = internal_state['current_bin'] + internal_state['saved_ts'] = current_timestamp + + if last_timestamp is None: + last_timestamp = internal_state['saved_ts'] + if current_bin is None: + current_bin = get_time_bin(last_timestamp, bin_size) + + if last_timestamp is not None: + delta_time = min( + time_delta_threshold, + internal_state['saved_ts'] - last_timestamp + ) + update_binned_time_on_task(internal_state, current_bin, last_timestamp, delta_time, bin_size) + + internal_state['current_bin'] = get_time_bin(internal_state['saved_ts'], bin_size) + return internal_state diff --git a/modules/writing_observer/VERSION b/modules/writing_observer/VERSION index de21e3a82..32e666dbe 100644 --- a/modules/writing_observer/VERSION +++ b/modules/writing_observer/VERSION @@ -1 +1 @@ -0.1.0+2025.10.13T19.31.35.177Z.f12616b4.master +0.1.0+2026.01.13T18.33.25.519Z.0984e08f.berickson.20260113.abstract.time.on.task.reducers diff --git a/modules/writing_observer/writing_observer/writing_analysis.py b/modules/writing_observer/writing_observer/writing_analysis.py index 7ebe5dd15..4d1a836e2 100644 --- a/modules/writing_observer/writing_observer/writing_analysis.py +++ b/modules/writing_observer/writing_observer/writing_analysis.py @@ -16,6 +16,7 @@ import learning_observer.adapters import learning_observer.communication_protocol.integration from learning_observer.stream_analytics.helpers import student_event_reducer, kvs_pipeline, KeyField, EventField, Scope +import learning_observer.stream_analytics.time_on_task import learning_observer.settings import learning_observer.util @@ -34,21 +35,6 @@ # (e.g. all the numbers would go up/down 20%, but behavior was # substantatively identical). -pmss.register_field( - name='time_on_task_threshold', - type=pmss.pmsstypes.TYPES.integer, - description='Maximum time to pass before marking a session as over. '\ - 'Should be 60-300 seconds in production, but 5 seconds is nice for '\ - 'debugging in a local deployment.', - default=60 -) -pmss.register_field( - name='binned_time_on_task_bin_size', - type=pmss.pmsstypes.TYPES.integer, - description='How large (in seconds) to make timestamp bins when '\ - 'recording binned time on task.', - default=600 -) pmss.register_field( name='activity_threshold', type=pmss.pmsstypes.TYPES.integer, @@ -93,64 +79,12 @@ async def time_on_task(event, internal_state): goes away for 2 hours without typing, we only add e.g. 5 minutes if `time_threshold` is set to 300. ''' - if internal_state is None: - internal_state = { - 'saved_ts': None, - 'total_time_on_task': 0 - } - last_ts = internal_state['saved_ts'] - internal_state['saved_ts'] = event['server']['time'] - - # Initial conditions - if last_ts is None: - last_ts = internal_state['saved_ts'] - if last_ts is not None: - delta_t = min( - learning_observer.settings.module_setting('writing_obersver', 'time_on_task_threshold'), # Maximum time step - internal_state['saved_ts'] - last_ts # Time step - ) - internal_state['total_time_on_task'] += delta_t - return internal_state, internal_state - - -def _get_time_delta(last_event_timestamp, current_event_timestamp): - return min( - learning_observer.settings.module_setting('writing_obersver', 'time_on_task_threshold'), # Maximum time step - last_event_timestamp - current_event_timestamp # Time step + internal_state = learning_observer.stream_analytics.time_on_task.apply_time_on_task( + internal_state, + event['server']['time'], + learning_observer.settings.module_setting('writing_obersver', 'time_on_task_threshold') ) - - -def _get_time_bin(timestamp): - bin_size = learning_observer.settings.module_setting('writing_obersver', 'binned_time_on_task_bin_size') - b = (timestamp // bin_size) * bin_size - b = int(b) - return b - - -def _update_binned_time_on_task(internal_state, current_bin, last_timestamp, delta_time): - '''Handle updating the internal state for binned time on task. - ''' - next_bin = current_bin + learning_observer.settings.module_setting('writing_obersver', 'binned_time_on_task_bin_size') - next_bin_str = str(next_bin) - - # default current_bin to 0 if it doesn't exist - current_bin_str = str(current_bin) - if current_bin_str not in internal_state['binned_time_on_task']: - internal_state['binned_time_on_task'][current_bin_str] = 0 - - # time-on-task overflows to the next bin - # first add a portion of the time to the current bin - # default the next bin to 0 if it doesn't exist - # add remaining time to next bin - if last_timestamp + delta_time >= next_bin: - internal_state['binned_time_on_task'][current_bin_str] += next_bin - last_timestamp - if next_bin_str not in internal_state['binned_time_on_task']: - internal_state['binned_time_on_task'][next_bin_str] = 0 - internal_state['binned_time_on_task'][next_bin_str] += last_timestamp + delta_time - next_bin - # process normal within bin time on task update - else: - internal_state['binned_time_on_task'][current_bin_str] += delta_time - + return internal_state, internal_state @kvs_pipeline(scope=gdoc_scope) @@ -159,28 +93,12 @@ async def binned_time_on_task(event, internal_state): Similar to the `time_on_task` reducer defined above, except it bins the time spent. ''' - if internal_state is None: - internal_state = { - 'saved_ts': None, - 'binned_time_on_task': {}, - 'current_bin': None - } - last_timestamp = internal_state['saved_ts'] - current_bin = internal_state['current_bin'] - internal_state['saved_ts'] = event['server']['time'] - - # Initialization - if last_timestamp is None: - last_timestamp = internal_state['saved_ts'] - if current_bin is None: - current_bin = _get_time_bin(last_timestamp) - - if last_timestamp is not None: - delta_time = _get_time_delta(internal_state['saved_ts'], last_timestamp) - _update_binned_time_on_task(internal_state, current_bin, last_timestamp, delta_time) - - # update our current bin with the current event's timestamp - internal_state['current_bin'] = _get_time_bin(internal_state['saved_ts']) + internal_state = learning_observer.stream_analytics.time_on_task.apply_binned_time_on_task( + internal_state, + event['server']['time'], + learning_observer.settings.module_setting('writing_obersver', 'time_on_task_threshold'), + learning_observer.settings.module_setting('writing_obersver', 'binned_time_on_task_bin_size') + ) return internal_state, internal_state From d2a08f2175be46d7110da536f4ae12648abf82f2 Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Thu, 15 Jan 2026 16:18:13 -0500 Subject: [PATCH 76/88] updated keys node in communication protocol to support more scoped items (#251) * updated keys node in communication protocol to support more scoped items * pr feedback * added documenation regarding how to scope reducers * updated user id related items * more pr feedback * small scope bug fix * fixed mapping dashboard scope issue * more fixes * more fixes --- VERSION | 2 +- docs/how-to/communication_protocol.md | 44 +++- learning_observer/VERSION | 2 +- .../communication_protocol/executor.py | 232 +++++++++++++----- .../learning_observer/dashboard.py | 128 ++++++++-- .../incoming_student_event.py | 16 +- 6 files changed, 341 insertions(+), 83 deletions(-) diff --git a/VERSION b/VERSION index 32e666dbe..a7d917490 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0+2026.01.13T18.33.25.519Z.0984e08f.berickson.20260113.abstract.time.on.task.reducers +0.1.0+2026.01.15T15.07.43.223Z.b5e4caee.berickson.20260113.comm.protocol.scope diff --git a/docs/how-to/communication_protocol.md b/docs/how-to/communication_protocol.md index 9159dd478..b42484a88 100644 --- a/docs/how-to/communication_protocol.md +++ b/docs/how-to/communication_protocol.md @@ -71,8 +71,10 @@ from learning_observer.communication_protocol import query roster = query.call("get_course_roster", args={"course_id": course_id}) reducer_keys = query.keys( - reducer="reading_fluency", - entities=query.variable(roster, "students"), + "reading_fluency", + scope_fields={ + "student": {"values": query.variable(roster), "path": "user_id"}, + }, ) reducer_docs = query.select( keys=reducer_keys, @@ -92,6 +94,44 @@ Guidelines: * Encapsulate repeated or complex logic in functions for reuse and testing. * Use explicit names and keyword argumentsβ€”avoid positional arguments for clarity. +### Defining reducer scopes for `keys` (preferred vs. legacy) + +Reducers define a scope (e.g., student, student+document, student+document+tab). When +building a `keys` node, pass scope values that align with the reducer scope so the +executor can build the right Redis keys. + +**Preferred: `scope_fields` (supports arbitrary scopes)** + +Use `scope_fields` to supply each scope axis with a `values` iterable and a `path` +into each item. The scope field names should match the reducer scope: `student`, +`doc_id`, `tab_id`, `page_id`, etc. + +```python +reducer_keys = query.keys( + "writing_observer.some_tabbed_reducer", + scope_fields={ + "student": {"values": query.variable("roster"), "path": "user_id"}, + "doc_id": {"values": query.variable("documents"), "path": "doc_id"}, + "tab_id": {"values": query.variable("tabs"), "path": "tab_id"}, + }, +) +``` + +**Legacy: `STUDENTS`/`RESOURCES`** + +The older hack only supported student-only or student+document scopes. It is still +accepted for backward compatibility, but prefer `scope_fields` for new work. + +```python +reducer_keys = query.keys( + "writing_observer.last_document", + STUDENTS=query.variable("roster"), + STUDENTS_path="user_id", + RESOURCES=query.variable("documents"), + RESOURCES_path="doc_id", +) +``` + ## 6. Define Exports and Integrations Choose which nodes should be externally accessible: diff --git a/learning_observer/VERSION b/learning_observer/VERSION index 32e666dbe..a7d917490 100644 --- a/learning_observer/VERSION +++ b/learning_observer/VERSION @@ -1 +1 @@ -0.1.0+2026.01.13T18.33.25.519Z.0984e08f.berickson.20260113.abstract.time.on.task.reducers +0.1.0+2026.01.15T15.07.43.223Z.b5e4caee.berickson.20260113.comm.protocol.scope diff --git a/learning_observer/learning_observer/communication_protocol/executor.py b/learning_observer/learning_observer/communication_protocol/executor.py index 8f609f2c8..64a6f3092 100644 --- a/learning_observer/learning_observer/communication_protocol/executor.py +++ b/learning_observer/learning_observer/communication_protocol/executor.py @@ -465,75 +465,182 @@ async def handle_select(keys, fields=learning_observer.communication_protocol.qu yield query_response_element -# @handler(learning_observer.communication_protocol.query.DISPATCH_MODES.KEYS) -def handle_keys(function, value_path, **kwargs): - """ - We WANT TO dispatch this function whenever we process a DISPATCH_MODES.KEYS node. - Whenever a user wants to perform a select operation, they first must make sure their - keys are formatted properly. This method builds the keys to access the appropriate - reducers output. +def _normalize_scope_field_key(key): + return str(key).strip().lower() + + +def _scope_field_candidates(field): + if isinstance(field, learning_observer.stream_analytics.fields.KeyField): + base = field.name + plural = f'{base.lower()}s' + if base == 'CLASS': + plural = 'classes' + return [ + base, + f'KeyField.{base}', + base.lower(), + plural + ] + + if isinstance(field, learning_observer.stream_analytics.helpers.EventField): + base = field.event + candidates = [ + base, + f'EventField.{base}', + base.lower(), + base.upper() + ] + if base == 'doc_id': + candidates.extend(['RESOURCE', 'RESOURCES', 'resource', 'resources']) + return candidates + + return [] + + +def _normalize_scope_field_specs(raw_specs): + normalized = {} + for key, spec in raw_specs.items(): + normalized_key = _normalize_scope_field_key(key) + if isinstance(spec, dict): + values = spec.get('values', spec.get('value', spec.get('items', spec.get('data')))) + path = spec.get('path', spec.get('value_path')) + else: + values = spec + path = None + normalized[normalized_key] = { + 'values': values, + 'path': path + } + return normalized - We have not yet implemented this because there is not a clear way of how different - sets of KeyFields should interact with one another. The easy solution is when we - just have a single KeyField. For example, with Students, we iterate over each one - and create the key. It is not clear how each item in the superset of KeyField - combinations should behave. - Currently we use `hack_handle_keys` instead. - """ - return unimplemented_handler() +def _provenance_key_for_field(field): + if isinstance(field, learning_observer.stream_analytics.fields.KeyField): + return field.name + if isinstance(field, learning_observer.stream_analytics.helpers.EventField): + if field.event == 'doc_id': + return 'RESOURCE' + return f'EventField.{field.event}' + return str(field) -async def _extract_fields_with_provenance_for_students(students, student_path): - '''This is a helper function for the `hack_handle_keys` function. - This function prepares the key field dictionary and the provenance - for each student. - The key field dictionary is used to create the key we are attempting - to fetch from the KVS (used later in `hack_handle_keys`). The passed in - `item_path` is used for setting the appropriate dictionary value. - The provenance is the current history of the communication protocol for each item. - ''' - async for s in ensure_async_generator(students): - # TODO if the item is just a string, we should use it instead of trying to get a nested item - s_field = get_nested_dict_value(s, student_path, '') - field = { - learning_observer.stream_analytics.fields.KeyField.STUDENT: s_field - } - provenance = s.get('provenance', {'value': s}) - provenance[student_path] = s_field - yield field, {'STUDENT': provenance} +async def _async_zip_many(iterables): + generators = [ensure_async_generator(it) for it in iterables] + try: + while True: + values = await asyncio.gather(*[gen.__anext__() for gen in generators]) + yield values + except StopAsyncIteration: + return -async def _extract_fields_with_provenance_for_students_and_resources(students, student_path, resources, resources_path): - '''This is a helper function for the `hack_handle_keys` function. - This function prepares the key field dictionary and the provenance - for each student/resource pair. +async def _extract_fields_with_provenance(scope_specs): + '''Prepare the key field dictionary and provenance for each scope tuple. The key field dictionary is used to create the key we are attempting to fetch from the KVS (used later in `hack_handle_keys`). The passed in - `item_path` is used for setting the appropriate dictionary value. + `path` is used for setting the appropriate dictionary value. The provenance is the current history of the communication protocol for each item. ''' - async for s, r in async_zip(students, resources): - # TODO if the item is just a string, we should use it instead of trying to get a nested item - s_field = get_nested_dict_value(s, student_path, '') - r_field = get_nested_dict_value(r, resources_path, '') - fields = { - learning_observer.stream_analytics.fields.KeyField.STUDENT: s_field, - learning_observer.stream_analytics.helpers.EventField('doc_id'): r_field - } - s_provenance = s.get('provenance', {'value': s}) - s_provenance[student_path] = s_field - r_provenance = r.get('provenance', {'value': r}) - r_provenance[resources_path] = r_field - provenance = { - 'STUDENT': s_provenance, - 'RESOURCE': r_provenance - } + if not scope_specs: + return + + if len(scope_specs) == 1: + field, values, path = scope_specs[0] + async for item in ensure_async_generator(values): + field_value = get_nested_dict_value(item, path or '', '') + fields = {field: field_value} + item_provenance = item.get('provenance', {'value': item}) if isinstance(item, dict) else {'value': item} + if path: + item_provenance[path] = field_value + provenance = {_provenance_key_for_field(field): item_provenance} + yield fields, provenance + return + + iterables = [values for _, values, _ in scope_specs] + async for items in _async_zip_many(iterables): + fields = {} + provenance = {} + for (field, _, path), item in zip(scope_specs, items): + field_value = get_nested_dict_value(item, path or '', '') + fields[field] = field_value + item_provenance = item.get('provenance', {'value': item}) if isinstance(item, dict) else {'value': item} + if path: + item_provenance[path] = field_value + provenance[_provenance_key_for_field(field)] = item_provenance yield fields, provenance +def _resolve_scope_specs(scope, kwargs): + scope_specs = {} + raw_scope_specs = kwargs.get('scope_fields', {}) + if isinstance(raw_scope_specs, dict): + scope_specs.update(_normalize_scope_field_specs(raw_scope_specs)) + + allowed_scope_keys = set() + for field in scope: + allowed_scope_keys.update( + _normalize_scope_field_key(candidate) + for candidate in _scope_field_candidates(field) + ) + + for key, value in kwargs.items(): + if key in {'scope_fields', 'STUDENTS', 'STUDENTS_path', 'RESOURCES', 'RESOURCES_path'}: + continue + if key.endswith('_path'): + continue + path_key = f"{key}_path" + if path_key in kwargs: + scope_specs.setdefault( + _normalize_scope_field_key(key), + {'values': value, 'path': kwargs[path_key]} + ) + + if 'STUDENTS' in kwargs: + scope_specs.setdefault( + 'student', + {'values': kwargs['STUDENTS'], 'path': kwargs.get('STUDENTS_path')} + ) + if 'RESOURCES' in kwargs: + scope_specs.setdefault( + 'doc_id', + {'values': kwargs['RESOURCES'], 'path': kwargs.get('RESOURCES_path')} + ) + + unexpected_scope_keys = set(scope_specs.keys()) - allowed_scope_keys + if unexpected_scope_keys: + raise DAGExecutionException( + 'Provided scope fields do not match reducer scope.', + inspect.currentframe().f_code.co_name, + { + 'scope': [str(field) for field in scope], + 'unexpected_fields': sorted(unexpected_scope_keys) + } + ) + + specs = [] + for field in sorted(scope, key=str): + field_specs = None + for candidate in _scope_field_candidates(field): + candidate_key = _normalize_scope_field_key(candidate) + if candidate_key in scope_specs: + field_specs = scope_specs[candidate_key] + break + if field_specs is None: + return None + specs.append((field, field_specs['values'], field_specs.get('path'))) + + return specs + + +def _find_reducer_by_key(function): + for reducer in learning_observer.module_loader.reducers(): + if reducer.get('id') == function or reducer.get('string_id') == function: + return reducer + return None + + @handler(learning_observer.communication_protocol.query.DISPATCH_MODES.KEYS) -async def hack_handle_keys(function, STUDENTS=None, STUDENTS_path=None, RESOURCES=None, RESOURCES_path=None): +async def handle_keys(function, **kwargs): """ This function is a HACK that is being used instead of `handle_keys` for any `DISPATCH_MODE.KEYS` nodes. @@ -542,17 +649,18 @@ async def hack_handle_keys(function, STUDENTS=None, STUDENTS_path=None, RESOURCE keys are formatted properly. This method builds the keys to access the appropriate reducers output. - This function only supports the creation of Student keys and Student/Resource pair keys. + This function supports creation of keys based on the reducer scope. We create a list of fields needed for the `make_key()` function as well as the provenance associated with each. These are zipped together and returned to the user. """ # TODO do something if `func` is not found - func = next((item for item in learning_observer.module_loader.reducers() if item['id'] == function), None) - fields_and_provenances = None - if STUDENTS is not None and RESOURCES is None: - fields_and_provenances = _extract_fields_with_provenance_for_students(STUDENTS, STUDENTS_path) - elif STUDENTS is not None and RESOURCES is not None: - fields_and_provenances = _extract_fields_with_provenance_for_students_and_resources(STUDENTS, STUDENTS_path, RESOURCES, RESOURCES_path) + func = _find_reducer_by_key(function) + if func is None: + return + scope_specs = _resolve_scope_specs(func.get('scope', []), kwargs) + if scope_specs is None: + return + fields_and_provenances = _extract_fields_with_provenance(scope_specs) if fields_and_provenances is None: return diff --git a/learning_observer/learning_observer/dashboard.py b/learning_observer/learning_observer/dashboard.py index 0199a3176..b22d001db 100644 --- a/learning_observer/learning_observer/dashboard.py +++ b/learning_observer/learning_observer/dashboard.py @@ -554,6 +554,82 @@ async def _create_dag_generator(client_query, target, request): return await _prepare_dag_as_generator(client_query, query, target, request) +def _scope_segment_for_provenance_key(key): + if key == 'RESOURCE': + return 'documents' + if key.startswith('EventField.'): + field_name = key.split('EventField.', 1)[1] + if field_name == 'doc_id': + return 'documents' + if field_name.endswith('_id'): + return f"{field_name}s" + return f"{field_name}s" + if key == 'CLASS': + return 'classes' + return f"{key.lower()}s" + + +def _provenance_key_for_scope_field(field): + if isinstance(field, sa_helpers.KeyField): + return field.name + if isinstance(field, sa_helpers.EventField): + if field.event == 'doc_id': + return 'RESOURCE' + return f'EventField.{field.event}' + return str(field) + + +def _scope_key_order_for_reducer(reducer): + if not reducer: + return [] + scope = reducer.get('scope', []) + ordered_scope = sorted( + scope, + key=lambda field: (0 if isinstance(field, sa_helpers.KeyField) else 1, getattr(field, 'name', str(field))) + ) + return [_provenance_key_for_scope_field(field) for field in ordered_scope] + + +def _find_reducer_from_provenance_key(key): + if not key: + return None + parts = key.split(',') + if len(parts) < 2: + return None + reducer_name = parts[1] + for reducer in learning_observer.module_loader.reducers(): + function_name = sa_helpers.fully_qualified_function_name(reducer['function']) + if function_name == reducer_name: + return reducer + return None + + +def _value_from_provenance_entry(key, entry): + if not isinstance(entry, dict): + return entry + if key == 'STUDENT': + for candidate in ('user_id', 'student_id', 'id'): + if candidate in entry: + return entry[candidate] + if key in ('RESOURCE', 'EventField.doc_id'): + for candidate in ('doc_id', 'resource_id', 'id'): + if candidate in entry: + return entry[candidate] + if key == 'CLASS': + for candidate in ('class_id', 'id'): + if candidate in entry: + return entry[candidate] + if key == 'TEACHER': + for candidate in ('user_id', 'teacher_id', 'id'): + if candidate in entry: + return entry[candidate] + if key.startswith('EventField.'): + field_name = key.split('EventField.', 1)[1] + if field_name in entry: + return entry[field_name] + return entry.get('value') + + def _find_student_or_resource(d): '''HACK the communication protocol does not provide an easy way to determine which student or student/document pair is being updated. @@ -563,28 +639,52 @@ def _find_student_or_resource(d): user output. However, this method assumes that the provenance is still around. This method digs into the provenance and extracts the corresponding - student or student/document id. This information is used to tell the - client which items in their data-tree to update (i.e. update Billy's - History Essay with this new information). + scope ids. This information is used to tell the client which items in + their data-tree to update (i.e. update Billy's History Essay with this + new information). ''' if not isinstance(d, dict): return [] if 'provenance' in d: provenance = d['provenance'] + provenance_data = provenance + provenance_key = None + if isinstance(provenance, dict): + provenance_data = provenance.get('provenance', provenance) + provenance_key = provenance.get('key') + while isinstance(provenance_data, dict) and 'provenance' in provenance_data: + provenance_key = provenance_data.get('key', provenance_key) + next_provenance = provenance_data.get('provenance') + if next_provenance is None or next_provenance is provenance_data: + break + provenance_data = next_provenance output = [] - if 'STUDENT' in provenance: - output.append('students') - output.append(str(provenance['STUDENT']['user_id'])) - if 'RESOURCE' in provenance: - if 'doc_id' in provenance['RESOURCE']: - output.append('documents') - output.append(str(provenance['RESOURCE']['doc_id'])) - if 'assignment_id' in provenance['RESOURCE']: - output.append('assignments') - output.append(str(provenance['RESOURCE']['assignment_id'])) + if isinstance(provenance_data, dict): + reducer = _find_reducer_from_provenance_key(provenance_key) + scope_order = _scope_key_order_for_reducer(reducer) + scope_order_index = {key: idx for idx, key in enumerate(scope_order)} if scope_order else {} + if scope_order_index: + ordered_entries = sorted( + ((key, entry) for key, entry in provenance_data.items() if key in scope_order_index), + key=lambda item: scope_order_index[item[0]] + ) + else: + ordered_entries = provenance_data.items() + for key, entry in ordered_entries: + if scope_order_index and key not in scope_order_index: + continue + segment = _scope_segment_for_provenance_key(key) + if segment is None: + continue + value = _value_from_provenance_entry(key, entry) + if value is None: + continue + output.append(segment) + output.append(str(value)) + if output: return output - return _find_student_or_resource(provenance) + return _find_student_or_resource(provenance_data) return [] diff --git a/learning_observer/learning_observer/incoming_student_event.py b/learning_observer/learning_observer/incoming_student_event.py index 53479bbd6..9cad037a3 100644 --- a/learning_observer/learning_observer/incoming_student_event.py +++ b/learning_observer/learning_observer/incoming_student_event.py @@ -498,18 +498,28 @@ async def process_blob_storage_events(events): async for event in events: # Extract metadata if event['event'] in ['save_blob', 'fetch_blob']: - user_id = event['auth']['user_id'] + # we previously used the `user_id` key for storing blobs + # we should be using the `safe_user_id` instead + safe_user_id = event['auth']['safe_user_id'] + legacy_user_id = event['auth']['user_id'] source = event['source'] activity = event['activity'] # Save, fetch, or ignore (continue) if event['event'] == 'save_blob': await learning_observer.blob_storage.save_blob( - user_id, source, activity, + safe_user_id, source, activity, event['blob'] ) elif event['event'] == 'fetch_blob': - blob = await learning_observer.blob_storage.fetch_blob(user_id, source, activity) + # Try fetching via our safe user id and fallback to the legacy user id + blob = await learning_observer.blob_storage.fetch_blob( + safe_user_id, source, activity + ) + if blob is None and legacy_user_id and legacy_user_id != safe_user_id: + blob = await learning_observer.blob_storage.fetch_blob( + legacy_user_id, source, activity + ) await ws.send_json({ 'status': 'fetch_blob', 'data': blob From ff0704787ffedbb3ed8009c2183062a7e56f0378 Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Tue, 20 Jan 2026 16:14:31 -0500 Subject: [PATCH 77/88] added basic integration logger with pmss setting (#255) --- VERSION | 2 +- docs/reference/system_settings.md | 6 +++ learning_observer/VERSION | 2 +- .../learning_observer/dashboard.py | 3 +- .../learning_observer/integrations/util.py | 38 ++++++++++++++++++- .../learning_observer/log_event.py | 21 ++++++++++ 6 files changed, 67 insertions(+), 5 deletions(-) diff --git a/VERSION b/VERSION index a7d917490..2ea3893e2 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0+2026.01.15T15.07.43.223Z.b5e4caee.berickson.20260113.comm.protocol.scope +0.1.0+2026.01.20T19.54.05.825Z.d2a08f21.berickson.20260120.integration.logging diff --git a/docs/reference/system_settings.md b/docs/reference/system_settings.md index 88c53e2c4..6333264d5 100644 --- a/docs/reference/system_settings.md +++ b/docs/reference/system_settings.md @@ -47,6 +47,12 @@ runtime. | --- | --- | --- | --- | | `dashboard_settings.logging_enabled` | Determine if we should log dashboard sessions. | `false` | [`learning_observer/learning_observer/dashboard.py`](../../learning_observer/learning_observer/dashboard.py) | +### LMS Integration (`lms_integration` namespace) + +| YAML path | Description | Default | Used in | +| --- | --- | --- | --- | +| `lms_integration.logging_enabled` | Determine if we should log lms integration calls. | `false` | [`learning_observer/learning_observer/log_event.py`](../../learning_observer/learning_observer/log_event.py) | + ### Redis connection (`redis_connection` namespace) | YAML path | Description | Default | Used in | diff --git a/learning_observer/VERSION b/learning_observer/VERSION index a7d917490..2ea3893e2 100644 --- a/learning_observer/VERSION +++ b/learning_observer/VERSION @@ -1 +1 @@ -0.1.0+2026.01.15T15.07.43.223Z.b5e4caee.berickson.20260113.comm.protocol.scope +0.1.0+2026.01.20T19.54.05.825Z.d2a08f21.berickson.20260120.integration.logging diff --git a/learning_observer/learning_observer/dashboard.py b/learning_observer/learning_observer/dashboard.py index b22d001db..8a1dea7f4 100644 --- a/learning_observer/learning_observer/dashboard.py +++ b/learning_observer/learning_observer/dashboard.py @@ -59,7 +59,8 @@ pmss.register_field( name='logging_enabled', type=pmss.pmsstypes.TYPES.boolean, - description='Log dashboard sessions to .dashboard.log files', + description='Allow data to be logged or not. Used in within namespaces '\ + 'such as `dashboard_settings` or `lms_integration`.', default=False ) diff --git a/learning_observer/learning_observer/integrations/util.py b/learning_observer/learning_observer/integrations/util.py index d526e5635..26831881d 100644 --- a/learning_observer/learning_observer/integrations/util.py +++ b/learning_observer/learning_observer/integrations/util.py @@ -82,6 +82,8 @@ async def raw_api_ajax( method='get', json_body=None, data=None, + api_name=None, + endpoint_name=None, **kwargs ): ''' @@ -139,6 +141,24 @@ async def raw_api_ajax( else: response = await resp.text() learning_observer.log_event.log_ajax(target_url, response, request) + lms_payload = { + 'event': 'lms_integration', + 'api_name': api_name, + 'endpoint': endpoint_name, + 'method': method.upper(), + 'url': url, + 'params': kwargs, + 'response': response + } + if json_body is not None: + lms_payload['request_json'] = json_body + if data is not None: + lms_payload['request_data'] = data + try: + lms_payload[learning_observer.constants.USER] = request[learning_observer.constants.USER] + except KeyError: + lms_payload[learning_observer.constants.USER] = None + learning_observer.log_event.log_lms_integration(lms_payload) if cache_available: if settings.feature_flag('use_clean_ajax') is not None: @@ -149,7 +169,17 @@ async def raw_api_ajax( return response -def raw_access_partial(remote_url, key_translator=None, cache=None, cache_key_prefix=None, name=None, headers=None, method='get'): +def raw_access_partial( + remote_url, + key_translator=None, + cache=None, + cache_key_prefix=None, + name=None, + headers=None, + method='get', + api_name=None, + endpoint_name=None +): ''' This is a helper which allows us to create a function which calls specific API endpoints. @@ -170,6 +200,8 @@ async def caller(runtime, **kwargs): method, json_body=json_body, data=data, + api_name=api_name, + endpoint_name=endpoint_name, **kwargs ) @@ -278,7 +310,9 @@ async def cleaner_local(runtime, **kwargs): cache_key_prefix=cache_key_prefix, name=e.name, headers=e.headers, - method=e.method + method=e.method, + api_name=api_name, + endpoint_name=e.name ) result_functions[function_name] = raw_function cleaners = e._cleaners() diff --git a/learning_observer/learning_observer/log_event.py b/learning_observer/learning_observer/log_event.py index fb1b76a22..122d32b1a 100644 --- a/learning_observer/learning_observer/log_event.py +++ b/learning_observer/learning_observer/log_event.py @@ -313,6 +313,27 @@ def log_ajax(url, resp_json, request): with open(filename, "w") as ajax_log_fp: ajax_log_fp.write(encoded_payload) + +def log_lms_integration(payload): + ''' + Log LMS integration payloads for long-term analysis. + + Captures LMS data like rosters, courses, and grades so we can + review historical context beyond immediate runtime. + ''' + user = payload.get(learning_observer.constants.USER, {}) or {} + user_domain = learning_observer.util.get_domain_from_email(user.get('email')) + should_log = settings.pmss_settings.logging_enabled( + types=['lms_integration'], + attributes={'domain': user_domain} + ) + if not should_log: + return + payload.setdefault('timestamp', datetime.datetime.utcnow().isoformat()) + encoded_payload = json.dumps(payload, sort_keys=True, default=str) + log_event(encoded_payload, filename="lms_integration", preencoded=True) + + def close_logfile(filename): # remove the file from the dict storing open log files and close it if filename not in files: From 7e55cfc8722d351f5a581555433f7ae8f7b79e70 Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Thu, 22 Jan 2026 13:36:54 -0500 Subject: [PATCH 78/88] added ability to pass in pmss rulesets via command line arguments (#258) --- VERSION | 2 +- docs/concepts/system_settings.md | 9 +++ docs/how-to/offline_replay.md | 5 ++ learning_observer/VERSION | 2 +- .../learning_observer/offline.py | 2 + .../learning_observer/settings.py | 68 +++++++++++++++++-- 6 files changed, 80 insertions(+), 8 deletions(-) diff --git a/VERSION b/VERSION index 2ea3893e2..220428769 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0+2026.01.20T19.54.05.825Z.d2a08f21.berickson.20260120.integration.logging +0.1.0+2026.01.22T18.35.16.272Z.0cc53c87.berickson.20260122.pmss.rulesets.argument diff --git a/docs/concepts/system_settings.md b/docs/concepts/system_settings.md index b24009c46..f1749dc40 100644 --- a/docs/concepts/system_settings.md +++ b/docs/concepts/system_settings.md @@ -25,6 +25,15 @@ To view all settings and what they do, checkout the [System Settings Reference]( boundaries, third-party credentials, or feature flags in one place and keep those choices under version control. +## Defining which settings files to use + +To load alternate or additional PMSS rulesets, start the server with +`--pmss-rulesets` and pass one or more file paths or a directory. The startup +logic expands directories into sorted file lists, then loads each file as a +`YAMLFileRuleset` when it ends in `.yaml`/`.yml` or a `PMSSFileRuleset` when it +ends in `.pmss`. Any other file suffix is skipped with a warning so you can +keep README files or notes alongside the rulesets without breaking startup. + ## The role of `creds.yaml` Most installations load configuration from `creds.yaml`. When the process diff --git a/docs/how-to/offline_replay.md b/docs/how-to/offline_replay.md index f0339cffb..12c97328e 100644 --- a/docs/how-to/offline_replay.md +++ b/docs/how-to/offline_replay.md @@ -26,6 +26,11 @@ from learning_observer import offline # Prepare the in-memory KVS and reducers for offline replay offline.init('creds.yaml') +# TODO: Offline replay currently initializes PMSS with default rulesets only. +# If you need custom `.pmss` overlays or alternate YAML files, update the +# offline initializer to accept ruleset paths. + + # Replace this path with the study log you want to replay log_path = "/path/to/your/session.study.log" diff --git a/learning_observer/VERSION b/learning_observer/VERSION index 2ea3893e2..220428769 100644 --- a/learning_observer/VERSION +++ b/learning_observer/VERSION @@ -1 +1 @@ -0.1.0+2026.01.20T19.54.05.825Z.d2a08f21.berickson.20260120.integration.logging +0.1.0+2026.01.22T18.35.16.272Z.0cc53c87.berickson.20260122.pmss.rulesets.argument diff --git a/learning_observer/learning_observer/offline.py b/learning_observer/learning_observer/offline.py index 6b94aa8b7..d968688d7 100644 --- a/learning_observer/learning_observer/offline.py +++ b/learning_observer/learning_observer/offline.py @@ -55,6 +55,8 @@ def init(settings=INTERACTIVE_SETTINGS): # anything before we've loaded the settings. This might not be necessary, # depending on the (still-changing) startup order learning_observer.log_event.DEBUG_LOG_LEVEL = learning_observer.log_event.LogLevel.NONE + # TODO: Allow offline callers to pass PMSS ruleset files or directories. + learning_observer.settings.init_pmss_settings() learning_observer.settings.load_settings(settings) learning_observer.prestartup.startup_checks_and_init() learning_observer.kvs.kvs_startup_check() # Set up the KVS diff --git a/learning_observer/learning_observer/settings.py b/learning_observer/learning_observer/settings.py index 7df530caa..5bb1a976c 100644 --- a/learning_observer/learning_observer/settings.py +++ b/learning_observer/learning_observer/settings.py @@ -21,12 +21,7 @@ import pmss -pmss_settings = pmss.init( - prog=__name__, - description="A system for monitoring", - epilog="For more information, see PMSS documentation.", - rulesets=[pmss.YAMLFileRuleset(filename=learning_observer.paths.config_file())] -) +pmss_settings = None # If we e.g. `import settings` and `import learning_observer.settings`, we # will load startup code twice, and end up with double the global variables. @@ -49,6 +44,56 @@ def str_to_bool(arg): raise argparse.ArgumentTypeError('Boolean like value expected.') +def _expand_ruleset_paths(ruleset_paths): + if not ruleset_paths: + return [learning_observer.paths.config_file()] + + expanded_paths = [] + for ruleset_path in ruleset_paths: + if not os.path.exists(ruleset_path): + raise FileNotFoundError( + f"PMSS ruleset path not found: {ruleset_path}" + ) + if os.path.isdir(ruleset_path): + entries = [ + os.path.join(ruleset_path, entry) + for entry in sorted(os.listdir(ruleset_path)) + ] + expanded_paths.extend( + entry for entry in entries if os.path.isfile(entry) + ) + else: + expanded_paths.append(ruleset_path) + return expanded_paths + + +def _build_rulesets(ruleset_paths): + rulesets = [] + for ruleset_path in _expand_ruleset_paths(ruleset_paths): + if ruleset_path.endswith(('.yaml', '.yml')): + rulesets.append(pmss.YAMLFileRuleset(filename=ruleset_path)) + elif ruleset_path.endswith('.pmss'): + rulesets.append(pmss.PMSSFileRuleset(filename=ruleset_path)) + else: + print( + f"Skipping PMSS ruleset file {ruleset_path}; " + "unsupported suffix." + ) + return rulesets + + +def init_pmss_settings(ruleset_paths=None): + global pmss_settings + if pmss_settings is None: + pmss_settings = pmss.init( + prog=__name__, + description="A system for monitoring", + epilog="For more information, see PMSS documentation.", + rulesets=_build_rulesets(ruleset_paths) + ) + return pmss_settings + + def parse_and_validate_arguments(): ''' Parse and validate command line arguments; for now, just the @@ -65,6 +110,12 @@ def parse_and_validate_arguments(): help='Specify an alternative configuration file', default=learning_observer.paths.config_file()) + parser.add_argument( + '--pmss-rulesets', + help='List of PMSS ruleset files or a directory of rulesets.', + nargs='+', + default=[learning_observer.paths.config_file()]) + parser.add_argument( '--watchdog', help='Run in watchdog mode. This will restart on file changes.', @@ -112,6 +163,7 @@ def parse_and_validate_arguments(): config_file=args.config_file ) ) + init_pmss_settings(args.pmss_rulesets) return args @@ -157,6 +209,10 @@ def load_settings(config): ''' global settings + # Ensure PMSS is initialized for callers that skip CLI parsing. + # If CLI parsing already ran, the init call is a no-op. + init_pmss_settings() + if isinstance(config, str): with open(config, 'r') as f: settings = yaml.safe_load(f) From 3866327776bbe60a807cd1d486f63d070aba7fce Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Mon, 26 Jan 2026 15:59:03 -0500 Subject: [PATCH 79/88] =?UTF-8?q?updated=20blacklist=20code=20to=20check?= =?UTF-8?q?=20for=20domain=20and=20determine=20action=20by=20se=E2=80=A6?= =?UTF-8?q?=20(#259)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * updated blacklist code to check for domain and determine action by settings * updated maintain to allow numbers --- VERSION | 2 +- docs/concepts/events.md | 31 ++++++++ docs/reference/system_settings.md | 7 ++ learning_observer/VERSION | 2 +- .../learning_observer/blacklist.py | 70 +++++++++++++++++-- 5 files changed, 106 insertions(+), 6 deletions(-) diff --git a/VERSION b/VERSION index 220428769..fc5d63b11 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0+2026.01.22T18.35.16.272Z.0cc53c87.berickson.20260122.pmss.rulesets.argument +0.1.0+2026.01.26T17.51.31.713Z.b83cda8e.berickson.20260126.blacklist.by.domain diff --git a/docs/concepts/events.md b/docs/concepts/events.md index e2f49301c..d4b1d12d2 100644 --- a/docs/concepts/events.md +++ b/docs/concepts/events.md @@ -129,3 +129,34 @@ authentication, safety checks, and analytics while keeping the event format itself lightweight. Clients only need to agree on the JSON structure of events, while the server handles durability and routing responsibilities on their behalf. + +## Configuring domain-based event blacklisting + +Incoming events can be blacklisted through PMSS rules so that specific domains +either continue streaming, are told to retry later, or drop events entirely. +The `blacklist_event_action` setting controls the action and defaults to +`TRANSMIT`. Define rules under the `incoming_events` namespace and include a +`domain` attribute to scope the behavior per organization. When the action is +`MAINTAIN`, the `blacklist_time_limit` setting controls whether the client +should wait a short time or stop sending forever. + +```pmss +incoming_events { + blacklist_event_action: TRANSMIT; +} + +incoming_events[domain="example.org"] { + blacklist_event_action: DROP; +} + +incoming_events[domain="pilot.example.edu"] { + blacklist_event_action: MAINTAIN; + blacklist_time_limit: DAYS; +} +``` + +When a client connects, the server extracts a candidate domain from the event +payload and uses it to resolve the `blacklist_event_action` setting. If +a rule returns `DROP`, the client is instructed to stop sending events. +`MAINTAIN` asks the client to retain events and retry after a delay (as defined +by `blacklist_time_limit`), while `TRANSMIT` streams events normally. diff --git a/docs/reference/system_settings.md b/docs/reference/system_settings.md index 6333264d5..354a27840 100644 --- a/docs/reference/system_settings.md +++ b/docs/reference/system_settings.md @@ -137,6 +137,13 @@ runtime. | `event_auth.hash_identify` | Enables hash-based identity hints (e.g., `/page#user=alice`) for one-off experiments. | disabled | [`learning_observer/learning_observer/auth/events.py`](../../learning_observer/learning_observer/auth/events.py) | | `event_auth.testcase_auth` | Allows automated tests to tag events with deterministic user IDs. | disabled | [`learning_observer/learning_observer/auth/events.py`](../../learning_observer/learning_observer/auth/events.py) | +### Incoming events blacklist (`incoming_events` namespace) + +| YAML path | Description | Default | Used in | +| --- | --- | --- | --- | +| `incoming_events.blacklist_event_action` | Action to take for incoming events (`TRANSMIT`, `MAINTAIN`, or `DROP`) when blacklist rules match. | `TRANSMIT` | [`learning_observer/learning_observer/blacklist.py`](../../learning_observer/learning_observer/blacklist.py) | +| `incoming_events.blacklist_time_limit` | Time limit to return when `blacklist_event_action` is `MAINTAIN` (`PERMANENT`, `MINUTES`, or `DAYS`). | `MINUTES` | [`learning_observer/learning_observer/blacklist.py`](../../learning_observer/learning_observer/blacklist.py) | + ## Modules Modules can define their own PMSS namespaces under `modules.`. diff --git a/learning_observer/VERSION b/learning_observer/VERSION index 220428769..fc5d63b11 100644 --- a/learning_observer/VERSION +++ b/learning_observer/VERSION @@ -1 +1 @@ -0.1.0+2026.01.22T18.35.16.272Z.0cc53c87.berickson.20260122.pmss.rulesets.argument +0.1.0+2026.01.26T17.51.31.713Z.b83cda8e.berickson.20260126.blacklist.by.domain diff --git a/learning_observer/learning_observer/blacklist.py b/learning_observer/learning_observer/blacklist.py index cc9857d62..ab084b22e 100644 --- a/learning_observer/learning_observer/blacklist.py +++ b/learning_observer/learning_observer/blacklist.py @@ -1,3 +1,8 @@ +import pmss + +import learning_observer.settings +import learning_observer.util + # TODO: # ACTIONS = enum.StrEnum("ACTIONS", names=('TRANSMIT', 'MAINTAIN', 'DROP')) # @@ -16,6 +21,56 @@ class TIME_LIMITS: [setattr(TIME_LIMITS, limit, limit) for limit in time_limits] +def _parse_blacklist_time_limit(value): + if isinstance(value, str): + normalized = value.strip().upper() + if normalized in time_limits: + return normalized + try: + numeric_value = float(normalized) + except ValueError as exc: + raise ValueError(f"Value '{value}' is not a valid blacklist time limit.") from exc + if numeric_value.is_integer(): + return int(numeric_value) + return numeric_value + if isinstance(value, bool): + raise ValueError(f"Value '{value}' is not a valid blacklist time limit.") + if isinstance(value, (int, float)): + return int(value) if isinstance(value, float) and value.is_integer() else value + raise ValueError(f"Value '{value}' is not a valid blacklist time limit.") + + +# Register blacklist settings +pmss.parser('blacklist_event_action', parent='string', choices=action_modes, transform=None) +pmss.register_field( + name='blacklist_event_action', + type='blacklist_event_action', + description='How to treat incoming events for blacklist checks.', + default=ACTIONS.TRANSMIT +) +pmss.parser('blacklist_time_limit', transform=_parse_blacklist_time_limit) +pmss.register_field( + name='blacklist_time_limit', + type='blacklist_time_limit', + description='Time limits for MAINTAINing blacklist responses. One of PERMANENT / MINUTES / DAYS or a number of milliseconds.', + default=TIME_LIMITS.MINUTES +) + + +def _extract_event_domain(event): + '''Find the domain of a user via the event + HACK we do not include the user's email throughout the incoming + event pipeline. We might do that in the future. For now, we look + in the places where an email *might* be to extract the domain. + ''' + if not isinstance(event, dict): + return None + email = None + if 'chrome_identity' in event: + email = event['chrome_identity'].get('email') + return learning_observer.util.get_domain_from_email(email) + + def get_blacklist_status(event): '''Return the blacklist status of a given event Returns should look like: @@ -26,16 +81,23 @@ def get_blacklist_status(event): message }` ''' - # TODO add in the logic to check our blacklist - if False: + event_domain = _extract_event_domain(event) + attributes = {'domain': event_domain} if event_domain else {} + action_type = learning_observer.settings.pmss_settings.blacklist_event_action(types=['incoming_events'], attributes=attributes) + + if action_type == ACTIONS.MAINTAIN: # tell client to keep messages around + time_limit = learning_observer.settings.pmss_settings.blacklist_time_limit( + types=['incoming_events'], + attributes=attributes + ) return { 'status': 'blocklist', 'action': ACTIONS.MAINTAIN, - 'time_limit': TIME_LIMITS.MINUTES, # Note this could (and usually would) be a number, but we have a few defaults like 'MINUTES' defined + 'time_limit': time_limit, 'message': 'You are blocked for now' } - if False: + if action_type == ACTIONS.DROP: # tell client to drop messages return { 'status': 'blocklist', From b1e0dd607cf87a778bc892a3ecf492c6d6c7622c Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Mon, 2 Feb 2026 15:36:14 -0500 Subject: [PATCH 80/88] Fixing comm protocol so roster is called multiple times for single dag with multiple targets (#256) Full commit list * working on making the comm protocol only call nodes in the generator once per run so other nodes can share thoes values * updated shared async iterable to not grow * each target gets their own cleaned generator * updated missing target node error --- VERSION | 2 +- learning_observer/VERSION | 2 +- .../communication_protocol/executor.py | 130 +++++++++++++++++- .../learning_observer/dashboard.py | 97 +++++++++---- 4 files changed, 195 insertions(+), 36 deletions(-) diff --git a/VERSION b/VERSION index fc5d63b11..fcc4404a5 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0+2026.01.26T17.51.31.713Z.b83cda8e.berickson.20260126.blacklist.by.domain +0.1.0+2026.02.02T16.19.01.249Z.2ae8ac1a.berickson.20260120.comm.protocol.targets diff --git a/learning_observer/VERSION b/learning_observer/VERSION index fc5d63b11..fcc4404a5 100644 --- a/learning_observer/VERSION +++ b/learning_observer/VERSION @@ -1 +1 @@ -0.1.0+2026.01.26T17.51.31.713Z.b83cda8e.berickson.20260126.blacklist.by.domain +0.1.0+2026.02.02T16.19.01.249Z.2ae8ac1a.berickson.20260120.comm.protocol.targets diff --git a/learning_observer/learning_observer/communication_protocol/executor.py b/learning_observer/learning_observer/communication_protocol/executor.py index 64a6f3092..46411c9ad 100644 --- a/learning_observer/learning_observer/communication_protocol/executor.py +++ b/learning_observer/learning_observer/communication_protocol/executor.py @@ -9,6 +9,7 @@ import concurrent.futures import functools import inspect +import weakref import learning_observer.communication_protocol.query import learning_observer.communication_protocol.util @@ -21,6 +22,110 @@ from learning_observer.util import get_nested_dict_value, clean_json, ensure_async_generator, async_zip from learning_observer.communication_protocol.exception import DAGExecutionException + +class _SharedAsyncIterable: + """Fan out one async iterable to multiple consumers without runaway memory use. + + The execution DAG can reuse a single async iterable in multiple downstream nodes. + We do not want to eagerly drain the source in a background task, because that + defeats backpressure and retains every item indefinitely. This wrapper only + pulls items when a consumer needs them and discards items once every consumer + has advanced past them. + """ + def __init__(self, source): + self._source = source + self._source_iter = source.__aiter__() + self._buffer = [] + self._start_index = 0 + self._done = False + self._exception = None + self._condition = asyncio.Condition() + self._fetch_lock = asyncio.Lock() + self._iterators = weakref.WeakSet() + + async def _fetch_next(self, target_index): + async with self._fetch_lock: + async with self._condition: + if self._exception is not None or self._done: + return + if target_index < self._start_index + len(self._buffer): + return + # Only fetch when a consumer needs a new item to avoid eager draining. + try: + item = await self._source_iter.__anext__() + except StopAsyncIteration: + async with self._condition: + self._done = True + self._condition.notify_all() + return + except Exception as e: + async with self._condition: + self._exception = e + self._done = True + self._condition.notify_all() + raise + async with self._condition: + self._buffer.append(item) + self._condition.notify_all() + + async def _trim_buffer(self): + async with self._condition: + if not self._iterators: + # No active consumers, so we can drop everything immediately. + self._start_index += len(self._buffer) + self._buffer.clear() + return + # Drop any buffered items that all active consumers have passed. + min_index = min(iterator._index for iterator in self._iterators) + trim_count = min_index - self._start_index + if trim_count > 0: + del self._buffer[:trim_count] + self._start_index = min_index + + def _discard_iterator(self, iterator): + if iterator not in self._iterators: + return + self._iterators.discard(iterator) + try: + loop = asyncio.get_running_loop() + except RuntimeError: + return + # Schedule trimming outside of __del__ to avoid blocking finalization. + loop.create_task(self._trim_buffer()) + + def __aiter__(self): + iterator = _SharedAsyncIterator(self) + self._iterators.add(iterator) + return iterator + + +class _SharedAsyncIterator: + """Advance through the shared buffer and coordinate with other consumers.""" + def __init__(self, shared): + self._shared = shared + self._index = shared._start_index + + async def __anext__(self): + while True: + async with self._shared._condition: + buffer_offset = self._index - self._shared._start_index + if buffer_offset < len(self._shared._buffer): + item = self._shared._buffer[buffer_offset] + self._index += 1 + break + if self._shared._exception is not None: + raise self._shared._exception + if self._shared._done: + raise StopAsyncIteration + # Trigger a fetch if we are caught up with the shared buffer. + await self._shared._fetch_next(self._index) + await self._shared._trim_buffer() + return item + + def __del__(self): + self._shared._discard_iterator(self) + + dispatch = learning_observer.communication_protocol.query.dispatch @@ -799,8 +904,9 @@ async def execute_dag(endpoint, parameters, functions, target_exports): target_node = exports[key].get('returns') if target_node not in nodes: # Export exists, but its `returns` node is missing from the DAG - target_nodes.append(target_node) - target_errors[target_node] = DAGExecutionException( + target_name = f'__missing_export__:{key}' + target_nodes.append(target_name) + target_errors[target_name] = DAGExecutionException( f'Target DAG node `{target_node}` not found in execution_dag.', inspect.currentframe().f_code.co_name, {'target_node': target_node, 'available_nodes': list(nodes.keys())} @@ -877,16 +983,32 @@ async def visit(node_name): f'{error_texts}') else: nodes[node_name] = await dispatch_node(nodes[node_name]) + if isinstance(nodes[node_name], collections.abc.AsyncIterable) and not isinstance(nodes[node_name], _SharedAsyncIterable): + nodes[node_name] = _SharedAsyncIterable(nodes[node_name]) + visited.add(node_name) return nodes[node_name] out = {} + async_iterable_cache = {} for e in target_nodes: if e in target_errors: out[e] = _clean_json_via_generator(target_errors[e]) - else: - out[e] = _clean_json_via_generator(await visit(e)) + continue + + node_result = await visit(e) + if isinstance(node_result, collections.abc.AsyncIterable): + shared_iterable = async_iterable_cache.get(id(node_result)) + if shared_iterable is None: + shared_iterable = node_result + async_iterable_cache[id(node_result)] = shared_iterable + out[e] = _clean_json_via_generator(shared_iterable) + continue + + out[e] = _clean_json_via_generator(node_result) + + return out # Include execution history in output if operating in development settings diff --git a/learning_observer/learning_observer/dashboard.py b/learning_observer/learning_observer/dashboard.py index 8a1dea7f4..7b07255a8 100644 --- a/learning_observer/learning_observer/dashboard.py +++ b/learning_observer/learning_observer/dashboard.py @@ -521,11 +521,11 @@ async def _handle_dependent_dags(query): return query -async def _prepare_dag_as_generator(client_query, query, target, request): +async def _prepare_dag_as_generators(client_query, query, targets, request): ''' Prepares the query for execution, sets up client parameters and runtime. ''' - target_exports = [target] + target_exports = list(targets) # Prepare the DAG execution function query_func = learning_observer.communication_protocol.integration.prepare_dag_execution(query, target_exports) @@ -535,12 +535,41 @@ async def _prepare_dag_as_generator(client_query, query, target, request): runtime = learning_observer.runtime.Runtime(request) client_parameters['runtime'] = runtime - # Execute the query and return the first value from the generator + # Execute the query and return generators keyed by export targets. generator_dictionary = await query_func(**client_parameters) - return next(iter(generator_dictionary.values())) + target_nodes_to_targets = {} + exports = query.get('exports', {}) + execution_nodes = query.get('execution_dag', {}) + for target in target_exports: + if target in exports: + node = exports[target].get('returns') + if node not in execution_nodes: + node = f'__missing_export__:{target}' + else: + node = f'__missing_export__:{target}' + target_nodes_to_targets.setdefault(node, []).append(target) + + generators_by_id = {} + for node, node_targets in target_nodes_to_targets.items(): + generator = generator_dictionary.get(node) + if generator is None: + debug_log(f'Missing generator for DAG node {node}') + continue + generator_id = id(generator) + if generator_id not in generators_by_id: + generators_by_id[generator_id] = { + 'generator': generator, + 'targets': [] + } + generators_by_id[generator_id]['targets'].extend(node_targets) + return [ + (entry['targets'], entry['generator']) + for entry in generators_by_id.values() + ] + -async def _create_dag_generator(client_query, target, request): +async def _create_dag_generators(client_query, targets, request): dag = client_query['execution_dag'] if type(dag) not in DAG_DISPATCH: debug_log(await dag_unsupported_type(type(dag))) @@ -552,7 +581,7 @@ async def _create_dag_generator(client_query, target, request): debug_log('The submitted query failed.') return query = await _handle_dependent_dags(query) - return await _prepare_dag_as_generator(client_query, query, target, request) + return await _prepare_dag_as_generators(client_query, query, targets, request) def _scope_segment_for_provenance_key(key): @@ -847,7 +876,7 @@ async def _send_pending_updates_to_client(): # TODO this ought to be pulled from somewhere await asyncio.sleep(1) - async def _execute_dag(dag_query, target, params): + async def _execute_dag(dag_query, targets, params): '''This method creates the DAG generator and drives it. Once finished, we wait until rescheduling it. If the parameters change, we exit before creating and driving the generator. @@ -857,8 +886,16 @@ async def _execute_dag(dag_query, target, params): return # Create DAG generator and drive - generator = await _create_dag_generator(dag_query, target, request) - await _drive_generator(generator, dag_query['kwargs'], target=target) + generators = await _create_dag_generators(dag_query, targets, request) + if generators is None: + return + drive_tasks = [] + for target_group, generator in generators: + drive_tasks.append(asyncio.create_task( + _drive_generator(generator, dag_query['kwargs'], targets=target_group) + )) + if drive_tasks: + await asyncio.gather(*drive_tasks) # Handle rescheduling the execution of the DAG for fresh data # TODO add some way to specify specific endpoint delays @@ -867,26 +904,30 @@ async def _execute_dag(dag_query, target, params): # if dag_delay is negative, we skip repeated execution return await asyncio.sleep(dag_delay) - await _execute_dag(dag_query, target, params) + await _execute_dag(dag_query, targets, params) - async def _drive_generator(generator, dag_kwargs, target=None): + async def _drive_generator(generator, dag_kwargs, targets=None): '''For each item in the generator, this method creates an update to send to the client. ''' + target_exports = targets or [None] async for item in generator: scope = _find_student_or_resource(item) update_path = ".".join(scope) - if 'option_hash' in dag_kwargs and target is not None: - item[f'option_hash_{target}'] = dag_kwargs['option_hash'] - # TODO this ought to be flag - we might want to see the provenance in some settings - item_without_provenance = learning_observer.communication_protocol.executor.strip_provenance(item) - update_payload = {'op': 'update', 'path': update_path, 'value': item_without_provenance} - _log_protocol_event( - 'update_enqueued', - payload=update_payload, - target_export=target - ) - await _queue_update(update_payload) + for target in target_exports: + item_payload = item + if 'option_hash' in dag_kwargs and target is not None and isinstance(item, dict): + item_payload = dict(item) + item_payload[f'option_hash_{target}'] = dag_kwargs['option_hash'] + # TODO this ought to be flag - we might want to see the provenance in some settings + item_without_provenance = learning_observer.communication_protocol.executor.strip_provenance(item_payload) + update_payload = {'op': 'update', 'path': update_path, 'value': item_without_provenance} + _log_protocol_event( + 'update_enqueued', + payload=update_payload, + target_export=target + ) + await _queue_update(update_payload) send_batches_task = asyncio.create_task(_send_pending_updates_to_client()) background_tasks.add(send_batches_task) @@ -932,15 +973,11 @@ async def _drive_generator(generator, dag_kwargs, target=None): if client_query != previous_client_query: previous_client_query = copy.deepcopy(client_query) - # HACK even though we can specify multiple targets for a - # single DAG, this creates a new DAG for each. This eventually - # allows us to specify different parameters (such as the - # reschedule timeout). for k, v in client_query.items(): - for target in v.get('target_exports', []): - execute_dag_task = asyncio.create_task(_execute_dag(v, target, client_query)) - background_tasks.add(execute_dag_task) - execute_dag_task.add_done_callback(background_tasks.discard) + targets = v.get('target_exports', []) + execute_dag_task = asyncio.create_task(_execute_dag(v, targets, client_query)) + background_tasks.add(execute_dag_task) + execute_dag_task.add_done_callback(background_tasks.discard) # Various ways we might encounter an exception except asyncio.CancelledError: From deeac6f1d1f1495e1c4d312432f92f029ccb0f45 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Feb 2026 15:37:03 -0500 Subject: [PATCH 81/88] Bump lodash from 4.17.21 to 4.17.23 in /modules/lo_dash_react_components (#257) Bumps [lodash](https://github.com/lodash/lodash) from 4.17.21 to 4.17.23. - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](https://github.com/lodash/lodash/compare/4.17.21...4.17.23) --- updated-dependencies: - dependency-name: lodash dependency-version: 4.17.23 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .../package-lock.json | 64 +++++-------------- 1 file changed, 16 insertions(+), 48 deletions(-) diff --git a/modules/lo_dash_react_components/package-lock.json b/modules/lo_dash_react_components/package-lock.json index 00e70911d..e9c4b7c64 100644 --- a/modules/lo_dash_react_components/package-lock.json +++ b/modules/lo_dash_react_components/package-lock.json @@ -104,7 +104,6 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", "license": "MIT", - "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.26.0", @@ -747,7 +746,6 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.26.0.tgz", "integrity": "sha512-B+O2DnPc0iG+YXFqOxv2WNuNU97ToWjOomUQ78DouOENWUaM5sVrmet9mcomUGQFwpJd//gvUagXBSdzO1fRKg==", "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1614,7 +1612,6 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.9.tgz", "integrity": "sha512-s5XwpQYCqGerXl+Pu6VDL3x0j2d82eiV77UJ8a2mDHAW7j9SWRqQ2y1fNo1Z74CdcYipl5Z41zvjj4Nfzq36rw==", "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-module-imports": "^7.25.9", @@ -2131,7 +2128,6 @@ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "license": "MIT", - "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -2259,7 +2255,6 @@ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "license": "MIT", - "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -3282,7 +3277,6 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.0.tgz", "integrity": "sha512-i0GV1yJnm2n3Yq1qw6QrUrd/LI9bE8WEBOTtOkpCXHHdyN3TAGgqAK/DAT05z4fq2x04cARXt2pDmjWjL92iTQ==", - "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -3322,7 +3316,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3343,7 +3336,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3364,7 +3356,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3385,7 +3376,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3406,7 +3396,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3427,7 +3416,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3448,7 +3436,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3469,7 +3456,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3490,7 +3476,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3511,7 +3496,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3532,7 +3516,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3553,7 +3536,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3574,7 +3556,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3658,7 +3639,6 @@ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", "license": "MIT", - "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/popperjs" @@ -4659,7 +4639,6 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", "license": "BSD-2-Clause", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "5.62.0", "@typescript-eslint/types": "5.62.0", @@ -5100,7 +5079,6 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -5187,7 +5165,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -6062,7 +6039,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001669", "electron-to-chromium": "^1.5.41", @@ -6109,6 +6085,7 @@ "integrity": "sha512-SW9lzGTLvWTP1AY8xeAMZimqDrIaSdLQUcVr9DMef51niJ022Ri87SwRRKYm4A6iHfkPaiVUu/Duw2Wc4J7kKg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "semver": "^7.0.0" } @@ -6119,6 +6096,7 @@ "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, "license": "ISC", + "peer": true, "bin": { "semver": "bin/semver.js" }, @@ -7583,7 +7561,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", - "dev": true, "license": "Apache-2.0", "optional": true, "bin": { @@ -8193,7 +8170,6 @@ "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -8250,6 +8226,7 @@ "integrity": "sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "semver": "^7.5.4" }, @@ -8266,6 +8243,7 @@ "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, "license": "ISC", + "peer": true, "bin": { "semver": "bin/semver.js" }, @@ -8420,6 +8398,7 @@ "https://opencollective.com/eslint" ], "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.1.2", "@eslint-community/regexpp": "^4.11.0", @@ -8455,7 +8434,6 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz", "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==", "license": "MIT", - "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.8", @@ -8564,6 +8542,7 @@ "integrity": "sha512-6TyDmZ1HXoFQXnhCTUjVFULReoBPOAjpuiKELMkeP40yffI/1ZRO+d9ug/VC6fqISo2WkuIBk3cvuRPALaWlOQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "builtins": "^5.0.1", @@ -8593,6 +8572,7 @@ "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "type-fest": "^0.20.2" }, @@ -8609,6 +8589,7 @@ "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, "license": "ISC", + "peer": true, "bin": { "semver": "bin/semver.js" }, @@ -8622,6 +8603,7 @@ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, "license": "(MIT OR CC0-1.0)", + "peer": true, "engines": { "node": ">=10" }, @@ -8656,7 +8638,6 @@ "integrity": "sha512-57Zzfw8G6+Gq7axm2Pdo3gW/Rx3h9Yywgn61uE/3elTCOePEHVrn2i5CdfBwA1BLK0Q0WqctICIUSqXZW/VprQ==", "dev": true, "license": "ISC", - "peer": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -9815,6 +9796,7 @@ "integrity": "sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "resolve-pkg-maps": "^1.0.0" }, @@ -10716,6 +10698,7 @@ "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "builtin-modules": "^3.3.0" }, @@ -11342,7 +11325,6 @@ "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", "integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==", "license": "MIT", - "peer": true, "dependencies": { "@jest/core": "^27.5.1", "import-local": "^3.0.2", @@ -12620,9 +12602,9 @@ } }, "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", "license": "MIT" }, "node_modules/lodash.debounce": { @@ -13036,7 +13018,6 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", - "dev": true, "license": "MIT", "optional": true }, @@ -14083,7 +14064,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.7", "picocolors": "^1.1.1", @@ -14974,7 +14954,6 @@ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "license": "MIT", - "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -15811,7 +15790,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -15946,7 +15924,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -15978,7 +15955,6 @@ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", "integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -16858,6 +16834,7 @@ "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", "dev": true, "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } @@ -16977,7 +16954,6 @@ "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz", "integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==", "license": "MIT", - "peer": true, "bin": { "rollup": "dist/bin/rollup" }, @@ -17136,7 +17112,6 @@ "integrity": "sha512-Q4fOxRfhmv3sqCLoGfvrC9pRV8btc0UtqL9mN6Yrv6Qi9ScL55CVH1vlPP863ISLEEMNLLuu9P+enCeGHlnzhA==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.0.2", @@ -17271,7 +17246,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -18997,8 +18971,7 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD", - "peer": true + "license": "0BSD" }, "node_modules/tsutils": { "version": "3.21.0", @@ -19047,7 +19020,6 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "license": "(MIT OR CC0-1.0)", - "peer": true, "engines": { "node": ">=10" }, @@ -19532,7 +19504,6 @@ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.96.1.tgz", "integrity": "sha512-l2LlBSvVZGhL4ZrPwyr8+37AunkcYj5qh8o6u2/2rzoPc8gxFJkLj1WxNgooi9pnoc06jh0BjuXnamM4qlujZA==", "license": "MIT", - "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.6", @@ -19580,7 +19551,6 @@ "integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@discoveryjs/json-ext": "^0.5.0", "@webpack-cli/configtest": "^2.1.1", @@ -19687,7 +19657,6 @@ "integrity": "sha512-ml/0HIj9NLpVKOMq+SuBPLHcmbG+TGIjXRHsYfZwocUBIqEvws8NnS/V9AFQ5FKP+tgn5adwVwRrTEpGL33QFQ==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "@types/bonjour": "^3.5.13", "@types/connect-history-api-fallback": "^1.5.4", @@ -20194,7 +20163,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", From 049e0ecee78556b95c708db8ecd87a40ac6ae2e4 Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Mon, 2 Feb 2026 15:56:09 -0500 Subject: [PATCH 82/88] Added tab ids to extension, updated reconstruction to include tabs, and added tab metadata reducer Full commit list: * added the tab sepecific code in extension * added tab_id to google doc extension * updated regex for tab ids * added nm command to reconstruct * code for tab_list reducer * added tab scoped time on task * strengthened tab list reducer * added back unnecessary line removals * added back in tab_id to nested structure --------- Co-authored-by: saminur --- VERSION | 2 +- extension/writing-process/src/background.js | 5 +- extension/writing-process/src/writing.js | 15 +- .../writing-process/src/writing_common.js | 24 +++ modules/writing_observer/VERSION | 2 +- .../writing_observer/module.py | 14 +- .../writing_observer/reconstruct_doc.py | 61 ++++++- .../writing_observer/writing_analysis.py | 160 +++++++++++++++++- 8 files changed, 270 insertions(+), 13 deletions(-) diff --git a/VERSION b/VERSION index fcc4404a5..0d5696b3a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0+2026.02.02T16.19.01.249Z.2ae8ac1a.berickson.20260120.comm.protocol.targets +0.1.0+2026.02.02T20.54.51.168Z.c0f2a8f0.berickson.20260113.extension.tab.ids diff --git a/extension/writing-process/src/background.js b/extension/writing-process/src/background.js index d5544d1a7..058232fb2 100644 --- a/extension/writing-process/src/background.js +++ b/extension/writing-process/src/background.js @@ -3,8 +3,7 @@ Background script. This works across all of Google Chrome. */ import { CONFIG } from "./service_worker_config.js"; -import { googledocs_id_from_url } from './writing_common'; - +import { googledocs_id_from_url, googledocs_tab_id_from_url } from './writing_common'; import * as loEvent from 'lo_event/lo_event/lo_event.js'; import * as loEventDebug from 'lo_event/lo_event/debugLog.js'; import { websocketLogger } from 'lo_event/lo_event/websocketLogger.js'; @@ -203,6 +202,7 @@ chrome.webRequest.onBeforeRequest.addListener( versus GMT. */ event = { 'doc_id': googledocs_id_from_url(request.url), + 'tab_id': googledocs_tab_id_from_url(request.url), 'url': request.url, 'bundles': JSON.parse(formdata.bundles), 'rev': formdata.rev, @@ -216,6 +216,7 @@ chrome.webRequest.onBeforeRequest.addListener( */ event = { 'doc_id': googledocs_id_from_url(request.url), + 'tab_id': googledocs_tab_id_from_url(request.url), 'url': request.url, 'formdata': formdata, 'rev': formdata.rev, diff --git a/extension/writing-process/src/writing.js b/extension/writing-process/src/writing.js index 1e63f9df1..084affc5e 100644 --- a/extension/writing-process/src/writing.js +++ b/extension/writing-process/src/writing.js @@ -5,7 +5,7 @@ /* For debugging purposes: we know the extension is active */ // document.body.style.border = "1px solid blue"; -import { googledocs_id_from_url, treeget } from './writing_common'; +import { googledocs_id_from_url, googledocs_tab_id_from_url, treeget } from './writing_common'; /* General Utility Functions */ @@ -49,6 +49,7 @@ function log_event(event_type, event) { "type": "http://schema.learning-observer.org/writing-observer/", "title": google_docs_title(), "id": doc_id(), + "tab_id": tab_id(), "url": window.location.href, }; @@ -78,6 +79,18 @@ function doc_id() { } } +function tab_id() { + /* + Extract the Google document's current Tab ID from the window + */ + try { + return googledocs_tab_id_from_url(window.location.href); + } catch(error) { + log_error("Couldn't read document's tab id"); + return null; + } +} + function this_is_a_google_doc() { /* diff --git a/extension/writing-process/src/writing_common.js b/extension/writing-process/src/writing_common.js index 6242b3ab6..12fc81072 100644 --- a/extension/writing-process/src/writing_common.js +++ b/extension/writing-process/src/writing_common.js @@ -78,6 +78,30 @@ export function googledocs_id_from_url(url) { return null; } +export function googledocs_tab_id_from_url(url) { + /* + Given a URL like: + https://docs.google.com/document/d//edit?tab=t.95yb7msfl8ul + https://docs.google.com/document/d//edit?tab=t.95yb7msfl8ul#heading=h.abc123 + extract the associated tab ID: + t.95yb7msfl8ul + Return null if not a valid Google Docs URL or tab param. + + Regex explanation: + 1. `/.*:\/\/` - match any protocol (http/https) followed by :// + 2. `docs\.google\.com\/document\/` - match google docs domain + 3. `.*` - match any characters until we find the tab param + 4. `[?&]tab=` - match tab parameter in query string + 5. `([^&#]+)` - capture tab value, stopping at & (next param) or # (hash fragment) + 6. `/i` - case insensitive + */ + var match = url.match(/.*:\/\/docs\.google\.com\/document\/.*[?&]tab=([^&#]+)/i); + if (match) { + return match[1]; + } + return null; +} + var writing_lasthash = ""; function unique_id() { /* diff --git a/modules/writing_observer/VERSION b/modules/writing_observer/VERSION index 32e666dbe..0d5696b3a 100644 --- a/modules/writing_observer/VERSION +++ b/modules/writing_observer/VERSION @@ -1 +1 @@ -0.1.0+2026.01.13T18.33.25.519Z.0984e08f.berickson.20260113.abstract.time.on.task.reducers +0.1.0+2026.02.02T20.54.51.168Z.c0f2a8f0.berickson.20260113.extension.tab.ids diff --git a/modules/writing_observer/writing_observer/module.py b/modules/writing_observer/writing_observer/module.py index 009262cef..b23df8323 100644 --- a/modules/writing_observer/writing_observer/module.py +++ b/modules/writing_observer/writing_observer/module.py @@ -261,7 +261,13 @@ { 'context': "org.mitros.writing_analytics", 'scope': writing_observer.writing_analysis.gdoc_scope, - 'function': writing_observer.writing_analysis.time_on_task, + 'function': writing_observer.writing_analysis.gdoc_scope_time_on_task, + 'default': {'saved_ts': 0} + }, + { + 'context': "org.mitros.writing_analytics", + 'scope': writing_observer.writing_analysis.gdoc_tab_scope, + 'function': writing_observer.writing_analysis.gdoc_tab_scope_time_on_task, 'default': {'saved_ts': 0} }, { @@ -286,6 +292,12 @@ 'function': writing_observer.writing_analysis.document_list, 'default': {'docs': []} }, + { + 'context': "org.mitros.writing_analytics", + 'scope': writing_observer.writing_analysis.gdoc_scope, + 'function': writing_observer.writing_analysis.tab_list, + 'default': {'tabs': {}} + }, { 'context': "org.mitros.writing_analytics", 'scope': writing_observer.writing_analysis.student_scope, diff --git a/modules/writing_observer/writing_observer/reconstruct_doc.py b/modules/writing_observer/writing_observer/reconstruct_doc.py index d060c083d..39cb57328 100644 --- a/modules/writing_observer/writing_observer/reconstruct_doc.py +++ b/modules/writing_observer/writing_observer/reconstruct_doc.py @@ -40,6 +40,7 @@ def __new__(cls): new_object._text = "" new_object._position = 0 new_object._edit_metadata = {} + new_object._tabs = {} new_object.fix_validity() return new_object @@ -100,6 +101,14 @@ def from_json(json_rep): new_object._text = json_rep.get('text', '') new_object._position = json_rep.get('position', 0) new_object._edit_metadata = json_rep.get('edit_metadata', {}) + + if 'tabs' in json_rep and json_rep['tabs']: + new_object._tabs = {} + for tab_id, tab_data in json_rep['tabs'].items(): + new_object._tabs[tab_id] = google_text.from_json(tab_data) + else: + new_object._tabs = {} + new_object.fix_validity() return new_object @@ -155,11 +164,14 @@ def json(self): ''' This serializes to JSON. ''' - return { + result = { 'text': self._text, 'position': self._position, 'edit_metadata': self._edit_metadata } + if self._tabs: + result['tabs'] = {tab_id: tab.json for tab_id, tab in self._tabs.items()} + return result def get_parsed_text(self): @@ -169,6 +181,15 @@ def get_parsed_text(self): return self._text.replace(PLACEHOLDER, "") +def dispatch_command(doc, cmd): + if cmd['ty'] in dispatch: + doc = dispatch[cmd['ty']](doc, **cmd) + else: + print("Unrecogized Google Docs command: " + repr(cmd['ty'])) + # TODO: Log issue and fix it! + return doc + + def command_list(doc, commands): ''' This will process a list of commands. It is helpful either when @@ -176,11 +197,7 @@ def command_list(doc, commands): new `save` requests. ''' for item in commands: - if item['ty'] in dispatch: - doc = dispatch[item['ty']](doc, **item) - else: - print("Unrecogized Google Docs command: " + repr(item['ty'])) - # TODO: Log issue and fix it! + doc = dispatch_command(doc, item) return doc @@ -301,6 +318,34 @@ def null(doc, **kwargs): return doc +def nm(doc, nmc, nmr, **kwargs): + ''' + Handle named commands for tabs (sub-documents). + + * `nmc` is the command to execute + * `nmr` is the name/reference list, which contains the target tab ID + ''' + # Find the target tab from the nmr list + target_tab = None + for item in reversed(nmr or []): + if isinstance(item, str) and item.startswith("t."): + target_tab = item + break + + if target_tab is None: + # No tab specified, apply to main document + doc = dispatch_command(doc, nmc) + else: + # Ensure the tab exists + if target_tab not in doc._tabs: + doc._tabs[target_tab] = google_text() + + # Apply the command to the sub-document + doc._tabs[target_tab] = dispatch_command(doc._tabs[target_tab], nmc) + + return doc + + # This dictionary maps the `ty` parameter to the function which # handles data of that type. @@ -312,6 +357,7 @@ def null(doc, **kwargs): # these can't be handled like plain 'is' or 'ds' because the include different fields # (e.g., 'sugid', presumably, suggestion id.) dispatch = { + 'ac': null, # new tab title 'ae': null, 'ase': null, # suggestion 'ast': null, # suggestion. Image? @@ -326,8 +372,10 @@ def null(doc, **kwargs): 'is': insert, 'iss': null, # suggested insertion 'mefd': null, # suggestion + 'mkch': null, # name of the first tab 'mlti': multi, 'msfd': null, # suggestion + 'nm': nm, # named command for tabs 'null': null, 'ord': null, 'ras': null, # suggestion. Autospell? @@ -344,6 +392,7 @@ def null(doc, **kwargs): 'sl': null, 'ste': null, # suggestion 'sue': null, # suggestion + 'ucp': null, # updated tab title 'uefd': null, # suggestion 'use': null, # suggestion 'umv': null, diff --git a/modules/writing_observer/writing_observer/writing_analysis.py b/modules/writing_observer/writing_observer/writing_analysis.py index 4d1a836e2..85dc87425 100644 --- a/modules/writing_observer/writing_observer/writing_analysis.py +++ b/modules/writing_observer/writing_observer/writing_analysis.py @@ -64,6 +64,8 @@ else: gdoc_scope = student_scope # HACK for backwards-compatibility +gdoc_tab_scope = Scope([KeyField.STUDENT, EventField('doc_id'), EventField('tab_id')]) + @learning_observer.communication_protocol.integration.publish_function('writing_observer.activity_map') def determine_activity_status(last_ts): @@ -71,7 +73,6 @@ def determine_activity_status(last_ts): return {'status': status} -@kvs_pipeline(scope=gdoc_scope) async def time_on_task(event, internal_state): ''' This adds up time intervals between successive timestamps. If the interval @@ -87,6 +88,10 @@ async def time_on_task(event, internal_state): return internal_state, internal_state +gdoc_scope_time_on_task = kvs_pipeline(scope=gdoc_scope)(time_on_task) +gdoc_tab_scope_time_on_task = kvs_pipeline(scope=gdoc_tab_scope)(time_on_task) + + @kvs_pipeline(scope=gdoc_scope) async def binned_time_on_task(event, internal_state): ''' @@ -262,6 +267,159 @@ async def document_list(event, internal_state): return False, False +def _iter_commands_from_client(client): + """Yield command dicts from either bundles (google_docs_save) or history (document_history).""" + event_type = client.get("event") + + if event_type == "google_docs_save": + for bundle in client.get("bundles") or []: + for command in bundle.get("commands") or []: + if isinstance(command, dict): + yield command + + elif event_type == "document_history": + history = client.get("history") or {} + changelog = history.get("changelog") or [] + # Each changelog item is expected to be like: [, ...] + for item in changelog: + if isinstance(item, (list, tuple)) and item and isinstance(item[0], dict): + yield item[0] + + +def _iter_leaf_commands(client): + for cmd in _iter_commands_from_client(client): + if not isinstance(cmd, dict): + continue + + if cmd.get("ty") == "mlti": + for sub in cmd.get("mts") or []: + if isinstance(sub, dict): + yield sub + else: + yield cmd + + +def _get_event_time(event, client): + """Resolve the timestamp once per event, with fallback.""" + server_time = (event.get("server") or {}).get("time") + if server_time is not None: + return server_time + return client.get("timestamp") or (client.get("metadata") or {}).get("ts") + + +def extract_from_ucp(command): + if command.get("ty") != "ucp": + return None, None + d = command.get("d") + try: + return d[0], d[1][1][1] + except (TypeError, IndexError, KeyError): + return None, None + + +def extract_from_mkch(command): + if command.get("ty") != "mkch": + return None, None + + d = command.get("d") + try: + return 't.0', d[0][1] + except (TypeError, IndexError, KeyError, AttributeError): + return None, None + + +def extract_from_ac(command): + if command.get("ty") != "ac": + return None, None + + d = command.get("d") + try: + return d[0], d[1][1] + except (TypeError, IndexError, KeyError, AttributeError): + return None, None + + +TITLE_EXTRACTORS = { + "ucp": extract_from_ucp, + "mkch": extract_from_mkch, + "ac": extract_from_ac, +} + + +def _extract_all_tab_titles(client): + """ + Extract all (tab_id, title) pairs from leaf commands (including those inside mlti). + """ + event_type = client.get("event") + if event_type not in ("google_docs_save", "document_history"): + return [] + + out = [] + for cmd in _iter_leaf_commands(client): + ty = cmd.get("ty") + extractor = TITLE_EXTRACTORS.get(ty) + if not extractor: + continue + tab_id, title = extractor(cmd) + if tab_id is None: + continue + out.append((tab_id, title)) + return out + + +def _extract_tab_id(event): + client = event.get("client", {}) or {} + tab_id = client.get("tab_id") or event.get("tab_id") + if tab_id: + return tab_id + url = client.get("url") or client.get("object", {}).get("url") or event.get("url") + if not url: + return None + match = re.search(r"tab=([^&#]+)", url) + return match.group(1) if match else None + + +@kvs_pipeline(scope=gdoc_scope, null_state={"tabs": {}}) +async def tab_list(event, internal_state): + """ + Track per-document tab metadata (tab_id, title, last_accessed) per student. + + Rules: + - If client.tab_id exists AND is already in state: ONLY update last_accessed for that tab. + - Still add new tabs discovered in commands (and set last_accessed for those new tabs). + - For existing tabs discovered in commands: update title if present, but do NOT touch last_accessed + unless it's the active existing tab (handled first). + """ + internal_state = internal_state or {"tabs": {}} + tabs = internal_state.get("tabs") or {} + + client = event.get("client") or {} + server_time = _get_event_time(event, client) + + active_tab_id = _extract_tab_id(event) + + # 1) Only bump last_accessed for the active tab IF it already exists in state + if active_tab_id is not None and active_tab_id in tabs: + tabs[active_tab_id]["last_accessed"] = server_time + + # 2) Add/update titles for all extracted tabs + for tab_id, title in _extract_all_tab_titles(client): + if tab_id not in tabs: + # New tab: initialize and set last_accessed now + tabs[tab_id] = { + "tab_id": tab_id, + "title": title, + "last_accessed": server_time, + } + else: + # Existing tab: update title if we learned one; do not update last_accessed here + if title is not None: + tabs[tab_id]["title"] = title + + internal_state["tabs"] = tabs + return internal_state, internal_state + + @kvs_pipeline(scope=student_scope) async def last_document(event, internal_state): ''' From 5f75e49bf760ffba61585ee9841a990a7c3805f9 Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Tue, 3 Feb 2026 10:39:51 -0500 Subject: [PATCH 83/88] Added ability to scope to a single value in execution dag Full commit list: * added ability to set a single value instead of relying on a list of values * added singleton value so that we can apply it to all remaining scopes * updated single value * added stop check when all scopes are singleton --- VERSION | 2 +- docs/how-to/communication_protocol.md | 7 +- learning_observer/VERSION | 2 +- .../communication_protocol/executor.py | 143 +++++++++++++++--- learning_observer/learning_observer/util.py | 2 +- 5 files changed, 133 insertions(+), 23 deletions(-) diff --git a/VERSION b/VERSION index 0d5696b3a..82977005b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0+2026.02.02T20.54.51.168Z.c0f2a8f0.berickson.20260113.extension.tab.ids +0.1.0+2026.02.03T15.21.57.253Z.1310c89e.berickson.20260130.execution.dag.single.value diff --git a/docs/how-to/communication_protocol.md b/docs/how-to/communication_protocol.md index b42484a88..a0725329a 100644 --- a/docs/how-to/communication_protocol.md +++ b/docs/how-to/communication_protocol.md @@ -102,8 +102,9 @@ executor can build the right Redis keys. **Preferred: `scope_fields` (supports arbitrary scopes)** -Use `scope_fields` to supply each scope axis with a `values` iterable and a `path` -into each item. The scope field names should match the reducer scope: `student`, +Use `scope_fields` to supply each scope axis with either a `values` iterable or a +single value (applied across all items), plus an optional `path` into each item. +The scope field names should match the reducer scope: `student`, `doc_id`, `tab_id`, `page_id`, etc. ```python @@ -113,6 +114,8 @@ reducer_keys = query.keys( "student": {"values": query.variable("roster"), "path": "user_id"}, "doc_id": {"values": query.variable("documents"), "path": "doc_id"}, "tab_id": {"values": query.variable("tabs"), "path": "tab_id"}, + # or a single value + "student": "bobs_user_id" }, ) ``` diff --git a/learning_observer/VERSION b/learning_observer/VERSION index fcc4404a5..82977005b 100644 --- a/learning_observer/VERSION +++ b/learning_observer/VERSION @@ -1 +1 @@ -0.1.0+2026.02.02T16.19.01.249Z.2ae8ac1a.berickson.20260120.comm.protocol.targets +0.1.0+2026.02.03T15.21.57.253Z.1310c89e.berickson.20260130.execution.dag.single.value diff --git a/learning_observer/learning_observer/communication_protocol/executor.py b/learning_observer/learning_observer/communication_protocol/executor.py index 46411c9ad..ef59307da 100644 --- a/learning_observer/learning_observer/communication_protocol/executor.py +++ b/learning_observer/learning_observer/communication_protocol/executor.py @@ -602,18 +602,125 @@ def _scope_field_candidates(field): return [] +# ============================================================================== +# Scope Value Handling +# ============================================================================== +# +# Scope fields can be specified as: +# 1. A single value (string, None, etc.) - broadcast across all items +# 2. An iterable of values - one per scope entry +# 3. An async iterable - same as above, but async +# +# SingleValue wraps case 1 to distinguish it from iterables. + + +class SingleValue: + """Wrapper indicating a single value to broadcast across all scope items. + + When building keys across multiple scope dimensions, single values are + repeated infinitely so they can be zipped with finite iterables from + other dimensions. + + Example: student="bob_id" with documents=[doc1, doc2, doc3] produces + keys for (bob_id, doc1), (bob_id, doc2), (bob_id, doc3). + """ + __slots__ = ('value',) + + def __init__(self, value): + self.value = value + + def __repr__(self): + return f'SingleValue({self.value!r})' + + +def _is_single_value(value): + """Check if value is a SingleValue wrapper.""" + return isinstance(value, SingleValue) + + +def _normalize_scope_value(value): + """Normalize a scope field value for consistent handling. + + - None, strings, and other scalars become SingleValue (broadcast) + - Lists and iterables pass through (one item per scope entry) + - Async iterables pass through unchanged + - Dicts pass through (will be iterated or accessed via path) + """ + if value is None: + return SingleValue(None) + if _is_single_value(value): + return value + if isinstance(value, collections.abc.AsyncIterable): + return value + if isinstance(value, (str, bytes)): + return SingleValue(value) + if isinstance(value, dict): + # Dicts represent structured data, not a collection to iterate + return value + if isinstance(value, collections.abc.Iterable): + return value + return SingleValue(value) + + +async def _repeat_forever(value): + """Async generator that yields the same value indefinitely.""" + while True: + yield value + + +def _expand_scope_value(value, *, broadcast=False): + """Convert a normalized scope value to an iterable. + + Args: + value: A normalized scope value (SingleValue or iterable). + broadcast: If True, single values repeat infinitely for zipping + with other dimensions. If False, yield once. + + Returns: + An iterable (sync or async) suitable for iteration. + """ + if _is_single_value(value): + return _repeat_forever(value.value) if broadcast else [value.value] + return value + + +def _parse_scope_spec(spec): + """Parse a scope specification into (values, path). + + Supports formats: + roster -> (roster, None) + {"values": roster, "path": "user_id"} -> (roster, "user_id") + {"value": roster} -> (roster, None) + + Returns: + Tuple of (values, path) where path may be None. + """ + if not isinstance(spec, dict): + return spec, None + + # Check for known value keys in priority order + for key in ('values', 'value', 'items', 'data'): + if key in spec: + values = spec[key] + path = spec.get('path') or spec.get('value_path') + return values, path + + # No recognized keys - treat entire dict as the value + return spec, None + + def _normalize_scope_field_specs(raw_specs): + """Normalize scope field specifications to a consistent format. + + Returns: + Dict mapping normalized field names to {"values": ..., "path": ...} + """ normalized = {} for key, spec in raw_specs.items(): normalized_key = _normalize_scope_field_key(key) - if isinstance(spec, dict): - values = spec.get('values', spec.get('value', spec.get('items', spec.get('data')))) - path = spec.get('path', spec.get('value_path')) - else: - values = spec - path = None + values, path = _parse_scope_spec(spec) normalized[normalized_key] = { - 'values': values, + 'values': _normalize_scope_value(values), 'path': path } return normalized @@ -640,18 +747,14 @@ async def _async_zip_many(iterables): async def _extract_fields_with_provenance(scope_specs): - '''Prepare the key field dictionary and provenance for each scope tuple. - The key field dictionary is used to create the key we are attempting - to fetch from the KVS (used later in `hack_handle_keys`). The passed in - `path` is used for setting the appropriate dictionary value. - The provenance is the current history of the communication protocol for each item. - ''' + """Prepare the key field dictionary and provenance for each scope tuple.""" if not scope_specs: return if len(scope_specs) == 1: + # Single dimension: simple iteration field, values, path = scope_specs[0] - async for item in ensure_async_generator(values): + async for item in ensure_async_generator(_expand_scope_value(values, broadcast=False)): field_value = get_nested_dict_value(item, path or '', '') fields = {field: field_value} item_provenance = item.get('provenance', {'value': item}) if isinstance(item, dict) else {'value': item} @@ -661,7 +764,11 @@ async def _extract_fields_with_provenance(scope_specs): yield fields, provenance return - iterables = [values for _, values, _ in scope_specs] + # Multiple dimensions: zip with broadcasting for single values + # Avoid infinite iteration when all dimensions are single values. + broadcast = not all(_is_single_value(values) for _, values, _ in scope_specs) + iterables = [_expand_scope_value(values, broadcast=broadcast) for _, values, _ in scope_specs] + async for items in _async_zip_many(iterables): fields = {} provenance = {} @@ -697,18 +804,18 @@ def _resolve_scope_specs(scope, kwargs): if path_key in kwargs: scope_specs.setdefault( _normalize_scope_field_key(key), - {'values': value, 'path': kwargs[path_key]} + {'values': _normalize_scope_value(value), 'path': kwargs[path_key]} ) if 'STUDENTS' in kwargs: scope_specs.setdefault( 'student', - {'values': kwargs['STUDENTS'], 'path': kwargs.get('STUDENTS_path')} + {'values': _normalize_scope_value(kwargs['STUDENTS']), 'path': kwargs.get('STUDENTS_path')} ) if 'RESOURCES' in kwargs: scope_specs.setdefault( 'doc_id', - {'values': kwargs['RESOURCES'], 'path': kwargs.get('RESOURCES_path')} + {'values': _normalize_scope_value(kwargs['RESOURCES']), 'path': kwargs.get('RESOURCES_path')} ) unexpected_scope_keys = set(scope_specs.keys()) - allowed_scope_keys diff --git a/learning_observer/learning_observer/util.py b/learning_observer/learning_observer/util.py index 525d2c9a8..c3b085129 100644 --- a/learning_observer/learning_observer/util.py +++ b/learning_observer/learning_observer/util.py @@ -146,7 +146,7 @@ def get_nested_dict_value(d, key_str=None, default=MissingType.Missing): key_str = '' keys = key_str.split('.') for key in keys: - if d is not None and key in d: + if isinstance(d, dict) and key in d: d = d[key] elif key == '': d = d From ed896690d65d6ef0af64f5dde6719d8502fdc327 Mon Sep 17 00:00:00 2001 From: Omar Al-Ithawi Date: Wed, 18 Feb 2026 23:43:46 +0300 Subject: [PATCH 84/88] fix: arglab package new URL (#269) on behalf of https://zeitlabs.com --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index d052bba15..ad7eb08ce 100644 --- a/requirements.txt +++ b/requirements.txt @@ -37,4 +37,4 @@ svgwrite uvloop watchdog -gitserve @ git+https://github.com/ETS-Next-Gen/writing_observer.git#subdirectory=gitserve +gitserve @ git+https://github.com/ArgLab/writing_observer.git#subdirectory=gitserve From 13cb4952a7cc35f2f3bdb0cda8480b1b66edb6d6 Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Thu, 19 Feb 2026 09:14:24 -0500 Subject: [PATCH 85/88] updated links to repo and rawgit to jsdeliver (#268) --- README.md | 2 +- VERSION | 2 +- devops/tasks/remote_scripts/gitpaths.py | 2 +- devops/tasks/scripts/baseline_packages.fab | 2 +- docs/tutorials/install.md | 4 ++-- docs/tutorials/workshop.md | 12 ++++++------ extension/writing-process/package.json | 2 +- extension/writing-process/src/manifest.json | 2 +- learning_observer/VERSION | 2 +- .../communication_protocol/README.md | 2 +- learning_observer/learning_observer/downloads.py | 4 ++-- learning_observer/learning_observer/module.py | 6 +++--- learning_observer/learning_observer/routes.py | 2 +- .../learning_observer/static/debug.html | 2 +- .../learning_observer/static/modules/login.html | 2 +- .../learning_observer/static/webapp.html | 2 +- modules/lo_event/package.json | 6 +++--- modules/toy-assess/README.md | 2 +- modules/wo_bulk_essay_analysis/VERSION | 2 +- modules/wo_bulk_essay_analysis/setup.cfg | 2 +- modules/wo_common_student_errors/setup.cfg | 2 +- modules/wo_document_list/setup.cfg | 2 +- modules/wo_highlight_dashboard/setup.cfg | 2 +- modules/writing_observer/VERSION | 2 +- modules/writing_observer/writing_observer/module.py | 2 +- package.json | 6 +++--- scripts/package.json | 6 +++--- ux/ux.html | 2 +- wo_requirements.txt | 8 ++++---- 29 files changed, 47 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index 8c56ebba8..b555f2276 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ learning analytics dashboard to help instructors be able to manage student learning processes, and in particular, student writing processes. -![linting](https://github.com/ETS-Next-Gen/writing_observer/actions/workflows/pycodestyle.yml/badge.svg) +![linting](https://github.com/ArgLab/writing_observer/actions/workflows/pycodestyle.yml/badge.svg) ## Learning Observer diff --git a/VERSION b/VERSION index 82977005b..2e1b98f63 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0+2026.02.03T15.21.57.253Z.1310c89e.berickson.20260130.execution.dag.single.value +0.1.0+2026.02.17T15.09.06.345Z.5f75e49b.berickson.20260217.updated.dead.links diff --git a/devops/tasks/remote_scripts/gitpaths.py b/devops/tasks/remote_scripts/gitpaths.py index 80801ca85..db0db4d52 100644 --- a/devops/tasks/remote_scripts/gitpaths.py +++ b/devops/tasks/remote_scripts/gitpaths.py @@ -10,7 +10,7 @@ def gitpath_to_name(packagepath): ''' Convert a git path to the name of the repo. For example: - `https://github.com/ETS-Next-Gen/writing_observer.git` ==> `writing_observer` + `https://github.com/ArgLab/writing_observer.git` ==> `writing_observer` ''' package = os.path.split(packagepath)[1] if package.endswith(".git"): diff --git a/devops/tasks/scripts/baseline_packages.fab b/devops/tasks/scripts/baseline_packages.fab index fbd88ece4..a4b19fe9c 100644 --- a/devops/tasks/scripts/baseline_packages.fab +++ b/devops/tasks/scripts/baseline_packages.fab @@ -1,4 +1,4 @@ cd sudo apt-get -y install git ansible awscli -git clone https://github.com/ETS-Next-Gen/writing_observer.git +git clone https://github.com/ArgLab/writing_observer.git cd writing_observer/devops/ansible ; sudo ansible-playbook local.yaml diff --git a/docs/tutorials/install.md b/docs/tutorials/install.md index 412765c04..9a5c177ce 100644 --- a/docs/tutorials/install.md +++ b/docs/tutorials/install.md @@ -22,13 +22,13 @@ Have two terminal windows or tabs open. We will run the server in one and use th 1. Open a terminal and clone the repository: ```bash - git clone https://github.com/ETS-Next-Gen/writing_observer.git lo_tutorial + git clone https://github.com/ArgLab/writing_observer.git lo_tutorial ``` If you have an SSH key configured with GitHub, you can use: ```bash - git clone git@github.com:ETS-Next-Gen/writing_observer.git lo_tutorial + git clone git@github.com:ArgLab/writing_observer.git lo_tutorial ``` 2. Change into the new directory: diff --git a/docs/tutorials/workshop.md b/docs/tutorials/workshop.md index b10d595f7..6fa12feed 100644 --- a/docs/tutorials/workshop.md +++ b/docs/tutorials/workshop.md @@ -48,13 +48,13 @@ If you don't have a way of managing Python virtual environments, or would prefer First make sure you clone the repository: ```bash -git clone https://github.com/ETS-Next-Gen/writing_observer.git lo_workshop +git clone https://github.com/ArgLab/writing_observer.git lo_workshop ``` **or**, if you have a github account properly configured with ssh: ```bash -git clone git@github.com:ETS-Next-Gen/writing_observer.git lo_workshop +git clone git@github.com:ArgLab/writing_observer.git lo_workshop ``` ```bash @@ -173,7 +173,7 @@ This will generate events for 10 students typing a set of loremipsum texts and s You can look at the format of these specific events in the `/learning_observer/learning_observer/logs/` directory. In the test system, we simply put events into log files, but we are gradually moving towards a more sophisticated, open-science, family-rights oriented data store (shown at the bottom of [this document](system_design.md). This is theoretically interesting, since it gives a cryptographically-verifiable way to audit what data was created and what analyses ran. -There are several good standards for [event formats](events.md), and to integrate learning data, we will need to support them all. Most of these have converged on a line of JSON per event, but the specifics are format-specific. We are including [some code](https://github.com/ETS-Next-Gen/writing_observer/blob/master/modules/lo_event/lo_event/xapi.cjs) to help support the major ones. However, the events here are somewhat bound to how Google Docs thinks about documents. +There are several good standards for [event formats](events.md), and to integrate learning data, we will need to support them all. Most of these have converged on a line of JSON per event, but the specifics are format-specific. We are including [some code](https://github.com/ArgLab/writing_observer/blob/master/modules/lo_event/lo_event/xapi.cjs) to help support the major ones. However, the events here are somewhat bound to how Google Docs thinks about documents. ## module.py @@ -251,7 +251,7 @@ In `module.py`, you see this line: EXECUTION_DAG = learning_observer.communication_protocol.util.generate_base_dag_for_student_reducer('student_event_counter', 'my_event_module') ``` -This is shorthand for a common query which JOINs the class roster with the output of the reducers. The Python code for the query itself is [here](https://github.com/ETS-Next-Gen/writing_observer/blob/berickson/workshop/learning_observer/learning_observer/communication_protocol/util.py#L58), but the jist of the code is: +This is shorthand for a common query which JOINs the class roster with the output of the reducers. The Python code for the query itself is [here](https://github.com/ArgLab/writing_observer/blob/berickson/workshop/learning_observer/learning_observer/communication_protocol/util.py#L58), but the jist of the code is: ```python 'roster': course_roster(runtime=q.parameter('runtime'), course_id=q.parameter("course_id", required=True)), @@ -326,11 +326,11 @@ Once set up, the development workflow here is rather fast, since the UX updates ### `lo_event` -Our data streaming library is [lo_event](https://github.com/ETS-Next-Gen/writing_observer/tree/master/modules/lo_event). This library is designed to stream events (typically) from a JavaScript client, and handles all of the complexity of things like persistance, queuing, and retries for you. Cookiecutter cde, but it should save you a bunch of time. +Our data streaming library is [lo_event](https://github.com/ArgLab/writing_observer/tree/master/modules/lo_event). This library is designed to stream events (typically) from a JavaScript client, and handles all of the complexity of things like persistance, queuing, and retries for you. Cookiecutter cde, but it should save you a bunch of time. ### `lo_assess` -Much more interesting, in development (and probably in need of renaming) is [`lo_assess`](https://github.com/ETS-Next-Gen/writing_observer/tree/pmitros/loevent-v2/modules/lo_event/lo_event/lo_assess). +Much more interesting, in development (and probably in need of renaming) is [`lo_assess`](https://github.com/ArgLab/writing_observer/tree/pmitros/loevent-v2/modules/lo_event/lo_event/lo_assess). There is an XML format (based on edX OLX, which is in turn based on LON-CAPA XML) for creating interactives. diff --git a/extension/writing-process/package.json b/extension/writing-process/package.json index b789a11c1..d1bb950f3 100644 --- a/extension/writing-process/package.json +++ b/extension/writing-process/package.json @@ -6,7 +6,7 @@ "author": "Piotr Mitros, Bradley Erickson", "repository": { "type": "git", - "url": "https://github.com/ETS-Next-Gen/writing_observer/tree/master/extension/writing-process" + "url": "https://github.com/ArgLab/writing_observer/tree/master/extension/writing-process" }, "scripts": { "ext:start": "xt-build -e dev -w", diff --git a/extension/writing-process/src/manifest.json b/extension/writing-process/src/manifest.json index ad7c064a0..945ec7709 100644 --- a/extension/writing-process/src/manifest.json +++ b/extension/writing-process/src/manifest.json @@ -2,7 +2,7 @@ "name": "__MSG_appName__", "short_name": "__MSG_appShortName__", "description": "__MSG_appDescription__", - "homepage_url": "https://github.com/ETS-Next-Gen/writing_observer/tree/master/extension/writing-process", + "homepage_url": "https://github.com/ArgLab/writing_observer/tree/master/extension/writing-process", "incognito": "not_allowed", "version": "1.0.0.2", "version_name": "1.0.0.2", diff --git a/learning_observer/VERSION b/learning_observer/VERSION index 82977005b..2e1b98f63 100644 --- a/learning_observer/VERSION +++ b/learning_observer/VERSION @@ -1 +1 @@ -0.1.0+2026.02.03T15.21.57.253Z.1310c89e.berickson.20260130.execution.dag.single.value +0.1.0+2026.02.17T15.09.06.345Z.5f75e49b.berickson.20260217.updated.dead.links diff --git a/learning_observer/learning_observer/communication_protocol/README.md b/learning_observer/learning_observer/communication_protocol/README.md index 86b3f7987..c86b24cec 100644 --- a/learning_observer/learning_observer/communication_protocol/README.md +++ b/learning_observer/learning_observer/communication_protocol/README.md @@ -25,7 +25,7 @@ In `module.py`, you see this line: EXECUTION_DAG = learning_observer.communication_protocol.util.generate_base_dag_for_student_reducer('student_event_counter', 'my_event_module') ``` -This is shorthand for a common query which JOINs the class roster with the output of the reducers. The Python code for the query itself is [here](https://github.com/ETS-Next-Gen/writing_observer/blob/berickson/workshop/learning_observer/learning_observer/communication_protocol/util.py#L58), but the jist of the code is: +This is shorthand for a common query which JOINs the class roster with the output of the reducers. The Python code for the query itself is [here](https://github.com/ArgLab/writing_observer/blob/berickson/workshop/learning_observer/learning_observer/communication_protocol/util.py#L58), but the jist of the code is: ```python 'roster': course_roster(runtime=q.parameter('runtime'), course_id=q.parameter("course_id", required=True)), diff --git a/learning_observer/learning_observer/downloads.py b/learning_observer/learning_observer/downloads.py index abea04761..f0c2c3a18 100644 --- a/learning_observer/learning_observer/downloads.py +++ b/learning_observer/learning_observer/downloads.py @@ -52,12 +52,12 @@ "62618d4bff6e51fafa25d41cb59bd97f3ebd72fd94ebd09a52c17c4c23fdca3962b" } SHOWDOWN_JS = { - "url": "https://rawgit.com/showdownjs/showdown/1.9.1/dist/showdown.js", + "url": "https://cdn.jsdelivr.net/gh/showdownjs/showdown@1.9.1/dist/showdown.js", "hash": "4fe14f17c2a1d0275d44e06d7e68d2b177779196c6d0c562d082eb5435eec" "4e710a625be524767aef3d9a1f6a5b88f912ddd71821f4a9df12ff7dd66d6fbb3c9" } SHOWDOWN_JS_MAP = { - "url": "https://rawgit.com/showdownjs/showdown/1.9.1/dist/showdown.js.map", + "url": "https://cdn.jsdelivr.net/gh/showdownjs/showdown@1.9.1/dist/showdown.js.map", "hash": "74690aa3cea07fd075942ba9e98cf7297752994b93930acb3a1baa2d3042a" "62b5523d3da83177f63e6c02fe2a09c8414af9e1774dad892a303e15a86dbeb29ba" } diff --git a/learning_observer/learning_observer/module.py b/learning_observer/learning_observer/module.py index e40fe77e3..ddfa5a93b 100644 --- a/learning_observer/learning_observer/module.py +++ b/learning_observer/learning_observer/module.py @@ -79,12 +79,12 @@ "62618d4bff6e51fafa25d41cb59bd97f3ebd72fd94ebd09a52c17c4c23fdca3962b" }, "showdown.js": { # Default markup library - "url": "https://rawgit.com/showdownjs/showdown/1.9.1/dist/showdown.js", + "url": "https://cdn.jsdelivr.net/gh/showdownjs/showdown@1.9.1/dist/showdown.js", "hash": "4fe14f17c2a1d0275d44e06d7e68d2b177779196c6d0c562d082eb5435eec" "4e710a625be524767aef3d9a1f6a5b88f912ddd71821f4a9df12ff7dd66d6fbb3c9" }, "showdown.js.map": { # Part of above - "url": "https://rawgit.com/showdownjs/showdown/1.9.1/dist/showdown.js.map", + "url": "https://cdn.jsdelivr.net/gh/showdownjs/showdown@1.9.1/dist/showdown.js.map", "hash": "74690aa3cea07fd075942ba9e98cf7297752994b93930acb3a1baa2d3042a" "62b5523d3da83177f63e6c02fe2a09c8414af9e1774dad892a303e15a86dbeb29ba" }, @@ -115,7 +115,7 @@ STATIC_FILE_GIT_REPOS = { # 'writing_observer': { # # Where we can grab a copy of the repo, if not already on the system - # 'url': 'https://github.com/ETS-Next-Gen/writing_observer.git', + # 'url': 'https://github.com/ArgLab/writing_observer.git', # # Where the static files in the repo lie # 'prefix': 'learning_observer/learning_observer/static', # # Branches we serve. This can either be a whitelist (e.g. which ones diff --git a/learning_observer/learning_observer/routes.py b/learning_observer/learning_observer/routes.py index fbe4e1a7a..5fc12b7b0 100644 --- a/learning_observer/learning_observer/routes.py +++ b/learning_observer/learning_observer/routes.py @@ -379,7 +379,7 @@ def register_repo_routes(app, repos): An example repo is: { - 'url': 'https://github.com/ETS-Next-Gen/writing_observer.git', // URL to the repo; downloaded if not already here + 'url': 'https://github.com/ArgLab/writing_observer.git', // URL to the repo; downloaded if not already here 'prefix': 'modules/writing_observer/writing_observer/static', // Path in repo to serve static files from 'module': 'wobserver', // Module name to use in the static path diff --git a/learning_observer/learning_observer/static/debug.html b/learning_observer/learning_observer/static/debug.html index c4cc6cdba..e647a8273 100644 --- a/learning_observer/learning_observer/static/debug.html +++ b/learning_observer/learning_observer/static/debug.html @@ -51,7 +51,7 @@ Service. The
source code is available under the - AGPLv3 license on github. + AGPLv3 license on github.

diff --git a/learning_observer/learning_observer/static/modules/login.html b/learning_observer/learning_observer/static/modules/login.html index 271e391de..d816638d7 100644 --- a/learning_observer/learning_observer/static/modules/login.html +++ b/learning_observer/learning_observer/static/modules/login.html @@ -56,7 +56,7 @@

{{ server_name }}

Contribute - on github

+ on github

diff --git a/learning_observer/learning_observer/static/webapp.html b/learning_observer/learning_observer/static/webapp.html index 9effc9380..9c27b1ffa 100644 --- a/learning_observer/learning_observer/static/webapp.html +++ b/learning_observer/learning_observer/static/webapp.html @@ -51,7 +51,7 @@ Service. The source code is available under the - AGPLv3 license on github. + AGPLv3 license on github.

diff --git a/modules/lo_event/package.json b/modules/lo_event/package.json index f5e85ee58..75b64066e 100644 --- a/modules/lo_event/package.json +++ b/modules/lo_event/package.json @@ -15,14 +15,14 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/ETS-Next-Gen/writing_observer.git" + "url": "git+https://github.com/ArgLab/writing_observer.git" }, "author": "Piotr Mitros", "license": "SEE LICENSE IN license.txt", "bugs": { - "url": "https://github.com/ETS-Next-Gen/writing_observer/issues" + "url": "https://github.com/ArgLab/writing_observer/issues" }, - "homepage": "https://github.com/ETS-Next-Gen/writing_observer#readme", + "homepage": "https://github.com/ArgLab/writing_observer#readme", "type": "module", "dependencies": { "aws-sdk": "^2.1614.0", diff --git a/modules/toy-assess/README.md b/modules/toy-assess/README.md index 1b05a7098..c161a3911 100644 --- a/modules/toy-assess/README.md +++ b/modules/toy-assess/README.md @@ -8,7 +8,7 @@ This may require also running npm audit fix You will also need to install writing_observer and package up lo_event to use inside the project. -Place to clone from: https://github.com/ETS-Next-Gen/writing_observer +Place to clone from: https://github.com/ArgLab/writing_observer cd to cloned directory, type npm install Then, inside writing_observer/modules/lo_event: diff --git a/modules/wo_bulk_essay_analysis/VERSION b/modules/wo_bulk_essay_analysis/VERSION index 696424a49..2e1b98f63 100644 --- a/modules/wo_bulk_essay_analysis/VERSION +++ b/modules/wo_bulk_essay_analysis/VERSION @@ -1 +1 @@ -0.1.0+2025.09.30T15.53.18.556Z.1c72d9e1.master +0.1.0+2026.02.17T15.09.06.345Z.5f75e49b.berickson.20260217.updated.dead.links diff --git a/modules/wo_bulk_essay_analysis/setup.cfg b/modules/wo_bulk_essay_analysis/setup.cfg index 9f5cfb10d..a1883dcdc 100644 --- a/modules/wo_bulk_essay_analysis/setup.cfg +++ b/modules/wo_bulk_essay_analysis/setup.cfg @@ -1,7 +1,7 @@ [metadata] name = WO Bulk Essay Analysis description = Dashboard for interfacing a classroom of essays with automated feedback -url = https://github.com/ETS-Next-Gen/writing_observer +url = https://github.com/ArgLab/writing_observer version = file:VERSION [options] diff --git a/modules/wo_common_student_errors/setup.cfg b/modules/wo_common_student_errors/setup.cfg index cec31a2ff..0f7c59fe2 100644 --- a/modules/wo_common_student_errors/setup.cfg +++ b/modules/wo_common_student_errors/setup.cfg @@ -1,7 +1,7 @@ [metadata] name = Writing Observer Common Student Errors description = Dashboard for viewing errors across a classroom of students. -url = https://github.com/ETS-Next-Gen/writing_observer +url = https://github.com/ArgLab/writing_observer version = 0.1 [options] diff --git a/modules/wo_document_list/setup.cfg b/modules/wo_document_list/setup.cfg index 01be4a27e..1d99323fc 100644 --- a/modules/wo_document_list/setup.cfg +++ b/modules/wo_document_list/setup.cfg @@ -1,7 +1,7 @@ [metadata] name = Writing Observer List of Documents description = Dashboard for viewing documents from various sources for each student. -url = https://github.com/ETS-Next-Gen/writing_observer +url = https://github.com/ArgLab/writing_observer version = 0.1 [options] diff --git a/modules/wo_highlight_dashboard/setup.cfg b/modules/wo_highlight_dashboard/setup.cfg index 617f176d5..af3d30f0d 100644 --- a/modules/wo_highlight_dashboard/setup.cfg +++ b/modules/wo_highlight_dashboard/setup.cfg @@ -1,7 +1,7 @@ [metadata] name = Dash Writing Observer Class Highlight Dashboard description = Dashboard using Dash for the Writing Observer -url = https://github.com/ETS-Next-Gen/writing_observer +url = https://github.com/ArgLab/writing_observer version = 0.1 [options] diff --git a/modules/writing_observer/VERSION b/modules/writing_observer/VERSION index 0d5696b3a..2e1b98f63 100644 --- a/modules/writing_observer/VERSION +++ b/modules/writing_observer/VERSION @@ -1 +1 @@ -0.1.0+2026.02.02T20.54.51.168Z.c0f2a8f0.berickson.20260113.extension.tab.ids +0.1.0+2026.02.17T15.09.06.345Z.5f75e49b.berickson.20260217.updated.dead.links diff --git a/modules/writing_observer/writing_observer/module.py b/modules/writing_observer/writing_observer/module.py index b23df8323..3b9154d9c 100644 --- a/modules/writing_observer/writing_observer/module.py +++ b/modules/writing_observer/writing_observer/module.py @@ -359,7 +359,7 @@ STATIC_FILE_GIT_REPOS = { 'writing_observer': { # Where we can grab a copy of the repo, if not already on the system - 'url': 'https://github.com/ETS-Next-Gen/writing_observer.git', + 'url': 'https://github.com/ArgLab/writing_observer.git', # Where the static files in the repo lie 'prefix': 'modules/writing_observer/writing_observer/static', # Branches we serve. This can either be a whitelist (e.g. which ones diff --git a/package.json b/package.json index 76f3136fb..a76186902 100644 --- a/package.json +++ b/package.json @@ -15,14 +15,14 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/ETS-Next-Gen/writing_observer.git" + "url": "git+https://github.com/ArgLab/writing_observer.git" }, "author": "", "license": "SEE LICENSE IN LICENSE.TXT", "bugs": { - "url": "https://github.com/ETS-Next-Gen/writing_observer/issues" + "url": "https://github.com/ArgLab/writing_observer/issues" }, - "homepage": "https://github.com/ETS-Next-Gen/writing_observer#readme", + "homepage": "https://github.com/ArgLab/writing_observer#readme", "devDependencies": { "@actions/core": "^1.10.0", "eslint": "^8.38.0", diff --git a/scripts/package.json b/scripts/package.json index ca6df8c8e..7a25eaade 100644 --- a/scripts/package.json +++ b/scripts/package.json @@ -8,14 +8,14 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/ETS-Next-Gen/writing_observer.git" + "url": "git+https://github.com/ArgLab/writing_observer.git" }, "author": "Piotr Mitros", "license": "AGPL-3.0", "bugs": { - "url": "https://github.com/ETS-Next-Gen/writing_observer/issues" + "url": "https://github.com/ArgLab/writing_observer/issues" }, - "homepage": "https://github.com/ETS-Next-Gen/writing_observer#readme", + "homepage": "https://github.com/ArgLab/writing_observer#readme", "dependencies": { "ws": "^8.17.1" } diff --git a/ux/ux.html b/ux/ux.html index 2ff54a621..d66bdd19e 100644 --- a/ux/ux.html +++ b/ux/ux.html @@ -13,7 +13,7 @@ - + diff --git a/wo_requirements.txt b/wo_requirements.txt index 5d42da251..6741b1cdd 100644 --- a/wo_requirements.txt +++ b/wo_requirements.txt @@ -1,4 +1,4 @@ -writing_observer @ git+https://github.com/ETS-Next-Gen/writing_observer.git#subdirectory=modules/writing_observer/ -lo_gpt @ git+https://github.com/ETS-Next-Gen/writing_observer.git#subdirectory=modules/lo_gpt/ -wo_bulk_essay_analysis @ git+https://github.com/ETS-Next-Gen/writing_observer.git#subdirectory=modules/wo_bulk_essay_analysis/ -wo_classroom_text_highlighter @ git+https://github.com/ETS-Next-Gen/writing_observer.git#subdirectory=modules/wo_classroom_text_highlighter/ +writing_observer @ git+https://github.com/ArgLab/writing_observer.git#subdirectory=modules/writing_observer/ +lo_gpt @ git+https://github.com/ArgLab/writing_observer.git#subdirectory=modules/lo_gpt/ +wo_bulk_essay_analysis @ git+https://github.com/ArgLab/writing_observer.git#subdirectory=modules/wo_bulk_essay_analysis/ +wo_classroom_text_highlighter @ git+https://github.com/ArgLab/writing_observer.git#subdirectory=modules/wo_classroom_text_highlighter/ From c20cfe161cdee286f12edccd1e28b11e3ef3be03 Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Sun, 22 Feb 2026 09:00:41 -0500 Subject: [PATCH 86/88] dashboard updates (#267) * added modal for options * added modal for individual student * added help walkthrough * updated google assignments to generic endpoint, started schoology assignment integration, and assignment dashboard piece * updated expanded student info and added document to student cards * brought llm feedback dashboard up to speed with other one * fixed auth headers for lti calls * fixed roster check for appropriate schoology keyword * added text in title option to document source aio * cleaned up schoology items * made it so when assignments are not available, we don't show them * fixed dag delay * fixed commented out code * pr feedback --- VERSION | 2 +- learning_observer/VERSION | 2 +- .../learning_observer/integrations/google.py | 10 +- .../integrations/schoology.py | 101 ++- .../learning_observer/integrations/util.py | 17 +- .../learning_observer/rosters.py | 43 +- learning_observer/learning_observer/routes.py | 6 + .../LODocumentSourceSelectorAIO.py | 101 ++- .../lib/components/WOStudentTextTile.react.js | 50 +- modules/wo_bulk_essay_analysis/VERSION | 2 +- .../wo_bulk_essay_analysis/assets/scripts.js | 777 ++++++++++++------ .../dashboard/layout.py | 617 ++++++++++---- modules/wo_classroom_text_highlighter/VERSION | 2 +- .../assets/general.css | 9 +- .../assets/scripts.js | 521 ++++++++++-- .../dash_dashboard.py | 447 +++++++--- modules/writing_observer/VERSION | 2 +- .../writing_observer/aggregator.py | 11 +- .../writing_observer/document_timestamps.py | 38 + .../writing_observer/module.py | 8 +- 20 files changed, 2038 insertions(+), 728 deletions(-) diff --git a/VERSION b/VERSION index 2e1b98f63..fff79c93a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0+2026.02.17T15.09.06.345Z.5f75e49b.berickson.20260217.updated.dead.links +0.1.0+2026.02.20T19.02.56.547Z.85e344b1.berickson.20260210.dashboard.updates diff --git a/learning_observer/VERSION b/learning_observer/VERSION index 2e1b98f63..fff79c93a 100644 --- a/learning_observer/VERSION +++ b/learning_observer/VERSION @@ -1 +1 @@ -0.1.0+2026.02.17T15.09.06.345Z.5f75e49b.berickson.20260217.updated.dead.links +0.1.0+2026.02.20T19.02.56.547Z.85e344b1.berickson.20260210.dashboard.updates diff --git a/learning_observer/learning_observer/integrations/google.py b/learning_observer/learning_observer/integrations/google.py index 95e09ff82..4ef51a895 100644 --- a/learning_observer/learning_observer/integrations/google.py +++ b/learning_observer/learning_observer/integrations/google.py @@ -231,14 +231,16 @@ def clean_assignment_docs(google_json): Retrieve set of documents per student associated with an assignment ''' student_submissions = google_json.get('studentSubmissions', []) + cleaned_submissions = [] for student_json in student_submissions: google_id = student_json[constants.USER_ID] local_id = learning_observer.auth.google_id_to_user_id(google_id) - student_json[constants.USER_ID] = local_id docs = [d['driveFile'] for d in learning_observer.util.get_nested_dict_value(student_json, 'assignmentSubmission.attachments', []) if 'driveFile' in d] - student_json['documents'] = docs - # TODO we should probably remove some of the keys provided - return student_submissions + cleaned_submissions.append({ + constants.USER_ID: local_id, + 'documents': docs + }) + return cleaned_submissions if __name__ == '__main__': diff --git a/learning_observer/learning_observer/integrations/schoology.py b/learning_observer/learning_observer/integrations/schoology.py index 5ab47943e..6e1eb467a 100644 --- a/learning_observer/learning_observer/integrations/schoology.py +++ b/learning_observer/learning_observer/integrations/schoology.py @@ -1,23 +1,49 @@ +# learning_observer/integrations/schoology.py import learning_observer.constants as constants -import learning_observer.kvs import learning_observer.settings as settings from . import util API = 'schoology' +# All endpoints use LTI service URLs, not direct Schoology REST API. +# These are accessed using the LTI access token negotiated at launch. +# +# LTI AGS spec: https://www.imsglobal.org/spec/lti-ags/v2p0 +# LTI NRPS spec: https://www.imsglobal.org/spec/lti-nrps/v2p0 +LTI_SERVICE_BASE = 'https://lti-service.svc.schoology.com/lti-service/tool/{clientId}' + ENDPOINTS = list(map(lambda x: util.Endpoint(**x, api_name=API), [ - {'name': 'course_list', 'remote_url': 'https://lti-service.svc.schoology.com/lti-service/tool/{clientId}/services/names-roles/v2p0/membership/{courseId}', 'headers': {'Accept': 'application/vnd.ims.lti-nrps.v2.membershipcontainer+json'}}, - {'name': 'course_roster', 'remote_url': 'https://lti-service.svc.schoology.com/lti-service/tool/{clientId}/services/names-roles/v2p0/membership/{courseId}', 'headers': {'Accept': 'application/vnd.ims.lti-nrps.v2.membershipcontainer+json'}}, - {'name': 'course_assignments', 'remote_url': 'https://api.schoology.com/v1/sections/{courseId}/assignments', 'headers': {'Accept': 'application/vnd.ims.lis.v2.lineitemcontainer+json'}}, + { + 'name': 'course_list', + 'remote_url': f'{LTI_SERVICE_BASE}/services/names-roles/v2p0/membership/{{courseId}}', + 'headers': {'Accept': 'application/vnd.ims.lti-nrps.v2.membershipcontainer+json'} # required Schoology header for LTI membership requests + }, + { + 'name': 'course_roster', + 'remote_url': f'{LTI_SERVICE_BASE}/services/names-roles/v2p0/membership/{{courseId}}', + 'headers': {'Accept': 'application/vnd.ims.lti-nrps.v2.membershipcontainer+json'} # required Schoology header for LTI membership requests + }, + { + # AGS line item container β€” lists all assignments for the course + 'name': 'course_assignments', + 'remote_url': f'{LTI_SERVICE_BASE}/services/ags/v2p0/lineitems/{{courseId}}', + 'headers': {'Accept': 'application/vnd.ims.lis.v2.lineitemcontainer+json'} + }, + { + # AGS results for a specific line item β€” lists per-student results/submissions + # The lineItemId is typically the full line item URL or the trailing segment. + 'name': 'assignment_results', + 'remote_url': f'{LTI_SERVICE_BASE}/services/ags/v2p0/lineitems/{{courseId}}/{{courseWorkId}}/results', + 'headers': {'Accept': 'application/vnd.ims.lis.v2.resultcontainer+json'} + }, ])) register_cleaner = util.make_cleaner_registrar(ENDPOINTS) def register_endpoints(app): - ''' - ''' + '''Register Schoology LTI endpoints with the application.''' if not settings.feature_flag('schoology_routes'): return @@ -29,6 +55,10 @@ def register_endpoints(app): ) +# --------------------------------------------------------------------------- +# Course list +# --------------------------------------------------------------------------- + @register_cleaner('course_list', 'courses') def clean_course_list(schoology_json): ''' @@ -45,21 +75,27 @@ def clean_course_list(schoology_json): return [course] +# --------------------------------------------------------------------------- +# Roster +# --------------------------------------------------------------------------- + def _process_schoology_user_for_system(member, google_id): - # Skip if no canvas id - canvas_id = member.get('user_id') - if not canvas_id: return None + '''Convert an LTI NRPS member record into an internal user dict.''' + lti_user_id = member.get('user_id') + if not lti_user_id: + return None - # Skip non students - is_student = 'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner' in member.get('roles', []) + is_student = ( + 'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner' + in member.get('roles', []) + ) if not is_student: return None - # Create user for our system email = member.get('email') local_id = google_id if not local_id: - local_id = f'canvas-{canvas_id}' + local_id = f'schoology-{lti_user_id}' member[constants.USER_ID] = local_id user = { @@ -73,8 +109,6 @@ def _process_schoology_user_for_system(member, google_id): 'photo_url': member.get('picture') }, constants.USER_ID: local_id, - # TODO is this needed? Other roster functions in LO include it - # 'course_id': course_id } return user @@ -82,9 +116,9 @@ def _process_schoology_user_for_system(member, google_id): @register_cleaner('course_roster', 'roster') async def clean_course_roster(schoology_json): ''' - Retrieve and clean the roster for a Canvas course, alphabetically sorted + Retrieve and clean the roster for a Schoology course, alphabetically sorted. - Conforms to LTI NRPS v2 response format + Conforms to LTI NRPS v2 response format. https://www.imsglobal.org/spec/lti-nrps/v2p0 ''' members = schoology_json.get('members', []) @@ -97,16 +131,37 @@ async def clean_course_roster(schoology_json): user = _process_schoology_user_for_system(member, google_id) if user is not None: users.append(user) + + users.sort( + key=lambda user: user.get('profile', {}).get('name', {}).get('family_name', '') + ) return users -@register_cleaner('course_assignments', 'assignments') +# --------------------------------------------------------------------------- +# Assignments (AGS line items) +# --------------------------------------------------------------------------- +@register_cleaner('course_assignments', 'assignments') def clean_course_assignments(schoology_json): ''' - Clean course line items (assignments) from Schoology + TODO implemement this function + When launching via LTI, Schoology only allows us to see assignments + created by our tool. To see all assignments we require an Oauth workflow. + Clean course line items (assignments) from Schoology via LTI AGS. + ''' + raise NotImplemented('Schoology assignments have not yet been implemented.') + - Conforms to LTI AGS response format - https://www.imsglobal.org/spec/lti-ags/v2p0 +# --------------------------------------------------------------------------- +# Assignment results / assigned docs (AGS results) +# --------------------------------------------------------------------------- +@register_cleaner('assignment_results', 'assigned_docs') +async def clean_assigned_docs(schoology_json): + ''' + TODO implemement this function + When launching via LTI, Schoology only allows us to see assignments + created by our tool. To see all assignments we require an Oauth workflow. + Extract per-student Google Doc attachments from LTI AGS results + for a single assignment. ''' - # print('Schoology assignments TODO', schoology_json) - return [] + raise NotImplemented('Schoology documents from assignments have not yet been implemented.') diff --git a/learning_observer/learning_observer/integrations/util.py b/learning_observer/learning_observer/integrations/util.py index 26831881d..bf122ea0f 100644 --- a/learning_observer/learning_observer/integrations/util.py +++ b/learning_observer/learning_observer/integrations/util.py @@ -19,6 +19,7 @@ import aiohttp import aiohttp.web +import aiohttp_session import learning_observer.constants as constants import learning_observer.settings as settings @@ -99,12 +100,24 @@ async def raw_api_ajax( request = runtime.get_request() url = target_url.format(**kwargs) user = await learning_observer.auth.get_active_user(request) - if constants.AUTH_HEADERS not in request or user is None: + + # Auth headers may live on the request (set during the LTI launch + # redirect) OR in the session (persisted for subsequent requests). + # We need to check both sources. + auth_headers = request.get(constants.AUTH_HEADERS) + if auth_headers is None: + session = await aiohttp_session.get_session(request) + auth_headers = session.get(constants.AUTH_HEADERS) + # Populate request so downstream code can find them too + if auth_headers is not None: + request[constants.AUTH_HEADERS] = auth_headers + + if auth_headers is None or user is None: raise aiohttp.web.HTTPUnauthorized(text="Please log in") if headers is None: headers = {} - headers.update(request.get(constants.AUTH_HEADERS, {})) + headers.update(auth_headers) method = method.lower() cache_available = method == 'get' and cache is not None and cache_key_prefix is not None diff --git a/learning_observer/learning_observer/rosters.py b/learning_observer/learning_observer/rosters.py index ce87966e4..9c557084b 100644 --- a/learning_observer/learning_observer/rosters.py +++ b/learning_observer/learning_observer/rosters.py @@ -360,7 +360,7 @@ def init(): # Google, Canvas, and Schoology all use integrations instead of ajax when called elif roster_source in ["google_api"]: ajax = google_ajax - elif roster_source in ["canvas_api", 'schoology_api']: + elif roster_source in ["canvas_api", 'schoology']: pass elif roster_source in ["all"]: ajax = all_ajax @@ -369,7 +369,7 @@ def init(): "Settings file `roster_data` element should have `source` field\n" "set to either:\n" " test (retrieve from files courses.json and students.json)\n" - " google_api | canvas_api | schoology_api (retrieve roster data from an api)\n" + " google_api | canvas_api | schoology (retrieve roster data from an api)\n" " filesystem (retrieve roster data from file system hierarchy\n" " all (retrieve roster data as all students)" ) @@ -555,6 +555,25 @@ async def courseroster(request, course_id): return roster +async def courseassignments(request, course_id): + '''Fetch all the assignments for a given course + ''' + assignments = await run_additional_module_func(request, 'assignments', kwargs={'courseId': course_id}) + if assignments is not None: + return assignments + return [] + + +async def courseassignment_assigned_docs(request, course_id, assignment_id): + ''' + Fetch all assigned docs for a given assignment + ''' + assigned_docs = await run_additional_module_func(request, 'assigned_docs', kwargs={'courseId': course_id, 'courseWorkId': assignment_id}) + if assigned_docs is not None: + return assigned_docs + return [] + + async def courselist_api(request): ''' List all of the courses a teacher manages: Handler @@ -562,9 +581,29 @@ async def courselist_api(request): return aiohttp.web.json_response(await courselist(request)) +async def course_api(request): + ''' + Fetch course information + ''' + course_id = request.match_info['course_id'] + courses = await courselist(request) + for course in courses: + if course['id'] == course_id: + return aiohttp.web.json_response(course) + return aiohttp.web.json_response({}) + + async def courseroster_api(request): ''' List all of the students in a course: Handler ''' course_id = int(request.match_info['course_id']) return aiohttp.web.json_response(await courseroster(request, course_id)) + + +async def courseassignments_api(request): + ''' + List all of the assignments in a course: Handler + ''' + course_id = request.match_info['course_id'] + return aiohttp.web.json_response(await courseassignments(request, course_id)) diff --git a/learning_observer/learning_observer/routes.py b/learning_observer/learning_observer/routes.py index 5fc12b7b0..763ea6217 100644 --- a/learning_observer/learning_observer/routes.py +++ b/learning_observer/learning_observer/routes.py @@ -72,9 +72,15 @@ def tracemalloc_handler(request): aiohttp.web.get( '/webapi/courselist/', rosters.courselist_api), + aiohttp.web.get( + '/webapi/course/{course_id}', + rosters.course_api), aiohttp.web.get( '/webapi/courseroster/{course_id}', rosters.courseroster_api), + aiohttp.web.get( + '/webapi/courseassignments/{course_id}', + rosters.courseassignments_api), ]) register_auth_webapp_views(app) diff --git a/modules/lo_dash_react_components/lo_dash_react_components/LODocumentSourceSelectorAIO.py b/modules/lo_dash_react_components/lo_dash_react_components/LODocumentSourceSelectorAIO.py index 8025ba28f..27cee4c61 100644 --- a/modules/lo_dash_react_components/lo_dash_react_components/LODocumentSourceSelectorAIO.py +++ b/modules/lo_dash_react_components/lo_dash_react_components/LODocumentSourceSelectorAIO.py @@ -41,14 +41,19 @@ class ids: 'subcomponent': 'timestamp_input', 'aio_id': aio_id } - kwargs_store = lambda aio_id: { + title_text_wrapper = lambda aio_id: { 'component': 'LODocumentSourceSelectorAIO', - 'subcomponent': 'kwargs_store', + 'subcomponent': 'title_text_wrapper', + 'aio_id': aio_id + } + title_text_input = lambda aio_id: { + 'component': 'LODocumentSourceSelectorAIO', + 'subcomponent': 'title_text_input', 'aio_id': aio_id } - apply = lambda aio_id: { + kwargs_store = lambda aio_id: { 'component': 'LODocumentSourceSelectorAIO', - 'subcomponent': 'apply', + 'subcomponent': 'kwargs_store', 'aio_id': aio_id } @@ -65,7 +70,8 @@ def __init__(self, aio_id=None): id=self.ids.source_selector(aio_id), options={'latest': 'Latest Document', 'assignment': 'Assignment', - 'timestamp': 'Specific Time'}, + 'timestamp': 'Specific Time', + 'title_text': 'Text in Title'}, inline=True, value='latest'), html.Div('Additional Arguments'), @@ -83,7 +89,13 @@ def __init__(self, aio_id=None): value=datetime.datetime.now().strftime("%H:%M")) ]) ], id=self.ids.datetime_wrapper(aio_id)), - dbc.Button('Apply', id=self.ids.apply(aio_id), class_name='mt-1', n_clicks=0), + html.Div([ + dbc.Input( + id=self.ids.title_text_input(aio_id), + type='text', + placeholder='Enter text to match document titles' + ) + ], id=self.ids.title_text_wrapper(aio_id)), dcc.Store(id=self.ids.kwargs_store(aio_id), data={'src': 'latest'}) ]) component = [ @@ -94,74 +106,79 @@ def __init__(self, aio_id=None): # Update data clientside_callback( - '''function (clicks, src, assignment, date, time) { - if (clicks === 0) { return window.dash_clientside.no_update; } + '''function (src, assignment, date, time, titleText) { + // if (clicks === 0) { return window.dash_clientside.no_update; } let kwargs = {}; if (src === 'assignment') { kwargs.assignment = assignment; } else if (src === 'timestamp') { kwargs.requested_timestamp = new Date(`${date}T${time}`).getTime().toString() + } else if (src === 'title_text') { + kwargs.title_text = titleText; } return {src, kwargs}; } ''', Output(ids.kwargs_store(MATCH), 'data'), - Input(ids.apply(MATCH), 'n_clicks'), - State(ids.source_selector(MATCH), 'value'), - State(ids.assignment_input(MATCH), 'value'), - State(ids.date_input(MATCH), 'date'), - State(ids.timestamp_input(MATCH), 'value'), + Input(ids.source_selector(MATCH), 'value'), + Input(ids.assignment_input(MATCH), 'value'), + Input(ids.date_input(MATCH), 'date'), + Input(ids.timestamp_input(MATCH), 'value'), + Input(ids.title_text_input(MATCH), 'value'), ) clientside_callback( '''function (src) { if (src === 'assignment') { - return ['d-none', '']; + return ['d-none', '', 'd-none']; } else if (src === 'timestamp') { - return ['', 'd-none'] + return ['', 'd-none', 'd-none'] + } else if (src === 'title_text') { + return ['d-none', 'd-none', ''] } - return ['d-none', 'd-none']; + return ['d-none', 'd-none', 'd-none']; } ''', Output(ids.datetime_wrapper(MATCH), 'className'), Output(ids.assignment_wrapper(MATCH), 'className'), + Output(ids.title_text_wrapper(MATCH), 'className'), Input(ids.source_selector(MATCH), 'value'), ) clientside_callback( - '''async function (id, hash) { - if (hash.length === 0) { return window.dash_clientside.no_update; } + '''async function (id, hash, currentSource) { + const noUpdate = window.dash_clientside.no_update; + if (!hash || hash.length === 0) { return [noUpdate, noUpdate, noUpdate]; } const decoded = decode_string_dict(hash.slice(1)); - if (!decoded.course_id) { return window.dash_clientside.no_update; } - const response = await fetch(`${window.location.protocol}//${window.location.hostname}:${window.location.port}/google/course_work/${decoded.course_id}`); + if (!decoded.course_id) { return [noUpdate, noUpdate, noUpdate]; } + + const response = await fetch(`${window.location.protocol}//${window.location.hostname}:${window.location.port}/webapi/courseassignments/${decoded.course_id}`); const data = await response.json(); - const options = data.courseWork.map(function (item) { + const assignmentOptions = data.map(function (item) { return { label: item.title, value: item.id }; }); - return options; - } - ''', - Output(ids.assignment_input(MATCH), 'options'), - Input(ids.source_selector(MATCH), 'id'), - Input('_pages_location', 'hash'), - ) - clientside_callback( - '''function (src, assignment, date, time, current) { - if (src === 'assignment' & (assignment === undefined | current.kwargs?.assignment === assignment)) { - return true; + const sourceOptions = [ + { label: 'Latest Document', value: 'latest' }, + { label: 'Specific Time', value: 'timestamp' }, + { label: 'Text in Title', value: 'title_text' }, + ]; + if (assignmentOptions.length > 0) { + sourceOptions.splice(1, 0, { label: 'Assignment', value: 'assignment' }); } - if (src === 'timestamp' & current.kwargs?.requested_timestamp === new Date(`${date}T${time}`).getTime().toString()) { - return true; + + let sourceValue = currentSource; + if (sourceValue === 'assignment' && assignmentOptions.length === 0) { + sourceValue = 'latest'; } - if (src === 'latest' & current.src === 'latest') { return true; } - return false; + + return [assignmentOptions, sourceOptions, sourceValue]; } ''', - Output(ids.apply(MATCH), 'disabled'), - Input(ids.source_selector(MATCH), 'value'), - Input(ids.assignment_input(MATCH), 'value'), - Input(ids.date_input(MATCH), 'date'), - Input(ids.timestamp_input(MATCH), 'value'), - Input(ids.kwargs_store(MATCH), 'data'), + Output(ids.assignment_input(MATCH), 'options'), + Output(ids.source_selector(MATCH), 'options'), + Output(ids.source_selector(MATCH), 'value'), + Input(ids.source_selector(MATCH), 'id'), + Input('_pages_location', 'hash'), + State(ids.source_selector(MATCH), 'value'), ) diff --git a/modules/lo_dash_react_components/src/lib/components/WOStudentTextTile.react.js b/modules/lo_dash_react_components/src/lib/components/WOStudentTextTile.react.js index 1bfecbf43..385d07565 100644 --- a/modules/lo_dash_react_components/src/lib/components/WOStudentTextTile.react.js +++ b/modules/lo_dash_react_components/src/lib/components/WOStudentTextTile.react.js @@ -11,7 +11,7 @@ import LONameTag from './LONameTag.react'; */ export default class WOStudentTextTile extends Component { render () { - const { id, className, style, showName, profile, currentStudentHash, currentOptionHash, childComponent, additionalButtons } = this.props; + const { id, className, style, showName, profile, documentTitle, currentStudentHash, currentOptionHash, childComponent, additionalButtons } = this.props; const isLoading = currentOptionHash !== currentStudentHash; let bodyClassName = isLoading ? 'loading' : ''; bodyClassName = `${bodyClassName} overflow-auto position-relative`; @@ -19,20 +19,29 @@ export default class WOStudentTextTile extends Component { return ( - - - {isLoading && ( - - )} - {additionalButtons && additionalButtons} - +
+
+ + {documentTitle && showName && ( +
+ {documentTitle} +
+ )} +
+ + {isLoading && ( + + )} + {additionalButtons && additionalButtons} + +
{childComponent} @@ -46,7 +55,8 @@ WOStudentTextTile.defaultProps = { className: '', showName: true, style: {}, - profile: {} + profile: {}, + documentTitle: '' }; WOStudentTextTile.propTypes = { @@ -72,6 +82,12 @@ WOStudentTextTile.propTypes = { */ showName: PropTypes.bool, + /** + * Title of the currently selected document. + * Displayed beneath the student name when present and showName is true. + */ + documentTitle: PropTypes.string, + /** * Hash of the current options, used to determine if we * should be in a loading state or not. @@ -104,4 +120,4 @@ WOStudentTextTile.propTypes = { * to Dash, to make them available for callbacks. */ setProps: PropTypes.func -}; +}; \ No newline at end of file diff --git a/modules/wo_bulk_essay_analysis/VERSION b/modules/wo_bulk_essay_analysis/VERSION index 2e1b98f63..fff79c93a 100644 --- a/modules/wo_bulk_essay_analysis/VERSION +++ b/modules/wo_bulk_essay_analysis/VERSION @@ -1 +1 @@ -0.1.0+2026.02.17T15.09.06.345Z.5f75e49b.berickson.20260217.updated.dead.links +0.1.0+2026.02.20T19.02.56.547Z.85e344b1.berickson.20260210.dashboard.updates diff --git a/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/assets/scripts.js b/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/assets/scripts.js index a055d73cb..8641af6ab 100644 --- a/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/assets/scripts.js +++ b/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/assets/scripts.js @@ -8,16 +8,75 @@ if (!window.dash_clientside) { pdfjsLib.GlobalWorkerOptions.workerSrc = '/static/3rd_party/pdf.worker.min.js'; -const createStudentCard = async function (s, prompt, width, height, showName, selectedMetrics) { +function fetchSelectedItemsFromOptions (value, options, type) { + return options.reduce(function (filtered, option) { + if (value?.[option.id]?.[type]?.value) { + const selected = { ...option, ...value[option.id] }; + filtered.push(selected); + } + return filtered; + }, []); +} + +function createProcessTags (document, metrics) { + const children = metrics.map(metric => { + switch (metric.id) { + case 'time_on_task': + return createDashComponent( + DASH_BOOTSTRAP_COMPONENTS, 'Badge', + { children: `${rendertime2(document[metric.id])} on task`, className: 'me-1' } + ); + case 'status': + const color = document[metric.id] === 'active' ? 'success' : 'warning'; + return createDashComponent( + DASH_BOOTSTRAP_COMPONENTS, 'Badge', + { children: document[metric.id], color } + ); + default: + break; + } + }); + return createDashComponent(DASH_HTML_COMPONENTS, 'Div', { children, className: 'sticky-top' }); +} + +async function hashObject (obj) { + const jsonString = JSON.stringify(obj); + const encoder = new TextEncoder(); + const data = encoder.encode(jsonString); + + if (crypto && crypto.subtle) { + try { + const hashBuffer = await crypto.subtle.digest('SHA-256', data); + const hashArray = Array.from(new Uint8Array(hashBuffer)); + const hashHex = hashArray.map(byte => byte.toString(16).padStart(2, '0')).join(''); + return hashHex; + } catch (error) { + console.warn('crypto.subtle.digest failed; falling back to simple hash.'); + } + } + + return simpleHash(jsonString); +} + +function simpleHash (str) { + let hash = 0; + for (let i = 0; i < str.length; i++) { + const char = str.charCodeAt(i); + hash = (hash << 5) - hash + char; + hash |= 0; + } + return hash.toString(16); +} + +const createStudentCard = function (s, promptHash, width, height, showName, selectedMetrics) { const selectedDocument = s.doc_id || Object.keys(s.documents || {})[0] || ''; + const documentTitle = s?.availableDocuments?.[selectedDocument]?.title ?? selectedDocument ?? ''; const student = s.documents?.[selectedDocument] ?? {}; - const promptHash = await hashObject({ prompt }); - const studentText = { - namespace: 'lo_dash_react_components', - type: 'WOAnnotatedText', - props: { text: student.text, breakpoints: [] } - }; + const studentText = createDashComponent( + LO_DASH_REACT_COMPONENTS, 'WOAnnotatedText', + { text: student.text, breakpoints: [] } + ); const studentTileChild = createDashComponent( DASH_HTML_COMPONENTS, 'Div', { @@ -27,48 +86,40 @@ const createStudentCard = async function (s, prompt, width, height, showName, se ] } ); - const errorMessage = { - namespace: 'dash_html_components', - type: 'Div', - props: { - children: 'An error occurred while processing the text.' - } - }; - const feedbackMessage = { - namespace: DASH_CORE_COMPONENTS, - type: 'Markdown', - props: { + const errorMessage = createDashComponent( + DASH_HTML_COMPONENTS, 'Div', + { children: 'An error occurred while processing the text.' } + ); + const feedbackMessage = createDashComponent( + DASH_CORE_COMPONENTS, 'Markdown', + { children: student?.feedback ? student.feedback : '', className: student?.feedback ? 'p-1 overflow-auto' : '', style: { whiteSpace: 'pre-line' } } - }; - const feedbackLoading = { - namespace: 'dash_html_components', - type: 'Div', - props: { - children: [{ - namespace: 'dash_bootstrap_components', - type: 'Spinner', - props: {} - }, { - namespace: 'dash_html_components', - type: 'Div', - props: { children: 'Waiting for a response.' } - }], + ); + const feedbackLoading = createDashComponent( + DASH_HTML_COMPONENTS, 'Div', + { + children: [ + createDashComponent(DASH_BOOTSTRAP_COMPONENTS, 'Spinner', {}), + createDashComponent(DASH_HTML_COMPONENTS, 'Div', { children: 'Waiting for a response.' }) + ], className: 'text-center' } - }; + ); const feedback = promptHash === student.option_hash_gpt_bulk ? feedbackMessage : feedbackLoading; const feedbackOrError = 'error' in student ? errorMessage : feedback; const userId = student?.user_id; - if (!userId) { return {}; } + if (!userId) { return null; } + const studentTile = createDashComponent( LO_DASH_REACT_COMPONENTS, 'WOStudentTextTile', { showName, profile: student?.profile || {}, selectedDocument, + documentTitle, childComponent: studentTileChild, id: { type: 'WOAIAssistStudentTileText', index: userId }, currentOptionHash: promptHash, @@ -102,17 +153,18 @@ const createStudentCard = async function (s, prompt, width, height, showName, se return tileWrapper; }; -/** - * Check for if we should trigger loading on a student or not. - * @param {*} s student - * @param {*} promptHash current hash of prompts - * @returns true if student's selected document's hash is the same as promptHash - */ -const checkForResponse = function (s, promptHash, options) { +const checkForBulkResponse = function (s, promptHash, options) { if (!('documents' in s)) { return false; } const selectedDocument = s.doc_id || Object.keys(s.documents || {})[0] || ''; const student = s.documents[selectedDocument]; - return options.every(option => promptHash === student[`option_hash_${option}`]); + if (!student) { return false; } + return options.every(option => { + const hashKey = `option_hash_${option}`; + if (hashKey in student) { + return promptHash === student[hashKey]; + } + return option in student; + }); }; const charactersAfterChar = function (str, char) { @@ -126,27 +178,19 @@ const charactersAfterChar = function (str, char) { // Helper functions for extracting text from files const extractPDF = async function (base64String) { const pdfData = atob(charactersAfterChar(base64String, ',')); - - // Use PDF.js to load and parse the PDF const pdf = await pdfjsLib.getDocument({ data: pdfData }).promise; - const totalPages = pdf.numPages; const allTextPromises = []; - for (let pageNumber = 1; pageNumber <= totalPages; pageNumber++) { const pageTextPromise = pdf.getPage(pageNumber).then(function (page) { return page.getTextContent(); }).then(function (textContent) { return textContent.items.map(item => item.str).join(' '); }); - allTextPromises.push(pageTextPromise); } - const allTexts = await Promise.all(allTextPromises); - const allText = allTexts.join('\n'); - - return allText; + return allTexts.join('\n'); }; const extractTXT = async function (base64String) { @@ -160,7 +204,7 @@ const extractMD = async function (base64String) { const extractDOCX = async function (base64String) { const arrayBuffer = Uint8Array.from(atob(charactersAfterChar(base64String, ',')), c => c.charCodeAt(0)).buffer; const result = await mammoth.extractRawText({ arrayBuffer }); - return result.value; // The raw text + return result.value; }; const fileTextExtractors = { @@ -172,11 +216,396 @@ const fileTextExtractors = { const AIAssistantLoadingQueries = ['gpt_bulk', 'time_on_task', 'activity']; +// ── Walkthrough step definitions ────────────────────────────────────── +const BULK_WALKTHROUGH_STEPS = [ + { + title: 'Welcome to the Classroom AI Feedback Assistant!', + icon: 'fas fa-robot', + body: [ + 'This dashboard lets you send every student\'s writing to an AI assistant ', + 'and view the feedback side-by-side with their original text.', + '\n\n', + 'Let\'s walk through how to get started β€” it only takes a minute.' + ].join('') + }, + { + title: 'Step 1 β€” Write Your Prompt', + icon: 'fas fa-pen-fancy', + body: [ + 'In the Prompt Input panel you\'ll see a text area for your query. ', + 'This is what the AI will do with each student\'s writing.\n\n', + 'Use placeholders like {student_text} to reference student essays. ', + 'You can also add custom placeholders (e.g. a rubric) via the ', + '"Add" button next to the placeholder word bank.\n\n', + 'There\'s also a system prompt that guides the AI\'s overall behavior β€” ', + 'you can edit it in Settings (βš™).' + ].join('') + }, + { + title: 'Step 2 β€” Click Submit', + icon: 'fas fa-paper-plane', + body: [ + 'Once your prompt is ready, click the "Submit" button.\n\n', + 'The dashboard will send your prompt (with each student\'s text filled in) ', + 'to the AI. A progress bar will appear while results load β€” this can take ', + 'a few minutes for larger classes.' + ].join('') + }, + { + title: 'Step 3 β€” Review AI Feedback', + icon: 'fas fa-comments', + body: [ + 'Each student tile shows their original writing on top and the AI\'s ', + 'feedback below.\n\n', + 'β€’ Click the expand icon (β€’) on any tile to open a larger view.\n', + 'β€’ Use the Settings (βš™) button to adjust tile sizes, change the document ', + 'source, or edit the system prompt.\n', + 'β€’ Your prompt history is saved so you can see what you\'ve already tried.' + ].join('') + }, + { + title: 'You\'re Ready!', + icon: 'fas fa-check-circle', + body: [ + 'That\'s everything you need to get started.\n\n', + 'You can change your prompt at any time and click "Submit" again ', + 'to get new feedback.\n\n', + 'To revisit this guide later, click the ', + 'help button (?) in the toolbar.' + ].join('') + } +]; + +/** + * Build the walkthrough modal body for a given step index. + */ +function buildBulkWalkthroughBody (step) { + const info = BULK_WALKTHROUGH_STEPS[step]; + const paragraphs = info.body.split('\n').filter(Boolean).map(line => + createDashComponent(DASH_HTML_COMPONENTS, 'P', { + children: line, + className: 'mb-2', + style: { whiteSpace: 'pre-wrap' } + }) + ); + return createDashComponent(DASH_HTML_COMPONENTS, 'Div', { + children: [ + createDashComponent(DASH_HTML_COMPONENTS, 'Div', { + children: createDashComponent(DASH_HTML_COMPONENTS, 'I', { + className: `${info.icon} fa-3x text-primary mb-3` + }), + className: 'text-center' + }), + ...paragraphs + ] + }); +} + +/** + * Build an empty-state placeholder when no student data is loaded yet. + */ +function buildBulkEmptyState () { + return createDashComponent(DASH_HTML_COMPONENTS, 'Div', { + className: 'd-flex flex-column align-items-center justify-content-center text-center py-5 w-100', + style: { minHeight: '300px', color: '#6c757d' }, + children: [ + createDashComponent(DASH_HTML_COMPONENTS, 'I', { + className: 'fas fa-users fa-4x mb-3', + style: { opacity: 0.3 } + }), + createDashComponent(DASH_HTML_COMPONENTS, 'H4', { + children: 'No student data loaded yet', + className: 'mb-3' + }), + createDashComponent(DASH_HTML_COMPONENTS, 'P', { + children: 'To get started:', + className: 'mb-2 fw-bold' + }), + createDashComponent(DASH_HTML_COMPONENTS, 'Div', { + className: 'text-start', + style: { maxWidth: '400px' }, + children: [ + createDashComponent(DASH_HTML_COMPONENTS, 'P', { + className: 'mb-1', + children: '1. Write your prompt in the Prompt Input panel above' + }), + createDashComponent(DASH_HTML_COMPONENTS, 'P', { + className: 'mb-1', + children: '2. Make sure {student_text} is included in your query' + }), + createDashComponent(DASH_HTML_COMPONENTS, 'P', { + className: 'mb-1', + children: '3. Click "Submit" to send the prompt to the AI for each student' + }) + ] + }), + createDashComponent(DASH_HTML_COMPONENTS, 'P', { + className: 'mt-3 text-muted small', + children: 'Click the ? button for a full walkthrough.' + }) + ] + }); +} + window.dash_clientside.bulk_essay_feedback = { + // ── Walkthrough callbacks ──────────────────────────────────────────── + + navigateWalkthrough: function (nextClicks, backClicks, doneClicks, skipClicks, helpClicks, currentStep) { + const triggered = window.dash_clientside.callback_context?.triggered_id; + if (!triggered) { return window.dash_clientside.no_update; } + + const totalSteps = BULK_WALKTHROUGH_STEPS.length; + + switch (triggered) { + case 'bulk-essay-analysis-walkthrough-next': + return Math.min(currentStep + 1, totalSteps - 1); + case 'bulk-essay-analysis-walkthrough-back': + return Math.max(currentStep - 1, 0); + case 'bulk-essay-analysis-walkthrough-done': + case 'bulk-essay-analysis-walkthrough-skip': + return -1; + case 'bulk-essay-analysis-help': + return 0; + default: + return window.dash_clientside.no_update; + } + }, + + renderWalkthroughStep: function (step) { + const totalSteps = BULK_WALKTHROUGH_STEPS.length; + const isOpen = step >= 0 && step < totalSteps; + + if (!isOpen) { + return [ + '', '', true, + { display: 'inline-block' }, + { display: 'none' }, + '', + false + ]; + } + + const info = BULK_WALKTHROUGH_STEPS[step]; + const body = buildBulkWalkthroughBody(step); + const isFirst = step === 0; + const isLast = step === totalSteps - 1; + const counter = `${step + 1} of ${totalSteps}`; + + return [ + info.title, + body, + isFirst, + { display: isLast ? 'none' : 'inline-block' }, + { display: isLast ? 'inline-block' : 'none' }, + counter, + true + ]; + }, + + initWalkthroughFromStorage: function (step, hasSeenWalkthrough) { + const triggered = window.dash_clientside.callback_context?.triggered_id; + + if (!triggered) { + if (hasSeenWalkthrough) { + return [-1, true]; + } + return [0, false]; + } + + if (step === -1) { + return [-1, true]; + } + + return [step, hasSeenWalkthrough]; + }, + + // ── Settings modal callbacks ───────────────────────────────────────── + + toggleSettingsModal: function (clicks, isOpen) { + if (!clicks) { return window.dash_clientside.no_update; } + return !isOpen; + }, + + applySettingsAndCloseModal: function (clicks, stagedSystemPrompt, docKwargs) { + if (!clicks) { + return [ + window.dash_clientside.no_update, + window.dash_clientside.no_update, + window.dash_clientside.no_update + ]; + } + return [stagedSystemPrompt, docKwargs, false]; + }, + + // ── Expanded student modal ─────────────────────────────────────────── + + /** + * When a student tile expand button is clicked, record which student + * was selected and open the modal. + * + * Returns [selectedStudentId, isModalOpen, showIdentity]. + */ + expandCurrentStudent: function (clicks, ids, isModalOpen, currentStudentId, globalShowName) { + const triggeredItem = window.dash_clientside.callback_context?.triggered_id ?? null; + if (!triggeredItem) { return window.dash_clientside.no_update; } + + // Only act on actual expand button clicks + if (triggeredItem?.type !== 'WOAIAssistStudentTileExpand') { + return window.dash_clientside.no_update; + } + + // Make sure something was actually clicked (not just initial callback fire) + const hasActualClick = clicks && clicks.some(c => c !== undefined && c !== null && c > 0); + if (!hasActualClick) { return window.dash_clientside.no_update; } + + const id = triggeredItem?.index; + const index = ids.findIndex(item => item.index === id); + if (index === -1) { return window.dash_clientside.no_update; } + + const showIdentity = globalShowName !== undefined ? globalShowName : true; + + return [id, true, showIdentity]; + }, + /** - * Sends data to server via websocket + * Reactively render the expanded student modal content from live + * websocket data. Only builds content when the modal is actually open + * and a student is selected. + * + * Returns [studentName, docTitle, childContent]. */ - send_to_loconnection: async function (state, hash, clicks, docKwargs, query, systemPrompt, tags) { + renderExpandedStudent: async function (wsStorageData, selectedStudentId, isModalOpen, history, value, options) { + // Don't do anything if the modal isn't open + if (!isModalOpen) { + return window.dash_clientside.no_update; + } + + if (!selectedStudentId || !wsStorageData?.students) { + return [ + '', + '', + createDashComponent(DASH_HTML_COMPONENTS, 'Div', { + children: 'No student selected.', + className: 'text-muted text-center py-5' + }) + ]; + } + + const student = wsStorageData.students[selectedStudentId]; + if (!student) { + return [ + 'Student', + '', + createDashComponent(DASH_HTML_COMPONENTS, 'Div', { + children: 'Student data not available.', + className: 'text-muted text-center py-5' + }) + ]; + } + + const selectedDocument = student.doc_id || Object.keys(student.documents || {})[0] || ''; + const documentName = student?.availableDocuments?.[selectedDocument]?.title ?? selectedDocument ?? ''; + const doc = student.documents?.[selectedDocument]; + + if (!doc) { + return [ + 'Student', + documentName, + createDashComponent(DASH_HTML_COMPONENTS, 'Div', { + children: 'Document data not available yet.', + className: 'text-muted text-center py-5' + }) + ]; + } + + const names = doc.profile?.name || {}; + const studentName = [names.given_name, names.family_name] + .filter(Boolean) + .join(' ') || 'Student'; + + const selectedMetrics = fetchSelectedItemsFromOptions(value, options, 'metric'); + + // Compute prompt hash for feedback status + const currPrompt = history.length > 0 ? history[history.length - 1] : ''; + const promptHash = await hashObject({ prompt: currPrompt }); + + // Build student text + const studentText = createDashComponent( + LO_DASH_REACT_COMPONENTS, 'WOAnnotatedText', + { text: doc.text, breakpoints: [] } + ); + + const studentTileChild = createDashComponent( + DASH_HTML_COMPONENTS, 'Div', + { + children: [ + createProcessTags({ ...doc }, selectedMetrics), + studentText + ] + } + ); + + // Build feedback + const errorMessage = createDashComponent( + DASH_HTML_COMPONENTS, 'Div', + { children: 'An error occurred while processing the text.' } + ); + const feedbackMessage = createDashComponent( + DASH_CORE_COMPONENTS, 'Markdown', + { + children: doc?.feedback ? doc.feedback : '', + className: doc?.feedback ? 'p-1 overflow-auto' : '', + style: { whiteSpace: 'pre-line' } + } + ); + const feedbackLoading = createDashComponent( + DASH_HTML_COMPONENTS, 'Div', + { + children: [ + createDashComponent(DASH_BOOTSTRAP_COMPONENTS, 'Spinner', {}), + createDashComponent(DASH_HTML_COMPONENTS, 'Div', { children: 'Waiting for a response.' }) + ], + className: 'text-center' + } + ); + const feedback = promptHash === doc.option_hash_gpt_bulk ? feedbackMessage : feedbackLoading; + const feedbackOrError = 'error' in doc ? errorMessage : feedback; + + const childContent = createDashComponent( + DASH_HTML_COMPONENTS, 'Div', + { + children: [ + studentTileChild, + createDashComponent( + DASH_BOOTSTRAP_COMPONENTS, 'Card', + { children: feedbackOrError, body: true } + ) + ] + } + ); + + return [studentName, documentName, childContent]; + }, + + toggleExpandedStudentIdentity: function (clicks, currentValue) { + if (!clicks) { return window.dash_clientside.no_update; } + return !currentValue; + }, + + renderExpandedStudentIdentity: function (showIdentity) { + const visible = {}; + const hidden = { display: 'none' }; + + const titleStyle = showIdentity ? visible : hidden; + const docTitleStyle = showIdentity ? visible : hidden; + const iconClass = showIdentity ? 'fas fa-eye' : 'fas fa-eye-slash'; + + return [titleStyle, docTitleStyle, iconClass]; + }, + + // ── Core dashboard callbacks ───────────────────────────────────────── + + send_to_loconnection: async function (state, hash, clicks, docKwargs, query, appliedSystemPrompt, tags) { if (state === undefined) { return window.dash_clientside.no_update; } @@ -189,14 +618,12 @@ window.dash_clientside.bulk_essay_feedback = { decoded.message_id = ''; decoded.doc_source = docKwargs.src; decoded.doc_source_kwargs = docKwargs.kwargs; - // TODO what is a reasonable time to wait inbetween subsequent calls for - // the same arguments decoded.rerun_dag_delay = 120; const trig = window.dash_clientside.callback_context.triggered[0]; if (trig.prop_id.includes('bulk-essay-analysis-submit-btn')) { decoded.gpt_prompt = query; - decoded.system_prompt = systemPrompt; + decoded.system_prompt = appliedSystemPrompt; decoded.tags = tags; } @@ -215,28 +642,6 @@ window.dash_clientside.bulk_essay_feedback = { return window.dash_clientside.no_update; }, - toggleAdvanced: function (clicks, shown) { - if (!clicks) { - return window.dash_clientside.no_update; - } - const optionPrefix = 'bulk-essay-analysis-advanced-collapse'; - if (shown.includes(optionPrefix)) { - shown = shown.filter(item => item !== optionPrefix); - } else { - shown = shown.concat(optionPrefix); - } - return shown; - }, - - closeAdvanced: function (clicks, shown) { - if (!clicks) { return window.dash_clientside.no_update; } - shown = shown.filter(item => item !== 'bulk-essay-analysis-advanced-collapse'); - return shown; - }, - - /** - * adds submitted query to history and clear input - */ update_input_history_on_query_submission: async function (clicks, query, history) { if (clicks > 0) { return history.concat(query); @@ -244,44 +649,37 @@ window.dash_clientside.bulk_essay_feedback = { return window.dash_clientside.no_update; }, - /** - * update history based on history browser storage - */ update_history_list: function (history) { const items = history.map((x) => { - return { - namespace: 'dash_html_components', - type: 'Li', - props: { children: x } - }; + return createDashComponent(DASH_HTML_COMPONENTS, 'Li', { children: x }); }); - return { - namespace: 'dash_html_components', - type: 'Ol', - props: { children: items } - }; + return createDashComponent(DASH_HTML_COMPONENTS, 'Ol', { children: items }); }, - /** - * update student cards based on new data in storage - */ updateStudentGridOutput: async function (wsStorageData, history, width, height, showName, value, options) { - if (!wsStorageData) { - return 'No students'; + if (!wsStorageData?.students) { + return buildBulkEmptyState(); } + + const students = wsStorageData.students; + if (Object.keys(students).length === 0) { + return buildBulkEmptyState(); + } + const currPrompt = history.length > 0 ? history[history.length - 1] : ''; + const promptHash = await hashObject({ prompt: currPrompt }); const selectedMetrics = fetchSelectedItemsFromOptions(value, options, 'metric'); let output = []; - for (const student in wsStorageData.students) { - output = output.concat(await createStudentCard(wsStorageData.students[student], currPrompt, width, height, showName, selectedMetrics)); + for (const student in students) { + const card = createStudentCard(students[student], promptHash, width, height, showName, selectedMetrics); + if (card) { + output = output.concat(card); + } } return output; }, - /** - * Uploads file content as str - */ handleFileUploadToTextField: async function (contents, filename, timestamp) { if (filename === undefined) { return ''; @@ -300,9 +698,6 @@ window.dash_clientside.bulk_essay_feedback = { return data; }, - /** - * append tag in curly braces to input - */ add_tag_to_input: function (clicks, curr, store) { const trig = window.dash_clientside.callback_context.triggered[0]; const trigProp = trig.prop_id; @@ -313,14 +708,6 @@ window.dash_clientside.bulk_essay_feedback = { return window.dash_clientside.no_update; }, - /** - * enable/disabled submit based on query - * makes sure there is a query and the tags are properly formatted - * - * updates the following components - * - submit query button disbaled status - * - helper text for why we disabled the submit query button - */ disableQuerySubmitButton: function (query, loading, store) { if (query.length === 0) { return [true, 'Please create a request before submitting.']; @@ -339,13 +726,6 @@ window.dash_clientside.bulk_essay_feedback = { return [false, '']; }, - /** - * enable/disable the save attachment button if tag is already in use/blank - * - * updates the following components - * - save button disbaled status - * - helper text for why we are disabled - */ disableAttachmentSaveButton: function (label, content, currentTagStore, replacementId) { const tags = Object.keys(currentTagStore); if (label.length === 0 & content.length === 0) { @@ -360,10 +740,6 @@ window.dash_clientside.bulk_essay_feedback = { return [false, '']; }, - /** - * Opens the tag modal when users want to add a new one or edit an - * existing tag. - */ openTagAddModal: function (clicks, editClicks, currentTagStore, ids) { const triggeredItem = window.dash_clientside.callback_context?.triggered_id ?? null; if (!triggeredItem) { return window.dash_clientside.no_update; } @@ -378,9 +754,6 @@ window.dash_clientside.bulk_essay_feedback = { return window.dash_clientside.no_update; }, - /** - * populate word bank of tags - */ update_tag_buttons: function (tagStore) { const tagLabels = Object.keys(tagStore); const tags = tagLabels.map((val) => { @@ -430,13 +803,10 @@ window.dash_clientside.bulk_essay_feedback = { return tags; }, - /** - * Save placeholder to browser storage and close edit placeholder modal - */ savePlaceholder: function (clicks, label, text, replacementId, tagStore) { if (clicks > 0) { - const newStore = tagStore; - if (!!replacementId & replacementId !== label) { + const newStore = { ...tagStore }; + if (!!replacementId && replacementId !== label) { delete newStore[replacementId]; } newStore[label] = text; @@ -445,31 +815,19 @@ window.dash_clientside.bulk_essay_feedback = { return window.dash_clientside.no_update; }, - /** - * Remove placeholder from store on confirm dialogue yes - */ removePlaceholder: function (clicks, tagStore, ids) { const triggeredItem = window.dash_clientside.callback_context?.triggered_id ?? null; if (!triggeredItem) { return window.dash_clientside.no_update; } const id = triggeredItem.index; const index = ids.findIndex(item => item.index === id); if (clicks[index]) { - const newStore = tagStore; + const newStore = { ...tagStore }; delete newStore[id]; return newStore; } return window.dash_clientside.no_update; }, - /** - * Check if we've received any errors and update - * the alert with the appropriate information - * - * returns an array which updates dash components - * - text to display on alert - * - show alert - * - JSON error data on the alert (only in debug) - */ updateAlertWithError: function (error) { if (Object.keys(error).length === 0) { return ['', false, '']; @@ -482,19 +840,19 @@ window.dash_clientside.bulk_essay_feedback = { return [text, true, error]; }, - /** - * Iterate over students and figure out if any of them have not loaded - * yet. We hash the last history item to compare to. - */ updateLoadingInformation: async function (wsStorageData, history) { const noLoading = [false, 0, '']; - if (!wsStorageData) { + if (!wsStorageData?.students) { + return noLoading; + } + const students = wsStorageData.students; + const totalStudents = Object.keys(students).length; + if (totalStudents === 0) { return noLoading; } const currentPrompt = history.length > 0 ? history[history.length - 1] : ''; const promptHash = await hashObject({ prompt: currentPrompt }); - const returnedResponses = Object.values(wsStorageData.students).filter(student => checkForResponse(student, promptHash, AIAssistantLoadingQueries)).length; - const totalStudents = Object.keys(wsStorageData.students).length; + const returnedResponses = Object.values(students).filter(student => checkForBulkResponse(student, promptHash, AIAssistantLoadingQueries)).length; if (totalStudents === returnedResponses) { return noLoading; } const loadingProgress = returnedResponses / totalStudents + 0.1; const outputText = `Fetching responses from server. This will take a few minutes. (${returnedResponses}/${totalStudents} received)`; @@ -509,117 +867,8 @@ window.dash_clientside.bulk_essay_feedback = { ]; }, - selectStudentForExpansion: function (clicks, shownPanels, ids) { - const triggeredItem = window.dash_clientside.callback_context?.triggered_id ?? null; - if (!triggeredItem) { return window.dash_clientside.no_update; } - let id = null; - if (triggeredItem?.type === 'WOAIAssistStudentTileExpand') { - id = triggeredItem?.index; - const index = ids.findIndex(item => item.index === id); - if (clicks[index]) { - shownPanels = shownPanels.concat('bulk-essay-analysis-expanded-student-panel'); - } else { - // No clicks occurred so we should keep the ID as it was - id = window.dash_clientside.no_update; - } - } else { - return window.dash_clientside.no_update; - } - return [id, shownPanels]; - }, - - expandSelectedStudent: async function (selectedStudent, wsData, showName, history, value, options) { - if (!selectedStudent | !(selectedStudent in (wsData.students || {}))) { - return window.dash_clientside.no_update; - } - const prompt = history.length > 0 ? history[history.length - 1] : ''; - const s = wsData.students[selectedStudent]; - const selectedDocument = s.doc_id || Object.keys(s.documents || {})[0] || ''; - const document = Object.keys(s.documents)[0]; - const student = s.documents[document]; - const promptHash = await hashObject({ prompt }); - const selectedMetrics = fetchSelectedItemsFromOptions(value, options, 'metric'); - - // TODO some of this can easily be abstracted - const studentText = { - namespace: 'lo_dash_react_components', - type: 'WOAnnotatedText', - props: { text: student.text, breakpoints: [] } - }; - const studentTileChild = createDashComponent( - DASH_HTML_COMPONENTS, 'Div', - { - children: [ - createProcessTags({ ...student }, selectedMetrics), - studentText - ] - } - ); - const errorMessage = { - namespace: 'dash_html_components', - type: 'Div', - props: { - children: 'An error occurred while processing the text.' - } - }; - const feedbackMessage = { - namespace: DASH_CORE_COMPONENTS, - type: 'Markdown', - props: { - children: student?.feedback ? student.feedback : '', - className: student?.feedback ? 'p-1' : '', - style: { whiteSpace: 'pre-line' } - } - }; - const feedbackLoading = { - namespace: 'dash_html_components', - type: 'Div', - props: { - children: [{ - namespace: 'dash_bootstrap_components', - type: 'Spinner', - props: {} - }, { - namespace: 'dash_html_components', - type: 'Div', - props: { children: 'Waiting for a response.' } - }], - className: 'text-center' - } - }; - const feedback = promptHash === student.option_hash_gpt_bulk ? feedbackMessage : feedbackLoading; - const feedbackOrError = 'error' in student ? errorMessage : feedback; - const studentTile = createDashComponent( - LO_DASH_REACT_COMPONENTS, 'WOStudentTextTile', - { - showName, - profile: student?.profile || {}, - selectedDocument, - childComponent: studentTileChild, - id: { type: 'WOAIAssistStudentTileText', index: student.user_id }, - currentOptionHash: promptHash, - currentStudentHash: student.option_hash_gpt_bulk - } - ); - const individualWrapper = createDashComponent( - DASH_HTML_COMPONENTS, 'Div', - { - className: '', - children: [ - studentTile, - createDashComponent( - DASH_BOOTSTRAP_COMPONENTS, 'Card', - { children: feedbackOrError, body: true } - ) - ] - } - ); - return individualWrapper; - }, - - closeExpandedStudent: function (clicks, shown) { - if (!clicks) { return window.dash_clientside.no_update; } - shown = shown.filter(item => item !== 'bulk-essay-analysis-expanded-student-panel'); - return shown; + showHideHeader: function (show, ids) { + const total = ids.length; + return Array(total).fill(show ? 'd-none' : ''); } }; diff --git a/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/dashboard/layout.py b/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/dashboard/layout.py index 59ae6ae3c..d98b034c3 100644 --- a/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/dashboard/layout.py +++ b/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/dashboard/layout.py @@ -9,37 +9,44 @@ from dash import html, dcc, clientside_callback, ClientsideFunction, Output, Input, State, ALL +import learning_observer.settings import wo_classroom_text_highlighter.options -# TODO pull this flag from settings -DEBUG_FLAG = True +DEBUG_FLAG = learning_observer.settings.RUN_MODE == learning_observer.settings.RUN_MODES.DEV prefix = 'bulk-essay-analysis' _websocket = f'{prefix}-websocket' _namespace = 'bulk_essay_feedback' -alert = f'{prefix}-alert' -alert_text = f'{prefix}-alert-text' -alert_error_dump = f'{prefix}-alert-error-dump' +# Alert +_alert = f'{prefix}-alert' +_alert_text = f'{prefix}-alert-text' +_alert_error_dump = f'{prefix}-alert-error-dump' +# Query input query_input = f'{prefix}-query-input' +# Panel layout panel_layout = f'{prefix}-panel-layout' -_advanced = f'{prefix}-advanced' -_advanced_doc_src = f'{_advanced}-document-source' -_advanced_toggle = f'{_advanced}-toggle' -_advanced_collapse = f'{_advanced}-collapse' -_advanced_close = f'{_advanced}-close' -_advanced_width = f'{_advanced}-width' -_advanced_height = f'{_advanced}-height' -_advanced_hide_header = f'{_advanced}-hide-header' -_advanced_text_information = f'{_advanced}-text-information' - -_system_input = f'{prefix}-system-prompt-input' -_system_input_tooltip = f'{_system_input}-tooltip' - -# placeholder DOM ids +# ── Settings modal DOM IDs ───────────────────────────────────────────── +_settings_prefix = f'{prefix}-settings' +_settings_toggle = f'{_settings_prefix}-toggle' +_settings_modal = f'{_settings_prefix}-modal' +_settings_run = f'{_settings_prefix}-run' +_settings_doc_src = f'{_settings_prefix}-document-source' +_settings_width = f'{_settings_prefix}-width' +_settings_height = f'{_settings_prefix}-height' +_settings_hide_header = f'{_settings_prefix}-hide-header' +_settings_text_information = f'{_settings_prefix}-text-information' + +# System prompt: staged (in modal) and applied (in store) +_system_input_staged = f'{prefix}-system-prompt-staged' +_system_input_tooltip = f'{_system_input_staged}-tooltip' +_applied_system_prompt = f'{prefix}-applied-system-prompt' +_applied_doc_src = f'{_settings_prefix}-applied-doc-src' + +# Placeholder / tag DOM ids _tags = f'{prefix}-tags' placeholder_tooltip = f'{_tags}-placeholder-tooltip' tag = f'{_tags}-tag' @@ -55,6 +62,7 @@ _tag_add_upload = f'{_tag_add}-upload' _tag_add_warning = f'{_tag_add}-warning' _tag_add_save = f'{_tag_add}-save' + tag_modal = dbc.Modal([ dbc.ModalHeader('Add Placeholder'), dbc.ModalBody([ @@ -86,12 +94,11 @@ ]) ], id=_tag_add_modal, is_open=False) -# prompt history DOM ids +# Prompt history DOM ids history_body = f'{prefix}-history-body' history_store = f'{prefix}-history-store' -favorite_store = f'{prefix}-favorite-store' -# loading message/bar DOM ids +# Loading message/bar DOM ids _loading_prefix = f'{prefix}-loading' _loading_collapse = f'{_loading_prefix}-collapse' _loading_progress = f'{_loading_prefix}-progress-bar' @@ -99,33 +106,143 @@ submit = f'{prefix}-submit-btn' submit_warning_message = f'{prefix}-submit-warning-msg' -_student_data_wrapper = f'{prefix}-student-data' grid = f'{prefix}-essay-grid' -# Expanded student +# ── Expanded student modal DOM IDs ───────────────────────────────────── _expanded_student = f'{prefix}-expanded-student' -_expanded_student_selected = f'{_expanded_student}-selected' -_expanded_student_panel = f'{_expanded_student}-panel' +_expanded_student_modal = f'{_expanded_student}-modal' +_expanded_student_title = f'{_expanded_student}-title' _expanded_student_child = f'{_expanded_student}-child' -_expanded_student_close = f'{_expanded_student}-close' -expanded_student_component = html.Div([ - html.Div([ - html.H3('Individual Student', className='d-inline-block'), - dbc.Button( - html.I(className='fas fa-close'), - className='float-end', id=_expanded_student_close, - color='transparent'), - ]), - dbc.Input(id=_expanded_student_selected, class_name='d-none'), - html.Div(id=_expanded_student_child) -], className='p-2') - -# default prompts -system_prompt = 'You are a helpful assistant for grade school teachers. Your task is to analyze '\ - 'student writing and provide clear, constructive, and age-appropriate feedback. '\ - 'Focus on key writing traits such as clarity, creativity, grammar, and organization. '\ - 'When summarizing, highlight the main ideas and key details. Always maintain a '\ +_expanded_student_show_identity = f'{_expanded_student}-show-identity' +_expanded_student_show_identity_toggle = f'{_expanded_student}-show-identity-toggle' +_expanded_student_doc_title = f'{_expanded_student}-doc-title' +_expanded_student_id = f'{_expanded_student}-id' + +expanded_student_id_store = dcc.Store( + id=_expanded_student_id, + data=None +) + +expanded_student_show_identity_store = dcc.Store( + id=_expanded_student_show_identity, + data=True +) + +expanded_student_modal = dbc.Modal( + [ + dbc.ModalHeader([ + dbc.ModalTitle([ + html.Div(id=_expanded_student_title), + html.Small( + id=_expanded_student_doc_title, + className='text-muted ms-2', + style={'fontSize': '0.75em'} + ), + ], className='d-flex align-items-baseline'), + dbc.Button( + html.I(className='fas fa-eye', id=f'{_expanded_student_show_identity_toggle}-icon'), + id=_expanded_student_show_identity_toggle, + color='link', + size='sm', + className='ms-2 text-secondary', + title='Show/hide student name and document title', + ), + ], close_button=True, className='d-flex align-items-center'), + dbc.ModalBody( + html.Div(id=_expanded_student_child), + style={'overflowY': 'auto'}, + ), + ], + id=_expanded_student_modal, + is_open=False, + size='xl', + centered=True, + scrollable=True, +) + +# ── Walkthrough DOM IDs ──────────────────────────────────────────────── +_walkthrough_prefix = f'{prefix}-walkthrough' +_walkthrough_store = f'{_walkthrough_prefix}-step' +_walkthrough_modal = f'{_walkthrough_prefix}-modal' +_walkthrough_title = f'{_walkthrough_prefix}-title' +_walkthrough_body = f'{_walkthrough_prefix}-body' +_walkthrough_counter = f'{_walkthrough_prefix}-counter' +_walkthrough_back = f'{_walkthrough_prefix}-back' +_walkthrough_next = f'{_walkthrough_prefix}-next' +_walkthrough_done = f'{_walkthrough_prefix}-done' +_walkthrough_skip = f'{_walkthrough_prefix}-skip' +_walkthrough_seen_store = f'{_walkthrough_prefix}-seen' +_help_button = f'{prefix}-help' + +walkthrough_seen_store = dcc.Store( + id=_walkthrough_seen_store, + storage_type='local', + data=False +) + +walkthrough_store = dcc.Store(id=_walkthrough_store, data=0) + +walkthrough_modal = dbc.Modal( + [ + dbc.ModalHeader( + dbc.ModalTitle(id=_walkthrough_title), + close_button=False, + ), + dbc.ModalBody( + html.Div(id=_walkthrough_body), + style={'minHeight': '200px'}, + ), + dbc.ModalFooter( + html.Div([ + html.Small(id=_walkthrough_counter, className='text-muted me-auto'), + dbc.Button( + [html.I(className='fas fa-forward me-1'), 'Skip intro'], + id=_walkthrough_skip, + color='link', + size='sm', + className='me-auto text-muted', + ), + dbc.Button( + [html.I(className='fas fa-arrow-left me-1'), 'Back'], + id=_walkthrough_back, + color='secondary', + outline=True, + size='sm', + className='me-2', + ), + dbc.Button( + ['Next', html.I(className='fas fa-arrow-right ms-1')], + id=_walkthrough_next, + color='primary', + size='sm', + className='me-2', + ), + dbc.Button( + [html.I(className='fas fa-check me-1'), 'Get Started!'], + id=_walkthrough_done, + color='success', + size='sm', + ), + ], className='d-flex align-items-center w-100'), + ), + ], + id=_walkthrough_modal, + is_open=False, + centered=True, + backdrop='static', + keyboard=False, + size='lg', +) + +# ── Settings Modal ───────────────────────────────────────────────────── +# Default prompts +system_prompt = ( + 'You are a helpful assistant for grade school teachers. Your task is to analyze ' + 'student writing and provide clear, constructive, and age-appropriate feedback. ' + 'Focus on key writing traits such as clarity, creativity, grammar, and organization. ' + 'When summarizing, highlight the main ideas and key details. Always maintain a ' 'positive and encouraging tone to support student growth.' +) starting_prompt = [ 'Provide 3 bullet points summarizing this text:\n{student_text}', @@ -135,75 +252,144 @@ 'Give one specific compliment and one gentle suggestion to improve this story:\n{student_text}' ] - -def layout(): - ''' - Generic layout function to create dashboard - ''' - # advanced menu for system prompt - advanced = html.Div([ - html.Div([ - html.H3('Settings', className='d-inline-block'), - dbc.Button( - html.I(className='fas fa-close'), - className='float-end', id=_advanced_close, - color='transparent'), - ]), - lodrc.LODocumentSourceSelectorAIO(aio_id=_advanced_doc_src), +settings_modal = dbc.Modal([ + dbc.ModalHeader(dbc.ModalTitle('Dashboard Settings'), close_button=True), + dbc.ModalBody([ + lodrc.LODocumentSourceSelectorAIO(aio_id=_settings_doc_src), dbc.Card([ - dbc.CardHeader('View Options'), + dbc.CardHeader('System Prompt'), + dbc.CardBody([ + html.P( + "The system prompt guides the AI's behavior. It sets the context " + "for how the AI should analyze or summarize student text.", + className='text-muted small mb-2', + ), + dbc.Textarea( + id=_system_input_staged, + value=system_prompt, + style={'minHeight': '150px'} + ), + ]) + ], className='my-3'), + dbc.Card([ + dbc.CardHeader('Display Settings'), dbc.CardBody([ dbc.Label('Students per row'), - dbc.Input(type='number', min=1, max=10, value=2, step=1, id=_advanced_width), + dbc.Input(type='number', min=1, max=10, value=2, step=1, id=_settings_width), dbc.Label('Height of student tile'), - dcc.Slider(min=100, max=800, marks=None, value=350, id=_advanced_height), + dcc.Slider(min=100, max=800, marks=None, value=350, id=_settings_height), dbc.Label('Student profile'), - dbc.Switch(value=True, id=_advanced_hide_header, label='Show/Hide'), + dbc.Switch(value=True, id=_settings_hide_header, label='Show/Hide'), ]) - ]), + ], className='mb-3'), dbc.Card([ - dbc.CardHeader('Information Options'), - dbc.CardBody(lodrc.WOSettings( - id=_advanced_text_information, - options=wo_classroom_text_highlighter.options.PROCESS_OPTIONS, - value=wo_classroom_text_highlighter.options.DEFAULT_VALUE, - className='table table-striped align-middle' - )) + dbc.CardHeader('Metrics'), + dbc.CardBody([ + html.P( + 'Select which metrics to display as badges on each student tile.', + className='text-muted small mb-3', + ), + lodrc.WOSettings( + id=_settings_text_information, + options=wo_classroom_text_highlighter.options.PROCESS_OPTIONS, + value=wo_classroom_text_highlighter.options.DEFAULT_VALUE, + className='table table-striped align-middle' + ) + ]) ]) - ]) + ], style={'overflowY': 'auto'}), + dbc.ModalFooter( + dbc.Button( + [html.I(className='fas fa-check me-2'), 'Apply'], + id=_settings_run, + color='success', + size='lg', + className='w-100' + ) + ), +], id=_settings_modal, is_open=False, size='lg', scrollable=True, + style={'maxHeight': '100vh'}) + +# Hidden stores for applied values +applied_system_prompt_store = dcc.Store( + id=_applied_system_prompt, + data=system_prompt +) +applied_doc_src_store = dcc.Store( + id=_applied_doc_src, + data={} +) + +# Alert Component +alert_component = dbc.Alert([ + html.Div(id=_alert_text), + html.Div(DashRenderjson(id=_alert_error_dump), className='' if DEBUG_FLAG else 'd-none') +], id=_alert, color='danger', is_open=False) + +# Loading component +loading_component = dbc.Collapse([ + html.Div(id=_loading_information), + dbc.Progress(id=_loading_progress, animated=True, striped=True, max=1.1) +], id=_loading_collapse, is_open=False, class_name='mb-1 sticky-top bg-light') + +# ── Settings toolbar ─────────────────────────────────────────────────── +input_group = dbc.InputGroup([ + dbc.InputGroupText(lodrc.LOConnectionAIO(aio_id=_websocket)), + dbc.Button( + [html.I(className='fas fa-cog me-1'), 'Document Source & Display Settings'], + id=_settings_toggle, + color='secondary' + ), + dbc.Button( + [ + html.I(className='fas fa-question-circle me-1'), + 'Help' + ], + id=_help_button, + color='primary', + title='Reopen the walkthrough guide', + ), + lodrc.ProfileSidebarAIO(class_name='rounded-0 rounded-end', color='secondary'), +], class_name='mb-1 align-items-center') - # history panel + +def layout(): + ''' + Generic layout function to create dashboard + ''' + # History panel history_favorite_panel = dbc.Card([ dbc.CardHeader('Prompt History'), dbc.CardBody([], id=history_body), dcc.Store(id=history_store, data=[]) ], class_name='h-100') - # query creator panel + # Query creator panel input_panel = dbc.Card([ dbc.CardHeader('Prompt Input'), dbc.CardBody([ - dbc.Label([ - 'System prompt', - html.I(className='fas fa-circle-question ms-1', id=_system_input_tooltip) - ]), - dbc.Tooltip( - "A system prompt guides the AI's responses. It sets the context for how the AI should analyze or summarize student text.", - target=_system_input_tooltip - ), - dbc.Textarea(id=_system_input, value=system_prompt, style={'minHeight': '120px'}), dbc.Label('Query'), - dbc.Textarea(id=query_input, value=random.choice(starting_prompt), class_name='h-100', style={'minHeight': '150px'}), + dbc.Textarea( + id=query_input, + value=random.choice(starting_prompt), + class_name='h-100', + style={'minHeight': '150px'} + ), html.Div([ html.Span([ 'Placeholders', html.I(className='fas fa-circle-question ms-1', id=placeholder_tooltip) ], className='me-1'), html.Span([], id=_tags), - dbc.Button([html.I(className='fas fa-add me-1'), 'Add'], id=_tag_add_open, class_name='ms-1 mb-1') + dbc.Button( + [html.I(className='fas fa-add me-1'), 'Add'], + id=_tag_add_open, + class_name='ms-1 mb-1' + ) ], className='mt-1'), dbc.Tooltip( - 'Click a placeholder to insert it into your query. Upon submission, it will be replaced with the corresponding value.', + 'Click a placeholder to insert it into your query. Upon submission, ' + 'it will be replaced with the corresponding value.', target=placeholder_tooltip ), tag_modal, @@ -211,28 +397,36 @@ def layout(): ]), dbc.CardFooter([ html.Small(id=submit_warning_message, className='text-secondary'), - dbc.Button('Submit', color='primary', id=submit, n_clicks=0, class_name='float-end') + dbc.Button( + [html.I(className='fas fa-paper-plane me-1'), 'Submit'], + color='primary', + id=submit, + n_clicks=0, + class_name='float-end' + ) ]) ]) - alert_component = dbc.Alert([ - html.Div(id=alert_text), - html.Div(DashRenderjson(id=alert_error_dump), className='' if DEBUG_FLAG else 'd-none') - ], id=alert, color='danger', is_open=False) - - loading_component = dbc.Collapse([ - html.Div(id=_loading_information), - dbc.Progress(id=_loading_progress, animated=True, striped=True, max=1.1) - ], id=_loading_collapse, is_open=False, class_name='mb-1 sticky-top bg-light') - - # overall container cont = dbc.Container([ - html.H1('Writing Observer - Classroom AI Feedback Assistant'), - dbc.InputGroup([ - dbc.InputGroupText(lodrc.LOConnectionAIO(aio_id=_websocket)), - dbc.Button([html.I(className='fas fa-cog me-1'), 'Advanced'], id=_advanced_toggle), - lodrc.ProfileSidebarAIO(class_name='rounded-0 rounded-end', color='secondary'), - ], class_name='mb-1'), + html.H1('Writing Observer β€” Classroom AI Feedback Assistant'), + # Stores + applied_system_prompt_store, + applied_doc_src_store, + walkthrough_store, + walkthrough_seen_store, + expanded_student_id_store, + expanded_student_show_identity_store, + # Modals + walkthrough_modal, + settings_modal, + expanded_student_modal, + # Toolbar + html.Div([ + html.Div(input_group, className='d-flex me-2'), + html.Div(loading_component, className='d-flex') + ], className='d-flex sticky-top pb-1 bg-light'), + alert_component, + # Prompt input + history lodrc.LOPanelLayout( input_panel, panels=[ @@ -241,57 +435,97 @@ def layout(): shown=['history-favorite'], id=panel_layout ), - alert_component, html.H3('Student Text', className='mt-1'), - loading_component, - lodrc.LOPanelLayout( - html.Div(id=grid, className='d-flex justify-content-between flex-wrap'), - panels=[ - {'children': advanced, 'width': '30%', 'id': _advanced_collapse, 'side': 'left' }, - {'children': expanded_student_component, - 'width': '30%', 'id': _expanded_student_panel, - 'side': 'right'} - ], - id=_student_data_wrapper, shown=[] - ), + html.Div(id=grid, className='d-flex justify-content-between flex-wrap'), ], fluid=True) return html.Div(cont) -# Toggle if the advanced menu collapse is open or not +# ══════════════════════════════════════════════════════════════════════ +# Walkthrough callbacks +# ══════════════════════════════════════════════════════════════════════ + +# On load or when step changes, sync with localStorage +clientside_callback( + ClientsideFunction(namespace=_namespace, function_name='initWalkthroughFromStorage'), + Output(_walkthrough_store, 'data', allow_duplicate=True), + Output(_walkthrough_seen_store, 'data'), + Input(_walkthrough_store, 'data'), + State(_walkthrough_seen_store, 'data'), + prevent_initial_call='initial_duplicate', +) + +# Navigate between walkthrough steps +clientside_callback( + ClientsideFunction(namespace=_namespace, function_name='navigateWalkthrough'), + Output(_walkthrough_store, 'data'), + Input(_walkthrough_next, 'n_clicks'), + Input(_walkthrough_back, 'n_clicks'), + Input(_walkthrough_done, 'n_clicks'), + Input(_walkthrough_skip, 'n_clicks'), + Input(_help_button, 'n_clicks'), + State(_walkthrough_store, 'data'), + prevent_initial_call=True, +) + +# Render walkthrough step content +clientside_callback( + ClientsideFunction(namespace=_namespace, function_name='renderWalkthroughStep'), + Output(_walkthrough_title, 'children'), + Output(_walkthrough_body, 'children'), + Output(_walkthrough_back, 'disabled'), + Output(_walkthrough_next, 'style'), + Output(_walkthrough_done, 'style'), + Output(_walkthrough_counter, 'children'), + Output(_walkthrough_modal, 'is_open'), + Input(_walkthrough_store, 'data'), +) + +# ══════════════════════════════════════════════════════════════════════ +# Settings modal callbacks +# ══════════════════════════════════════════════════════════════════════ + +# Toggle settings modal open clientside_callback( - ClientsideFunction(namespace=_namespace, function_name='toggleAdvanced'), - Output(_student_data_wrapper, 'shown', allow_duplicate=True), - Input(_advanced_toggle, 'n_clicks'), - State(_student_data_wrapper, 'shown'), + ClientsideFunction(namespace=_namespace, function_name='toggleSettingsModal'), + Output(_settings_modal, 'is_open'), + Input(_settings_toggle, 'n_clicks'), + State(_settings_modal, 'is_open'), prevent_initial_call=True ) +# Apply settings and close modal clientside_callback( - ClientsideFunction(namespace=_namespace, function_name='closeAdvanced'), - Output(_student_data_wrapper, 'shown', allow_duplicate=True), - Input(_advanced_close, 'n_clicks'), - State(_student_data_wrapper, 'shown'), + ClientsideFunction(namespace=_namespace, function_name='applySettingsAndCloseModal'), + Output(_applied_system_prompt, 'data'), + Output(_applied_doc_src, 'data'), + Output(_settings_modal, 'is_open', allow_duplicate=True), + Input(_settings_run, 'n_clicks'), + State(_system_input_staged, 'value'), + State(lodrc.LODocumentSourceSelectorAIO.ids.kwargs_store(_settings_doc_src), 'data'), prevent_initial_call=True ) -# send request on websocket +# ══════════════════════════════════════════════════════════════════════ +# Core dashboard callbacks +# ══════════════════════════════════════════════════════════════════════ + +# Send request on websocket clientside_callback( - ClientsideFunction(namespace='bulk_essay_feedback', function_name='send_to_loconnection'), + ClientsideFunction(namespace=_namespace, function_name='send_to_loconnection'), Output(lodrc.LOConnectionAIO.ids.websocket(_websocket), 'send'), - Input(lodrc.LOConnectionAIO.ids.websocket(_websocket), 'state'), # used for initial setup + Input(lodrc.LOConnectionAIO.ids.websocket(_websocket), 'state'), Input('_pages_location', 'hash'), Input(submit, 'n_clicks'), - Input(lodrc.LODocumentSourceSelectorAIO.ids.kwargs_store(_advanced_doc_src), 'data'), + Input(_applied_doc_src, 'data'), State(query_input, 'value'), - State(_system_input, 'value'), + State(_applied_system_prompt, 'data'), State(tag_store, 'data'), ) -# enable/disabled submit based on query -# makes sure there is a query and the tags are properly formatted +# Enable/disable submit based on query clientside_callback( - ClientsideFunction(namespace='bulk_essay_feedback', function_name='disableQuerySubmitButton'), + ClientsideFunction(namespace=_namespace, function_name='disableQuerySubmitButton'), Output(submit, 'disabled'), Output(submit_warning_message, 'children'), Input(query_input, 'value'), @@ -299,24 +533,23 @@ def layout(): Input(tag_store, 'data') ) -# add submitted query to history and clear input +# Add submitted query to history clientside_callback( - ClientsideFunction(namespace='bulk_essay_feedback', function_name='update_input_history_on_query_submission'), + ClientsideFunction(namespace=_namespace, function_name='update_input_history_on_query_submission'), Output(history_store, 'data'), Input(submit, 'n_clicks'), State(query_input, 'value'), State(history_store, 'data') ) -# update history based on history browser storage -# TODO create a history component that can handle favorites +# Update history list display clientside_callback( - ClientsideFunction(namespace='bulk_essay_feedback', function_name='update_history_list'), + ClientsideFunction(namespace=_namespace, function_name='update_history_list'), Output(history_body, 'children'), Input(history_store, 'data') ) -# Toggle if the add placeholder is open or not +# Toggle add placeholder modal clientside_callback( ClientsideFunction(namespace=_namespace, function_name='openTagAddModal'), Output(_tag_add_modal, 'is_open'), @@ -329,9 +562,9 @@ def layout(): State({'type': _tag_edit, 'index': ALL}, 'id'), ) -# show attachment panel upon uploading document and populate fields +# Handle file upload to placeholder text field clientside_callback( - ClientsideFunction(namespace='bulk_essay_feedback', function_name='handleFileUploadToTextField'), + ClientsideFunction(namespace=_namespace, function_name='handleFileUploadToTextField'), Output(_tag_add_text, 'value', allow_duplicate=True), Input(_tag_add_upload, 'contents'), Input(_tag_add_upload, 'filename'), @@ -339,30 +572,31 @@ def layout(): prevent_initial_call=True ) +# Update alert with errors clientside_callback( ClientsideFunction(namespace=_namespace, function_name='updateAlertWithError'), - Output(alert_text, 'children'), - Output(alert, 'is_open'), - Output(alert_error_dump, 'data'), + Output(_alert_text, 'children'), + Output(_alert, 'is_open'), + Output(_alert_error_dump, 'data'), Input(lodrc.LOConnectionAIO.ids.error_store(_websocket), 'data') ) -# update student cards based on new data in storage +# Update student grid clientside_callback( ClientsideFunction(namespace=_namespace, function_name='updateStudentGridOutput'), Output(grid, 'children'), Input(lodrc.LOConnectionAIO.ids.ws_store(_websocket), 'data'), Input(history_store, 'data'), - Input(_advanced_width, 'value'), - Input(_advanced_height, 'value'), - Input(_advanced_hide_header, 'value'), - Input(_advanced_text_information, 'value'), - State(_advanced_text_information, 'options') + Input(_settings_width, 'value'), + Input(_settings_height, 'value'), + Input(_settings_hide_header, 'value'), + Input(_settings_text_information, 'value'), + State(_settings_text_information, 'options') ) -# append tag in curly braces to input +# Append tag to query input clientside_callback( - ClientsideFunction(namespace='bulk_essay_feedback', function_name='add_tag_to_input'), + ClientsideFunction(namespace=_namespace, function_name='add_tag_to_input'), Output(query_input, 'value', allow_duplicate=True), Input({'type': tag, 'index': ALL}, 'n_clicks'), State(query_input, 'value'), @@ -370,9 +604,9 @@ def layout(): prevent_initial_call=True ) -# enable/disable the save attachment button if tag is already in use/blank +# Enable/disable save attachment button clientside_callback( - ClientsideFunction(namespace='bulk_essay_feedback', function_name='disableAttachmentSaveButton'), + ClientsideFunction(namespace=_namespace, function_name='disableAttachmentSaveButton'), Output(_tag_add_save, 'disabled'), Output(_tag_add_warning, 'children'), Input(_tag_add_label, 'value'), @@ -381,16 +615,16 @@ def layout(): State(_tag_replacement_id, 'value') ) -# populate word bank of tags +# Populate tag word bank clientside_callback( - ClientsideFunction(namespace='bulk_essay_feedback', function_name='update_tag_buttons'), + ClientsideFunction(namespace=_namespace, function_name='update_tag_buttons'), Output(_tags, 'children'), Input(tag_store, 'data') ) -# save placeholder to storage +# Save placeholder to storage clientside_callback( - ClientsideFunction(namespace='bulk_essay_feedback', function_name='savePlaceholder'), + ClientsideFunction(namespace=_namespace, function_name='savePlaceholder'), Output(tag_store, 'data'), Output(_tag_add_modal, 'is_open', allow_duplicate=True), Input(_tag_add_save, 'n_clicks'), @@ -401,9 +635,9 @@ def layout(): prevent_initial_call=True ) -# remove placeholder from storage +# Remove placeholder from storage clientside_callback( - ClientsideFunction(namespace='bulk_essay_feedback', function_name='removePlaceholder'), + ClientsideFunction(namespace=_namespace, function_name='removePlaceholder'), Output(tag_store, 'data', allow_duplicate=True), Input({'type': _tag_delete, 'index': ALL}, 'submit_n_clicks'), State(tag_store, 'data'), @@ -411,7 +645,7 @@ def layout(): prevent_initial_call=True ) -# update loading information +# Update loading information clientside_callback( ClientsideFunction(namespace=_namespace, function_name='updateLoadingInformation'), Output(_loading_collapse, 'is_open'), @@ -426,40 +660,63 @@ def layout(): ClientsideFunction(namespace=_namespace, function_name='adjustTileSize'), Output({'type': 'WOAIAssistStudentTile', 'index': ALL}, 'style', allow_duplicate=True), Output({'type': 'WOAIAssistStudentTileText', 'index': ALL}, 'style', allow_duplicate=True), - Input(_advanced_width, 'value'), - Input(_advanced_height, 'value'), + Input(_settings_width, 'value'), + Input(_settings_height, 'value'), State({'type': 'WOAIAssistStudentTile', 'index': ALL}, 'id'), prevent_initial_call=True ) -# Expand a single student +# Expand a single student into modal +# ── Expand: record which student was clicked, open modal ────────────── clientside_callback( - ClientsideFunction(namespace=_namespace, function_name='selectStudentForExpansion'), - Output(_expanded_student_selected, 'value'), - Output(_student_data_wrapper, 'shown', allow_duplicate=True), + ClientsideFunction(namespace=_namespace, function_name='expandCurrentStudent'), + Output(_expanded_student_id, 'data'), + Output(_expanded_student_modal, 'is_open'), + Output(_expanded_student_show_identity, 'data'), Input({'type': 'WOAIAssistStudentTileExpand', 'index': ALL}, 'n_clicks'), - State(_student_data_wrapper, 'shown'), State({'type': 'WOAIAssistStudentTile', 'index': ALL}, 'id'), + State(_expanded_student_modal, 'is_open'), + State(_expanded_student_id, 'data'), + State(_settings_hide_header, 'value'), prevent_initial_call=True ) -# Update expanded children based on selected student +# ── Expand: reactively render content from live websocket data ──────── clientside_callback( - ClientsideFunction(namespace=_namespace, function_name='expandSelectedStudent'), + ClientsideFunction(namespace=_namespace, function_name='renderExpandedStudent'), + Output(_expanded_student_title, 'children'), + Output(_expanded_student_doc_title, 'children'), Output(_expanded_student_child, 'children'), - Input(_expanded_student_selected, 'value'), Input(lodrc.LOConnectionAIO.ids.ws_store(_websocket), 'data'), - Input(_advanced_hide_header, 'value'), - Input(history_store, 'data'), - Input(_advanced_text_information, 'value'), - State(_advanced_text_information, 'options') + Input(_expanded_student_id, 'data'), + State(_expanded_student_modal, 'is_open'), + State(history_store, 'data'), + State(_settings_text_information, 'value'), + State(_settings_text_information, 'options'), ) -# Close expanded student +# Toggle identity visibility within the expanded modal clientside_callback( - ClientsideFunction(namespace=_namespace, function_name='closeExpandedStudent'), - Output(_student_data_wrapper, 'shown', allow_duplicate=True), - Input(_expanded_student_close, 'n_clicks'), - State(_student_data_wrapper, 'shown'), + ClientsideFunction(namespace=_namespace, function_name='toggleExpandedStudentIdentity'), + Output(_expanded_student_show_identity, 'data', allow_duplicate=True), + Input(_expanded_student_show_identity_toggle, 'n_clicks'), + State(_expanded_student_show_identity, 'data'), prevent_initial_call=True ) + +# Render identity visibility in the expanded modal (hide/show name, doc title, icon) +clientside_callback( + ClientsideFunction(namespace=_namespace, function_name='renderExpandedStudentIdentity'), + Output(_expanded_student_title, 'style'), + Output(_expanded_student_doc_title, 'style'), + Output(f'{_expanded_student_show_identity_toggle}-icon', 'className'), + Input(_expanded_student_show_identity, 'data'), +) + +# Show/hide student tile headers +clientside_callback( + ClientsideFunction(namespace=_namespace, function_name='showHideHeader'), + Output({'type': 'WOAIAssistStudentTileText', 'index': ALL}, 'showName'), + Input(_settings_hide_header, 'value'), + State({'type': 'WOAIAssistStudentTileText', 'index': ALL}, 'id'), +) diff --git a/modules/wo_classroom_text_highlighter/VERSION b/modules/wo_classroom_text_highlighter/VERSION index 696424a49..fff79c93a 100644 --- a/modules/wo_classroom_text_highlighter/VERSION +++ b/modules/wo_classroom_text_highlighter/VERSION @@ -1 +1 @@ -0.1.0+2025.09.30T15.53.18.556Z.1c72d9e1.master +0.1.0+2026.02.20T19.02.56.547Z.85e344b1.berickson.20260210.dashboard.updates diff --git a/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/assets/general.css b/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/assets/general.css index 9138b7239..839d40551 100644 --- a/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/assets/general.css +++ b/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/assets/general.css @@ -21,4 +21,11 @@ .preset button:last-child { border-top-left-radius: 0; border-bottom-left-radius: 0; -} \ No newline at end of file +} + +.modal-backdrop.show { + backdrop-filter: blur(20px) saturate(0.5); + -webkit-backdrop-filter: blur(20px) saturate(0.5); + background-color: rgba(0, 0, 0, 0.7) !important; + opacity: 1 !important; +} diff --git a/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/assets/scripts.js b/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/assets/scripts.js index 7510b2d37..7ffae744e 100644 --- a/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/assets/scripts.js +++ b/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/assets/scripts.js @@ -2,7 +2,6 @@ * Javascript callbacks to be used with the LO Example dashboard */ -// Initialize the `dash_clientside` object if it doesn't exist if (!window.dash_clientside) { window.dash_clientside = {}; } @@ -12,7 +11,6 @@ const DASH_CORE_COMPONENTS = 'dash_core_components'; const DASH_BOOTSTRAP_COMPONENTS = 'dash_bootstrap_components'; const LO_DASH_REACT_COMPONENTS = 'lo_dash_react_components'; -// TODO this ought to move to a more common place function createDashComponent (namespace, type, props) { return { namespace, type, props }; } @@ -25,13 +23,25 @@ function determineSelectedNLPOptionsList (optionsObj) { ); } -// TODO this ought to move to a more common place like liblo.js +const checkForResponse = function (s, promptHash, options) { + if (!('documents' in s)) { return false; } + const selectedDocument = s.doc_id || Object.keys(s.documents || {})[0] || ''; + const student = s.documents[selectedDocument]; + if (!student) { return false; } + return options.every(option => { + const hashKey = `option_hash_${option}`; + if (hashKey in student) { + return promptHash === student[hashKey]; + } + return option in student; + }); +}; + async function hashObject (obj) { const jsonString = JSON.stringify(obj); const encoder = new TextEncoder(); const data = encoder.encode(jsonString); - // Check if crypto.subtle is available if (crypto && crypto.subtle) { try { const hashBuffer = await crypto.subtle.digest('SHA-256', data); @@ -43,7 +53,6 @@ async function hashObject (obj) { } } - // Fallback to the simple hash if crypto.subtle is unavailable return simpleHash(jsonString); } @@ -52,7 +61,7 @@ function simpleHash (str) { for (let i = 0; i < str.length; i++) { const char = str.charCodeAt(i); hash = (hash << 5) - hash + char; - hash |= 0; // Convert to 32-bit integer + hash |= 0; } return hash.toString(16); } @@ -83,9 +92,9 @@ function styleStudentTile (width, height) { } function fetchSelectedItemsFromOptions (value, options, type) { - return options.reduce(function(filtered, option) { + return options.reduce(function (filtered, option) { if (value?.[option.id]?.[type]?.value) { - const selected = {...option, ...value[option.id]}; + const selected = { ...option, ...value[option.id] }; filtered.push(selected); } return filtered; @@ -107,21 +116,237 @@ function createProcessTags (document, metrics) { { children: document[metric.id], color } ); default: - break + break; } }); - return createDashComponent(DASH_HTML_COMPONENTS, 'Div', { children, className: 'sticky-top' }) + return createDashComponent(DASH_HTML_COMPONENTS, 'Div', { children, className: 'sticky-top' }); } + +function studentHasResponded (student, appliedHash) { + const documents = student.documents || {}; + const docKeys = Object.keys(documents); + + if (docKeys.length === 0) { return false; } + + for (const docKey of docKeys) { + const doc = documents[docKey]; + if (!doc) { return false; } + + const docHash = doc.option_hash_docs_with_nlp_annotations; + if (docHash !== appliedHash) { + return false; + } + } + return true; +} + const ClassroomTextHighlightLoadingQueries = ['docs_with_nlp_annotations', 'time_on_task', 'activity']; +// ── Walkthrough step definitions ────────────────────────────────────── +const WALKTHROUGH_STEPS = [ + { + title: 'Welcome to the Classroom Text Highlighter!', + icon: 'fas fa-chalkboard-teacher', + body: [ + 'This dashboard lets you see every student\'s writing at a glance, with ', + 'color-coded highlights that surface things like grammar patterns, ', + 'argument strength, vocabulary usage, and more.', + '\n\n', + 'Let\'s walk through how to get started β€” it only takes a minute.' + ].join('') + }, + { + title: 'Step 1 β€” Choose What to Highlight', + icon: 'fas fa-highlighter', + body: [ + 'Click the "Choose What to Highlight" button in the toolbar at the top. ', + 'This opens a setup panel where you pick which analyses to run on ', + 'your students\' writing.\n\n', + 'You\'ll see two types of options:\n', + 'β€’ Highlights β€” color-coded annotations on the text (e.g. grammar types, ', + 'key claims)\n', + 'β€’ Metrics β€” summary badges shown on each tile (e.g. time on task, ', + 'active/inactive status)' + ].join('') + }, + { + title: 'Step 2 β€” Click Run to Load Results', + icon: 'fas fa-play-circle', + body: [ + 'After choosing your highlights and metrics, click the green "Run" button ', + 'at the bottom of the panel.\n\n', + 'The dashboard will fetch analysis for every student in your class. ', + 'A progress bar will appear while results load β€” this can take a minute ', + 'or two for larger classes.' + ].join('') + }, + { + title: 'Step 3 β€” Read and Explore Student Work', + icon: 'fas fa-search-plus', + body: [ + 'Each tile represents one student. Their writing is displayed with your ', + 'chosen highlights applied directly to the text.\n\n', + 'β€’ Click the expand icon (β€’) on any tile to open that student\'s writing ', + 'in a larger, more readable view.\n', + 'β€’ Use the "Highlight Key" button in the toolbar to see what each color means.\n', + 'β€’ Hover over any highlighted word to see which specific annotations apply to it.' + ].join('') + }, + { + title: 'You\'re Ready!', + icon: 'fas fa-check-circle', + body: [ + 'That\'s everything you need to get started.\n\n', + 'You can change your highlight selections at any time by clicking ', + '"Choose What to Highlight" again and pressing "Run."\n\n', + 'To revisit this guide later, click the ', + 'help button (?) in the toolbar.' + ].join('') + } +]; + +/** + * Build the walkthrough modal body for a given step index. + */ +function buildWalkthroughBody (step) { + const info = WALKTHROUGH_STEPS[step]; + const paragraphs = info.body.split('\n').filter(Boolean).map(line => + createDashComponent(DASH_HTML_COMPONENTS, 'P', { + children: line, + className: 'mb-2', + style: { whiteSpace: 'pre-wrap' } + }) + ); + return createDashComponent(DASH_HTML_COMPONENTS, 'Div', { + children: [ + createDashComponent(DASH_HTML_COMPONENTS, 'Div', { + children: createDashComponent(DASH_HTML_COMPONENTS, 'I', { + className: `${info.icon} fa-3x text-primary mb-3` + }), + className: 'text-center' + }), + ...paragraphs + ] + }); +} + +/** + * Build an empty-state placeholder when no student data is loaded yet. + */ +function buildEmptyState () { + return createDashComponent(DASH_HTML_COMPONENTS, 'Div', { + className: 'd-flex flex-column align-items-center justify-content-center text-center py-5 w-100', + style: { minHeight: '300px', color: '#6c757d' }, + children: [ + createDashComponent(DASH_HTML_COMPONENTS, 'I', { + className: 'fas fa-users fa-4x mb-3', + style: { opacity: 0.3 } + }), + createDashComponent(DASH_HTML_COMPONENTS, 'H4', { + children: 'No student data loaded yet', + className: 'mb-3' + }), + createDashComponent(DASH_HTML_COMPONENTS, 'P', { + children: 'To get started:', + className: 'mb-2 fw-bold' + }), + createDashComponent(DASH_HTML_COMPONENTS, 'Div', { + className: 'text-start', + style: { maxWidth: '400px' }, + children: [ + createDashComponent(DASH_HTML_COMPONENTS, 'P', { + className: 'mb-1', + children: '1. Click "Choose What to Highlight" in the toolbar above' + }), + createDashComponent(DASH_HTML_COMPONENTS, 'P', { + className: 'mb-1', + children: '2. Select the highlights and metrics you\'d like to see' + }), + createDashComponent(DASH_HTML_COMPONENTS, 'P', { + className: 'mb-1', + children: '3. Click the green "Run" button to load student data' + }) + ] + }), + createDashComponent(DASH_HTML_COMPONENTS, 'P', { + className: 'mt-3 text-muted small', + children: 'Click the ? button for a full walkthrough.' + }) + ] + }); +} + window.dash_clientside.wo_classroom_text_highlighter = { + // ── Walkthrough callbacks ──────────────────────────────────────────── + /** - * Send updated queries to the communication protocol. - * @param {object} wsReadyState LOConnection status object - * @param {string} urlHash query string from hash for determining course id - * @returns stringified json object that is sent to the communication protocl + * Navigate walkthrough steps. Triggered by next, back, done, or help button. + * Returns the new step index (0..N-1) or -1 when dismissed. */ - sendToLOConnection: async function (wsReadyState, urlHash, docKwargs, nlpValue) { + navigateWalkthrough: function (nextClicks, backClicks, doneClicks, skipClicks, helpClicks, currentStep) { + const triggered = window.dash_clientside.callback_context?.triggered_id; + if (!triggered) { return window.dash_clientside.no_update; } + + const totalSteps = WALKTHROUGH_STEPS.length; + + switch (triggered) { + case 'wo-classroom-text-highlighter-walkthrough-next': + return Math.min(currentStep + 1, totalSteps - 1); + case 'wo-classroom-text-highlighter-walkthrough-back': + return Math.max(currentStep - 1, 0); + case 'wo-classroom-text-highlighter-walkthrough-done': + case 'wo-classroom-text-highlighter-walkthrough-skip': + return -1; + case 'wo-classroom-text-highlighter-help': + return 0; + default: + return window.dash_clientside.no_update; + } + }, + + /** + * Render the walkthrough modal based on the current step. + * Returns [title, body, backDisabled, nextStyle, doneStyle, stepCounter, isOpen]. + */ + renderWalkthroughStep: function (step) { + const totalSteps = WALKTHROUGH_STEPS.length; + const isOpen = step >= 0 && step < totalSteps; + + if (!isOpen) { + return [ + '', '', true, + { display: 'inline-block' }, + { display: 'none' }, + '', + false + ]; + } + + const info = WALKTHROUGH_STEPS[step]; + const body = buildWalkthroughBody(step); + const isFirst = step === 0; + const isLast = step === totalSteps - 1; + const counter = `${step + 1} of ${totalSteps}`; + + return [ + info.title, + body, + isFirst, + { display: isLast ? 'none' : 'inline-block' }, + { display: isLast ? 'inline-block' : 'none' }, + counter, + true + ]; + }, + + computeAppliedHash: async function (appliedValue) { + if (!appliedValue) { return ''; } + const h = await hashObject(appliedValue); + // console.log('[computeAppliedHash] computed hash:', h.substring(0, 12) + '...'); + return h; + }, + + sendToLOConnection: function (wsReadyState, urlHash, docKwargs, appliedHash, nlpValue) { if (wsReadyState === undefined) { return window.dash_clientside.no_update; } @@ -130,12 +355,17 @@ window.dash_clientside.wo_classroom_text_highlighter = { const decodedParams = decode_string_dict(urlHash.slice(1)); if (!decodedParams.course_id) { return window.dash_clientside.no_update; } - const optionsHash = await hashObject(nlpValue); + if (!appliedHash) { + // console.log('[sendToLOConnection] No hash yet, skipping'); + return window.dash_clientside.no_update; + } + const nlpOptions = determineSelectedNLPOptionsList(nlpValue); decodedParams.nlp_options = nlpOptions; - decodedParams.option_hash = optionsHash; + decodedParams.option_hash = appliedHash; decodedParams.doc_source = docKwargs.src; decodedParams.doc_source_kwargs = docKwargs.kwargs; + decodedParams.rerun_dag_delay = 45; const outgoingMessage = { wo_classroom_text_highlighter_query: { execution_dag: 'writing_observer', @@ -143,35 +373,27 @@ window.dash_clientside.wo_classroom_text_highlighter = { kwargs: decodedParams } }; + // console.log('[sendToLOConnection] Sending with hash:', appliedHash.substring(0, 12) + '...'); return JSON.stringify(outgoingMessage); } return window.dash_clientside.no_update; }, - // toggleOptions: function (clicks, isOpen) { - // if (!clicks) { - // return window.dash_clientside.no_update; - // } - // return !isOpen; - // }, + toggleOptionsModal: function (clicks, isOpen) { + if (!clicks) { return window.dash_clientside.no_update; } + return !isOpen; + }, - toggleOptions: function (clicks, shown) { + applyOptionsAndCloseModal: function (clicks, stagedValue, docKwargs) { if (!clicks) { - return window.dash_clientside.no_update; - } - const optionPrefix = 'wo-classroom-text-highlighter-options'; - if (shown.includes(optionPrefix)) { - shown = shown.filter(item => item !== optionPrefix); - } else { - shown = shown.concat(optionPrefix); + return [ + window.dash_clientside.no_update, + window.dash_clientside.no_update, + window.dash_clientside.no_update + ]; } - return shown; - }, - - closeOptions: function (clicks, shown) { - if (!clicks) { return window.dash_clientside.no_update; } - shown = shown.filter(item => item !== 'wo-classroom-text-highlighter-options'); - return shown; + // console.log('[applyOptionsAndCloseModal] Applying staged options and doc source'); + return [stagedValue, docKwargs, false]; }, adjustTileSize: function (width, height, studentIds) { @@ -184,31 +406,35 @@ window.dash_clientside.wo_classroom_text_highlighter = { return Array(total).fill(show ? 'd-none' : ''); }, - updateCurrentOptionHash: async function (value, ids) { - const optionHash = await hashObject(value); + updateCurrentOptionHash: function (appliedHash, ids) { + if (!appliedHash) { + return window.dash_clientside.no_update; + } const total = ids.length; - return Array(total).fill(optionHash); + // console.log('[updateCurrentOptionHash] Broadcasting hash to', total, 'tiles:', appliedHash.substring(0, 12) + '...'); + return Array(total).fill(appliedHash); }, - /** - * Build the student UI components based on the stored websocket data - * @param {*} wsStorageData information stored in the websocket store - * @returns Dash object to be displayed on page - */ - populateOutput: async function (wsStorageData, value, width, height, showName, options) { - // console.log('wsStorageData', wsStorageData); + populateOutput: function (wsStorageData, value, width, height, showName, options, optionHash) { if (!wsStorageData?.students) { - return 'No students'; + return buildEmptyState(); + } + + const students = wsStorageData.students; + if (Object.keys(students).length === 0) { + return buildEmptyState(); } + let output = []; const selectedHighlights = fetchSelectedItemsFromOptions(value, options, 'highlight'); const selectedMetrics = fetchSelectedItemsFromOptions(value, options, 'metric'); - const optionHash = await hashObject(value); - const students = wsStorageData.students; + // console.log('[populateOutput] Using hash:', optionHash ? optionHash.substring(0, 12) + '...' : 'NONE'); + for (const student in students) { const selectedDocument = students[student].doc_id || Object.keys(students[student].documents || {})[0] || ''; + const documentTitle = students[student]?.availableDocuments?.[selectedDocument]?.title ?? selectedDocument ?? ''; const studentTileChild = createDashComponent( DASH_HTML_COMPONENTS, 'Div', { @@ -227,6 +453,7 @@ window.dash_clientside.wo_classroom_text_highlighter = { showName, profile: students[student].documents[selectedDocument]?.profile || {}, selectedDocument, + documentTitle, childComponent: studentTileChild, id: { type: 'WOStudentTextTile', index: student }, currentStudentHash: students[student].documents[selectedDocument]?.option_hash_docs_with_nlp_annotations, @@ -284,44 +511,157 @@ window.dash_clientside.wo_classroom_text_highlighter = { return data[preset]; }, - updateLoadingInformation: async function (wsStorageData, nlpValue) { + updateLoadingInformation: function (wsStorageData, appliedHash) { const noLoading = [false, 0, '']; - if (!wsStorageData?.students) { + + if (!wsStorageData?.students || !appliedHash) { return noLoading; } + const students = wsStorageData.students; - const promptHash = await hashObject(nlpValue); - const returnedResponses = Object.values(students).filter(student => checkForResponse(student, promptHash, ClassroomTextHighlightLoadingQueries)).length; const totalStudents = Object.keys(students).length; - if (totalStudents === returnedResponses) { return noLoading; } + + if (totalStudents === 0) { + return noLoading; + } + + let returnedResponses = 0; + + for (const studentId of Object.keys(students)) { + const student = students[studentId]; + if (checkForResponse(student, appliedHash, ClassroomTextHighlightLoadingQueries)) { + returnedResponses++; + } + } + + // console.log(`[updateLoadingInformation] ${returnedResponses}/${totalStudents} responded for hash=${appliedHash.substring(0, 12)}...`); + + if (totalStudents === returnedResponses) { + return noLoading; + } + const loadingProgress = returnedResponses / totalStudents + 0.1; const outputText = `Fetching responses from server. This will take a few minutes. (${returnedResponses}/${totalStudents} received)`; return [true, loadingProgress, outputText]; }, - expandCurrentStudent: function (clicks, children, ids, shownPanels, currentChild) { + /** + * When a student tile expand button is clicked, record which student + * was selected and open the modal. + * + * Returns [selectedStudentId, isModalOpen, showIdentity]. + */ + expandCurrentStudent: function (clicks, ids, isModalOpen, currentStudentId, globalShowName) { const triggeredItem = window.dash_clientside.callback_context?.triggered_id ?? null; if (!triggeredItem) { return window.dash_clientside.no_update; } - let child = ''; - let id = null; - if (triggeredItem?.type === 'WOStudentTile') { - if (!currentChild) { return window.dash_clientside.no_update; } - id = currentChild?.props.id.index; - } else if (triggeredItem?.type === 'WOStudentTileExpand') { - id = triggeredItem?.index; - shownPanels = shownPanels.concat('wo-classroom-text-highlighter-expanded-student-panel'); - } else { + + // Only act on actual expand button clicks + if (triggeredItem?.type !== 'WOStudentTileExpand') { return window.dash_clientside.no_update; } + + // Make sure something was actually clicked (not just initial callback fire) + const hasActualClick = clicks && clicks.some(c => c !== undefined && c !== null && c > 0); + if (!hasActualClick) { return window.dash_clientside.no_update; } + + const id = triggeredItem?.index; const index = ids.findIndex(item => item.index === id); - child = children[index][0]; - return [child, shownPanels]; + if (index === -1) { return window.dash_clientside.no_update; } + + const showIdentity = globalShowName !== undefined ? globalShowName : true; + + return [id, true, showIdentity]; + }, + + /** + * Reactively render the expanded student modal content from live + * websocket data. Only builds content when the modal is actually open + * and a student is selected. + * + * Returns [studentName, docTitle, childContent]. + */ + renderExpandedStudent: function (wsStorageData, selectedStudentId, isModalOpen, value, options, optionHash) { + // Don't do anything if the modal isn't open + if (!isModalOpen) { + return window.dash_clientside.no_update; + } + + if (!selectedStudentId || !wsStorageData?.students) { + return [ + '', + '', + createDashComponent(DASH_HTML_COMPONENTS, 'Div', { + children: 'No student selected.', + className: 'text-muted text-center py-5' + }) + ]; + } + + const student = wsStorageData.students[selectedStudentId]; + if (!student) { + return [ + 'Student', + '', + createDashComponent(DASH_HTML_COMPONENTS, 'Div', { + children: 'Student data not available.', + className: 'text-muted text-center py-5' + }) + ]; + } + + const selectedDocument = student.doc_id || Object.keys(student.documents || {})[0] || ''; + const documentName = student?.availableDocuments?.[selectedDocument]?.title ?? selectedDocument ?? ''; + const doc = student.documents?.[selectedDocument]; + + if (!doc) { + return [ + 'Student', + documentName, + createDashComponent(DASH_HTML_COMPONENTS, 'Div', { + children: 'Document data not available yet.', + className: 'text-muted text-center py-5' + }) + ]; + } + + const names = doc.profile?.name || {}; + const studentName = [names.given_name, names.family_name] + .filter(Boolean) + .join(' ') || 'Student'; + + const selectedHighlights = fetchSelectedItemsFromOptions(value, options, 'highlight'); + const selectedMetrics = fetchSelectedItemsFromOptions(value, options, 'metric'); + + const childContent = createDashComponent( + DASH_HTML_COMPONENTS, 'Div', + { + children: [ + createProcessTags({ ...doc }, selectedMetrics), + createDashComponent( + LO_DASH_REACT_COMPONENTS, 'WOAnnotatedText', + formatStudentData({ ...doc }, selectedHighlights) + ) + ] + } + ); + + return [studentName, documentName, childContent]; }, - closeExpandedStudent: function (clicks, shown) { + toggleExpandedStudentIdentity: function (clicks, currentValue) { if (!clicks) { return window.dash_clientside.no_update; } - shown = shown.filter(item => item !== 'wo-classroom-text-highlighter-expanded-student-panel'); - return shown; + return !currentValue; + }, + + renderExpandedStudentIdentity: function (showIdentity) { + const visible = { }; + const hidden = { display: 'none' }; + + const titleStyle = showIdentity ? visible : hidden; + const docTitleStyle = showIdentity ? visible : hidden; + const iconClass = showIdentity ? 'fas fa-eye' : 'fas fa-eye-slash'; + + return [titleStyle, docTitleStyle, iconClass]; }, updateLegend: function (value, options) { @@ -330,7 +670,10 @@ window.dash_clientside.wo_classroom_text_highlighter = { const total = selectedHighlights.length + selectedMetrics.length; if (selectedHighlights.length === 0) { - return ['No options selected. Click on the `Options` to select them.', total]; + return [ + 'No highlights selected yet. Click "Choose What to Highlight" in the toolbar, select your options, then click "Run."', + total + ]; } let output = selectedHighlights.map(highlight => { const color = highlight.highlight.color; @@ -348,7 +691,37 @@ window.dash_clientside.wo_classroom_text_highlighter = { ); return legendItem; }); - output = output.concat('Note: words in the student text may have multiple highlights. Hover over a word for the full list of which options apply'); + output = output.concat('Note: words in the student text may have multiple highlights. Hover over a word for the full list of which options apply.'); return [output, total]; - } + }, + + /** + * Determine the initial walkthrough step based on whether the user + * has previously completed/skipped it (persisted in localStorage). + * Also writes back to localStorage when the walkthrough is dismissed. + * + * Returns [walkthroughStep, seenFlag]. + */ + initWalkthroughFromStorage: function (step, hasSeenWalkthrough) { + // On initial load (no trigger), check localStorage flag + const triggered = window.dash_clientside.callback_context?.triggered_id; + + if (!triggered) { + // Initial load: if they've seen it before, keep it closed + if (hasSeenWalkthrough) { + return [-1, true]; + } + // First visit: open at step 0 + return [0, false]; + } + + // Step changed (user navigated/dismissed/reopened) + if (step === -1) { + // User dismissed or completed β€” mark as seen + return [-1, true]; + } + + // User reopened via help button or is navigating + return [step, hasSeenWalkthrough]; + }, }; diff --git a/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/dash_dashboard.py b/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/dash_dashboard.py index 1ac0f18b0..a7badda57 100644 --- a/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/dash_dashboard.py +++ b/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/dash_dashboard.py @@ -32,70 +32,217 @@ # Option components _options_toggle = f'{_prefix}-options-toggle' _options_toggle_count = f'{_prefix}-options-toggle-count' -_options_collapse = f'{_prefix}-options-collapse' -_options_close = f'{_prefix}-options-close' -# TODO abstract these into a more generic options component +_options_modal = f'{_prefix}-options-modal' +_options_run = f'{_prefix}-options-run' _options_prefix = f'{_prefix}-options' _options_doc_src = f'{_options_prefix}-document-source' _options_width = f'{_options_prefix}-width' _options_height = f'{_options_prefix}-height' _options_hide_header = f'{_options_prefix}-hide-names' _options_text_information = f'{_options_prefix}-text-information' +_options_text_information_staged = f'{_options_text_information}-staged' -options_component = html.Div([ - html.Div([ - html.H3('Settings', className='d-inline-block'), - dbc.Button( - html.I(className='fas fa-close'), - className='float-end', id=_options_close, - color='transparent'), - ]), - lodrc.LODocumentSourceSelectorAIO(aio_id=_options_doc_src), - dbc.Card([ - dbc.CardHeader('View Options'), - dbc.CardBody([ - dbc.Label('Students per row'), - dbc.Input(type='number', min=1, max=10, value=2, step=1, id=_options_width), - dbc.Label('Height of student tile'), - dcc.Slider(min=100, max=800, marks=None, value=500, id=_options_height), - dbc.Label('Student profile'), - dbc.Switch(value=True, id=_options_hide_header, label='Show/Hide'), - ]) - ]), - dbc.Card([ - dbc.CardHeader('Information Options'), - dbc.CardBody([ - wo_classroom_text_highlighter.preset_component.create_layout(), - lodrc.WOSettings( - id=_options_text_information, - options=wo_classroom_text_highlighter.options.OPTIONS, - value=wo_classroom_text_highlighter.options.DEFAULT_VALUE, - className='table table-striped align-middle' - ) +# Store that holds the hash of the currently applied options. +_applied_option_hash = f'{_options_prefix}-applied-hash' +_applied_doc_src = f'{_options_prefix}-applied-doc-src' + +# ── Walkthrough DOM IDs ──────────────────────────────────────────────── +_walkthrough_prefix = f'{_prefix}-walkthrough' +_walkthrough_store = f'{_walkthrough_prefix}-step' +_walkthrough_modal = f'{_walkthrough_prefix}-modal' +_walkthrough_title = f'{_walkthrough_prefix}-title' +_walkthrough_body = f'{_walkthrough_prefix}-body' +_walkthrough_counter = f'{_walkthrough_prefix}-counter' +_walkthrough_back = f'{_walkthrough_prefix}-back' +_walkthrough_next = f'{_walkthrough_prefix}-next' +_walkthrough_done = f'{_walkthrough_prefix}-done' +_walkthrough_skip = f'{_walkthrough_prefix}-skip' +_walkthrough_seen_store = f'{_walkthrough_prefix}-seen' +_help_button = f'{_prefix}-help' + +# ── Walkthrough persistent store (localStorage) ─────────────────────── +walkthrough_seen_store = dcc.Store( + id=_walkthrough_seen_store, + storage_type='local', + data=False +) + +# Store holds current walkthrough step (0-based, -1 = dismissed). +# Initialised to 0 so the walkthrough opens on first page load. +walkthrough_store = dcc.Store(id=_walkthrough_store, data=0) + +walkthrough_modal = dbc.Modal( + [ + dbc.ModalHeader( + dbc.ModalTitle(id=_walkthrough_title), + close_button=False, + ), + dbc.ModalBody( + html.Div(id=_walkthrough_body), + style={'minHeight': '200px'}, + ), + dbc.ModalFooter( + html.Div([ + html.Small(id=_walkthrough_counter, className='text-muted me-auto'), + dbc.Button( + [html.I(className='fas fa-forward me-1'), 'Skip intro'], + id=_walkthrough_skip, + color='link', + size='sm', + className='me-auto text-muted', + ), + dbc.Button( + [html.I(className='fas fa-arrow-left me-1'), 'Back'], + id=_walkthrough_back, + color='secondary', + outline=True, + size='sm', + className='me-2', + ), + dbc.Button( + ['Next', html.I(className='fas fa-arrow-right ms-1')], + id=_walkthrough_next, + color='primary', + size='sm', + className='me-2', + ), + dbc.Button( + [html.I(className='fas fa-check me-1'), 'Get Started!'], + id=_walkthrough_done, + color='success', + size='sm', + ), + ], className='d-flex align-items-center w-100'), + ), + ], + id=_walkthrough_modal, + is_open=False, # <-- start closed; callback will open if needed + centered=True, + backdrop='static', + keyboard=False, + size='lg', +) + +# ── Options Modal ────────────────────────────────────────────────────── +options_modal = dbc.Modal([ + dbc.ModalHeader(dbc.ModalTitle('Dashboard Setup'), close_button=True), + dbc.ModalBody([ + lodrc.LODocumentSourceSelectorAIO(aio_id=_options_doc_src), + dbc.Card([ + dbc.CardHeader('Display Settings'), + dbc.CardBody([ + dbc.Label('Students per row'), + dbc.Input(type='number', min=1, max=10, value=2, step=1, id=_options_width), + dbc.Label('Height of student tile'), + dcc.Slider(min=100, max=800, marks=None, value=500, id=_options_height), + dbc.Label('Student profile'), + dbc.Switch(value=True, id=_options_hide_header, label='Show/Hide'), + ]) + ], className='mb-3'), + dbc.Card([ + dbc.CardHeader('Highlights & Metrics'), + dbc.CardBody([ + html.P( + 'Select which analyses to apply to student writing. ' + 'Highlights add color-coded annotations to the text; ' + 'metrics add summary badges to each student tile.', + className='text-muted small mb-3', + ), + wo_classroom_text_highlighter.preset_component.create_layout(), + lodrc.WOSettings( + id=_options_text_information_staged, + options=wo_classroom_text_highlighter.options.OPTIONS, + value=wo_classroom_text_highlighter.options.DEFAULT_VALUE, + className='table table-striped align-middle' + ) + ]) ]) - ]) -], className='p-2') + ], style={'overflowY': 'auto'}), + dbc.ModalFooter( + dbc.Button( + [html.I(className='fas fa-play me-2'), 'Run'], + id=_options_run, + color='success', + size='lg', + className='w-100' + ) + ), +], id=_options_modal, is_open=False, size='lg', scrollable=True, + style={'maxHeight': '100vh'}) + +# Hidden store that holds the "applied" text information value. +applied_options_store = dcc.Store( + id=_options_text_information, + data=wo_classroom_text_highlighter.options.DEFAULT_VALUE +) + +# Hidden store for the pre-computed hash of the applied options. +applied_option_hash_store = dcc.Store( + id=_applied_option_hash, + data='' +) +applied_doc_src_store = dcc.Store( + id=_applied_doc_src, + data={} +) # Legend _legend = f'{_prefix}-legend' _legend_button = f'{_legend}-button' _legend_children = f'{_legend}-children' -# Expanded student +# Expanded student modal _expanded_student = f'{_prefix}-expanded-student' -_expanded_student_panel = f'{_expanded_student}-panel' +_expanded_student_modal = f'{_expanded_student}-modal' +_expanded_student_title = f'{_expanded_student}-title' _expanded_student_child = f'{_expanded_student}-child' -_expanded_student_close = f'{_expanded_student}-close' -expanded_student_component = html.Div([ - html.Div([ - html.H3('Individual Student', className='d-inline-block'), - dbc.Button( - html.I(className='fas fa-close'), - className='float-end', id=_expanded_student_close, - color='transparent'), - ]), - html.Div(id=_expanded_student_child) -], className='p-2') +_expanded_student_show_identity = f'{_expanded_student}-show-identity' +_expanded_student_show_identity_toggle = f'{_expanded_student}-show-identity-toggle' +_expanded_student_doc_title = f'{_expanded_student}-doc-title' +_expanded_student_id = f'{_expanded_student}-id' + +expanded_student_id_store = dcc.Store( + id=_expanded_student_id, + data=None +) + +expanded_student_modal = dbc.Modal( + [ + dbc.ModalHeader([ + dbc.ModalTitle([ + html.Div(id=_expanded_student_title), + html.Small( + id=_expanded_student_doc_title, + className='text-muted ms-2', + style={'fontSize': '0.75em'} + ), + ], className='d-flex align-items-baseline'), + dbc.Button( + html.I(className='fas fa-eye', id=f'{_expanded_student_show_identity_toggle}-icon'), + id=_expanded_student_show_identity_toggle, + color='link', + size='sm', + className='ms-2 text-secondary', + title='Show/hide student name and document title', + ), + ], close_button=True, className='d-flex align-items-center'), + dbc.ModalBody( + html.Div(id=_expanded_student_child), + style={'overflowY': 'auto'}, + ), + ], + id=_expanded_student_modal, + is_open=False, + size='xl', + centered=True, + scrollable=True, +) + +# Store for the modal-local identity visibility +expanded_student_show_identity_store = dcc.Store( + id=_expanded_student_show_identity, + data=True +) # Alert Component _alert = f'{_prefix}-alert' @@ -107,88 +254,151 @@ html.Div(dash_renderjson.DashRenderjson(id=_alert_error_dump), className='' if DEBUG_FLAG else 'd-none') ], id=_alert, color='danger', is_open=False) -# Settings buttons +# Panels layout ID +_panels_layout = f'{_prefix}-panels-layout' + +# ── Settings toolbar ─────────────────────────────────────────────────── input_group = dbc.InputGroup([ dbc.InputGroupText(lodrc.LOConnectionAIO(aio_id=_websocket)), dbc.Button([ - html.I(className='fas fa-cog me-1'), - 'Options (', + html.I(className='fas fa-highlighter me-1'), + 'Choose What to Highlight (', html.Span('0', id=_options_toggle_count), ')' - ], id=_options_toggle), + ], id=_options_toggle, color='primary'), dbc.Button( - 'Legend', - id=_legend_button, color='primary'), + [html.I(className='fas fa-palette me-1'), 'Highlight Key'], + id=_legend_button, color='secondary'), dbc.Popover( id=_legend_children, target=_legend_button, trigger='focus', body=True, placement='bottom'), + dbc.Button( + html.I(className='fas fa-question-circle'), + id=_help_button, + color='primary', + title='Reopen the walkthrough guide', + ), lodrc.ProfileSidebarAIO(class_name='rounded-0 rounded-end', color='secondary'), ], class_name='align-items-center') def layout(): - ''' - Function to define the page's layout. - ''' page_layout = html.Div([ - html.H1('Writing Observer - Classroom Text Highlighter'), + html.H1('Writing Observer β€” Classroom Text Highlighter'), alert_component, + applied_options_store, + applied_option_hash_store, + applied_doc_src_store, + expanded_student_show_identity_store, + expanded_student_id_store, + walkthrough_store, + walkthrough_seen_store, + walkthrough_modal, + options_modal, + expanded_student_modal, html.Div([ html.Div(input_group, className='d-flex me-2'), html.Div(loading_component, className='d-flex') ], className='d-flex sticky-top pb-1 bg-light'), lodrc.LOPanelLayout( html.Div(id=_output, className='d-flex justify-content-between flex-wrap'), - panels=[ - {'children': options_component, 'width': '30%', 'id': _options_prefix, 'side': 'left' }, - {'children': expanded_student_component, - 'width': '30%', 'id': _expanded_student_panel, - 'side': 'right'} - ], - id=_options_collapse, shown=[] + panels=[], + id=_panels_layout, shown=[] ), ]) return page_layout +# ══════════════════════════════════════════════════════════════════════ +# Walkthrough callbacks +# ══════════════════════════════════════════════════════════════════════ + +# On load or when step changes, sync with localStorage to decide +# whether to show the walkthrough. +clientside_callback( + ClientsideFunction(namespace=_namespace, function_name='initWalkthroughFromStorage'), + Output(_walkthrough_store, 'data', allow_duplicate=True), + Output(_walkthrough_seen_store, 'data'), + Input(_walkthrough_store, 'data'), + State(_walkthrough_seen_store, 'data'), + prevent_initial_call='initial_duplicate', +) + +# Navigate between walkthrough steps (next / back / done / skip / help reopen) +clientside_callback( + ClientsideFunction(namespace=_namespace, function_name='navigateWalkthrough'), + Output(_walkthrough_store, 'data'), + Input(_walkthrough_next, 'n_clicks'), + Input(_walkthrough_back, 'n_clicks'), + Input(_walkthrough_done, 'n_clicks'), + Input(_walkthrough_skip, 'n_clicks'), + Input(_help_button, 'n_clicks'), + State(_walkthrough_store, 'data'), + prevent_initial_call=True, +) + +# Render the correct step content in the walkthrough modal (unchanged) +clientside_callback( + ClientsideFunction(namespace=_namespace, function_name='renderWalkthroughStep'), + Output(_walkthrough_title, 'children'), + Output(_walkthrough_body, 'children'), + Output(_walkthrough_back, 'disabled'), + Output(_walkthrough_next, 'style'), + Output(_walkthrough_done, 'style'), + Output(_walkthrough_counter, 'children'), + Output(_walkthrough_modal, 'is_open'), + Input(_walkthrough_store, 'data'), +) + # Send the initial state based on the url hash to LO. -# If this is not included, nothing will be returned from -# the communication protocol. clientside_callback( ClientsideFunction(namespace=_namespace, function_name='sendToLOConnection'), Output(lodrc.LOConnectionAIO.ids.websocket(_websocket), 'send'), - Input(lodrc.LOConnectionAIO.ids.websocket(_websocket), 'state'), # used for initial setup + Input(lodrc.LOConnectionAIO.ids.websocket(_websocket), 'state'), Input('_pages_location', 'hash'), - Input(lodrc.LODocumentSourceSelectorAIO.ids.kwargs_store(_options_doc_src), 'data'), - Input(_options_text_information, 'value') + Input(_applied_doc_src, 'data'), + Input(_applied_option_hash, 'data'), + State(_options_text_information, 'data') +) + +# When the applied options store changes, compute and store the hash. +clientside_callback( + ClientsideFunction(namespace=_namespace, function_name='computeAppliedHash'), + Output(_applied_option_hash, 'data'), + Input(_options_text_information, 'data'), +) + +# When Run is clicked, apply staged options, snapshot doc source, and close modal. +clientside_callback( + ClientsideFunction(namespace=_namespace, function_name='applyOptionsAndCloseModal'), + Output(_options_text_information, 'data'), + Output(_applied_doc_src, 'data'), + Output(_options_modal, 'is_open', allow_duplicate=True), + Input(_options_run, 'n_clicks'), + State(_options_text_information_staged, 'value'), + State(lodrc.LODocumentSourceSelectorAIO.ids.kwargs_store(_options_doc_src), 'data'), + prevent_initial_call=True ) -# Build the UI based on what we've received from the -# communicaton protocol +# Build the UI clientside_callback( ClientsideFunction(namespace=_namespace, function_name='populateOutput'), Output(_output, 'children'), Input(lodrc.LOConnectionAIO.ids.ws_store(_websocket), 'data'), - Input(_options_text_information, 'value'), + Input(_options_text_information, 'data'), Input(_options_width, 'value'), Input(_options_height, 'value'), Input(_options_hide_header, 'value'), - State(_options_text_information, 'options'), + State(_options_text_information_staged, 'options'), + State(_applied_option_hash, 'data'), ) -# Toggle if the options collapse is open or not +# Toggle the options modal open clientside_callback( - ClientsideFunction(namespace=_namespace, function_name='toggleOptions'), - Output(_options_collapse, 'shown'), + ClientsideFunction(namespace=_namespace, function_name='toggleOptionsModal'), + Output(_options_modal, 'is_open'), Input(_options_toggle, 'n_clicks'), - State(_options_collapse, 'shown') -) - -clientside_callback( - ClientsideFunction(namespace=_namespace, function_name='closeOptions'), - Output(_options_collapse, 'shown', allow_duplicate=True), - Input(_options_close, 'n_clicks'), - State(_options_collapse, 'shown'), + State(_options_modal, 'is_open'), prevent_initial_call=True ) @@ -209,40 +419,61 @@ def layout(): State({'type': 'WOStudentTextTile', 'index': ALL}, 'id'), ) -# When options change, update the current option hash for all students. -# When the option hash is different from the students internal option hash -# a loading class is applied to each student tile. +# When applied hash changes, push to all existing student tiles. clientside_callback( ClientsideFunction(namespace=_namespace, function_name='updateCurrentOptionHash'), Output({'type': 'WOStudentTextTile', 'index': ALL}, 'currentOptionHash'), - Input(_options_text_information, 'value'), + Input(_applied_option_hash, 'data'), State({'type': 'WOStudentTextTile', 'index': ALL}, 'id'), ) -# Expand a single student +# ── Expand: record which student was clicked, open modal ────────────── clientside_callback( ClientsideFunction(namespace=_namespace, function_name='expandCurrentStudent'), - Output(_expanded_student_child, 'children'), - Output(_options_collapse, 'shown', allow_duplicate=True), + Output(_expanded_student_id, 'data'), + Output(_expanded_student_modal, 'is_open'), + Output(_expanded_student_show_identity, 'data'), Input({'type': 'WOStudentTileExpand', 'index': ALL}, 'n_clicks'), - Input({'type': 'WOStudentTile', 'index': ALL}, 'children'), State({'type': 'WOStudentTile', 'index': ALL}, 'id'), - State(_options_collapse, 'shown'), - State(_expanded_student_child, 'children'), + State(_expanded_student_modal, 'is_open'), + State(_expanded_student_id, 'data'), + State(_options_hide_header, 'value'), prevent_initial_call=True ) -# Close expanded student +# ── Expand: reactively render content from live websocket data ──────── clientside_callback( - ClientsideFunction(namespace=_namespace, function_name='closeExpandedStudent'), - Output(_options_collapse, 'shown', allow_duplicate=True), - Input(_expanded_student_close, 'n_clicks'), - State(_options_collapse, 'shown'), + ClientsideFunction(namespace=_namespace, function_name='renderExpandedStudent'), + Output(_expanded_student_title, 'children'), + Output(_expanded_student_doc_title, 'children'), + Output(_expanded_student_child, 'children'), + Input(lodrc.LOConnectionAIO.ids.ws_store(_websocket), 'data'), + Input(_expanded_student_id, 'data'), + State(_expanded_student_modal, 'is_open'), + State(_options_text_information, 'data'), + State(_options_text_information_staged, 'options'), + State(_applied_option_hash, 'data'), +) + +# Toggle identity visibility within the expanded modal +clientside_callback( + ClientsideFunction(namespace=_namespace, function_name='toggleExpandedStudentIdentity'), + Output(_expanded_student_show_identity, 'data', allow_duplicate=True), + Input(_expanded_student_show_identity_toggle, 'n_clicks'), + State(_expanded_student_show_identity, 'data'), prevent_initial_call=True ) +# Render identity visibility in the expanded modal (hide/show name, doc title, icon) +clientside_callback( + ClientsideFunction(namespace=_namespace, function_name='renderExpandedStudentIdentity'), + Output(_expanded_student_title, 'style'), + Output(_expanded_student_doc_title, 'style'), + Output(f'{_expanded_student_show_identity_toggle}-icon', 'className'), + Input(_expanded_student_show_identity, 'data'), +) -# Update the alert component with any errors that come through +# Update the alert component with any errors clientside_callback( ClientsideFunction(namespace=_namespace, function_name='updateAlertWithError'), Output(_alert_text, 'children'), @@ -257,27 +488,27 @@ def layout(): Output(wo_classroom_text_highlighter.preset_component._store, 'data'), Input(wo_classroom_text_highlighter.preset_component._add_button, 'n_clicks'), State(wo_classroom_text_highlighter.preset_component._add_input, 'value'), - State(_options_text_information, 'value'), + State(_options_text_information_staged, 'value'), State(wo_classroom_text_highlighter.preset_component._store, 'data') ) -# Apply clicked preset +# Apply clicked preset to the staged settings clientside_callback( ClientsideFunction(namespace=_namespace, function_name='applyPreset'), - Output(_options_text_information, 'value'), + Output(_options_text_information_staged, 'value'), Input({'type': wo_classroom_text_highlighter.preset_component._set_item, 'index': ALL}, 'n_clicks'), State(wo_classroom_text_highlighter.preset_component._store, 'data'), prevent_initial_call=True ) -# update loading information +# Update loading information clientside_callback( ClientsideFunction(namespace=_namespace, function_name='updateLoadingInformation'), Output(_loading_collapse, 'is_open'), Output(_loading_progress, 'value'), Output(_loading_information, 'children'), Input(lodrc.LOConnectionAIO.ids.ws_store(_websocket), 'data'), - Input(_options_text_information, 'value') + Input(_applied_option_hash, 'data') ) # Update legend @@ -285,6 +516,6 @@ def layout(): ClientsideFunction(namespace=_namespace, function_name='updateLegend'), Output(_legend_children, 'children'), Output(_options_toggle_count, 'children'), - Input(_options_text_information, 'value'), - State(_options_text_information, 'options') + Input(_options_text_information, 'data'), + State(_options_text_information_staged, 'options') ) diff --git a/modules/writing_observer/VERSION b/modules/writing_observer/VERSION index 2e1b98f63..e5c21f01e 100644 --- a/modules/writing_observer/VERSION +++ b/modules/writing_observer/VERSION @@ -1 +1 @@ -0.1.0+2026.02.17T15.09.06.345Z.5f75e49b.berickson.20260217.updated.dead.links +0.1.0+2026.02.20T12.50.18.986Z.f3ada3ba.berickson.20260210.dashboard.updates diff --git a/modules/writing_observer/writing_observer/aggregator.py b/modules/writing_observer/writing_observer/aggregator.py index 85b3809c5..280f10ef4 100644 --- a/modules/writing_observer/writing_observer/aggregator.py +++ b/modules/writing_observer/writing_observer/aggregator.py @@ -12,6 +12,7 @@ import learning_observer.communication_protocol.integration import learning_observer.constants as constants import learning_observer.kvs +import learning_observer.rosters import learning_observer.settings from learning_observer.stream_analytics.fields import KeyField, KeyStateType, EventField import learning_observer.stream_analytics.helpers @@ -278,11 +279,11 @@ async def fetch_doc_from_google(student, doc_id): """ if student is None or doc_id is None or len(doc_id) == 0: return None - import learning_observer.google + import learning_observer.integrations.google kvs = learning_observer.kvs.KVS() - text = await learning_observer.google.doctext(runtime, documentId=doc_id) + text = await learning_observer.integrations.google.doctext(runtime, documentId=doc_id) # Only update Redis is we have text available. If `text` is missing, then # we likely encountered an error, usually related to document permissions. if 'text' not in text: @@ -332,13 +333,13 @@ async def fetch_doc_from_google(student): :param student: A student object :return: The text of the latest document """ - import learning_observer.google + import learning_observer.integrations.google kvs = learning_observer.kvs.KVS() docId = get_last_document_id(student) # fetch text - text = await learning_observer.google.doctext(runtime, documentId=docId) + text = await learning_observer.integrations.google.doctext(runtime, documentId=docId) # set reconstruction data to ground truth key = learning_observer.stream_analytics.helpers.make_key( writing_observer.writing_analysis.reconstruct, @@ -427,7 +428,7 @@ async def fetch_assignment_docs(runtime, course_id, kwargs=None): assignment_id = kwargs.get('assignment') output = [] if assignment_id: - output = await learning_observer.google.assigned_docs(runtime, courseId=course_id, courseWorkId=assignment_id) + output = await learning_observer.rosters.courseassignment_assigned_docs(runtime.get_request(), course_id, assignment_id) async for student in learning_observer.util.ensure_async_generator(output): s = {} s['doc_id'] = student['documents'][0]['id'] diff --git a/modules/writing_observer/writing_observer/document_timestamps.py b/modules/writing_observer/writing_observer/document_timestamps.py index 4f872655d..ee8464eb3 100644 --- a/modules/writing_observer/writing_observer/document_timestamps.py +++ b/modules/writing_observer/writing_observer/document_timestamps.py @@ -21,6 +21,44 @@ def select_source(sources, source): return sources[source] +@learning_observer.communication_protocol.integration.publish_function('writing_observer.fetch_doc_by_title_text') +async def fetch_doc_by_title_text(document_lists, kwargs=None): + ''' + Iterate over student document metadata and choose the most recently + accessed document whose title contains the provided `kwargs.title_text`. + ''' + if kwargs is None: + kwargs = {} + title_text = (kwargs.get('title_text') or '').strip().lower() + + async for student in document_lists: + docs = student.get('docs', {}) + student['doc_id'] = '' + if not title_text: + yield student + continue + + best_doc_id = '' + best_last_access = -1 + for doc_id, metadata in docs.items(): + title = str(metadata.get('title', '') or '').lower() + if title_text not in title: + continue + + last_access = metadata.get('last_access') + try: + last_access = float(last_access) + except (TypeError, ValueError): + last_access = -1 + + if last_access > best_last_access: + best_last_access = last_access + best_doc_id = doc_id + + student['doc_id'] = best_doc_id + yield student + + @learning_observer.communication_protocol.integration.publish_function('writing_observer.fetch_doc_at_timestamp') async def fetch_doc_at_timestamp(overall_timestamps, kwargs=None): ''' diff --git a/modules/writing_observer/writing_observer/module.py b/modules/writing_observer/writing_observer/module.py index 3b9154d9c..5036c853f 100644 --- a/modules/writing_observer/writing_observer/module.py +++ b/modules/writing_observer/writing_observer/module.py @@ -40,6 +40,7 @@ group_docs_by = q.call('writing_observer.group_docs_by') document_access_ts = q.call('writing_observer.fetch_doc_at_timestamp') +document_by_title_text = q.call('writing_observer.fetch_doc_by_title_text') source_selector = q.call('source_selector') @@ -85,7 +86,8 @@ document_sources = source_selector( sources={'timestamp': q.variable('docs_at_ts'), 'latest': q.variable('doc_ids'), - 'assignment': q.variable('assignment_docs') + 'assignment': q.variable('assignment_docs'), + 'title_text': q.variable('docs_by_title_text') }, source=q.parameter('doc_source', required=False, default='latest') ) @@ -143,6 +145,10 @@ 'timestamped_docs': q.select(q.keys('writing_observer.document_access_timestamps', STUDENTS=q.variable('roster'), STUDENTS_path='user_id'), fields={'timestamps': 'timestamps'}), 'docs_at_ts': document_access_ts(overall_timestamps=q.variable('timestamped_docs'), kwargs=q.parameter('doc_source_kwargs')), + # fetch the latest matching document by title text + 'raw_doc_lists': q.select(q.keys('writing_observer.document_list', STUDENTS=q.variable('roster'), STUDENTS_path='user_id'), fields={'docs': 'docs'}), + 'docs_by_title_text': document_by_title_text(document_lists=q.variable('raw_doc_lists'), kwargs=q.parameter('doc_source_kwargs')), + # figure out where to source document ids from # current options include `ts` for a given timestamp # or `latest` for the most recently accessed From 40d17a5465f943e63e3025e20e9b9b177f5ed610 Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Wed, 4 Mar 2026 12:50:27 -0500 Subject: [PATCH 87/88] individual student writing portfolio dashboard (#271) * Initial commit for the porfolio dashboard * Add new endpoints for portfolio dashboard * Add LO entry point file * Update portfolio dashboard * Update to a compatible react-redux * Update DAG endpoints * update wo_portfolio_diff in .gitignore * Update system * fixed some linting * fix build and add to script * cleaned up lo connection to with singular values and only 1 connection per page * small improvements to communication protocol queries * added title to compare selection bar * updated document object properly for easier metadata access * various improvements to show doc title and student names * Update the readme * updated search to use title, cleaned up graphs, removed clickable homepage items * added more documentation * added roster with provenance endpoint * updated student breadcrumb href: * added error handling for catching the message * removed non-implement overall metric cards * remove hardcoded name * hid id column from student table * added modal for presets / metrics to smaller view on comparison page * updated name column and added link * removed hardcoded tags about course * update title to match others * added dashboard links and hid notifications * removed unimplemented schoology endponits --------- Co-authored-by: JohnDamilola --- .gitignore | 3 +- VERSION | 2 +- learning_observer/VERSION | 2 +- .../learning_observer/dashboard.py | 14 +- .../integrations/schoology.py | 29 - modules/lo_event/package.json | 1 + modules/portfolio_diff/.gitignore | 41 + modules/portfolio_diff/README.md | 36 + .../portfolio_diff/build_and_add_to_module.sh | 5 + modules/portfolio_diff/eslint.config.mjs | 14 + modules/portfolio_diff/jsconfig.json | 7 + modules/portfolio_diff/lo_event.sh | 14 + modules/portfolio_diff/next.config.mjs | 21 + modules/portfolio_diff/package.json | 32 + modules/portfolio_diff/postcss.config.mjs | 5 + .../src/app/components/Breadcrumb.js | 70 + .../src/app/components/MetricsPanel.js | 904 ++++++++ .../src/app/components/Navbar.js | 224 ++ .../src/app/components/TypeFilterDropdown.js | 104 + modules/portfolio_diff/src/app/favicon.ico | Bin 0 -> 25931 bytes modules/portfolio_diff/src/app/globals.css | 43 + .../src/app/hooks/useCourseId.js | 92 + modules/portfolio_diff/src/app/layout.js | 21 + modules/portfolio_diff/src/app/page.js | 7 + .../src/app/providers/CourseIdProvider.jsx | 35 + .../src/app/students/compare/page.js | 1946 +++++++++++++++++ .../StudentDetail/SingleEssayModel.js | 1076 +++++++++ .../StudentDetail/StudentDetailCompare.js | 594 +++++ .../StudentDetail/StudentDetailGrowth.js | 556 +++++ .../components/StudentDetail/index.js | 915 ++++++++ .../app/students/components/StudentsIndex.js | 668 ++++++ .../portfolio_diff/src/app/students/page.js | 23 + modules/portfolio_diff/src/app/utils/data.js | 359 +++ .../src/app/utils/navigation.js | 22 + modules/portfolio_diff/src/app/utils/ws.js | 15 + modules/wo_portfolio_diff/README.md | 56 + modules/wo_portfolio_diff/VERSION | 1 + modules/wo_portfolio_diff/pyproject.toml | 3 + modules/wo_portfolio_diff/setup.cfg | 11 + .../wo_portfolio_diff/__init__.py | 0 .../wo_portfolio_diff/module.py | 31 + modules/writing_observer/VERSION | 2 +- .../writing_observer/document_timestamps.py | 10 + .../writing_observer/module.py | 93 +- 44 files changed, 8065 insertions(+), 42 deletions(-) create mode 100644 modules/portfolio_diff/.gitignore create mode 100644 modules/portfolio_diff/README.md create mode 100755 modules/portfolio_diff/build_and_add_to_module.sh create mode 100644 modules/portfolio_diff/eslint.config.mjs create mode 100644 modules/portfolio_diff/jsconfig.json create mode 100755 modules/portfolio_diff/lo_event.sh create mode 100644 modules/portfolio_diff/next.config.mjs create mode 100644 modules/portfolio_diff/package.json create mode 100644 modules/portfolio_diff/postcss.config.mjs create mode 100644 modules/portfolio_diff/src/app/components/Breadcrumb.js create mode 100644 modules/portfolio_diff/src/app/components/MetricsPanel.js create mode 100644 modules/portfolio_diff/src/app/components/Navbar.js create mode 100644 modules/portfolio_diff/src/app/components/TypeFilterDropdown.js create mode 100644 modules/portfolio_diff/src/app/favicon.ico create mode 100644 modules/portfolio_diff/src/app/globals.css create mode 100644 modules/portfolio_diff/src/app/hooks/useCourseId.js create mode 100644 modules/portfolio_diff/src/app/layout.js create mode 100644 modules/portfolio_diff/src/app/page.js create mode 100644 modules/portfolio_diff/src/app/providers/CourseIdProvider.jsx create mode 100644 modules/portfolio_diff/src/app/students/compare/page.js create mode 100644 modules/portfolio_diff/src/app/students/components/StudentDetail/SingleEssayModel.js create mode 100644 modules/portfolio_diff/src/app/students/components/StudentDetail/StudentDetailCompare.js create mode 100644 modules/portfolio_diff/src/app/students/components/StudentDetail/StudentDetailGrowth.js create mode 100644 modules/portfolio_diff/src/app/students/components/StudentDetail/index.js create mode 100644 modules/portfolio_diff/src/app/students/components/StudentsIndex.js create mode 100644 modules/portfolio_diff/src/app/students/page.js create mode 100644 modules/portfolio_diff/src/app/utils/data.js create mode 100644 modules/portfolio_diff/src/app/utils/navigation.js create mode 100644 modules/portfolio_diff/src/app/utils/ws.js create mode 100644 modules/wo_portfolio_diff/README.md create mode 100644 modules/wo_portfolio_diff/VERSION create mode 100644 modules/wo_portfolio_diff/pyproject.toml create mode 100644 modules/wo_portfolio_diff/setup.cfg create mode 100644 modules/wo_portfolio_diff/wo_portfolio_diff/__init__.py create mode 100644 modules/wo_portfolio_diff/wo_portfolio_diff/module.py diff --git a/.gitignore b/.gitignore index 65002d617..6f37666ef 100644 --- a/.gitignore +++ b/.gitignore @@ -39,4 +39,5 @@ learning_observer/learning_observer/static_data/google/ learning_observer/learning_observer/static_data/admins.yaml .ipynb_checkpoints/ .eggs/ -.next/ \ No newline at end of file +.next/ +modules/wo_portfolio_diff/wo_portfolio_diff/portfolio_diff/* \ No newline at end of file diff --git a/VERSION b/VERSION index fff79c93a..20ad29970 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0+2026.02.20T19.02.56.547Z.85e344b1.berickson.20260210.dashboard.updates +0.1.0+2026.02.27T21.25.37.849Z.3207a114.berickson.20260220.dami.portfolio.pr diff --git a/learning_observer/VERSION b/learning_observer/VERSION index fff79c93a..20ad29970 100644 --- a/learning_observer/VERSION +++ b/learning_observer/VERSION @@ -1 +1 @@ -0.1.0+2026.02.20T19.02.56.547Z.85e344b1.berickson.20260210.dashboard.updates +0.1.0+2026.02.27T21.25.37.849Z.3207a114.berickson.20260220.dami.portfolio.pr diff --git a/learning_observer/learning_observer/dashboard.py b/learning_observer/learning_observer/dashboard.py index 7b07255a8..0552d88d4 100644 --- a/learning_observer/learning_observer/dashboard.py +++ b/learning_observer/learning_observer/dashboard.py @@ -953,7 +953,7 @@ async def _drive_generator(generator, dag_kwargs, targets=None): continue except (TypeError, ValueError) as e: _log_protocol_event( - 'json_parse_error', + 'json_parse_error', error_type=type(e).__name__, error_message=str(e) ) @@ -982,18 +982,18 @@ async def _drive_generator(generator, dag_kwargs, targets=None): # Various ways we might encounter an exception except asyncio.CancelledError: _close_connection_and_cleanup('server_cancelled') - except (aiohttp.web_ws.WebSocketError, + except (aiohttp.web_ws.WebSocketError, aiohttp.client_exceptions.ClientConnectionResetError, ConnectionResetError) as e: _log_protocol_event( - 'connection_closed_gracefully', - exception_type=type(e).__name__, + 'connection_closed_gracefully', + exception_type=type(e).__name__, detail=str(e)) _close_connection_and_cleanup('client_disconnected') except Exception as e: _log_protocol_event( - 'handler_exception', - exception_type=type(e).__name__, + 'handler_exception', + exception_type=type(e).__name__, detail=repr(e)) _close_connection_and_cleanup('server_exception') finally: @@ -1002,7 +1002,7 @@ async def _drive_generator(generator, dag_kwargs, targets=None): t.cancel() if background_tasks: await asyncio.gather(*background_tasks, return_exceptions=True) - + # Close WebSocket gracefully if not already closed if not ws.closed: try: diff --git a/learning_observer/learning_observer/integrations/schoology.py b/learning_observer/learning_observer/integrations/schoology.py index 6e1eb467a..3952ca09c 100644 --- a/learning_observer/learning_observer/integrations/schoology.py +++ b/learning_observer/learning_observer/integrations/schoology.py @@ -136,32 +136,3 @@ async def clean_course_roster(schoology_json): key=lambda user: user.get('profile', {}).get('name', {}).get('family_name', '') ) return users - - -# --------------------------------------------------------------------------- -# Assignments (AGS line items) -# --------------------------------------------------------------------------- -@register_cleaner('course_assignments', 'assignments') -def clean_course_assignments(schoology_json): - ''' - TODO implemement this function - When launching via LTI, Schoology only allows us to see assignments - created by our tool. To see all assignments we require an Oauth workflow. - Clean course line items (assignments) from Schoology via LTI AGS. - ''' - raise NotImplemented('Schoology assignments have not yet been implemented.') - - -# --------------------------------------------------------------------------- -# Assignment results / assigned docs (AGS results) -# --------------------------------------------------------------------------- -@register_cleaner('assignment_results', 'assigned_docs') -async def clean_assigned_docs(schoology_json): - ''' - TODO implemement this function - When launching via LTI, Schoology only allows us to see assignments - created by our tool. To see all assignments we require an Oauth workflow. - Extract per-student Google Doc attachments from LTI AGS results - for a single assignment. - ''' - raise NotImplemented('Schoology documents from assignments have not yet been implemented.') diff --git a/modules/lo_event/package.json b/modules/lo_event/package.json index 75b64066e..f6319d98c 100644 --- a/modules/lo_event/package.json +++ b/modules/lo_event/package.json @@ -33,6 +33,7 @@ "lodash": "^4.17.21", "react": "^18.3.1", "react-dom": "^18.3.1", + "react-redux": "^9.2.0", "redux": "^5.0.1", "redux-state-sync": "^3.1.4", "redux-thunk": "^3.1.0", diff --git a/modules/portfolio_diff/.gitignore b/modules/portfolio_diff/.gitignore new file mode 100644 index 000000000..5ef6a5207 --- /dev/null +++ b/modules/portfolio_diff/.gitignore @@ -0,0 +1,41 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files (can opt-in for committing if needed) +.env* + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/modules/portfolio_diff/README.md b/modules/portfolio_diff/README.md new file mode 100644 index 000000000..66bb426ff --- /dev/null +++ b/modules/portfolio_diff/README.md @@ -0,0 +1,36 @@ +This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). + +## Getting Started + +First, run the development server: + +```bash +npm run dev +# or +yarn dev +# or +pnpm dev +# or +bun dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `app/page.js`. The page auto-updates as you edit the file. + +This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. diff --git a/modules/portfolio_diff/build_and_add_to_module.sh b/modules/portfolio_diff/build_and_add_to_module.sh new file mode 100755 index 000000000..4f2fbbdd8 --- /dev/null +++ b/modules/portfolio_diff/build_and_add_to_module.sh @@ -0,0 +1,5 @@ +rm -rf out/ +npm run build +rm -rf ../wo_portfolio_diff/wo_portfolio_diff/portfolio_diff/ +mkdir ../wo_portfolio_diff/wo_portfolio_diff/portfolio_diff/ +cp -r out/. ../wo_portfolio_diff/wo_portfolio_diff/portfolio_diff/ diff --git a/modules/portfolio_diff/eslint.config.mjs b/modules/portfolio_diff/eslint.config.mjs new file mode 100644 index 000000000..348c45a2f --- /dev/null +++ b/modules/portfolio_diff/eslint.config.mjs @@ -0,0 +1,14 @@ +import { dirname } from "path"; +import { fileURLToPath } from "url"; +import { FlatCompat } from "@eslint/eslintrc"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +const compat = new FlatCompat({ + baseDirectory: __dirname, +}); + +const eslintConfig = [...compat.extends("next/core-web-vitals")]; + +export default eslintConfig; diff --git a/modules/portfolio_diff/jsconfig.json b/modules/portfolio_diff/jsconfig.json new file mode 100644 index 000000000..b8d6842d7 --- /dev/null +++ b/modules/portfolio_diff/jsconfig.json @@ -0,0 +1,7 @@ +{ + "compilerOptions": { + "paths": { + "@/*": ["./src/*"] + } + } +} diff --git a/modules/portfolio_diff/lo_event.sh b/modules/portfolio_diff/lo_event.sh new file mode 100755 index 000000000..4efe395b5 --- /dev/null +++ b/modules/portfolio_diff/lo_event.sh @@ -0,0 +1,14 @@ +# `lo_event` is the Learning Observer event library (and also `lo_assess` within it is the Learning Observer activity library, which should be factored out eventually). + +# * It's a library, and there's no practical stand-alone way to do more extensive development without some use-cases. This is a good set of use cases. +# * As we develop those, we often make changes to `lo_event` and `lo_assess`. Quite a lot, actually. + +# This script packages `lo_event` into a node package, wipes the `next.js` cache (which often contains relics before changes), and installs it. + +# Without this, development of `lo_event` is painful. Even with this, in an ideal case, we would rerun this whenever there were changes to `lo_event` automatically with some kind of watch daemon. +rm -Rf .next/cache/ +pushd ../lo_event/ +npm run prebuild +npm pack +popd +npm install ../lo_event/lo_event-0.0.3.tgz --no-save diff --git a/modules/portfolio_diff/next.config.mjs b/modules/portfolio_diff/next.config.mjs new file mode 100644 index 000000000..0f9816ced --- /dev/null +++ b/modules/portfolio_diff/next.config.mjs @@ -0,0 +1,21 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = { + basePath: "/_next/wo_portfolio_diff/portfolio_diff", + eslint: { + ignoreDuringBuilds: true, + }, + transpilePackages: ["lo_event"], + webpack: ( + config, + { buildId, dev, isServer, defaultLoaders, nextRuntime, webpack } + ) => { + config.resolve.fallback = { + ...config.resolve.fallback, + fs: false, // tells Webpack to ignore fs in client bundle + }; + return config; + }, + output: "export", +}; + +export default nextConfig; diff --git a/modules/portfolio_diff/package.json b/modules/portfolio_diff/package.json new file mode 100644 index 000000000..19f1880f9 --- /dev/null +++ b/modules/portfolio_diff/package.json @@ -0,0 +1,32 @@ +{ + "name": "portfolio_diff", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint" + }, + "dependencies": { + "babel-loader": "^10.0.0", + "echarts": "^6.0.0", + "echarts-for-react": "^3.0.5", + "framer-motion": "^12.23.6", + "lo_event": "file:../lo_event", + "lucide-react": "^0.525.0", + "next": "15.3.5", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "react-redux": "^9.2.0", + "recharts": "^3.1.2", + "redux": "^5.0.1" + }, + "devDependencies": { + "@eslint/eslintrc": "^3", + "@tailwindcss/postcss": "^4", + "eslint": "^9", + "eslint-config-next": "15.3.5", + "tailwindcss": "^4" + } +} diff --git a/modules/portfolio_diff/postcss.config.mjs b/modules/portfolio_diff/postcss.config.mjs new file mode 100644 index 000000000..c7bcb4b1e --- /dev/null +++ b/modules/portfolio_diff/postcss.config.mjs @@ -0,0 +1,5 @@ +const config = { + plugins: ["@tailwindcss/postcss"], +}; + +export default config; diff --git a/modules/portfolio_diff/src/app/components/Breadcrumb.js b/modules/portfolio_diff/src/app/components/Breadcrumb.js new file mode 100644 index 000000000..ce4cb03e3 --- /dev/null +++ b/modules/portfolio_diff/src/app/components/Breadcrumb.js @@ -0,0 +1,70 @@ +"use client"; + +import Link from "next/link"; +import { Home } from "lucide-react"; +import PropTypes from "prop-types"; + +export default function Breadcrumb({ + items = [], + homeHref = "/", + homeLabel = "Dashboard", + className = "", +}) { + const wrapCls = `mb-4 ${className}`.trim(); + + return ( + + ); +} + +Breadcrumb.propTypes = { + items: PropTypes.arrayOf( + PropTypes.shape({ + label: PropTypes.string.isRequired, + href: PropTypes.string, + }) + ), + homeHref: PropTypes.string, + homeLabel: PropTypes.string, + className: PropTypes.string, +}; diff --git a/modules/portfolio_diff/src/app/components/MetricsPanel.js b/modules/portfolio_diff/src/app/components/MetricsPanel.js new file mode 100644 index 000000000..e44412ee0 --- /dev/null +++ b/modules/portfolio_diff/src/app/components/MetricsPanel.js @@ -0,0 +1,904 @@ +"use client"; + +import { useCallback, useEffect, useState } from "react"; +import { + ArrowLeftRight, + ChevronDown, + FileText, + Gauge, + Languages, + ListCollapse, + MessageSquareText, + MessagesSquare, + Quote, + Speech, + Trash2, + Users, + WholeWord, +} from "lucide-react"; + +/* ---------------------- deterministic helpers ---------------------- */ +const seedFrom = (s) => { + let h = 2166136261; + for (let i = 0; i < s.length; i++) h = ((h ^ s.charCodeAt(i)) * 16777619) >>> 0; + return h >>> 0; +}; + +const HIGHLIGHT_CLASSES = [ + "bg-emerald-200/70", + "bg-sky-200/70", + "bg-amber-200/70", + "bg-rose-200/70", + "bg-indigo-200/70", + "bg-lime-200/70", + "bg-violet-200/60", + "bg-teal-200/70", + "bg-fuchsia-200/60", + "bg-orange-200/70", +]; + +const highlightClassForMetric = (metricId) => { + const idx = seedFrom(metricId || "metric") % HIGHLIGHT_CLASSES.length; + return HIGHLIGHT_CLASSES[idx]; +}; + +/* ============================================================= + METRICS (FULL LIST) β€” matches EssayComparison + ============================================================= */ + +const CATEGORY_LABELS = { + language: "Language", + argumentation: "Argumentation", + statements: "Statements", + transitions: "Transition Words", + pos: "Parts of Speech", + sentence_type: "Sentence Types", + source_information: "Source Information", + dialogue: "Dialogue", + tone: "Tone", + details: "Details", + other: "Other", +}; + +const iconForCategory = (catKey) => { + switch (catKey) { + case "language": + return Languages; + case "argumentation": + return MessagesSquare; + case "statements": + return MessageSquareText; + case "transitions": + return ArrowLeftRight; + case "pos": + return Speech; + case "sentence_type": + return WholeWord; + case "source_information": + return Quote; + case "dialogue": + return Users; + case "tone": + return Gauge; + case "details": + return ListCollapse; + default: + return FileText; + } +}; + +const METRIC_DEFS = [ + // language + { + id: "academic_language", + title: "Academic Language", + icon: iconForCategory("language"), + category: CATEGORY_LABELS.language, + function: "percent", + desc: "Percent of tokens flagged academic", + }, + { + id: "informal_language", + title: "Informal Language", + icon: iconForCategory("language"), + category: CATEGORY_LABELS.language, + function: "percent", + desc: "Percent of tokens flagged informal", + }, + { + id: "latinate_words", + title: "Latinate Words", + icon: iconForCategory("language"), + category: CATEGORY_LABELS.language, + function: "percent", + desc: "Percent of tokens flagged latinate", + }, + { + id: "opinion_words", + title: "Opinion Words", + icon: iconForCategory("language"), + category: CATEGORY_LABELS.language, + function: "total", + desc: "Total opinion-word signals", + }, + { + id: "emotion_words", + title: "Emotion Words", + icon: iconForCategory("language"), + category: CATEGORY_LABELS.language, + function: "percent", + desc: "Percent emotion words", + }, + + // argumentation + { + id: "argument_words", + title: "Argument Words", + icon: iconForCategory("argumentation"), + category: CATEGORY_LABELS.argumentation, + function: "percent", + desc: "Percent argument words", + }, + { + id: "explicit_argument", + title: "Explicit argument", + icon: iconForCategory("argumentation"), + category: CATEGORY_LABELS.argumentation, + function: "percent", + desc: "Percent explicit argument markers", + }, + + // statements + { + id: "statements_of_opinion", + title: "Statements of Opinion", + icon: iconForCategory("statements"), + category: CATEGORY_LABELS.statements, + function: "percent", + desc: "Percent of sentences classified as opinion", + }, + { + id: "statements_of_fact", + title: "Statements of Fact", + icon: iconForCategory("statements"), + category: CATEGORY_LABELS.statements, + function: "percent", + desc: "Percent of sentences classified as fact", + }, + + // transitions + { + id: "transition_words", + title: "Transition Words", + icon: iconForCategory("transitions"), + category: CATEGORY_LABELS.transitions, + function: "counts", + desc: "Transition counts (by type)", + }, + { + id: "positive_transition_words", + title: "Positive Transition Words", + icon: iconForCategory("transitions"), + category: CATEGORY_LABELS.transitions, + function: "total", + desc: "Total positive transitions", + }, + { + id: "conditional_transition_words", + title: "Conditional Transition Words", + icon: iconForCategory("transitions"), + category: CATEGORY_LABELS.transitions, + function: "total", + desc: "Total conditional transitions", + }, + { + id: "consequential_transition_words", + title: "Consequential Transition Words", + icon: iconForCategory("transitions"), + category: CATEGORY_LABELS.transitions, + function: "total", + desc: "Total consequential transitions", + }, + { + id: "contrastive_transition_words", + title: "Contrastive Transition Words", + icon: iconForCategory("transitions"), + category: CATEGORY_LABELS.transitions, + function: "total", + desc: "Total contrastive transitions", + }, + { + id: "counterpoint_transition_words", + title: "Counterpoint Transition Words", + icon: iconForCategory("transitions"), + category: CATEGORY_LABELS.transitions, + function: "total", + desc: "Total counterpoint transitions", + }, + { + id: "comparative_transition_words", + title: "Comparative Transition Words", + icon: iconForCategory("transitions"), + category: CATEGORY_LABELS.transitions, + function: "total", + desc: "Total comparative transitions", + }, + { + id: "cross_referential_transition_words", + title: "Cross Referential Transition Words", + icon: iconForCategory("transitions"), + category: CATEGORY_LABELS.transitions, + function: "total", + desc: "Total cross-referential transitions", + }, + { + id: "illustrative_transition_words", + title: "Illustrative Transition Words", + icon: iconForCategory("transitions"), + category: CATEGORY_LABELS.transitions, + function: "total", + desc: "Total illustrative transitions", + }, + { + id: "negative_transition_words", + title: "Negative Transition Words", + icon: iconForCategory("transitions"), + category: CATEGORY_LABELS.transitions, + function: "total", + desc: "Total negative transitions", + }, + { + id: "emphatic_transition_words", + title: "Emphatic Transition Words", + icon: iconForCategory("transitions"), + category: CATEGORY_LABELS.transitions, + function: "total", + desc: "Total emphatic transitions", + }, + { + id: "evenidentiary_transition_words", + title: "Evenidentiary_transition_words", + icon: iconForCategory("transitions"), + category: CATEGORY_LABELS.transitions, + function: "total", + desc: "Total evidentiary transitions", + }, + { + id: "general_transition_words", + title: "General Transition Words", + icon: iconForCategory("transitions"), + category: CATEGORY_LABELS.transitions, + function: "total", + desc: "Total general transitions", + }, + { + id: "ordinal_transition_words", + title: "Ordinal Transition Words", + icon: iconForCategory("transitions"), + category: CATEGORY_LABELS.transitions, + function: "total", + desc: "Total ordinal transitions", + }, + { + id: "purposive_transition_words", + title: "Purposive Transition Words", + icon: iconForCategory("transitions"), + category: CATEGORY_LABELS.transitions, + function: "total", + desc: "Total purposive transitions", + }, + { + id: "periphrastic_transition_words", + title: "Periphrastic Transition Words", + icon: iconForCategory("transitions"), + category: CATEGORY_LABELS.transitions, + function: "total", + desc: "Total periphrastic transitions", + }, + { + id: "hypothetical_transition_words", + title: "Hypothetical Transition Words", + icon: iconForCategory("transitions"), + category: CATEGORY_LABELS.transitions, + function: "total", + desc: "Total hypothetical transitions", + }, + { + id: "summative_transition_words", + title: "Summative Transition Words", + icon: iconForCategory("transitions"), + category: CATEGORY_LABELS.transitions, + function: "total", + desc: "Total summative transitions", + }, + { + id: "introductory_transition_words", + title: "Introductory Transition Words", + icon: iconForCategory("transitions"), + category: CATEGORY_LABELS.transitions, + function: "total", + desc: "Total introductory transitions", + }, + + // parts of speech + { + id: "adjectives", + title: "Adjectives", + icon: iconForCategory("pos"), + category: CATEGORY_LABELS.pos, + function: "total", + desc: "Total adjectives", + }, + { + id: "adverbs", + title: "Adverbs", + icon: iconForCategory("pos"), + category: CATEGORY_LABELS.pos, + function: "total", + desc: "Total adverbs", + }, + { + id: "nouns", + title: "Nouns", + icon: iconForCategory("pos"), + category: CATEGORY_LABELS.pos, + function: "total", + desc: "Total nouns", + }, + { + id: "proper_nouns", + title: "Proper Nouns", + icon: iconForCategory("pos"), + category: CATEGORY_LABELS.pos, + function: "total", + desc: "Total proper nouns", + }, + { + id: "verbs", + title: "Verbs", + icon: iconForCategory("pos"), + category: CATEGORY_LABELS.pos, + function: "total", + desc: "Total verbs", + }, + { + id: "numbers", + title: "Numbers", + icon: iconForCategory("pos"), + category: CATEGORY_LABELS.pos, + function: "total", + desc: "Total numbers", + }, + { + id: "prepositions", + title: "Prepositions", + icon: iconForCategory("pos"), + category: CATEGORY_LABELS.pos, + function: "total", + desc: "Total prepositions", + }, + { + id: "coordinating_conjunction", + title: "Coordinating Conjunction", + icon: iconForCategory("pos"), + category: CATEGORY_LABELS.pos, + function: "total", + desc: "Total coordinating conjunctions", + }, + { + id: "subordinating_conjunction", + title: "Subordinating Conjunction", + icon: iconForCategory("pos"), + category: CATEGORY_LABELS.pos, + function: "total", + desc: "Total subordinating conjunctions", + }, + { + id: "auxiliary_verb", + title: "Auxiliary Verb", + icon: iconForCategory("pos"), + category: CATEGORY_LABELS.pos, + function: "total", + desc: "Total auxiliary verbs", + }, + { + id: "pronoun", + title: "Pronoun", + icon: iconForCategory("pos"), + category: CATEGORY_LABELS.pos, + function: "total", + desc: "Total pronouns", + }, + + // sentence types + { + id: "simple_sentences", + title: "Simple Sentences", + icon: iconForCategory("sentence_type"), + category: CATEGORY_LABELS.sentence_type, + function: "total", + desc: "Total simple sentences", + }, + { + id: "simple_with_complex_predicates", + title: "Simple with Complex Predicates", + icon: iconForCategory("sentence_type"), + category: CATEGORY_LABELS.sentence_type, + function: "total", + desc: "Total simple (complex predicates)", + }, + { + id: "simple_with_compound_predicates", + title: "Simple with Compound Predicates", + icon: iconForCategory("sentence_type"), + category: CATEGORY_LABELS.sentence_type, + function: "total", + desc: "Total simple (compound predicates)", + }, + { + id: "simple_with_compound_complex_predicates", + title: "Simple with Compound Complex Predicates", + icon: iconForCategory("sentence_type"), + category: CATEGORY_LABELS.sentence_type, + function: "total", + desc: "Total simple (compound complex predicates)", + }, + { + id: "compound_sentences", + title: "Compound Sentences", + icon: iconForCategory("sentence_type"), + category: CATEGORY_LABELS.sentence_type, + function: "total", + desc: "Total compound sentences", + }, + { + id: "complex_sentences", + title: "Complex Sentences", + icon: iconForCategory("sentence_type"), + category: CATEGORY_LABELS.sentence_type, + function: "total", + desc: "Total complex sentences", + }, + { + id: "compound_complex_sentences", + title: "Compound Complex Sentences", + icon: iconForCategory("sentence_type"), + category: CATEGORY_LABELS.sentence_type, + function: "total", + desc: "Total compound-complex sentences", + }, + + // source info + { + id: "information_sources", + title: "Information Sources", + icon: iconForCategory("source_information"), + category: CATEGORY_LABELS.source_information, + function: "percent", + desc: "Percent source references", + }, + { + id: "attributions", + title: "Attributions", + icon: iconForCategory("source_information"), + category: CATEGORY_LABELS.source_information, + function: "percent", + desc: "Percent attributions", + }, + { + id: "citations", + title: "Citations", + icon: iconForCategory("source_information"), + category: CATEGORY_LABELS.source_information, + function: "percent", + desc: "Percent citations", + }, + { + id: "quoted_words", + title: "Quoted Words", + icon: iconForCategory("source_information"), + category: CATEGORY_LABELS.source_information, + function: "percent", + desc: "Percent quoted words", + }, + + // dialogue + { + id: "direct_speech_verbs", + title: "Direct Speech Verbs", + icon: iconForCategory("dialogue"), + category: CATEGORY_LABELS.dialogue, + function: "percent", + desc: "Percent direct speech verbs", + }, + { + id: "indirect_speech", + title: "Indirect Speech", + icon: iconForCategory("dialogue"), + category: CATEGORY_LABELS.dialogue, + function: "percent", + desc: "Percent indirect speech", + }, + + // tone + { + id: "positive_tone", + title: "Positive Tone", + icon: iconForCategory("tone"), + category: CATEGORY_LABELS.tone, + function: "percent", + desc: "Percent positive tone", + }, + { + id: "negative_tone", + title: "Negative Tone", + icon: iconForCategory("tone"), + category: CATEGORY_LABELS.tone, + function: "percent", + desc: "Percent negative tone", + }, + + // details + { + id: "concrete_details", + title: "Concrete Details", + icon: iconForCategory("details"), + category: CATEGORY_LABELS.details, + function: "percent", + desc: "Percent concrete details", + }, + { + id: "main_idea_sentences", + title: "Main Idea Sentences", + icon: iconForCategory("details"), + category: CATEGORY_LABELS.details, + function: "total", + desc: "Total main idea sentences", + }, + { + id: "supporting_idea_sentences", + title: "Supporting Idea Sentences", + icon: iconForCategory("details"), + category: CATEGORY_LABELS.details, + function: "total", + desc: "Total supporting idea sentences", + }, + { + id: "supporting_detail_sentences", + title: "Supporting Detail Sentences", + icon: iconForCategory("details"), + category: CATEGORY_LABELS.details, + function: "total", + desc: "Total supporting detail sentences", + }, + + // other + { + id: "polysyllabic_words", + title: "Polysyllabic Words", + icon: iconForCategory("other"), + category: CATEGORY_LABELS.other, + function: "percent", + desc: "Percent polysyllabic tokens", + }, + { + id: "low_frequency_words", + title: "Low Frequency Words", + icon: iconForCategory("other"), + category: CATEGORY_LABELS.other, + function: "percent", + desc: "Percent low-frequency tokens", + }, + { + id: "sentences", + title: "Sentences", + icon: iconForCategory("other"), + category: CATEGORY_LABELS.other, + function: "total", + desc: "Total sentences", + }, + { + id: "paragraphs", + title: "Paragraphs", + icon: iconForCategory("other"), + category: CATEGORY_LABELS.other, + function: "total", + desc: "Total paragraphs", + }, + { + id: "character_trait_words", + title: "Character Trait Words", + icon: iconForCategory("other"), + category: CATEGORY_LABELS.other, + function: "percent", + desc: "Percent character trait tokens", + }, + { + id: "in_past_tense", + title: "In Past Tense", + icon: iconForCategory("other"), + category: CATEGORY_LABELS.other, + function: "percent", + desc: "Percent past tense scope", + }, + { + id: "explicit_claims", + title: "Explicit Claims", + icon: iconForCategory("other"), + category: CATEGORY_LABELS.other, + function: "percent", + desc: "Percent explicit claims", + }, + { + id: "social_awareness", + title: "Social Awareness", + icon: iconForCategory("other"), + category: CATEGORY_LABELS.other, + function: "percent", + desc: "Percent social awareness", + }, +]; + +const ALL_KEYS = METRIC_DEFS.map((m) => m.id); +const CATEGORIES = Array.from(new Set(METRIC_DEFS.map((m) => m.category))); + +const DEFAULT_PRESETS = { + "Core (language + structure)": [ + "academic_language", + "informal_language", + "latinate_words", + "transition_words", + "citations", + "sentences", + "paragraphs", + ], + "Sources & Evidence": ["information_sources", "attributions", "citations", "quoted_words"], +}; + +const PRESETS_STORAGE_KEY = "wo_metric_presets_v1"; + +/* ---------------------- presets helpers ---------------------- */ +function safeParseJSON(s) { + try { + return JSON.parse(s); + } catch { + return null; + } +} + +function normalizePresetMetrics(arr) { + const uniq = Array.from(new Set((arr || []).filter(Boolean))); + const known = new Set(ALL_KEYS); + return uniq.filter((id) => known.has(id)); +} + +/* ============================================================= + MetricsPanel (updated to match EssayComparison sidebar) + - Keeps backward compat with your existing call: + + ============================================================= */ + +export function MetricsPanel({ + // Backward-compatible props + metrics, + setMetrics, + + // Optional UI knobs + stickyTopClassName = "top-24", + title = "Metrics", + className = "", + panelClassName = "", + useSticky = true, +}) { + const selectedMetrics = Array.isArray(metrics) ? metrics : []; + const setSelectedMetrics = typeof setMetrics === "function" ? setMetrics : () => {}; + + /* ---------------------- Presets (stateful, deletable, creatable) ---------------------- */ + const [presets, setPresets] = useState(DEFAULT_PRESETS); + const [presetName, setPresetName] = useState(""); + + useEffect(() => { + if (typeof window === "undefined") return; + const raw = window.localStorage.getItem(PRESETS_STORAGE_KEY); + const parsed = raw ? safeParseJSON(raw) : null; + + if (parsed && typeof parsed === "object") { + const merged = { ...DEFAULT_PRESETS }; + for (const [k, v] of Object.entries(parsed)) { + if (!k) continue; + merged[k] = normalizePresetMetrics(v); + } + setPresets(merged); + } else { + setPresets(DEFAULT_PRESETS); + } + }, []); + + useEffect(() => { + if (typeof window === "undefined") return; + window.localStorage.setItem(PRESETS_STORAGE_KEY, JSON.stringify(presets)); + }, [presets]); + + const createPreset = useCallback(() => { + const name = (presetName || "").trim(); + if (!name) return; + + const arr = normalizePresetMetrics(selectedMetrics); + if (!arr.length) return; + + setPresets((prev) => ({ + ...prev, + [name]: arr, + })); + setPresetName(""); + }, [presetName, selectedMetrics]); + + const deletePreset = useCallback((name) => { + setPresets((prev) => { + const next = { ...prev }; + delete next[name]; + if (!Object.keys(next).length) return { ...DEFAULT_PRESETS }; + return next; + }); + }, []); + + const applyPreset = useCallback( + (name) => { + const arr = presets?.[name] || []; + setSelectedMetrics(normalizePresetMetrics(arr)); + }, + [presets, setSelectedMetrics] + ); + + /* ---------------------- Category collapse state ---------------------- */ + const [expanded, setExpanded] = useState(() => { + const o = {}; + CATEGORIES.forEach((c) => (o[c] = true)); + return o; + }); + + const handleMetricToggle = useCallback( + (id) => { + setSelectedMetrics((prev) => + (prev || []).includes(id) ? prev.filter((x) => x !== id) : [...(prev || []), id] + ); + }, + [setSelectedMetrics] + ); + + const selectedCount = selectedMetrics.length; + + return ( + + ); +} diff --git a/modules/portfolio_diff/src/app/components/Navbar.js b/modules/portfolio_diff/src/app/components/Navbar.js new file mode 100644 index 000000000..042a97a4d --- /dev/null +++ b/modules/portfolio_diff/src/app/components/Navbar.js @@ -0,0 +1,224 @@ +"use client"; + +import { useEffect, useRef, useState } from "react"; +import { + Search, + Bell, + User, + ChevronDown, + Menu, + X, + BookOpen, + LogOut, + Link, + Home +} from "lucide-react"; +import { navigateTo } from "../utils/navigation"; + +export default function Navbar() { + const [open, setOpen] = useState(false); // mobile sheet + const [menuOpen, setMenuOpen] = useState(false); // desktop profile menu + const [courseDashboards, setCourseDashboards] = useState([]); + const menuRef = useRef(null); + + const dashboardLinks = [ + { name: "Home", url: "/", preserveHash: false }, + ...courseDashboards.map((dashboard) => ({ + ...dashboard, + preserveHash: true, + })), + ]; + + const getDashboardHref = (dashboard) => { + if (typeof window === "undefined") return dashboard.url; + + if (!dashboard.preserveHash) return dashboard.url; + return `${dashboard.url}${window.location.hash || ""}`; + }; + + useEffect(() => { + const loadCourseDashboards = async () => { + try { + const response = await fetch("/webapi/course_dashboards"); + if (!response.ok) return; + const dashboards = await response.json(); + if (!Array.isArray(dashboards)) return; + + setCourseDashboards( + dashboards.filter((dashboard) => dashboard?.name && dashboard?.url) + ); + } catch { + setCourseDashboards([]); + } + }; + + loadCourseDashboards(); + }, []); + + // Close profile menu on outside click + useEffect(() => { + const onClick = (e) => { + if (!menuRef.current) return; + if (!menuRef.current.contains(e.target)) setMenuOpen(false); + }; + const onKey = (e) => { + if (e.key === "Escape") setMenuOpen(false); + }; + window.addEventListener("mousedown", onClick); + window.addEventListener("keydown", onKey); + return () => { + window.removeEventListener("mousedown", onClick); + window.removeEventListener("keydown", onKey); + }; + }, []); + + return ( +
+ + + {/* Mobile sheet */} +
+
+
+ + +
+ + {/* Mobile profile actions */} +
+
+
+
+ +
+
+

Dashboards

+
+
+
+ +
+ {dashboardLinks.map((dashboard) => ( + setOpen(false)} + > + {dashboard.name} + + ))} + +
+
+
+
+
+ ); +} diff --git a/modules/portfolio_diff/src/app/components/TypeFilterDropdown.js b/modules/portfolio_diff/src/app/components/TypeFilterDropdown.js new file mode 100644 index 000000000..d97516057 --- /dev/null +++ b/modules/portfolio_diff/src/app/components/TypeFilterDropdown.js @@ -0,0 +1,104 @@ +import { useState, useRef, useEffect } from "react"; +import { ChevronDown, Filter } from "lucide-react"; +import { motion, AnimatePresence } from "framer-motion"; + +const essayTypes = [ + "Argumentative", + "Narrative", + "Personal", + "Analytical", + "Reflective", + "Opinion", + "Education", + "Descriptive", + "Experience", + "Economics", + "Research", +]; + +export default function TypeFilterDropdown({ selectedTypes, setSelectedTypes }) { + const [isDropdownOpen, setIsDropdownOpen] = useState(false); + const dropdownRef = useRef(null); + + const toggleType = (type) => { + setSelectedTypes((prev) => + prev.includes(type) ? prev.filter((t) => t !== type) : [...prev, type] + ); + }; + + const handleClearAll = () => { + setSelectedTypes([]); + }; + + // Close on outside click + useEffect(() => { + function handleClickOutside(event) { + if (dropdownRef.current && !dropdownRef.current.contains(event.target)) { + setIsDropdownOpen(false); + } + } + document.addEventListener("mousedown", handleClickOutside); + return () => document.removeEventListener("mousedown", handleClickOutside); + }, []); + + return ( +
+ {/* Toggle Button */} + + + {/* Dropdown Panel with Animation */} + + {isDropdownOpen && ( + +
+ + + {essayTypes.map((type) => ( + + ))} +
+
+ )} +
+
+ ); +} diff --git a/modules/portfolio_diff/src/app/favicon.ico b/modules/portfolio_diff/src/app/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..718d6fea4835ec2d246af9800eddb7ffb276240c GIT binary patch literal 25931 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 LrJugUA?W`A8`#=m literal 0 HcmV?d00001 diff --git a/modules/portfolio_diff/src/app/globals.css b/modules/portfolio_diff/src/app/globals.css new file mode 100644 index 000000000..7ddd1f015 --- /dev/null +++ b/modules/portfolio_diff/src/app/globals.css @@ -0,0 +1,43 @@ +@import "tailwindcss"; + +:root { + --background: #ffffff; + --foreground: #171717; +} + +@theme inline { + --color-background: var(--background); + --color-foreground: var(--foreground); + --font-sans: var(--font-geist-sans); + --font-mono: var(--font-geist-mono); +} + +@media (prefers-color-scheme: dark) { + :root { + --background: #0a0a0a; + --background: #f2f2f2; + --foreground: #ededed; + } +} + +body { + background: var(--background); + font-family: Arial, Helvetica, sans-serif; +} + +a, button { + cursor: pointer; +} + +.text-p { + font-size: 15px; +} + + +.dashboard-card { + cursor: pointer; + box-sizing: border-box; + border: 1px solid rgba(60,73,81,0.16); + border-radius: 8px; + box-shadow: 0 4px 0 0 rgba(35,57,91,0.08); +} \ No newline at end of file diff --git a/modules/portfolio_diff/src/app/hooks/useCourseId.js b/modules/portfolio_diff/src/app/hooks/useCourseId.js new file mode 100644 index 000000000..6a57b5b51 --- /dev/null +++ b/modules/portfolio_diff/src/app/hooks/useCourseId.js @@ -0,0 +1,92 @@ +"use client"; + +import { useCallback, useEffect, useMemo, useState } from "react"; + +const STORAGE_KEY = "wo_course_id"; + +/** + * Parses hash params like: + * "#course_id=12345678901;tool=WritingObserver" + * Supports separators: ";" or "&" + */ +function parseHashParams(hash) { + const raw = (hash || "").replace(/^#/, ""); + const out = {}; + if (!raw) return out; + + const parts = raw.split(/[;&]/g).filter(Boolean); + for (const part of parts) { + const [k, ...rest] = part.split("="); + if (!k) continue; + const v = rest.join("="); + out[decodeURIComponent(k)] = decodeURIComponent(v ?? ""); + } + return out; +} + +function getCourseIdFromHash() { + if (typeof window === "undefined") return null; + const params = parseHashParams(window.location.hash); + return params.course_id || null; +} + +function getCourseIdFromStorage() { + if (typeof window === "undefined") return null; + try { + return window.localStorage.getItem(STORAGE_KEY); + } catch { + return null; + } +} + +function setCourseIdToStorage(courseId) { + if (typeof window === "undefined") return; + try { + if (courseId) window.localStorage.setItem(STORAGE_KEY, String(courseId)); + else window.localStorage.removeItem(STORAGE_KEY); + } catch { + // ignore storage failures (private mode, etc.) + } +} + +/** + * useCourseId + * - If URL hash has course_id => use it AND persist it. + * - Else fall back to localStorage. + * - Listens to hash changes. + */ +export function useCourseId() { + const [courseId, _setCourseId] = useState(null); + + // initialize + keep in sync with hash changes + useEffect(() => { + const sync = () => { + const fromHash = getCourseIdFromHash(); + if (fromHash) { + _setCourseId(fromHash); + setCourseIdToStorage(fromHash); + return; + } + + const fromStorage = getCourseIdFromStorage(); + _setCourseId(fromStorage || null); + }; + + sync(); + window.addEventListener("hashchange", sync); + return () => window.removeEventListener("hashchange", sync); + }, []); + + // allow manual overrides (and persist them) + const setCourseId = useCallback((next) => { + const value = next ? String(next) : null; + _setCourseId(value); + setCourseIdToStorage(value); + }, []); + + const ready = useMemo(() => courseId !== undefined, [courseId]); + + console.log("courseId: ", courseId) + + return { courseId, setCourseId, ready }; +} diff --git a/modules/portfolio_diff/src/app/layout.js b/modules/portfolio_diff/src/app/layout.js new file mode 100644 index 000000000..a588683e3 --- /dev/null +++ b/modules/portfolio_diff/src/app/layout.js @@ -0,0 +1,21 @@ +import Navbar from "./components/Navbar"; +import { CourseIdProvider } from "@/app/providers/CourseIdProvider"; +import "./globals.css"; + +export const metadata = { + title: "Writing Portfolio", + description: "Generated by create next app", +}; + +export default function RootLayout({ children }) { + return ( + + + + + {children} + + + + ); +} diff --git a/modules/portfolio_diff/src/app/page.js b/modules/portfolio_diff/src/app/page.js new file mode 100644 index 000000000..24252a56f --- /dev/null +++ b/modules/portfolio_diff/src/app/page.js @@ -0,0 +1,7 @@ +"use client"; + +import StudentsPage from "./students/page"; + +export default function WritingPortfolioDashboard() { + return ; +} \ No newline at end of file diff --git a/modules/portfolio_diff/src/app/providers/CourseIdProvider.jsx b/modules/portfolio_diff/src/app/providers/CourseIdProvider.jsx new file mode 100644 index 000000000..d765d6f77 --- /dev/null +++ b/modules/portfolio_diff/src/app/providers/CourseIdProvider.jsx @@ -0,0 +1,35 @@ +"use client"; + +import React, { createContext, useContext } from "react"; +import { useCourseId } from "@/app/hooks/useCourseId"; + +/** + * React context for sharing the current course ID state + * across the component tree without prop drilling. + */ +const CourseIdContext = createContext(null); + +/** + * Provides the course ID context to all descendant components. + * + * Internally calls `useCourseId()` and exposes its return value + * via React Context. + * + * Must wrap any component that calls `useCourseIdContext()`. + */ +export function CourseIdProvider({ children }) { + const value = useCourseId(); + return {children}; +} + +/** + * Custom hook to access the course ID context. + * + * Throws an error if used outside of `CourseIdProvider` + * to prevent silent failures and undefined access. + */ +export function useCourseIdContext() { + const ctx = useContext(CourseIdContext); + if (!ctx) throw new Error("useCourseIdContext must be used within CourseIdProvider"); + return ctx; +} diff --git a/modules/portfolio_diff/src/app/students/compare/page.js b/modules/portfolio_diff/src/app/students/compare/page.js new file mode 100644 index 000000000..1a9fe796f --- /dev/null +++ b/modules/portfolio_diff/src/app/students/compare/page.js @@ -0,0 +1,1946 @@ +"use client"; + +import { navigateTo } from "@/app/utils/navigation"; +import { + ArrowLeftRight, + Check, + ChevronDown, + Clock, + Eye, + FileText, + Focus, + Gauge, + Languages, + ListCollapse, + MessageSquareText, + MessagesSquare, + Minus, + Quote, + RefreshCw, + Search, + Speech, + TrendingDown, + TrendingUp, + Users, + WholeWord, + X, +} from "lucide-react"; +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; + +import { useLOConnectionDataManager, LOConnectionLastUpdated } from "lo_event/lo_event/lo_assess/components/components.jsx"; +import dynamic from "next/dynamic"; + +import { MetricsPanel } from "@/app/components/MetricsPanel"; +import { useCourseIdContext } from "@/app/providers/CourseIdProvider"; +import { getWsOriginFromWindow } from "@/app/utils/ws"; + +/* ---------------------- deterministic helpers ---------------------- */ +const seedFrom = (s) => { + let h = 2166136261; + for (let i = 0; i < s.length; i++) h = ((h ^ s.charCodeAt(i)) * 16777619) >>> 0; + return h >>> 0; +}; + +/* ============================================================= + OFFSET HIGHLIGHTING HELPERS (multi-metric, overlap-safe) + ============================================================= */ + +const HIGHLIGHT_CLASSES = [ + "bg-emerald-200/70", + "bg-sky-200/70", + "bg-amber-200/70", + "bg-rose-200/70", + "bg-indigo-200/70", + "bg-lime-200/70", + "bg-violet-200/60", + "bg-teal-200/70", + "bg-fuchsia-200/60", + "bg-orange-200/70", +]; + +const highlightClassForMetric = (metricId) => { + const idx = seedFrom(metricId || "metric") % HIGHLIGHT_CLASSES.length; + return HIGHLIGHT_CLASSES[idx]; +}; + +function buildSpansFromDoc(doc, metricIds) { + const text = (doc?.text || "").toString(); + const spans = []; + + for (const metricId of metricIds || []) { + const m = doc?.[metricId]; + const offsets = m?.offsets; + if (!Array.isArray(offsets)) continue; + + for (const pair of offsets) { + if (!Array.isArray(pair) || pair.length < 2) continue; + const start = Number(pair[0]); + const len = Number(pair[1]); + if (!Number.isFinite(start) || !Number.isFinite(len) || len <= 0) continue; + + const end = start + len; + + const s = Math.max(0, Math.min(text.length, start)); + const e = Math.max(0, Math.min(text.length, end)); + if (e > s) spans.push({ start: s, end: e, metricId }); + } + } + + spans.sort((a, b) => a.start - b.start || b.end - b.start - (a.end - a.start)); + return { text, spans }; +} + +function segmentTextBySpans(text, spans) { + const cuts = new Set([0, text.length]); + for (const s of spans) { + cuts.add(s.start); + cuts.add(s.end); + } + const points = Array.from(cuts).sort((a, b) => a - b); + + const segs = []; + for (let i = 0; i < points.length - 1; i++) { + const a = points[i], + b = points[i + 1]; + if (b <= a) continue; + + const active = []; + for (const sp of spans) { + if (sp.start <= a && sp.end >= b) active.push(sp.metricId); + } + + segs.push({ start: a, end: b, text: text.slice(a, b), active }); + } + return segs; +} + +/* ============================================================= + METRICS (FULL LIST) + ============================================================= */ + +const CATEGORY_LABELS = { + language: "Language", + argumentation: "Argumentation", + statements: "Statements", + transitions: "Transition Words", + pos: "Parts of Speech", + sentence_type: "Sentence Types", + source_information: "Source Information", + dialogue: "Dialogue", + tone: "Tone", + details: "Details", + other: "Other", +}; + +const iconForCategory = (catKey) => { + switch (catKey) { + case "language": + return Languages; + case "argumentation": + return MessagesSquare; + case "statements": + return MessageSquareText; + case "transitions": + return ArrowLeftRight; + case "pos": + return Speech; + case "sentence_type": + return WholeWord; + case "source_information": + return Quote; + case "dialogue": + return Users; + case "tone": + return Gauge; + case "details": + return ListCollapse; + default: + return FileText; + } +}; + +const METRIC_DEFS = [ + // language + { + id: "academic_language", + title: "Academic Language", + icon: iconForCategory("language"), + category: CATEGORY_LABELS.language, + function: "percent", + desc: "Percent of tokens flagged academic", + }, + { + id: "informal_language", + title: "Informal Language", + icon: iconForCategory("language"), + category: CATEGORY_LABELS.language, + function: "percent", + desc: "Percent of tokens flagged informal", + }, + { + id: "latinate_words", + title: "Latinate Words", + icon: iconForCategory("language"), + category: CATEGORY_LABELS.language, + function: "percent", + desc: "Percent of tokens flagged latinate", + }, + { + id: "opinion_words", + title: "Opinion Words", + icon: iconForCategory("language"), + category: CATEGORY_LABELS.language, + function: "total", + desc: "Total opinion-word signals", + }, + { + id: "emotion_words", + title: "Emotion Words", + icon: iconForCategory("language"), + category: CATEGORY_LABELS.language, + function: "percent", + desc: "Percent emotion words", + }, + + // argumentation + { + id: "argument_words", + title: "Argument Words", + icon: iconForCategory("argumentation"), + category: CATEGORY_LABELS.argumentation, + function: "percent", + desc: "Percent argument words", + }, + { + id: "explicit_argument", + title: "Explicit argument", + icon: iconForCategory("argumentation"), + category: CATEGORY_LABELS.argumentation, + function: "percent", + desc: "Percent explicit argument markers", + }, + + // statements + { + id: "statements_of_opinion", + title: "Statements of Opinion", + icon: iconForCategory("statements"), + category: CATEGORY_LABELS.statements, + function: "percent", + desc: "Percent of sentences classified as opinion", + }, + { + id: "statements_of_fact", + title: "Statements of Fact", + icon: iconForCategory("statements"), + category: CATEGORY_LABELS.statements, + function: "percent", + desc: "Percent of sentences classified as fact", + }, + + // transitions + { + id: "transition_words", + title: "Transition Words", + icon: iconForCategory("transitions"), + category: CATEGORY_LABELS.transitions, + function: "counts", + desc: "Transition counts (by type)", + }, + { + id: "positive_transition_words", + title: "Positive Transition Words", + icon: iconForCategory("transitions"), + category: CATEGORY_LABELS.transitions, + function: "total", + desc: "Total positive transitions", + }, + { + id: "conditional_transition_words", + title: "Conditional Transition Words", + icon: iconForCategory("transitions"), + category: CATEGORY_LABELS.transitions, + function: "total", + desc: "Total conditional transitions", + }, + { + id: "consequential_transition_words", + title: "Consequential Transition Words", + icon: iconForCategory("transitions"), + category: CATEGORY_LABELS.transitions, + function: "total", + desc: "Total consequential transitions", + }, + { + id: "contrastive_transition_words", + title: "Contrastive Transition Words", + icon: iconForCategory("transitions"), + category: CATEGORY_LABELS.transitions, + function: "total", + desc: "Total contrastive transitions", + }, + { + id: "counterpoint_transition_words", + title: "Counterpoint Transition Words", + icon: iconForCategory("transitions"), + category: CATEGORY_LABELS.transitions, + function: "total", + desc: "Total counterpoint transitions", + }, + { + id: "comparative_transition_words", + title: "Comparative Transition Words", + icon: iconForCategory("transitions"), + category: CATEGORY_LABELS.transitions, + function: "total", + desc: "Total comparative transitions", + }, + { + id: "cross_referential_transition_words", + title: "Cross Referential Transition Words", + icon: iconForCategory("transitions"), + category: CATEGORY_LABELS.transitions, + function: "total", + desc: "Total cross-referential transitions", + }, + { + id: "illustrative_transition_words", + title: "Illustrative Transition Words", + icon: iconForCategory("transitions"), + category: CATEGORY_LABELS.transitions, + function: "total", + desc: "Total illustrative transitions", + }, + { + id: "negative_transition_words", + title: "Negative Transition Words", + icon: iconForCategory("transitions"), + category: CATEGORY_LABELS.transitions, + function: "total", + desc: "Total negative transitions", + }, + { + id: "emphatic_transition_words", + title: "Emphatic Transition Words", + icon: iconForCategory("transitions"), + category: CATEGORY_LABELS.transitions, + function: "total", + desc: "Total emphatic transitions", + }, + { + id: "evenidentiary_transition_words", + title: "Evenidentiary_transition_words", + icon: iconForCategory("transitions"), + category: CATEGORY_LABELS.transitions, + function: "total", + desc: "Total evidentiary transitions", + }, + { + id: "general_transition_words", + title: "General Transition Words", + icon: iconForCategory("transitions"), + category: CATEGORY_LABELS.transitions, + function: "total", + desc: "Total general transitions", + }, + { + id: "ordinal_transition_words", + title: "Ordinal Transition Words", + icon: iconForCategory("transitions"), + category: CATEGORY_LABELS.transitions, + function: "total", + desc: "Total ordinal transitions", + }, + { + id: "purposive_transition_words", + title: "Purposive Transition Words", + icon: iconForCategory("transitions"), + category: CATEGORY_LABELS.transitions, + function: "total", + desc: "Total purposive transitions", + }, + { + id: "periphrastic_transition_words", + title: "Periphrastic Transition Words", + icon: iconForCategory("transitions"), + category: CATEGORY_LABELS.transitions, + function: "total", + desc: "Total periphrastic transitions", + }, + { + id: "hypothetical_transition_words", + title: "Hypothetical Transition Words", + icon: iconForCategory("transitions"), + category: CATEGORY_LABELS.transitions, + function: "total", + desc: "Total hypothetical transitions", + }, + { + id: "summative_transition_words", + title: "Summative Transition Words", + icon: iconForCategory("transitions"), + category: CATEGORY_LABELS.transitions, + function: "total", + desc: "Total summative transitions", + }, + { + id: "introductory_transition_words", + title: "Introductory Transition Words", + icon: iconForCategory("transitions"), + category: CATEGORY_LABELS.transitions, + function: "total", + desc: "Total introductory transitions", + }, + + // parts of speech + { + id: "adjectives", + title: "Adjectives", + icon: iconForCategory("pos"), + category: CATEGORY_LABELS.pos, + function: "total", + desc: "Total adjectives", + }, + { + id: "adverbs", + title: "Adverbs", + icon: iconForCategory("pos"), + category: CATEGORY_LABELS.pos, + function: "total", + desc: "Total adverbs", + }, + { + id: "nouns", + title: "Nouns", + icon: iconForCategory("pos"), + category: CATEGORY_LABELS.pos, + function: "total", + desc: "Total nouns", + }, + { + id: "proper_nouns", + title: "Proper Nouns", + icon: iconForCategory("pos"), + category: CATEGORY_LABELS.pos, + function: "total", + desc: "Total proper nouns", + }, + { + id: "verbs", + title: "Verbs", + icon: iconForCategory("pos"), + category: CATEGORY_LABELS.pos, + function: "total", + desc: "Total verbs", + }, + { + id: "numbers", + title: "Numbers", + icon: iconForCategory("pos"), + category: CATEGORY_LABELS.pos, + function: "total", + desc: "Total numbers", + }, + { + id: "prepositions", + title: "Prepositions", + icon: iconForCategory("pos"), + category: CATEGORY_LABELS.pos, + function: "total", + desc: "Total prepositions", + }, + { + id: "coordinating_conjunction", + title: "Coordinating Conjunction", + icon: iconForCategory("pos"), + category: CATEGORY_LABELS.pos, + function: "total", + desc: "Total coordinating conjunctions", + }, + { + id: "subordinating_conjunction", + title: "Subordinating Conjunction", + icon: iconForCategory("pos"), + category: CATEGORY_LABELS.pos, + function: "total", + desc: "Total subordinating conjunctions", + }, + { + id: "auxiliary_verb", + title: "Auxiliary Verb", + icon: iconForCategory("pos"), + category: CATEGORY_LABELS.pos, + function: "total", + desc: "Total auxiliary verbs", + }, + { + id: "pronoun", + title: "Pronoun", + icon: iconForCategory("pos"), + category: CATEGORY_LABELS.pos, + function: "total", + desc: "Total pronouns", + }, + + // sentence types + { + id: "simple_sentences", + title: "Simple Sentences", + icon: iconForCategory("sentence_type"), + category: CATEGORY_LABELS.sentence_type, + function: "total", + desc: "Total simple sentences", + }, + { + id: "simple_with_complex_predicates", + title: "Simple with Complex Predicates", + icon: iconForCategory("sentence_type"), + category: CATEGORY_LABELS.sentence_type, + function: "total", + desc: "Total simple (complex predicates)", + }, + { + id: "simple_with_compound_predicates", + title: "Simple with Compound Predicates", + icon: iconForCategory("sentence_type"), + category: CATEGORY_LABELS.sentence_type, + function: "total", + desc: "Total simple (compound predicates)", + }, + { + id: "simple_with_compound_complex_predicates", + title: "Simple with Compound Complex Predicates", + icon: iconForCategory("sentence_type"), + category: CATEGORY_LABELS.sentence_type, + function: "total", + desc: "Total simple (compound complex predicates)", + }, + { + id: "compound_sentences", + title: "Compound Sentences", + icon: iconForCategory("sentence_type"), + category: CATEGORY_LABELS.sentence_type, + function: "total", + desc: "Total compound sentences", + }, + { + id: "complex_sentences", + title: "Complex Sentences", + icon: iconForCategory("sentence_type"), + category: CATEGORY_LABELS.sentence_type, + function: "total", + desc: "Total complex sentences", + }, + { + id: "compound_complex_sentences", + title: "Compound Complex Sentences", + icon: iconForCategory("sentence_type"), + category: CATEGORY_LABELS.sentence_type, + function: "total", + desc: "Total compound-complex sentences", + }, + + // source info + { + id: "information_sources", + title: "Information Sources", + icon: iconForCategory("source_information"), + category: CATEGORY_LABELS.source_information, + function: "percent", + desc: "Percent source references", + }, + { + id: "attributions", + title: "Attributions", + icon: iconForCategory("source_information"), + category: CATEGORY_LABELS.source_information, + function: "percent", + desc: "Percent attributions", + }, + { + id: "citations", + title: "Citations", + icon: iconForCategory("source_information"), + category: CATEGORY_LABELS.source_information, + function: "percent", + desc: "Percent citations", + }, + { + id: "quoted_words", + title: "Quoted Words", + icon: iconForCategory("source_information"), + category: CATEGORY_LABELS.source_information, + function: "percent", + desc: "Percent quoted words", + }, + + // dialogue + { + id: "direct_speech_verbs", + title: "Direct Speech Verbs", + icon: iconForCategory("dialogue"), + category: CATEGORY_LABELS.dialogue, + function: "percent", + desc: "Percent direct speech verbs", + }, + { + id: "indirect_speech", + title: "Indirect Speech", + icon: iconForCategory("dialogue"), + category: CATEGORY_LABELS.dialogue, + function: "percent", + desc: "Percent indirect speech", + }, + + // tone + { + id: "positive_tone", + title: "Positive Tone", + icon: iconForCategory("tone"), + category: CATEGORY_LABELS.tone, + function: "percent", + desc: "Percent positive tone", + }, + { + id: "negative_tone", + title: "Negative Tone", + icon: iconForCategory("tone"), + category: CATEGORY_LABELS.tone, + function: "percent", + desc: "Percent negative tone", + }, + + // details + { + id: "concrete_details", + title: "Concrete Details", + icon: iconForCategory("details"), + category: CATEGORY_LABELS.details, + function: "percent", + desc: "Percent concrete details", + }, + { + id: "main_idea_sentences", + title: "Main Idea Sentences", + icon: iconForCategory("details"), + category: CATEGORY_LABELS.details, + function: "total", + desc: "Total main idea sentences", + }, + { + id: "supporting_idea_sentences", + title: "Supporting Idea Sentences", + icon: iconForCategory("details"), + category: CATEGORY_LABELS.details, + function: "total", + desc: "Total supporting idea sentences", + }, + { + id: "supporting_detail_sentences", + title: "Supporting Detail Sentences", + icon: iconForCategory("details"), + category: CATEGORY_LABELS.details, + function: "total", + desc: "Total supporting detail sentences", + }, + + // other + { + id: "polysyllabic_words", + title: "Polysyllabic Words", + icon: iconForCategory("other"), + category: CATEGORY_LABELS.other, + function: "percent", + desc: "Percent polysyllabic tokens", + }, + { + id: "low_frequency_words", + title: "Low Frequency Words", + icon: iconForCategory("other"), + category: CATEGORY_LABELS.other, + function: "percent", + desc: "Percent low-frequency tokens", + }, + { + id: "sentences", + title: "Sentences", + icon: iconForCategory("other"), + category: CATEGORY_LABELS.other, + function: "total", + desc: "Total sentences", + }, + { + id: "paragraphs", + title: "Paragraphs", + icon: iconForCategory("other"), + category: CATEGORY_LABELS.other, + function: "total", + desc: "Total paragraphs", + }, + { + id: "character_trait_words", + title: "Character Trait Words", + icon: iconForCategory("other"), + category: CATEGORY_LABELS.other, + function: "percent", + desc: "Percent character trait tokens", + }, + { + id: "in_past_tense", + title: "In Past Tense", + icon: iconForCategory("other"), + category: CATEGORY_LABELS.other, + function: "percent", + desc: "Percent past tense scope", + }, + { + id: "explicit_claims", + title: "Explicit Claims", + icon: iconForCategory("other"), + category: CATEGORY_LABELS.other, + function: "percent", + desc: "Percent explicit claims", + }, + { + id: "social_awareness", + title: "Social Awareness", + icon: iconForCategory("other"), + category: CATEGORY_LABELS.other, + function: "percent", + desc: "Percent social awareness", + }, +]; + +const METRIC_BY_ID = Object.fromEntries(METRIC_DEFS.map((m) => [m.id, m])); + +/* ---------------------- Tooltip values from backend ---------------------- */ +const PERCENT_IDS = new Set(METRIC_DEFS.filter((m) => m.function === "percent").map((m) => m.id)); + +const formatMetricValue = (value, id) => { + if (value == null) return "β€”"; + if (PERCENT_IDS.has(id)) return `${Math.round(Number(value))}%`; + if (typeof value === "number") return Number.isInteger(value) ? String(value) : value.toFixed(1); + const n = Number(value); + return Number.isNaN(n) ? String(value) : n.toFixed(1); +}; + +/* ============================================================= + coverage-based metric value from offsets + ============================================================= */ +function metricCoveragePercent(doc, metricId) { + const text = (doc?.text || "").toString(); + const L = text.length; + if (!L) return 0; + + const offsets = doc?.[metricId]?.offsets; + if (!Array.isArray(offsets) || offsets.length === 0) return 0; + + const ranges = []; + for (const pair of offsets) { + if (!Array.isArray(pair) || pair.length < 2) continue; + const start = Number(pair[0]); + const len = Number(pair[1]); + if (!Number.isFinite(start) || !Number.isFinite(len) || len <= 0) continue; + + let s = Math.max(0, Math.min(L, start)); + let e = Math.max(0, Math.min(L, start + len)); + if (e > s) ranges.push([s, e]); + } + if (!ranges.length) return 0; + + ranges.sort((a, b) => a[0] - b[0] || a[1] - b[1]); + + let covered = 0; + let [curS, curE] = ranges[0]; + + for (let i = 1; i < ranges.length; i++) { + const [s, e] = ranges[i]; + if (s <= curE) { + curE = Math.max(curE, e); + } else { + covered += curE - curS; + curS = s; + curE = e; + } + } + covered += curE - curS; + + return (covered / L) * 100; +} + +/* ---------------------- Tooltip builder for highlights ---------------------- */ +function buildHighlightTooltip(doc, metricIds) { + const uniq = Array.from(new Set(metricIds || [])); + if (uniq.length === 0) return ""; + + const lines = []; + for (const id of uniq) { + const meta = METRIC_BY_ID[id]; + const label = meta?.title || id; + + const v = doc?.[id]?.metric; + const hasNum = v != null && !Number.isNaN(Number(v)); + + const cov = metricCoveragePercent(doc, id); + const covStr = `${cov.toFixed(1)}% of text`; + + if (hasNum) { + lines.push(`${label}: ${formatMetricValue(v, id)} (${covStr})`); + } else { + lines.push(`${label}: ${covStr}`); + } + } + return lines.join("\n"); +} + +/* ---------------------- Floating tooltip (custom, reliable) ---------------------- */ +function clamp(n, lo, hi) { + return Math.max(lo, Math.min(hi, n)); +} + +function FloatingTooltip({ tooltip }) { + if (!tooltip?.visible) return null; + + return ( +
+
+ {tooltip.content} +
+
+ ); +} + +function HighlightedEssay({ + doc, + activeMetricIds, + containerRef, + onShowTooltip, + onMoveTooltip, + onHideTooltip, +}) { + const { text, spans } = useMemo(() => buildSpansFromDoc(doc, activeMetricIds), [doc, activeMetricIds]); + const segments = useMemo(() => segmentTextBySpans(text, spans), [text, spans]); + + if (!text.trim()) { + return ( +
+ (No text returned for this document.) +
+ ); + } + + return ( +
+ {segments.map((seg, idx) => { + if (!seg.active.length) return {seg.text}; + + const top = seg.active[0]; + const cls = highlightClassForMetric(top); + const tooltipText = buildHighlightTooltip(doc, seg.active); + + return ( + onShowTooltip(tooltipText, e)} + onMouseMove={(e) => onMoveTooltip(e)} + onMouseLeave={() => onHideTooltip()} + onPointerEnter={(e) => onShowTooltip(tooltipText, e)} + onPointerMove={(e) => onMoveTooltip(e)} + onPointerLeave={() => onHideTooltip()} + > + {seg.text} + + ); + })} +
+ ); +} + +/* ---------------------- URL param reader (client-safe) ---------------------- */ +function readCompareParamsFromLocation() { + if (typeof window === "undefined") { + return { urlReady: false, studentID: "", docIds: [] }; + } + + const sp = new URLSearchParams(window.location.search); + const studentID = (sp.get("student_id") || "").trim(); + const idsRaw = (sp.get("ids") || "").trim(); + + const parts = idsRaw + .split(",") + .map((s) => s.trim()) + .filter(Boolean); + + const seen = new Set(); + const docIds = []; + for (const p of parts) { + if (!seen.has(p)) { + seen.add(p); + docIds.push(p); + } + if (docIds.length === 2) break; + } + + return { urlReady: true, studentID, docIds }; +} + +function buildEssayFromDoc({ docId, text, side, title }) { + const content = (text || "").trim(); + const words = content ? content.split(/\s+/).filter(Boolean).length : 0; + + return { + id: docId || `${side}-unknown`, + title: title || (docId ? `Document: ${docId}` : `Document (${side})`), + date: "", + minutes: Math.max(10, Math.round(words / 30)), + words, + grade: "β€”", + tags: [], + content: content || "(No text returned for this document.)", + }; +} + +/* ---------------------- Metrics comparison UI helpers ---------------------- */ +function formatPct(n) { + const x = Number.isFinite(Number(n)) ? Number(n) : 0; + return `${x.toFixed(1)}%`; +} +function formatDelta(n) { + const x = Number.isFinite(Number(n)) ? Number(n) : 0; + const sign = x > 0 ? "+" : x < 0 ? "βˆ’" : "Β±"; + const abs = Math.abs(x).toFixed(1); + return `${sign}${abs}%`; +} + +/* ---------------------- Evidence extraction (short excerpts) ---------------------- */ +function extractMetricExamples(doc, metricId, maxExamples = 2) { + const text = (doc?.text || "").toString(); + if (!text.trim()) return []; + + const offsets = doc?.[metricId]?.offsets; + if (!Array.isArray(offsets) || offsets.length === 0) return []; + + const L = text.length; + const spans = []; + for (const pair of offsets) { + if (!Array.isArray(pair) || pair.length < 2) continue; + const start = Number(pair[0]); + const len = Number(pair[1]); + if (!Number.isFinite(start) || !Number.isFinite(len) || len <= 0) continue; + const s = Math.max(0, Math.min(L, start)); + const e = Math.max(0, Math.min(L, start + len)); + if (e > s) spans.push([s, e]); + } + if (!spans.length) return []; + + spans.sort((a, b) => a[0] - b[0] || a[1] - b[1]); + + const seen = new Set(); + const out = []; + for (const [s, e] of spans) { + if (out.length >= maxExamples) break; + + const pad = 70; + const a = Math.max(0, s - pad); + const b = Math.min(L, e + pad); + + let snippet = text.slice(a, b).replace(/\s+/g, " ").trim(); + + if (a > 0) snippet = `…${snippet}`; + if (b < L) snippet = `${snippet}…`; + + const key = snippet.toLowerCase(); + if (seen.has(key)) continue; + seen.add(key); + + out.push(snippet); + } + return out; +} + +function MetricDeltaIcon({ delta }) { + const d = Number(delta) || 0; + if (d > 0.0001) return ; + if (d < -0.0001) return ; + return ; +} + +function MetricDeltaPill({ delta }) { + const d = Number(delta) || 0; + const cls = + d > 0.0001 + ? "bg-emerald-50 text-emerald-700 ring-1 ring-emerald-200" + : d < -0.0001 + ? "bg-rose-50 text-rose-700 ring-1 ring-rose-200" + : "bg-gray-100 text-gray-700"; + return ( + + Ξ” {formatDelta(d)} + + ); +} + +function StoryCard({ label, metricTitle, category, left, right, delta, tone, isDisabled }) { + const toneCls = + tone === "up" + ? "border-emerald-200 bg-emerald-50/40" + : tone === "down" + ? "border-rose-200 bg-rose-50/40" + : "border-gray-200 bg-gray-50"; + + return ( +
+
{label}
+
+
+
+ {isDisabled ? "β€”" : metricTitle || "β€”"} +
+
{isDisabled ? "" : category || ""}
+
+ +
+ +
+ {formatPct(left)} + β†’ + {formatPct(right)} +
+
+ ); +} + +function MetricRow({ row, isFocused, onFocusToggle, onShow }) { + const { def, left, right, delta } = row; + return ( +
+
+ +
+
+
{def.title}
+ Β· {def.category} +
+
{def.desc}
+
+ +
+
+ {formatPct(left)} + β†’ + {formatPct(right)} +
+ +
+ + +
+ + + + +
+
+
+ ); +} + +export default function EssayComparison() { + const initial = useMemo(() => readCompareParamsFromLocation(), []); + const [urlReady, setUrlReady] = useState(initial.urlReady); + const [studentID, setStudentID] = useState(initial.studentID); + const [docIds, setDocIds] = useState(initial.docIds); + const { courseId } = useCourseIdContext(); + + useEffect(() => { + const next = readCompareParamsFromLocation(); + if (!next.urlReady) return; + + const sameStudent = next.studentID === studentID; + const sameDocs = + next.docIds.length === docIds.length && + next.docIds[0] === docIds[0] && + next.docIds[1] === docIds[1]; + + if (!sameStudent) setStudentID(next.studentID); + if (!sameDocs) setDocIds(next.docIds); + if (!urlReady) setUrlReady(true); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + const leftDocId = docIds[0] || ""; + const rightDocId = docIds[1] || ""; + + const enabled = urlReady && !!studentID && docIds.length === 2; + const missingParams = urlReady && (!studentID || docIds.length !== 2); + + const [selectedMetrics, setSelectedMetrics] = useState([ + "academic_language", + "informal_language", + "latinate_words", + "transition_words", + "citations", + "sentences", + "paragraphs", + ]); + + /* ---------------------- comparison data fetch ---------------------- */ + const origin = + process.env.NEXT_PUBLIC_LO_WS_ORIGIN?.replace(/\/+$/, "") || + getWsOriginFromWindow() || + "ws://localhost:8888"; + + const dataScope = useMemo(() => { + if (!urlReady || !studentID) { + return { + wo: { + execution_dag: "writing_observer", + target_exports: [], + kwargs: {}, + }, + }; + } + + const target_exports = ["student_with_docs", "single_student_profile"]; + + // Only add NLP annotation export when we have 2 docs selected + if (docIds.length === 2 && docIds[0] && docIds[1]) { + target_exports.push("single_student_docs_with_nlp_annotations"); + } + + return { + wo: { + execution_dag: "writing_observer", + target_exports, + kwargs: { + course_id: courseId, + student_id: studentID, + ...(docIds.length === 2 && docIds[0] && docIds[1] + ? { + document: docIds, + nlp_options: selectedMetrics, + } + : {}), + }, + }, + }; + }, [urlReady, studentID, courseId, docIds, selectedMetrics]); + + const { data: loData, errors: loErrors, connection: loConnection } = useLOConnectionDataManager({ + url: `${origin}/wsapi/communication_protocol`, + dataScope, + }); + + /* ---------------------- Available docs list (for replacement selection) ---------------------- */ + const availableDocIds = useMemo(() => { + const docsObj = loData?.students?.[studentID]?.documents || {}; + const ids = Object.keys(docsObj || {}); + ids.sort(); + return ids; + }, [loData, studentID]); + + useEffect(() => { + if (loConnection && loConnection.sendMessage && dataScope?.wo?.target_exports?.length > 0) { + try { + loConnection.sendMessage(JSON.stringify(dataScope)); + } catch (e) { + console.warn("Failed to resend dataScope:", e); + } + } + }, [dataScope, loConnection]); + + const docTitle = useCallback( + (docId) => { + if (!docId) return "β€”"; + const doc = loData?.students?.[studentID]?.documents?.[docId]; + return doc?.title || docId; + }, + [loData, studentID] + ); + + const studentProfile = loData?.students?.[studentID]; + const studentDisplayName = studentProfile?.profile?.name?.full_name || studentID || "β€”"; + const docsObj = loData?.students?.[studentID]?.documents || {}; + const leftDoc = leftDocId ? docsObj?.[leftDocId] : null; + const rightDoc = rightDocId ? docsObj?.[rightDocId] : null; + + // ----------------- LOADING GATE (non-empty text) ----------------- + const leftHasTextField = !!(leftDoc && Object.prototype.hasOwnProperty.call(leftDoc, "text")); + const rightHasTextField = !!(rightDoc && Object.prototype.hasOwnProperty.call(rightDoc, "text")); + + const leftTextNonEmpty = leftHasTextField && typeof leftDoc.text === "string" && leftDoc.text.trim().length > 0; + const rightTextNonEmpty = rightHasTextField && typeof rightDoc.text === "string" && rightDoc.text.trim().length > 0; + + const docsReady = enabled && leftTextNonEmpty && rightTextNonEmpty; + const isDocsLoading = enabled && !docsReady; + // ------------------------------------------------------------------- + + const leftText = leftHasTextField ? leftDoc?.text || "" : ""; + const rightText = rightHasTextField ? rightDoc?.text || "" : ""; + + const showInlineWarning = enabled && isDocsLoading && !!loErrors; + + const [leftEssay, setLeftEssay] = useState(() => buildEssayFromDoc({ docId: leftDocId, text: "", side: "left" })); + const [rightEssay, setRightEssay] = useState(() => buildEssayFromDoc({ docId: rightDocId, text: "", side: "right" })); + + useEffect(() => { + setLeftEssay(buildEssayFromDoc({ docId: leftDocId, text: "", side: "left", title: docTitle(leftDocId) })); + setRightEssay(buildEssayFromDoc({ docId: rightDocId, text: "", side: "right", title: docTitle(rightDocId) })); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [leftDocId, rightDocId, docTitle]); + + useEffect(() => { + if (!enabled) return; + if (leftHasTextField) setLeftEssay(buildEssayFromDoc({ docId: leftDocId, text: leftText, side: "left", title: docTitle(leftDocId) })); + if (rightHasTextField) setRightEssay(buildEssayFromDoc({ docId: rightDocId, text: rightText, side: "right", title: docTitle(rightDocId) })); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [enabled, leftHasTextField, rightHasTextField, leftDocId, rightDocId, leftText, rightText, docTitle]); + + /* ---------------------- CUSTOM TOOLTIP STATE ---------------------- */ + const [tooltip, setTooltip] = useState({ visible: false, x: 0, y: 0, content: "" }); + + const positionFromMouse = useCallback((e) => { + const pad = 12; + const vw = typeof window !== "undefined" ? window.innerWidth : 1200; + const vh = typeof window !== "undefined" ? window.innerHeight : 800; + + const maxW = 420; + const maxH = 220; + + const x = clamp(e.clientX + pad, 8, vw - maxW); + const y = clamp(e.clientY + pad, 8, vh - maxH); + return { x, y }; + }, []); + + const onShowTooltip = useCallback( + (content, e) => { + if (!content) return; + const { x, y } = positionFromMouse(e); + setTooltip({ visible: true, x, y, content: content || "" }); + }, + [positionFromMouse] + ); + + const onMoveTooltip = useCallback( + (e) => { + setTooltip((t) => { + if (!t.visible) return t; + const { x, y } = positionFromMouse(e); + return { ...t, x, y }; + }); + }, + [positionFromMouse] + ); + + const onHideTooltip = useCallback(() => { + setTooltip((t) => ({ ...t, visible: false })); + }, []); + + /* ---------------------- URL update (no navigation, no page shift) ---------------------- */ + const updateUrlIds = useCallback( + (nextDocIds) => { + if (typeof window === "undefined") return; + const sp = new URLSearchParams(window.location.search); + sp.set("student_id", studentID || ""); + sp.set("ids", nextDocIds.join(",")); + const next = `${window.location.pathname}?${sp.toString()}`; + window.history.replaceState({}, "", next); + }, + [studentID] + ); + + const setDocIdForSide = useCallback( + (side, newId) => { + const id = (newId || "").trim(); + if (!id) return; + + setDocIds((prev) => { + const next = [...prev]; + const L = next[0] || ""; + const R = next[1] || ""; + + // Prevent selecting the same doc for both sides; if chosen, swap. + if (side === "left") { + if (id === R) { + next[0] = R; + next[1] = L; + } else { + next[0] = id; + next[1] = R; + } + } else { + if (id === L) { + next[0] = R; + next[1] = L; + } else { + next[0] = L; + next[1] = id; + } + } + + // Ensure length 2 + if (!next[0]) next[0] = L; + if (!next[1]) next[1] = R; + + updateUrlIds(next); + return next; + }); + }, + [updateUrlIds] + ); + + /* ---------------------- Replace Modal (no shifting, full doc list) ---------------------- */ + const [replaceModal, setReplaceModal] = useState({ open: false, side: "left" }); + const [replaceQuery, setReplaceQuery] = useState(""); + const [replaceActiveIdx, setReplaceActiveIdx] = useState(0); + const [metricsModalOpen, setMetricsModalOpen] = useState(false); + + const openReplace = (side) => { + setReplaceQuery(""); + setReplaceActiveIdx(0); + setReplaceModal({ open: true, side }); + }; + const closeReplace = () => { + setReplaceModal({ open: false, side: "left" }); + setReplaceQuery(""); + setReplaceActiveIdx(0); + }; + + const openMetricsModal = () => setMetricsModalOpen(true); + const closeMetricsModal = () => setMetricsModalOpen(false); + + useEffect(() => { + if (typeof document === "undefined") return; + document.body.style.overflow = replaceModal.open || metricsModalOpen ? "hidden" : ""; + + return () => { + document.body.style.overflow = ""; + }; + }, [replaceModal.open, metricsModalOpen]); + + const currentIdForSide = replaceModal.side === "left" ? leftDocId : rightDocId; + const otherIdForSide = replaceModal.side === "left" ? rightDocId : leftDocId; + + const replaceMatches = useMemo(() => { + const q = replaceQuery.trim().toLowerCase(); + const pool = availableDocIds || []; + if (!q) return pool; + return pool.filter((id) => { + const title = docTitle(id).toLowerCase(); + return title.includes(q) || id.toLowerCase().includes(q); + }); + }, [replaceQuery, availableDocIds, docTitle]); + + const replacePick = (id) => { + setDocIdForSide(replaceModal.side, id); + closeReplace(); + }; + + const onReplaceKeyDown = (e) => { + if (!replaceModal.open) return; + + if (e.key === "Escape") { + e.preventDefault(); + closeReplace(); + return; + } + if (e.key === "ArrowDown") { + e.preventDefault(); + setReplaceActiveIdx((i) => Math.min(replaceMatches.length - 1, i + 1)); + return; + } + if (e.key === "ArrowUp") { + e.preventDefault(); + setReplaceActiveIdx((i) => Math.max(0, i - 1)); + return; + } + if (e.key === "Enter") { + e.preventDefault(); + const id = replaceMatches[replaceActiveIdx]; + if (id) replacePick(id); + return; + } + }; + + // Keep active index in bounds as filter changes + useEffect(() => { + if (!replaceModal.open) return; + setReplaceActiveIdx(0); + }, [replaceModal.open, replaceQuery]); + + /* ============================================================= + METRICS COMPARISON (Coverage-based) + ============================================================= */ + + const [focusedMetricId, setFocusedMetricId] = useState(null); + const [showAllMetrics, setShowAllMetrics] = useState(false); + + // If focused metric is removed from selection, clear focus + useEffect(() => { + if (focusedMetricId && !selectedMetrics.includes(focusedMetricId)) { + setFocusedMetricId(null); + } + }, [focusedMetricId, selectedMetrics]); + + const activeMetricIds = focusedMetricId ? [focusedMetricId] : selectedMetrics; + + const coverageRows = useMemo(() => { + if (!selectedMetrics.length) return []; + const defs = selectedMetrics.map((id) => METRIC_BY_ID[id]).filter(Boolean); + + const rows = defs.map((def) => { + const a = metricCoveragePercent(leftDoc, def.id); + const b = metricCoveragePercent(rightDoc, def.id); + const delta = (Number(b) || 0) - (Number(a) || 0); + const absDelta = Math.abs(delta); + + return { def, left: Number(a) || 0, right: Number(b) || 0, delta, absDelta }; + }); + + rows.sort((x, y) => y.absDelta - x.absDelta || String(x.def.title).localeCompare(String(y.def.title))); + return rows; + }, [selectedMetrics, leftDoc, rightDoc]); + + const metricsSummary = useMemo(() => { + if (!coverageRows.length) { + return { mostIncreased: null, mostDecreased: null, mostStable: null }; + } + + const byDeltaDesc = [...coverageRows].sort((a, b) => b.delta - a.delta); + const mostIncreased = byDeltaDesc[0] || null; + + const byStable = [...coverageRows].sort((a, b) => a.absDelta - b.absDelta); + const mostStable = byStable[0] || null; + + const negatives = coverageRows.filter((r) => r.delta < -0.0001); + let mostDecreased = null; + if (negatives.length) { + negatives.sort((a, b) => a.delta - b.delta); + mostDecreased = negatives[0]; + } + + return { mostIncreased, mostDecreased, mostStable }; + }, [coverageRows]); + + const topChanges = useMemo(() => coverageRows.slice(0, 8), [coverageRows]); + const allRemaining = useMemo(() => (coverageRows.length > 8 ? coverageRows.slice(8) : []), [coverageRows]); + + const leftEssayRef = useRef(null); + const rightEssayRef = useRef(null); + + const scrollToFirstHighlight = useCallback((metricId) => { + if (!metricId) return; + + const sel = `mark[data-metrics*="${metricId}"], mark[data-primary-metric="${metricId}"]`; + + const leftEl = leftEssayRef.current ? leftEssayRef.current.querySelector(sel) : null; + const rightEl = rightEssayRef.current ? rightEssayRef.current.querySelector(sel) : null; + + const target = leftEl || rightEl; + if (target && typeof target.scrollIntoView === "function") { + target.scrollIntoView({ behavior: "smooth", block: "center", inline: "nearest" }); + } + }, []); + + const focusMetric = useCallback( + (metricId, shouldScroll = false) => { + if (!metricId) return; + + setFocusedMetricId((cur) => (cur === metricId ? null : metricId)); + + if (shouldScroll) setTimeout(() => scrollToFirstHighlight(metricId), 30); + }, + [scrollToFirstHighlight] + ); + + const focusedMeta = focusedMetricId ? METRIC_BY_ID[focusedMetricId] : null; + + const focusedExamples = useMemo(() => { + if (!focusedMetricId) return { left: [], right: [] }; + return { + left: extractMetricExamples(leftDoc, focusedMetricId, 2), + right: extractMetricExamples(rightDoc, focusedMetricId, 2), + }; + }, [focusedMetricId, leftDoc, rightDoc]); + + return ( +
+ + + {/* Metrics Modal (small screens) */} + {metricsModalOpen && ( +
+
+
+
+
+
+
Metrics & Presets
+
Choose metrics and manage presets for this comparison.
+
+ +
+ +
+ +
+
+
+
+ )} + + {/* Replace Modal */} + {replaceModal.open && ( +
+
+
+
+
+
+
+ Replace {replaceModal.side === "left" ? "Left" : "Right"} document +
+
+ Student: {studentDisplayName} + {" β€’ "} + Docs: {availableDocIds.length} + {" β€’ "} + Current: {docTitle(currentIdForSide)} +
+
+ +
+ +
+
+ + setReplaceQuery(e.target.value)} + autoFocus + placeholder="Search by document…" + className="w-full pl-9 pr-3 py-2.5 text-sm border border-gray-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-emerald-500" + /> +
+ +
Tip: Use ↑ / ↓ then Enter to select.
+
+ +
+ {replaceMatches.length === 0 ? ( +
No matches.
+ ) : ( + replaceMatches.map((id, idx) => { + const isActive = idx === replaceActiveIdx; + const isCurrent = id === currentIdForSide; + const isOther = id === otherIdForSide; + + return ( + + ); + }) + )} +
+ +
+ + +
+
+
+
+ )} + +
+ + + {missingParams ? ( +
+ Missing URL params. Expected: ?student_id=...&ids=docA,docB +
+ ) : null} + {urlReady ? ( +
+ +
+ ) : null} +
+ +
+ {isDocsLoading ? ( +
+
+
+
Loading documents…
+
+
+ Waiting until both documents return non-empty text. +
+
+ ) : ( +
+ {/* βœ… Sidebar replaced with MetricsPanel */} +
+ +
+ +
+
+ +
+ + {showInlineWarning ? ( +
+ Some data errors were reported while loading documents. +
+ ) : null} + + {/* Essays */} +
+ {/* Left */} +
+
+
+

{leftEssay.title}

+ +
+ +
+ + {leftEssay.minutes} min + + + {leftEssay.words.toLocaleString()} words + +
+ + {focusedMetricId ? ( +
+ + + Focus: {focusedMeta?.title || focusedMetricId} + + +
+ ) : null} +
+ +
+
+ Hover highlights to see metric tooltip. + {focusedMetricId ? Showing only the focused metric highlights. : null} +
+ +
+
+ + {/* Right */} +
+
+
+

{rightEssay.title}

+ +
+ +
+ + {rightEssay.minutes} min + + + {rightEssay.words.toLocaleString()} words + +
+ + {focusedMetricId ? ( +
+ + + Focus: {focusedMeta?.title || focusedMetricId} + + +
+ ) : null} +
+ +
+
+ Hover highlights to see metric tooltip. + {focusedMetricId ? Showing only the focused metric highlights. : null} +
+ +
+
+
+ + {/* ========================= + What changed in the writing (Coverage) + ========================= */} + {selectedMetrics.length > 0 && ( +
+
+
+

What is different

+
+ Coverage = % of essay text highlighted for a signal. +
+
+ + {focusedMetricId ? ( + + ) : null} +
+ + {/* Story cards (Improved | Consistent | Dropped) */} +
+ + + + + +
+ +
+
+
+
Top changes
+
+ Click β€œFocus” to show only that metric’s highlights in both essays. +
+
+
+ Showing {Math.min(8, coverageRows.length)} of{" "} + {coverageRows.length} +
+
+ +
+ {topChanges.length === 0 ? ( +
No metrics selected.
+ ) : ( + topChanges.map((r) => ( + focusMetric(r.def.id, false)} + onShow={() => { + setFocusedMetricId(r.def.id); + setTimeout(() => scrollToFirstHighlight(r.def.id), 30); + }} + /> + )) + )} +
+ + {coverageRows.length > 8 ? ( +
+ + + {showAllMetrics ? ( +
+ {allRemaining.map((r) => ( + focusMetric(r.def.id, false)} + onShow={() => { + setFocusedMetricId(r.def.id); + setTimeout(() => scrollToFirstHighlight(r.def.id), 30); + }} + /> + ))} +
+ ) : null} +
+ ) : null} +
+
+ )} +
+
+ )} +
+
+ ); +} diff --git a/modules/portfolio_diff/src/app/students/components/StudentDetail/SingleEssayModel.js b/modules/portfolio_diff/src/app/students/components/StudentDetail/SingleEssayModel.js new file mode 100644 index 000000000..cc62510d3 --- /dev/null +++ b/modules/portfolio_diff/src/app/students/components/StudentDetail/SingleEssayModel.js @@ -0,0 +1,1076 @@ +"use client"; + +import React, { useEffect, useMemo, useRef, useState } from "react"; +import { + X, + History, + Sparkles, + Gauge, + Layers, + Activity, + Loader2, + AlertTriangle, + SlidersHorizontal, + ArrowUpDown, +} from "lucide-react"; + +import { MetricsPanel } from "@/app/components/MetricsPanel"; +import { useLOConnectionDataManager } from "lo_event/lo_event/lo_assess/components/components.jsx"; +import { useCourseIdContext } from "@/app/providers/CourseIdProvider"; +import { getWsOriginFromWindow } from "@/app/utils/ws"; + +/* ========================================================= + Helpers +========================================================= */ + +const DEBUG = true; + +function clamp(v, lo, hi) { + return Math.max(lo, Math.min(hi, v)); +} + +function mean(arr) { + if (!arr.length) return 0; + return arr.reduce((s, x) => s + x, 0) / arr.length; +} + +function stableStringify(obj) { + const seen = new WeakSet(); + const sortObj = (v) => { + if (v === null || typeof v !== "object") return v; + if (seen.has(v)) return "[Circular]"; + seen.add(v); + if (Array.isArray(v)) return v.map(sortObj); + const keys = Object.keys(v).sort(); + const out = {}; + for (const k of keys) out[k] = sortObj(v[k]); + return out; + }; + try { + return JSON.stringify(sortObj(obj)); + } catch { + return String(obj); + } +} + +/** + * Robust normalization to handle whatever MetricsPanel emits. + */ +function normalizeSelectedMetrics(input) { + if (!input) return []; + + const pickMetricId = (x) => { + if (!x) return null; + if (typeof x === "string") return x; + + if (x && typeof x === "object") { + return ( + x.metricKey || + x.metric_key || + x.metricId || + x.metric_id || + x.metric || + x.metric_name || + x.metricName || + x.id || + x.key || + x.name || + x.value || + x.label || + null + ); + } + return null; + }; + + if (Array.isArray(input)) { + return input + .map(pickMetricId) + .filter(Boolean) + .map((s) => String(s).trim()) + .filter(Boolean); + } + + if (typeof input === "object") { + return Object.entries(input) + .filter(([, v]) => !!v) + .map(([k]) => String(k).trim()) + .filter(Boolean); + } + + return []; +} + +function metricCoveragePercent(doc, metricId) { + const text = (doc?.text || "").toString(); + const L = text.length; + if (!L) return 0; + + const offsets = doc?.[metricId]?.offsets; + if (!Array.isArray(offsets) || offsets.length === 0) return 0; + + const ranges = []; + for (const pair of offsets) { + if (!Array.isArray(pair) || pair.length < 2) continue; + const start = Number(pair[0]); + const len = Number(pair[1]); + if (!Number.isFinite(start) || !Number.isFinite(len) || len <= 0) continue; + + const s = clamp(start, 0, L); + const e = clamp(start + len, 0, L); + if (e > s) ranges.push([s, e]); + } + if (!ranges.length) return 0; + + ranges.sort((a, b) => a[0] - b[0] || a[1] - b[1]); + + let covered = 0; + let [curS, curE] = ranges[0]; + for (let i = 1; i < ranges.length; i++) { + const [s, e] = ranges[i]; + if (s <= curE) curE = Math.max(curE, e); + else { + covered += curE - curS; + curS = s; + curE = e; + } + } + covered += curE - curS; + return (covered / L) * 100; +} + +function initialsFromStudentKey(studentKey) { + const s = String(studentKey || "").trim(); + if (!s) return "ST"; + const parts = s.split(/[^a-zA-Z0-9]+/).filter(Boolean); + const a = (parts[0]?.[0] || "S").toUpperCase(); + const b = (parts[1]?.[0] || "T").toUpperCase(); + return `${a}${b}`.slice(0, 2); +} + +/* ========================================================= + Charts (Auto-zoom for low ranges + delta annotation) +========================================================= */ + +function niceStep(range) { + if (range <= 0.5) return 0.1; + if (range <= 1) return 0.2; + if (range <= 2) return 0.5; + if (range <= 5) return 1; + if (range <= 10) return 2; + if (range <= 20) return 5; + if (range <= 40) return 10; + return 25; +} + +function buildTicks(yMin, yMax, maxTicks = 5) { + const span = Math.max(1e-6, yMax - yMin); + const step = niceStep(span); + const start = Math.floor(yMin / step) * step; + const end = Math.ceil(yMax / step) * step; + + const ticks = []; + for (let v = start; v <= end + 1e-9; v += step) { + if (v >= yMin - 1e-9 && v <= yMax + 1e-9) ticks.push(Number(v.toFixed(3))); + } + + if (ticks.length > maxTicks) { + const stride = Math.ceil(ticks.length / maxTicks); + return ticks.filter((_, i) => i % stride === 0); + } + + if (!ticks.length) return [yMin, yMax]; + if (ticks[0] !== yMin) ticks.unshift(yMin); + if (ticks[ticks.length - 1] !== yMax) ticks.push(yMax); + return ticks; +} + +function BaselineCurrentChart({ + baselinePct, + currentPct, + height = 120, + autoZoom = true, +}) { + const width = 520; + const padL = 42; + const padR = 10; + const padT = 10; + const padB = 28; + + const b = Number.isFinite(baselinePct) ? Number(baselinePct) : 0; + const c = Number.isFinite(currentPct) ? Number(currentPct) : 0; + + const deltaPP = c - b; + const relDelta = b !== 0 ? (deltaPP / b) * 100 : null; + + const minVal = Math.min(b, c); + const maxVal = Math.max(b, c); + const spread = maxVal - minVal; + + const shouldZoom = + autoZoom && (maxVal <= 15 || spread <= 3) && maxVal < 98; + + let yMin = 0; + let yMax = 100; + let zoomLabel = ""; + + if (shouldZoom) { + const pad = Math.max(0.6, spread * 0.6); + yMin = clamp(minVal - pad, 0, 100); + yMax = clamp(maxVal + pad, 0, 100); + + const minSpan = 4; + if (yMax - yMin < minSpan) { + const mid = (yMin + yMax) / 2; + yMin = clamp(mid - minSpan / 2, 0, 100); + yMax = clamp(mid + minSpan / 2, 0, 100); + } + + const span = yMax - yMin; + const step = niceStep(span); + yMin = clamp(Math.floor(yMin / step) * step, 0, 100); + yMax = clamp(Math.ceil(yMax / step) * step, 0, 100); + + if (yMax <= yMin) { + yMin = clamp(minVal - 2, 0, 100); + yMax = clamp(maxVal + 2, 0, 100); + } + + zoomLabel = `Zoomed: ${yMin.toFixed(1)}–${yMax.toFixed(1)}%`; + } + + const xBaseline = padL; + const xCurrent = width - padR; + + const Y = (v) => { + const t = (v - yMin) / Math.max(1e-6, yMax - yMin); + return padT + (1 - t) * (height - padT - padB); + }; + + const yBaseline = Y(b); + const yCurrent = Y(c); + + const ticks = shouldZoom ? buildTicks(yMin, yMax, 5) : [0, 25, 50, 75, 100]; + + const tickFmt = (v) => { + const span = yMax - yMin; + const needsDecimal = span <= 10 || shouldZoom; + return `${needsDecimal ? Number(v).toFixed(1) : Number(v).toFixed(0)}%`; + }; + + const deltaText = `${deltaPP >= 0 ? "+" : ""}${deltaPP.toFixed(1)} pp${ + relDelta === null ? "" : ` (${relDelta >= 0 ? "+" : ""}${relDelta.toFixed(0)}%)` + }`; + + return ( + + {ticks.map((t) => { + const y = Y(t); + return ( + + + + {tickFmt(t)} + + + ); + })} + + + + + + Baseline + + + Current + + + {shouldZoom ? ( + + + + {zoomLabel} + + + ) : null} + + + + + + + {b.toFixed(1)}% + + + {c.toFixed(1)}% + + + + + + {deltaText} + + + + ); +} + +function MetricTile({ metricKey, baseline, currentValue }) { + return ( +
+
+
{metricKey}
+
Baseline (prior essays only) β†’ Current (this essay)
+
+
+ +
+
+ ); +} + +/* ========================================================= + Evidence blocks for Actionable Feedback +========================================================= */ + +function EvidenceProduct({ cues }) { + const list = Array.isArray(cues) ? cues : []; + if (!list.length) { + return ( +
+ No product evidence attached yet. +
+ ); + } + return ( +
+
+ Evidence cues (justification signals) +
+
+ {list.map((c, i) => ( +
+
{c.label}
+ {c.sub ?
{c.sub}
: null} +
+ ))} +
+
+ ); +} + +function EvidenceProcess({ features }) { + const list = Array.isArray(features) ? features : []; + if (!list.length) { + return ( +
+ No process evidence attached yet. +
+ ); + } + return ( +
+
+ Process signals (keystroke / behavior metrics) +
+
+ {list.map((f, i) => ( +
+
{f.name}
+
+ Baseline {Number(f.baseline).toFixed(1)} β†’ Current {Number(f.current).toFixed(1)} (Score{" "} + {Number(f.score).toFixed(0)}/100) +
+
+ ))} +
+
+ ); +} + +/* ========================================================= + Feedback Controls (left column in feedback tab) +========================================================= */ + +function FeedbackControls({ + detailLevel, + setDetailLevel, + ordering, + setOrdering, + priority, + setPriority, + includeEvidence, + setIncludeEvidence, + includeProcessSignals, + setIncludeProcessSignals, +}) { + return ( +
+
+
+ + + +
+
Feedback Controls
+
+
+
Choose ordering and how many items to show.
+
+ +
+
+
Detail level
+
+ {["brief", "standard"].map((k) => ( + + ))} +
+
+ +
+
+
Sequence / ordering
+ + + Order + +
+
+ {[ + { key: "highest_impact", label: "Highest impact" }, + { key: "lowest_impact", label: "Lowest impact" }, + ].map((opt) => ( + + ))} +
+
+ +
+
Priority
+
+ {[ + { key: "top1", label: "Top 1" }, + { key: "top2", label: "Top 2" }, + { key: "all", label: "All" }, + ].map((opt) => ( + + ))} +
+
+ +
+
+ Include in feedback +
+
+ + + +
+
+ +
+ Tip: "Highest impact + Top 1 + Brief" works well for quick LMS comments. +
+
+
+ ); +} + +/* ========================================================= + Exported Modal +========================================================= */ + +export function SingleEssayModal({ + studentKey, + docId, + docIds, + docTitle, + docIndex, + initialWords, + subtitleDate, + onClose, +}) { + useEffect(() => { + const prev = document.body.style.overflow; + document.body.style.overflow = "hidden"; + return () => { + document.body.style.overflow = prev || ""; + }; + }, []); + + useEffect(() => { + const onKey = (e) => { + if (e.key === "Escape") onClose?.(); + }; + window.addEventListener("keydown", onKey); + return () => window.removeEventListener("keydown", onKey); + }, [onClose]); + + const title = docTitle || (docIndex ? `Document ${docIndex}` : "Document"); + const subtitle = subtitleDate ? `Document β€’ ${subtitleDate}` : "Document"; + + return ( +
+
onClose?.()} /> + +
+
e.stopPropagation()} + > +
+
+
+
+
{title}
+
{subtitle}
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+ ); +} + +/* ========================================================= + Inner modal +========================================================= */ + +function SingleEssayInnerModal({ studentKey, docId, docIds }) { + const [activeTab, setActiveTab] = useState("trajectory"); + const [feedbackMode, setFeedbackMode] = useState("product"); + const [selectedMetrics, setSelectedMetricsState] = useState(["academic_language"]); + + // feedback controls + const [detailLevel, setDetailLevel] = useState("standard"); + const [ordering, setOrdering] = useState("highest_impact"); + const [priority, setPriority] = useState("all"); + const [includeEvidence, setIncludeEvidence] = useState(true); + const [includeProcessSignals, setIncludeProcessSignals] = useState(true); + const { courseId } = useCourseIdContext(); + + const setSelectedMetrics = (next) => { + setSelectedMetricsState((prev) => { + const resolved = typeof next === "function" ? next(prev) : next; + const normalized = normalizeSelectedMetrics(resolved); + + if (DEBUG) { + console.groupCollapsed("[SingleEssayInnerModal] metrics-change"); + console.log("resolved from panel:", resolved); + console.log("normalized ids:", normalized); + console.groupEnd(); + } + + return normalized; + }); + }; + + const exportEnabled = + activeTab === "trajectory" && + !!studentKey && + !!docId && + Array.isArray(docIds) && + docIds.length > 0 && + selectedMetrics.length > 0; + + const dataScope = useMemo(() => { + if (!exportEnabled) return { wo: { execution_dag: "writing_observer", target_exports: [], kwargs: {} } }; + + return { + wo: { + execution_dag: "writing_observer", + target_exports: ["single_student_docs_with_nlp_annotations"], + kwargs: { + course_id: courseId, + student_id: studentKey, + document: docIds, + nlp_options: selectedMetrics, + }, + }, + }; + }, [exportEnabled, courseId, docIds, selectedMetrics, studentKey]); + + // Connect to LO websocket + const origin = + process.env.NEXT_PUBLIC_LO_WS_ORIGIN?.replace(/\/+$/, "") || + getWsOriginFromWindow() || + "ws://localhost:8888"; + + const url = `${origin}/wsapi/communication_protocol`; + const { connection, data: loData, errors: loErrors } = useLOConnectionDataManager({ url, dataScope }); + + // When dataScope changes, send the new scope over the existing connection + const prevScopeRef = useRef(null); + useEffect(() => { + if (!connection?.sendMessage || !exportEnabled) return; + + const scopeStr = stableStringify(dataScope); + if (scopeStr === prevScopeRef.current) return; + prevScopeRef.current = scopeStr; + + if (DEBUG) { + console.groupCollapsed("[SingleEssayInnerModal] sending updated dataScope"); + console.log("dataScope:", dataScope); + console.groupEnd(); + } + + try { + connection.sendMessage(JSON.stringify(dataScope)); + } catch (e) { + console.warn("[SingleEssayInnerModal] failed to send updated dataScope", e); + } + }, [connection, dataScope, exportEnabled]); + + const docsObj = useMemo(() => loData?.students?.[studentKey]?.documents || {}, [loData, studentKey]); + + const hasError = useMemo(() => { + if (!exportEnabled) return false; + if (!loErrors) return false; + if (Array.isArray(loErrors)) return loErrors.length > 0; + if (typeof loErrors === "object") return Object.keys(loErrors).length > 0; + return true; + }, [exportEnabled, loErrors]); + + const hasAllDocs = useMemo(() => { + if (!exportEnabled) return false; + return docIds.every((id) => { + const d = docsObj?.[id]; + return d && typeof d.text === "string" && d.text.length > 0; + }); + }, [exportEnabled, docIds, docsObj]); + + const isLOLoading = useMemo(() => exportEnabled && !hasError && !hasAllDocs, [exportEnabled, hasError, hasAllDocs]); + + const currentDocIndex = useMemo(() => { + const idx = docIds.findIndex((id) => String(id) === String(docId)); + return Math.max(0, idx); + }, [docIds, docId]); + + const hasPriorData = currentDocIndex > 0; + + const metricSummaries = useMemo(() => { + if (!exportEnabled || !hasAllDocs) return []; + return selectedMetrics.map((metricKey) => { + const series = docIds.map((id) => metricCoveragePercent(docsObj?.[id], metricKey)); + const current = Number(series[currentDocIndex] ?? 0); + const prior = series.slice(0, currentDocIndex).map((x) => Number(x) || 0); + const baseline = prior.length ? mean(prior) : 0; + return { key: metricKey, baseline, currentValue: current }; + }); + }, [exportEnabled, hasAllDocs, selectedMetrics, docIds, docsObj, currentDocIndex]); + + const currentText = useMemo(() => (docsObj?.[docId]?.text || "").toString(), [docsObj, docId]); + + const wordCount = useMemo(() => { + const t = (currentText || "").trim(); + if (!t) return 0; + return t.split(/\s+/).filter(Boolean).length; + }, [currentText]); + + /* -------------------------- + Actionable feedback content + -------------------------- */ + + const feedbackBlocks = useMemo(() => { + const product = [ + { + category: "clarity", + heading: "Clarify the claim early", + why: "Your central claim becomes clear only midway through the essay.", + suggestion: "Rewrite the opening as: claim β†’ reason β†’ preview of evidence (2–3 lines).", + evidence: { cues: [{ label: "Thesis appears late", sub: "Main stance introduced after several sentences." }] }, + impact: 0.9, + }, + { + category: "organization", + heading: "Use a stronger paragraph map", + why: "Paragraph purposes aren't clearly signposted.", + suggestion: "Add a 1-sentence topic line at the start of each paragraph to guide the reader.", + evidence: { cues: [{ label: "Weak topic sentences", sub: "Paragraph goals inferred rather than stated." }] }, + impact: 0.75, + }, + { + category: "evidence", + heading: "Connect evidence to the claim explicitly", + why: "Evidence is present, but the link back to the thesis is implicit.", + suggestion: 'After each quote/example, add one sentence: "This shows ___ because ___."', + evidence: { cues: [{ label: "Evidence-to-claim bridge", sub: "Explanation is shorter than evidence in places." }] }, + impact: 0.7, + }, + ]; + + const process = [ + { + category: "overall", + heading: "Revise globally before polishing", + why: "Edits appear late and are mostly sentence-level.", + suggestion: "Next time: draft quickly β†’ structure pass β†’ line edits.", + evidence: { features: [{ name: "Late revision burst", baseline: 42.1, current: 61.4, score: 68 }] }, + impact: 0.8, + }, + { + category: "organization", + heading: "Pause to outline before drafting", + why: "Drafting begins immediately without a planning phase.", + suggestion: "Spend 3–5 minutes outlining: claim β†’ reasons β†’ evidence before writing.", + evidence: { features: [{ name: "Planning time", baseline: 18.0, current: 6.5, score: 42 }] }, + impact: 0.7, + }, + { + category: "focus", + heading: "Avoid long uninterrupted drafting runs", + why: "Long runs often reduce clarity and increase later cleanup work.", + suggestion: 'Try a short checkpoint every 5–7 minutes: "Does this paragraph support my claim?"', + evidence: { features: [{ name: "Longest uninterrupted run (min)", baseline: 9.2, current: 14.8, score: 55 }] }, + impact: 0.6, + }, + ]; + + return { product, process }; + }, []); + + const applyDetailLevel = (b) => { + if (detailLevel === "brief") { + return { ...b, why: "" }; + } + return b; + }; + + const orderingLabel = useMemo(() => { + return ordering === "lowest_impact" ? "Lowest impact" : "Highest impact"; + }, [ordering]); + + const sortedAndFilteredFeedbackBlocks = useMemo(() => { + const base = feedbackMode === "product" ? feedbackBlocks.product : feedbackBlocks.process; + const blocks = [...base]; + + if (ordering === "highest_impact") { + blocks.sort((a, b) => (Number(b.impact) || 0) - (Number(a.impact) || 0)); + } else { + blocks.sort((a, b) => (Number(a.impact) || 0) - (Number(b.impact) || 0)); + } + + if (priority === "top1") return blocks.slice(0, 1); + if (priority === "top2") return blocks.slice(0, 2); + return blocks; + }, [feedbackBlocks, feedbackMode, ordering, priority]); + + return ( +
+ {/* Tab bar */} +
+
+ + + +
+
+ +
+
+ {/* Left column */} + + + {/* Middle column: essay text */} +
+
+
+
+ + {wordCount.toLocaleString()} words + +
+ +
+
+ {currentText ? ( +

{currentText}

+ ) : ( +

No text available yet.

+ )} +
+
+
+
+
+ + {/* Right column */} +
+ {activeTab === "trajectory" ? ( +
+
+
+
+
+ + + +

Writing Trajectory

+
+
+ Baseline = average of prior essays only. Current = this essay. +
+
+
+
+ +
+ {selectedMetrics.length === 0 ? ( +
+
+ Select metrics on the left to view trajectory +
+
This starts empty by design β€” pick signals first.
+
+ ) : isLOLoading ? ( +
+
+ + Computing trajectory… +
+
+ ) : hasError ? ( +
+
+ + Couldn't compute trajectory +
+
+                        {JSON.stringify(loErrors, null, 2)}
+                      
+
+ ) : !hasPriorData ? ( +
+
No prior data to compare with
+
+ This is the student's first essay in the trajectory sequence, so we can't compute a baseline yet. +
+
+ ) : ( +
+ {metricSummaries.map((m) => ( + + ))} +
+ )} +
+
+ ) : ( +
+
+
+
+
+ + + +

Actionable Feedback

+
+
+ {orderingLabel} β€’ {priority === "top1" ? "Top 1" : priority === "top2" ? "Top 2" : "All"} β€’{" "} + {detailLevel === "brief" ? "Brief" : "Standard"} +
+
+ +
+ + +
+
+
+ +
+ {sortedAndFilteredFeedbackBlocks.length === 0 ? ( +
+
No feedback items available
+
+ Try switching Product/Process or set Priority to "All". +
+
+ ) : ( + sortedAndFilteredFeedbackBlocks.map((raw, i) => { + const b = applyDetailLevel(raw); + + return ( +
+
{b.heading}
+ + {b.why ?
{b.why}
: null} + +
+
Comment
+
{b.suggestion}
+
+ + {feedbackMode === "product" ? ( + includeEvidence ? ( +
+
Evidence (justification)
+ +
+ ) : null + ) : includeProcessSignals ? ( +
+
Evidence (process signals)
+ +
+ ) : null} +
+ ); + }) + )} +
+
+ )} +
+
+
+
+ ); +} diff --git a/modules/portfolio_diff/src/app/students/components/StudentDetail/StudentDetailCompare.js b/modules/portfolio_diff/src/app/students/components/StudentDetail/StudentDetailCompare.js new file mode 100644 index 000000000..77d90ea9e --- /dev/null +++ b/modules/portfolio_diff/src/app/students/components/StudentDetail/StudentDetailCompare.js @@ -0,0 +1,594 @@ +"use client"; + +import React, { useMemo, useState } from "react"; +import { + X, + Calendar, + Search, + ChevronDown, + Info, + Maximize2, + GitCompareArrows, +} from "lucide-react"; + +import { SingleEssayModal } from "./SingleEssayModel"; + +/* ========================================================= + Student Compare (Modal imported) +========================================================= */ + +export default function StudentDetailCompare({ + groupedEssays, + studentId, + + selectedEssays, + setSelectedEssays, + handleEssaySelect, + + cardsPerRow, + setCardsPerRow, + sortBy, + setSortBy, + search, + setSearch, + filterTags, + setFilterTags, + tagOpen, + setTagOpen, + tagQuery, + setTagQuery, + tagRef, + baseTags, + clearFilters, + isAnyFilter, + + getGridCols, + getGradeColor, + strengthAndFocusForEssay, + + loDocData, + loDocErrors, + loDocConnection, + documentIDS, +}) { + const safeGetGridCols = typeof getGridCols === "function" ? getGridCols : () => "grid-cols-3"; + const safeGetGradeColor = + typeof getGradeColor === "function" + ? getGradeColor + : () => "bg-gray-50 text-gray-700 ring-1 ring-gray-200"; + const safeStrengthAndFocus = + typeof strengthAndFocusForEssay === "function" + ? strengthAndFocusForEssay + : () => ({ strength: null, focus: null }); + const safeHandleEssaySelect = typeof handleEssaySelect === "function" ? handleEssaySelect : () => {}; + const safeSetSelectedEssays = typeof setSelectedEssays === "function" ? setSelectedEssays : () => {}; + + // ---- Modal state (inside compare) ---- + const [openEssay, setOpenEssay] = useState(null); + + // ---- LO docs for compare list ---- + const loStudentID = String(studentId); + const docsObj = loDocData?.students?.[loStudentID]?.documents || {}; + + const expectedDocIds = Array.isArray(documentIDS) ? documentIDS : []; + const receivedDocIds = Object.keys(docsObj || {}); + const hasAllExpectedDocs = + expectedDocIds.length === 0 || expectedDocIds.every((id) => receivedDocIds.includes(id)); + + const isDocsLoading = + !!(loDocConnection && + (loDocConnection.loading || loDocConnection.isLoading || loDocConnection.status === "loading")) || + (expectedDocIds.length > 0 && !hasAllExpectedDocs); + + const isDocsEmpty = !isDocsLoading && Object.keys(docsObj || {}).length === 0; + + // ---- Build doc list (used for modal props too) ---- + const docList = useMemo(() => { + return Object.entries(docsObj || {}).map(([docId, doc], index) => { + const text = typeof doc?.text === "string" ? doc.text : ""; + const words = text ? text.trim().split(/\s+/).filter(Boolean).length : 0; + + let dateISO = + doc?.dateISO || doc?.date_iso || doc?.date || doc?.submitted_at || doc?.created_at || ""; + + if (!dateISO && doc?.last_access != null) { + const la = Number(doc.last_access); + if (Number.isFinite(la) && la > 0) { + // Handle both seconds and milliseconds timestamps + const ms = la > 1e12 ? la : la * 1000; + dateISO = new Date(ms).toISOString(); + } + } + const grade = doc?.grade ?? doc?.score ?? ""; + const tagsFromDoc = Array.isArray(doc?.tags) + ? doc.tags + : Array.isArray(doc?.meta?.tags) + ? doc.meta.tags + : ["Document"]; + + return { + id: docId, + title: doc?.title || `Document ${index + 1}`, + date: dateISO + ? new Date(dateISO).toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" }) + : "", + dateISO: dateISO ? new Date(dateISO).toISOString() : "", + words, + grade: grade === null || grade === undefined ? "" : String(grade), + preview: text, + tags: tagsFromDoc.map(String), + _raw: doc, + _index: index + 1, + }; + }); + }, [docsObj]); + + const allDocIds = useMemo(() => docList.map((d) => d.id).filter(Boolean), [docList]); + + const docMetaById = useMemo(() => { + const m = new Map(); + for (const d of docList) m.set(String(d.id), d); + return m; + }, [docList]); + + // ---- tags base ---- + const safeBaseTags = useMemo(() => { + if (Array.isArray(baseTags) && baseTags.length) return baseTags; + const s = new Set(); + for (const d of docList) { + for (const t of (Array.isArray(d?.tags) ? d.tags : [])) s.add(String(t)); + } + return Array.from(s).sort((a, b) => a.localeCompare(b)); + }, [baseTags, docList]); + + // ---- filtering + sorting ---- + const filteredDocs = useMemo(() => { + const q = String(search || "").trim().toLowerCase(); + const activeTags = Array.isArray(filterTags) ? filterTags : []; + + return (docList || []) + .filter((d) => { + if (activeTags.length > 0) { + const dtags = Array.isArray(d?.tags) ? d.tags.map(String) : []; + if (!activeTags.every((t) => dtags.includes(t))) return false; + } + + if (q) { + const hay = [ + d?.title || "", + d?.preview || "", + Array.isArray(d?.tags) ? d.tags.join(" ") : "", + d?.grade || "", + d?.date || "", + ] + .join(" ") + .toLowerCase(); + if (!hay.includes(q)) return false; + } + return true; + }) + .sort((a, b) => { + const mode = String(sortBy || "date"); + if (mode === "words") return (Number(b.words) || 0) - (Number(a.words) || 0); + if (mode === "title") return String(a.title || "").localeCompare(String(b.title || "")); + if (mode === "grade") return (Number(b.grade) || 0) - (Number(a.grade) || 0); + + const ad = a?.dateISO ? new Date(a.dateISO).getTime() : 0; + const bd = b?.dateISO ? new Date(b.dateISO).getTime() : 0; + return bd - ad; + }); + }, [docList, search, filterTags, sortBy]); + + const groupedDocs = useMemo(() => { + return filteredDocs.reduce((acc, d) => { + const key = d.dateISO + ? new Date(d.dateISO).toLocaleString("en-US", { month: "long", year: "numeric" }) + : "Undated"; + (acc[key] ||= []).push(d); + return acc; + }, {}); + }, [filteredDocs]); + + const SkeletonCard = ({ i }) => ( +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ); + + return ( + <> + {isDocsLoading ? ( +
+
+ {Array.from({ length: 6 }).map((_, i) => ( + + ))} +
+
+ ) : isDocsEmpty ? ( +
+
No documents yet
+
We didn’t find any documents for this student.
+
+ ) : ( + <> + {Object.entries(groupedDocs).map(([category, list], index) => { + const wordsAvg = Math.round( + list.reduce((s, e) => s + (Number(e.words) || 0), 0) / Math.max(1, list.length) + ); + + return ( +
+ {index !== 0 &&
} + +
+
+

{category}

+
+ {list.length} essays β€’ Avg {wordsAvg.toLocaleString()} words +
+
+ + {index === 0 && ( +
+
+
+ + + {tagOpen && ( +
+
+ + typeof setTagQuery === "function" && setTagQuery(e.target.value)} + className="w-full bg-transparent text-sm outline-none" + /> +
+
+ {safeBaseTags + .filter((t) => String(t).toLowerCase().includes(String(tagQuery || "").toLowerCase())) + .map((t) => ( + + ))} +
+
+ )} +
+ +
+ + typeof setSearch === "function" && setSearch(e.target.value)} + placeholder="Search title, tags, text…" + className="pl-8 pr-3 py-2 border border-gray-300 rounded-md text-sm bg-white w-64 focus:outline-none focus:ring-2 focus:ring-emerald-500" + /> +
+ +
+ +
+ Sort by: + +
+ +
+ Cards per row + +
+
+ + {isAnyFilter && ( +
+ {(Array.isArray(filterTags) ? filterTags : []).map((t) => ( + + Tag: {t} + + + ))} + + {String(search || "").trim().length > 0 && ( + + Search: β€œ{search}” + + + )} + + +
+ )} +
+ )} +
+ +
+ {list.map((essay) => { + const essayTags = Array.isArray(essay?.tags) ? essay.tags : []; + const isSelected = Array.isArray(selectedEssays) ? selectedEssays.includes(essay.id) : false; + const { strength, focus } = safeStrengthAndFocus(essay); + + return ( +
safeHandleEssaySelect(essay.id)} + > +
+
+
+ safeHandleEssaySelect(essay.id)} + className="w-5 h-5 accent-emerald-600" + onClick={(e) => e.stopPropagation()} + aria-label={`Select ${essay.title}`} + /> +

{essay.title}

+
+ + +
+ +
+

+ + {essay.date || "Unknown date"} +

+
+ + {(Number(essay.words) || 0).toLocaleString()} words + +
+
+ +
+

{essay.preview || ""}

+
+ +
+ {strength && ( + + Strength: {String(strength.label || "").split("(")[0].trim()}{" "} + {Number(strength.delta) > 0 ? "β–²" : ""} + + )} + {focus && ( + + Focus: {String(focus.label || "").split("(")[0].trim()}{" "} + {Number(focus.delta) < 0 ? "β–Ό" : ""} + + )} +
+ +
+ {essayTags.map((tag, i) => ( + + {tag} + + ))} +
+
+
+ ); + })} +
+
+ ); + })} + + )} + + {Array.isArray(selectedEssays) && selectedEssays.length < 2 && ( +
+
+ +

Tip: click cards or use the checkboxes to add essays to the selection tray (max 2).

+
+
+ )} + +
+
+
+
+
+
+ + + Selected {Array.isArray(selectedEssays) ? selectedEssays.length : 0}/2 + + +
+ {[0, 1].map((i) => { + const id = Array.isArray(selectedEssays) ? selectedEssays[i] : undefined; + const meta = id ? docMetaById.get(String(id)) : null; + const displayName = meta?.title || (id ? `#${id}` : "β€”"); + return ( +
+ {displayName} + {id && ( + + )} +
+ ); + })} +
+ + +
+ + +
+
+ + {openEssay?.docId && ( + setOpenEssay(null)} + /> + )} + + ); +} diff --git a/modules/portfolio_diff/src/app/students/components/StudentDetail/StudentDetailGrowth.js b/modules/portfolio_diff/src/app/students/components/StudentDetail/StudentDetailGrowth.js new file mode 100644 index 000000000..eb0bf51e7 --- /dev/null +++ b/modules/portfolio_diff/src/app/students/components/StudentDetail/StudentDetailGrowth.js @@ -0,0 +1,556 @@ +"use client"; + +import { useMemo, useCallback, useState, useEffect, useRef } from "react"; +import dynamic from "next/dynamic"; +import { Loader2 } from "lucide-react"; + +import { useLOConnectionDataManager } from "lo_event/lo_event/lo_assess/components/components.jsx"; +import { MetricsPanel } from "@/app/components/MetricsPanel"; +import { useCourseIdContext } from "@/app/providers/CourseIdProvider"; +import { getWsOriginFromWindow } from "@/app/utils/ws"; + +const ReactECharts = dynamic(() => import("echarts-for-react"), { ssr: false }); + +const DEBUG = false; + +/* ---------------------- stable stringify ---------------------- */ +function stableStringify(obj) { + const seen = new WeakSet(); + const sortObj = (v) => { + if (v === null || typeof v !== "object") return v; + if (seen.has(v)) return "[Circular]"; + seen.add(v); + + if (Array.isArray(v)) return v.map(sortObj); + + const keys = Object.keys(v).sort(); + const out = {}; + for (const k of keys) out[k] = sortObj(v[k]); + return out; + }; + + try { + return JSON.stringify(sortObj(obj)); + } catch { + return String(obj); + } +} + +/* ---------------------- Metric normalization ---------------------- */ +function normalizeSelectedMetrics(input) { + if (!input) return []; + + const pickMetricId = (x) => { + if (!x) return null; + if (typeof x === "string") return x; + + if (x && typeof x === "object") { + return ( + x.metricKey || + x.metric_key || + x.metricId || + x.metric_id || + x.metric || + x.metric_name || + x.metricName || + x.id || + x.key || + x.name || + x.value || + x.label || + null + ); + } + return null; + }; + + if (Array.isArray(input)) { + return input + .map(pickMetricId) + .filter(Boolean) + .map((s) => String(s).trim()) + .filter(Boolean); + } + + if (typeof input === "object") { + return Object.entries(input) + .filter(([, v]) => !!v) + .map(([k]) => String(k).trim()) + .filter(Boolean); + } + + return []; +} + +/* ---------------------- Coverage helpers ---------------------- */ +function coveragePercentFromDoc(doc, metricId) { + const text = (doc?.text || "").toString(); + const L = text.length; + if (!L) return 0; + + const offsets = doc?.[metricId]?.offsets; + if (!Array.isArray(offsets) || offsets.length === 0) return 0; + + const ranges = []; + for (const pair of offsets) { + if (!Array.isArray(pair) || pair.length < 2) continue; + const start = Number(pair[0]); + const len = Number(pair[1]); + if (!Number.isFinite(start) || !Number.isFinite(len) || len <= 0) continue; + + const s = Math.max(0, Math.min(L, start)); + const e = Math.max(0, Math.min(L, start + len)); + if (e > s) ranges.push([s, e]); + } + if (!ranges.length) return 0; + + ranges.sort((a, b) => a[0] - b[0] || a[1] - b[1]); + + let covered = 0; + let [curS, curE] = ranges[0]; + for (let i = 1; i < ranges.length; i++) { + const [s, e] = ranges[i]; + if (s <= curE) curE = Math.max(curE, e); + else { + covered += curE - curS; + curS = s; + curE = e; + } + } + covered += curE - curS; + + return (covered / L) * 100; +} + +/* ---------------------- LO Runner ---------------------- */ +function LORunner({ wsUrl, dataScope, scopeKey, onData, onErrors }) { + const { data, errors } = useLOConnectionDataManager({ + url: wsUrl, + dataScope, + }); + + useEffect(() => onData?.(data), [data, onData]); + useEffect(() => onErrors?.(errors), [errors, onErrors]); + + useEffect(() => { + if (!DEBUG) return; + console.log("[LORunner] mounted scopeKey:", scopeKey); + return () => console.log("[LORunner] unmounted scopeKey:", scopeKey); + }, [scopeKey]); + + return null; +} + +/* ---------------------- ECharts option builder ---------------------- */ +function buildEChartOption({ metricId, points }) { + const labels = points.map((p) => p.label); + + const values = points.map((p) => p.raw).filter(Number.isFinite); + const dataMin = values.length ? Math.min(...values) : 0; + const dataMax = values.length ? Math.max(...values) : 100; + const range = dataMax - dataMin; + + // Add padding: 20% of range on each side, minimum Β±5 percentage points + const padding = Math.max(range * 0.2, 5); + const yMin = Math.max(0, Math.floor((dataMin - padding) / 5) * 5); + const yMax = Math.min(100, Math.ceil((dataMax + padding) / 5) * 5); + + // If all values are identical or very close, center around that value + const finalMin = yMin === yMax ? Math.max(0, yMin - 10) : yMin; + const finalMax = yMin === yMax ? Math.min(100, yMax + 10) : yMax; + + const barData = points.map((p) => ({ + value: p.barValue, + docId: p.docId, + label: p.label, + title: p.title, + raw: p.raw, + })); + + const lineData = points.map((p) => ({ + value: p.value, + docId: p.docId, + label: p.label, + title: p.title, + raw: p.raw, + })); + + return { + animation: false, + grid: { top: 20, right: 20, bottom: 40, left: 60 }, + + tooltip: { + trigger: "axis", + confine: true, + axisPointer: { + type: "line", + shadowStyle: { opacity: 0 }, + lineStyle: { color: "rgba(107,114,128,0.55)", width: 1 }, + }, + formatter: (params) => { + const primary = Array.isArray(params) ? params[0] : params; + const d = primary?.data || {}; + const pct = Number.isFinite(Number(d.raw)) ? Number(d.raw).toFixed(1) : "0.0"; + const docId = d.docId || "β€”"; + const title = d.title || "Untitled"; + const date = d.label || primary?.axisValue || ""; + + return ` +
+
${metricId}
+
${title}
+
${date}
+
Coverage: ${pct}%
+
Document: ${docId}
+
+ `; + }, + }, + + xAxis: { + type: "category", + data: labels, + axisLabel: { fontSize: 11, interval: "auto" }, + axisPointer: { + show: true, + type: "line", + shadowStyle: { opacity: 0 }, + lineStyle: { color: "rgba(107,114,128,0.55)", width: 1 }, + }, + }, + + yAxis: { + type: "value", + min: finalMin, + max: finalMax, + axisLabel: { formatter: "{value}%" }, + }, + + series: [ + { + name: "Coverage (bar)", + type: "bar", + data: barData, + barMaxWidth: 28, + emphasis: { focus: "none" }, + select: { disabled: true }, + blur: { itemStyle: { opacity: 1 } }, + }, + { + name: "Coverage (line)", + type: "line", + data: lineData, + smooth: true, + symbol: "circle", + symbolSize: 8, + emphasis: { focus: "none" }, + select: { disabled: true }, + blur: { lineStyle: { opacity: 1 }, itemStyle: { opacity: 1 } }, + }, + ], + }; +} + +export default function StudentDetailGrowth({ + metrics, + setMetrics, + + studentID, + + essaysInRangeAsc = [], +}) { + const selectedMetrics = useMemo(() => normalizeSelectedMetrics(metrics), [metrics]); + const { courseId } = useCourseIdContext(); + const wsUrl = `${origin}/wsapi/communication_protocol`; + + const docIdsAsc = useMemo(() => { + return (Array.isArray(essaysInRangeAsc) ? essaysInRangeAsc : []) + .map((e) => (e?.id || "").toString().trim()) + .filter(Boolean); + }, [essaysInRangeAsc]); + + const enabled = !!studentID && docIdsAsc.length > 0 && selectedMetrics.length > 0; + + const origin = + process.env.NEXT_PUBLIC_LO_WS_ORIGIN?.replace(/\/+$/, "") || + getWsOriginFromWindow() || + "ws://localhost:8888"; + + const dataScope = useMemo(() => { + if (!enabled) { + return { wo: { execution_dag: "writing_observer", target_exports: [], kwargs: {} } }; + } + + return { + wo: { + execution_dag: "writing_observer", + target_exports: ["single_student_docs_with_nlp_annotations", 'student_with_docs'], + kwargs: { + course_id: courseId, + student_id: studentID, + document: docIdsAsc, + nlp_options: selectedMetrics, + }, + }, + }; + }, [enabled, courseId, studentID, docIdsAsc, selectedMetrics]); + + const scopeKey = useMemo(() => { + const signature = { enabled, studentID, courseId, docIdsAsc, selectedMetrics }; + return stableStringify(signature); + }, [enabled, studentID, courseId, docIdsAsc, selectedMetrics]); + + const [loData, setLoData] = useState(null); + const [loErrors, setLoErrors] = useState(null); + const [isFetching, setIsFetching] = useState(false); + const prevScopeKeyRef = useRef(scopeKey); + + useEffect(() => { + if (!enabled) { + setIsFetching(false); + return; + } + if (prevScopeKeyRef.current !== scopeKey) { + prevScopeKeyRef.current = scopeKey; + setIsFetching(true); + setLoErrors(null); + } + }, [enabled, scopeKey]); + + useEffect(() => { + if (!enabled) return; + + const hasErrors = + loErrors && + ((Array.isArray(loErrors) && loErrors.length > 0) || + (typeof loErrors === "object" && Object.keys(loErrors).length > 0)); + + if (hasErrors) { + setIsFetching(false); + return; + } + if (loData) setIsFetching(false); + }, [enabled, loData, loErrors]); + + const docsObj = loData?.students?.[studentID]?.documents || {}; + + const isMetricReady = useCallback( + (metricId) => { + if (!metricId) return false; + for (const d of docIdsAsc) { + const doc = docsObj?.[d]; + const offsets = doc?.[metricId]?.offsets; + if (Array.isArray(offsets) && offsets.length > 0) return true; + } + return false; + }, + [docsObj, docIdsAsc] + ); + + const seriesByMetric = useMemo(() => { + const out = {}; + if (!enabled || !loData) return out; + + for (const metricId of selectedMetrics) { + // keep per-metric loader behavior for newly added metrics + if (isFetching && !isMetricReady(metricId)) { + out[metricId] = null; + continue; + } + + const points = []; + for (let i = 0; i < docIdsAsc.length; i++) { + const docId = docIdsAsc[i]; + const doc = docsObj?.[docId]; + + const essay = essaysInRangeAsc[i] || {}; + const label = + (essay?.date && String(essay.date)) || + `Essay ${i + 1}`; + const title = + (essay?.title && String(essay.title)) || + `Document ${i + 1}`; + + const raw = coveragePercentFromDoc(doc, metricId); + + points.push({ + idx: i, + label, + title, + docId, + raw, + value: raw, + barValue: raw, + metricLabel: metricId, + }); + } + + out[metricId] = points; + } + + return out; + }, [enabled, loData, selectedMetrics, docIdsAsc, docsObj, essaysInRangeAsc, isFetching, isMetricReady]); + + // lock tooltip per metric + const [lockedIndexByMetric, setLockedIndexByMetric] = useState({}); + const chartRefs = useRef({}); + + const showEmpty = selectedMetrics.length === 0; + + return ( +
+ {enabled ? ( + setLoData(d)} + onErrors={(e) => setLoErrors(e)} + /> + ) : null} + + + +
+
+
+ Showing {docIdsAsc.length} docs +
+ + {enabled && isFetching ? ( +
+ + Updating… +
+ ) : null} +
+ + {showEmpty ? ( +
+ Select one or more metrics from the left to view trends over time. +
+ ) : !loData ? ( +
+ + Loading documents… +
+ ) : ( +
+ {selectedMetrics.map((metricId) => { + const points = seriesByMetric?.[metricId]; + + if (enabled && isFetching && points === null) { + return ( +
+
+

{metricId}

+
+ + Fetching… +
+
+ +
+
+ + Computing new metric… +
+
+
+ ); + } + + const data = Array.isArray(points) ? points : []; + const lockedIndex = lockedIndexByMetric?.[metricId]; + + const option = buildEChartOption({ metricId, points: data }); + + const onEvents = { + click: (params) => { + const idx = params?.dataIndex; + if (typeof idx !== "number") return; + + setLockedIndexByMetric((prev) => { + const cur = prev?.[metricId]; + if (typeof cur === "number" && cur === idx) { + const next = { ...prev }; + delete next[metricId]; + return next; + } + return { ...prev, [metricId]: idx }; + }); + + const inst = chartRefs.current?.[metricId]; + if (inst) { + // lock tooltip without any highlight/selection + inst.dispatchAction({ type: "showTip", seriesIndex: 0, dataIndex: idx }); + } + }, + }; + + return ( +
+
+

{metricId}

+ +
+ {data.length} points + + {typeof lockedIndex === "number" ? ( + + ) : null} +
+
+ +
+ { + const inst = ref?.getEchartsInstance?.(); + if (inst) chartRefs.current[metricId] = inst; + }} + /> + + {enabled && isFetching ? ( +
+
+ + Fetching updated metrics… +
+
+ ) : null} +
+ +

+ Hover to show more information. +

+
+ ); + })} +
+ )} +
+
+ ); +} diff --git a/modules/portfolio_diff/src/app/students/components/StudentDetail/index.js b/modules/portfolio_diff/src/app/students/components/StudentDetail/index.js new file mode 100644 index 000000000..183060368 --- /dev/null +++ b/modules/portfolio_diff/src/app/students/components/StudentDetail/index.js @@ -0,0 +1,915 @@ +"use client"; + +import { Calendar, FileText, GitCompareArrows, TrendingUp, Users } from "lucide-react"; +import { useRouter, useSearchParams } from "next/navigation"; +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; + +import { useLOConnectionDataManager } from "lo_event/lo_event/lo_assess/components/components.jsx"; + +import StudentDetailCompare from "./StudentDetailCompare"; +import StudentDetailGrowth from "./StudentDetailGrowth"; +import { useCourseIdContext } from "@/app/providers/CourseIdProvider"; +import { getWsOriginFromWindow } from "@/app/utils/ws"; + +/* ============================================================= + CONSTANTS + ============================================================= */ + +const MODES = { COMPARE: "compare", GROWTH: "growth" }; + +const STUDENTS_BREADCRUMB_HREF = + "/wo_portfolio_diff/portfolio_diff/students"; + +const monthsShort = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; +const monthsLong = [ + "January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December", +]; + +const CATEGORY_KEYS = { + language: "Language", + argumentation: "Argumentation", + statements: "Statements", + transitions: "Transition Words", + pos: "Parts of Speech", + sentence_type: "Sentence Types", + source_information: "Source Information", + dialogue: "Dialogue", + tone: "Tone", + details: "Details", + other: "Other", +}; + +const iconForCategoryKey = (catKey) => { + switch (catKey) { + case "tone": + return TrendingUp; + case "dialogue": + return Users; + case "details": + return FileText; + default: + return FileText; + } +}; + +const METRIC_DEFS_RAW = [ + // language + { id: "academic_language", title: "Academic Language", categoryKey: "language", function: "percent", desc: "Percent of tokens flagged academic" }, + { id: "informal_language", title: "Informal Language", categoryKey: "language", function: "percent", desc: "Percent of tokens flagged informal" }, + { id: "latinate_words", title: "Latinate Words", categoryKey: "language", function: "percent", desc: "Percent of tokens flagged latinate" }, + { id: "opinion_words", title: "Opinion Words", categoryKey: "language", function: "total", desc: "Total opinion-word signals" }, + { id: "emotion_words", title: "Emotion Words", categoryKey: "language", function: "percent", desc: "Percent emotion words" }, + + // argumentation + { id: "argument_words", title: "Argument Words", categoryKey: "argumentation", function: "percent", desc: "Percent argument words" }, + { id: "explicit_argument", title: "Explicit argument", categoryKey: "argumentation", function: "percent", desc: "Percent explicit argument markers" }, + + // statements + { id: "statements_of_opinion", title: "Statements of Opinion", categoryKey: "statements", function: "percent", desc: "Percent of sentences classified as opinion" }, + { id: "statements_of_fact", title: "Statements of Fact", categoryKey: "statements", function: "percent", desc: "Percent of sentences classified as fact" }, + + // transitions + { id: "transition_words", title: "Transition Words", categoryKey: "transitions", function: "counts", desc: "Transition counts (by type)" }, + { id: "positive_transition_words", title: "Positive Transition Words", categoryKey: "transitions", function: "total", desc: "Total positive transitions" }, + { id: "conditional_transition_words", title: "Conditional Transition Words", categoryKey: "transitions", function: "total", desc: "Total conditional transitions" }, + { id: "consequential_transition_words", title: "Consequential Transition Words", categoryKey: "transitions", function: "total", desc: "Total consequential transitions" }, + { id: "contrastive_transition_words", title: "Contrastive Transition Words", categoryKey: "transitions", function: "total", desc: "Total contrastive transitions" }, + { id: "counterpoint_transition_words", title: "Counterpoint Transition Words", categoryKey: "transitions", function: "total", desc: "Total counterpoint transitions" }, + { id: "comparative_transition_words", title: "Comparative Transition Words", categoryKey: "transitions", function: "total", desc: "Total comparative transitions" }, + { id: "cross_referential_transition_words", title: "Cross Referential Transition Words", categoryKey: "transitions", function: "total", desc: "Total cross-referential transitions" }, + { id: "illustrative_transition_words", title: "Illustrative Transition Words", categoryKey: "transitions", function: "total", desc: "Total illustrative transitions" }, + { id: "negative_transition_words", title: "Negative Transition Words", categoryKey: "transitions", function: "total", desc: "Total negative transitions" }, + { id: "emphatic_transition_words", title: "Emphatic Transition Words", categoryKey: "transitions", function: "total", desc: "Total emphatic transitions" }, + { id: "evenidentiary_transition_words", title: "Evenidentiary Transition Words", categoryKey: "transitions", function: "total", desc: "Total evidentiary transitions" }, + { id: "general_transition_words", title: "General Transition Words", categoryKey: "transitions", function: "total", desc: "Total general transitions" }, + { id: "ordinal_transition_words", title: "Ordinal Transition Words", categoryKey: "transitions", function: "total", desc: "Total ordinal transitions" }, + { id: "purposive_transition_words", title: "Purposive Transition Words", categoryKey: "transitions", function: "total", desc: "Total purposive transitions" }, + { id: "periphrastic_transition_words", title: "Periphrastic Transition Words", categoryKey: "transitions", function: "total", desc: "Total periphrastic transitions" }, + { id: "hypothetical_transition_words", title: "Hypothetical Transition Words", categoryKey: "transitions", function: "total", desc: "Total hypothetical transitions" }, + { id: "summative_transition_words", title: "Summative Transition Words", categoryKey: "transitions", function: "total", desc: "Total summative transitions" }, + { id: "introductory_transition_words", title: "Introductory Transition Words", categoryKey: "transitions", function: "total", desc: "Total introductory transitions" }, + + // parts of speech + { id: "adjectives", title: "Adjectives", categoryKey: "pos", function: "total", desc: "Total adjectives" }, + { id: "adverbs", title: "Adverbs", categoryKey: "pos", function: "total", desc: "Total adverbs" }, + { id: "nouns", title: "Nouns", categoryKey: "pos", function: "total", desc: "Total nouns" }, + { id: "proper_nouns", title: "Proper Nouns", categoryKey: "pos", function: "total", desc: "Total proper nouns" }, + { id: "verbs", title: "Verbs", categoryKey: "pos", function: "total", desc: "Total verbs" }, + { id: "numbers", title: "Numbers", categoryKey: "pos", function: "total", desc: "Total numbers" }, + { id: "prepositions", title: "Prepositions", categoryKey: "pos", function: "total", desc: "Total prepositions" }, + { id: "coordinating_conjunction", title: "Coordinating Conjunction", categoryKey: "pos", function: "total", desc: "Total coordinating conjunctions" }, + { id: "subordinating_conjunction", title: "Subordinating Conjunction", categoryKey: "pos", function: "total", desc: "Total subordinating conjunctions" }, + { id: "auxiliary_verb", title: "Auxiliary Verb", categoryKey: "pos", function: "total", desc: "Total auxiliary verbs" }, + { id: "pronoun", title: "Pronoun", categoryKey: "pos", function: "total", desc: "Total pronouns" }, + + // sentence types + { id: "simple_sentences", title: "Simple Sentences", categoryKey: "sentence_type", function: "total", desc: "Total simple sentences" }, + { id: "simple_with_complex_predicates", title: "Simple with Complex Predicates", categoryKey: "sentence_type", function: "total", desc: "Total simple (complex predicates)" }, + { id: "simple_with_compound_predicates", title: "Simple with Compound Predicates", categoryKey: "sentence_type", function: "total", desc: "Total simple (compound predicates)" }, + { id: "simple_with_compound_complex_predicates", title: "Simple with Compound Complex Predicates", categoryKey: "sentence_type", function: "total", desc: "Total simple (compound complex predicates)" }, + { id: "compound_sentences", title: "Compound Sentences", categoryKey: "sentence_type", function: "total", desc: "Total compound sentences" }, + { id: "complex_sentences", title: "Complex Sentences", categoryKey: "sentence_type", function: "total", desc: "Total complex sentences" }, + { id: "compound_complex_sentences", title: "Compound Complex Sentences", categoryKey: "sentence_type", function: "total", desc: "Total compound-complex sentences" }, + + // source info + { id: "information_sources", title: "Information Sources", categoryKey: "source_information", function: "percent", desc: "Percent source references" }, + { id: "attributions", title: "Attributions", categoryKey: "source_information", function: "percent", desc: "Percent attributions" }, + { id: "citations", title: "Citations", categoryKey: "source_information", function: "percent", desc: "Percent citations" }, + { id: "quoted_words", title: "Quoted Words", categoryKey: "source_information", function: "percent", desc: "Percent quoted words" }, + + // dialogue + { id: "direct_speech_verbs", title: "Direct Speech Verbs", categoryKey: "dialogue", function: "percent", desc: "Percent direct speech verbs" }, + { id: "indirect_speech", title: "Indirect Speech", categoryKey: "dialogue", function: "percent", desc: "Percent indirect speech" }, + + // tone + { id: "positive_tone", title: "Positive Tone", categoryKey: "tone", function: "percent", desc: "Percent positive tone" }, + { id: "negative_tone", title: "Negative Tone", categoryKey: "tone", function: "percent", desc: "Percent negative tone" }, + + // details + { id: "concrete_details", title: "Concrete Details", categoryKey: "details", function: "percent", desc: "Percent concrete details" }, + { id: "main_idea_sentences", title: "Main Idea Sentences", categoryKey: "details", function: "total", desc: "Total main idea sentences" }, + { id: "supporting_idea_sentences", title: "Supporting Idea Sentences", categoryKey: "details", function: "total", desc: "Total supporting idea sentences" }, + { id: "supporting_detail_sentences", title: "Supporting Detail Sentences", categoryKey: "details", function: "total", desc: "Total supporting detail sentences" }, + + // other + { id: "polysyllabic_words", title: "Polysyllabic Words", categoryKey: "other", function: "percent", desc: "Percent polysyllabic tokens" }, + { id: "low_frequency_words", title: "Low Frequency Words", categoryKey: "other", function: "percent", desc: "Percent low-frequency tokens" }, + { id: "sentences", title: "Sentences", categoryKey: "other", function: "total", desc: "Total sentences" }, + { id: "paragraphs", title: "Paragraphs", categoryKey: "other", function: "total", desc: "Total paragraphs" }, + { id: "character_trait_words", title: "Character Trait Words", categoryKey: "other", function: "percent", desc: "Percent character trait tokens" }, + { id: "in_past_tense", title: "In Past Tense", categoryKey: "other", function: "percent", desc: "Percent past tense scope" }, + { id: "explicit_claims", title: "Explicit Claims", categoryKey: "other", function: "percent", desc: "Percent explicit claims" }, + { id: "social_awareness", title: "Social Awareness", categoryKey: "other", function: "percent", desc: "Percent social awareness" }, +]; + +const METRIC_BY_ID = Object.fromEntries(METRIC_DEFS_RAW.map((m) => [m.id, m])); + +const DEFAULT_METRICS = [ + "academic_language", + "informal_language", + "latinate_words", + "transition_words", + "citations", + "sentences", + "paragraphs", +]; + +const GENRE_COLORS = { Document: "hsl(160 70% 40%)" }; + +/* ============================================================= + HELPERS + ============================================================= */ + + + +const median = (arr) => { + if (!arr.length) return 0; + const a = [...arr].sort((x, y) => x - y); + const mid = Math.floor(a.length / 2); + return a.length % 2 ? a[mid] : (a[mid - 1] + a[mid]) / 2; +}; +const mean = (a) => a.reduce((s, x) => s + x, 0) / Math.max(1, a.length); +const std = (a) => { + if (a.length < 2) return 0; + const m = mean(a); + return Math.sqrt(mean(a.map((v) => (v - m) * (v - m)))); +}; +const slopePerIndex = (series) => { + if (series.length < 2) return 0; + const xs = series.map((p) => p.idx); + const ys = series.map((p) => p.value); + const xbar = mean(xs); + const ybar = mean(ys); + const num = xs.reduce((s, x, i) => s + (x - xbar) * (ys[i] - ybar), 0); + const den = xs.reduce((s, x) => s + (x - xbar) * (x - xbar), 0) || 1; + return num / den; +}; + +const safeNum = (v, fallback = 0) => { + const n = Number(v); + return Number.isFinite(n) ? n : fallback; +}; + +const sentenceSplit = (text) => { + const t = (text || "").trim(); + if (!t) return []; + return t.split(/(?<=[.!?])\s+/).filter(Boolean); +}; + +const wordSplit = (text) => { + const t = (text || "").trim(); + if (!t) return []; + return t + .split(/\s+/) + .map((w) => w.replace(/[^\p{L}\p{N}'-]+/gu, "").trim()) + .filter(Boolean); +}; + +const makePreviewFromText = (text, maxChars = 420) => { + const t = (text || "").replace(/\s+/g, " ").trim(); + if (!t) return ""; + return t.length > maxChars ? `${t.slice(0, maxChars)}…` : t; +}; + +const formatDocTitle = (docId, meta) => { + const fromMeta = + meta?.title || + meta?.name || + meta?.doc_title || + meta?.document_title || + meta?.filename || + meta?.file_name; + if (fromMeta && String(fromMeta).trim()) return String(fromMeta).trim(); + return docId ? String(docId).replace(/[-_]/g, " ") : "Document"; +}; + +const getDocObjFromLO = (data, studentID, docId) => { + const s = data?.students?.[studentID]; + const d1 = s?.documents?.[docId]; + if (d1 && typeof d1 === "object") return d1; + const d2 = s?.docs?.[docId]; + if (d2 && typeof d2 === "object") return d2; + const d3 = s?.doc_by_id?.[docId]; + if (d3 && typeof d3 === "object") return d3; + const d4 = s?.documents?.[docId]?.value; + if (d4 && typeof d4 === "object") return d4; + return null; +}; + +const getDocTextFromLO = (data, studentID, docId) => { + const doc = getDocObjFromLO(data, studentID, docId); + const t = doc?.text; + return typeof t === "string" ? t : ""; +}; + +function metricCoveragePercent(doc, metricId) { + const text = (doc?.text || "").toString(); + const L = text.length; + if (!L) return 0; + + const offsets = doc?.[metricId]?.offsets; + if (!Array.isArray(offsets) || offsets.length === 0) return 0; + + const ranges = []; + for (const pair of offsets) { + if (!Array.isArray(pair) || pair.length < 2) continue; + const start = Number(pair[0]); + const len = Number(pair[1]); + if (!Number.isFinite(start) || !Number.isFinite(len) || len <= 0) continue; + + let s = Math.max(0, Math.min(L, start)); + let e = Math.max(0, Math.min(L, start + len)); + if (e > s) ranges.push([s, e]); + } + if (!ranges.length) return 0; + + ranges.sort((a, b) => a[0] - b[0] || a[1] - b[1]); + + let covered = 0; + let [curS, curE] = ranges[0]; + + for (let i = 1; i < ranges.length; i++) { + const [s, e] = ranges[i]; + if (s <= curE) curE = Math.max(curE, e); + else { + covered += curE - curS; + curS = s; + curE = e; + } + } + covered += curE - curS; + + return (covered / L) * 100; +} + +const buildEssaysFromDocs = ({ studentID, documentIDS, docsObj, data }) => { + const out = (documentIDS || []).map((docId) => { + const meta = docsObj?.[docId] || {}; + const lastAccess = meta?.last_access; + const lastAccessMs = + typeof lastAccess === "number" + ? (lastAccess > 1e12 ? lastAccess : lastAccess * 1000) + : null; + + const dateISO = lastAccessMs ? new Date(lastAccessMs).toISOString() : ""; + const dateObj = lastAccessMs ? new Date(lastAccessMs) : null; + + const dateStr = dateObj + ? `${monthsShort[dateObj.getMonth()]} ${dateObj.getDate()}, ${dateObj.getFullYear()}` + : "β€”"; + + const category = dateObj ? `${monthsLong[dateObj.getMonth()]} ${dateObj.getFullYear()}` : "Unknown date"; + + const doc = getDocObjFromLO(data, studentID, docId); + const text = + doc?.text && typeof doc.text === "string" ? doc.text : getDocTextFromLO(data, studentID, docId); + + const wordsArr = wordSplit(text); + const words = wordsArr.length; + + const lowerWords = wordsArr.map((w) => w.toLowerCase()); + const uniqueWords = new Set(lowerWords).size; + const lexicalDiversity = words ? Number(((uniqueWords / words) * 100).toFixed(1)) : 0; + + const sents = sentenceSplit(text); + const sentences = Math.max(1, sents.length || 1); + const avgSentenceLen = words ? Math.round(words / sentences) : 0; + + return { + id: docId, + title: formatDocTitle(docId, meta), + date: dateStr, + dateISO: dateISO || new Date(0).toISOString(), + category, + words, + uniqueWords, + lexicalDiversity, + avgSentenceLen, + grade: "β€”", + preview: makePreviewFromText(text), + tags: ["Document"], // βœ… never empty + _doc: doc || { text }, + }; + }); + + return out.sort((a, b) => new Date(b.dateISO) - new Date(a.dateISO)); +}; + +/* ============================================================= + component + ============================================================= */ + +export default function StudentDetail({ studentId }) { + // console.count("StudentDetail render"); + const router = useRouter(); + const searchParams = useSearchParams(); + + const studentID = searchParams.get("student_id") || String(studentId); + + const [mode, setMode] = useState(MODES.COMPARE); + const [selectedEssays, setSelectedEssays] = useState([]); + + const [cardsPerRow, setCardsPerRow] = useState(3); + const [sortBy, setSortBy] = useState("date"); + const [search, setSearch] = useState(""); + + const [filterTags, setFilterTags] = useState([]); + const [tagOpen, setTagOpen] = useState(false); + const [tagQuery, setTagQuery] = useState(""); + + const [startDate, setStartDate] = useState(""); + const [endDate, setEndDate] = useState(""); + + const [metrics, setMetrics] = useState([...DEFAULT_METRICS]); + + const [openEssay, setOpenEssay] = useState(null); + const { courseId } = useCourseIdContext(); + + const [activeQuickRange, setActiveQuickRange] = useState("all"); + + // ------ Initial dataScope: only student_with_docs ------ + const initialDataScope = useMemo(() => ({ + wo: { + execution_dag: "writing_observer", + target_exports: ["student_with_docs", "single_student_profile"], + kwargs: { + course_id: courseId, + student_id: studentID, + }, + }, + }), [courseId, studentID]); + + const origin = + process.env.NEXT_PUBLIC_LO_WS_ORIGIN?.replace(/\/+$/, "") || + getWsOriginFromWindow() || + "ws://localhost:8888"; + + // Single hook for the entire page + const { data, errors, connection } = useLOConnectionDataManager({ + url: `${origin}/wsapi/communication_protocol`, + dataScope: initialDataScope, + }); + + // ------ Derive docsObj & documentIDS from student_with_docs response ------ + const docsObj = data?.students?.[studentID]?.documents || {}; + const studentProfile = data?.students?.[studentID]?.profile?.name || {}; + const studentName = studentProfile?.full_name || studentProfile?.name || studentID; + + const getStudentById = useCallback((id) => { + const profile = data?.students?.[id]?.profile; + const name = profile?.name?.full_name || profile?.name?.name || (id ? String(id).replace(/[-_]/g, " ") : "Student"); + + const initials = name + .split(" ") + .map((w) => w[0]) + .join("") + .toUpperCase() + .slice(0, 2) || "ST"; + return { + id, + name, + initials, + avatarColor: "bg-gray-100", + textColor: "text-gray-700", + gradeLevel: profile?.grade_level || "β€”", + section: profile?.section || "β€”", + }; + }, [data]); + + const documentIDS = useMemo(() => { + const ids = Object.keys(docsObj || {}); + ids.sort(); + return ids; + }, [docsObj]); + + // ------ When documentIDS changes, send updated dataScope via connection ------ + const prevDocIDSSigRef = useRef(""); + + useEffect(() => { + const sig = documentIDS.join(","); + if (sig === prevDocIDSSigRef.current) return; + if (documentIDS.length === 0) return; + if (!connection?.sendMessage) return; + + prevDocIDSSigRef.current = sig; + + const updatedDataScope = { + wo: { + execution_dag: "writing_observer", + target_exports: ["student_with_docs", "single_student_doc_by_id", "single_student_profile"], + kwargs: { + course_id: courseId, + student_id: studentID, + doc_ids: documentIDS, + }, + } + }; + + connection.sendMessage(JSON.stringify(updatedDataScope)); + }, [documentIDS, connection, courseId, studentID]); + + const essays = useMemo(() => { + return buildEssaysFromDocs({ + studentID, + documentIDS, + docsObj, + data, + }); + }, [studentID, documentIDS, docsObj, data]); + + const metricSections = useMemo(() => { + return Object.entries(CATEGORY_KEYS).map(([categoryKey, title]) => { + const list = METRIC_DEFS_RAW.filter((m) => m.categoryKey === categoryKey).map((m) => m.id); + return { + title, + icon: iconForCategoryKey(categoryKey), + metrics: list, + }; + }); + }, []); + + const metricByKey = useCallback((key) => { + const raw = METRIC_BY_ID[key]; + if (!raw) return null; + + const get = (essay) => { + const doc = essay?._doc || {}; + const direct = doc?.[key]?.metric; + if (direct != null && !Number.isNaN(Number(direct))) return Number(direct); + return metricCoveragePercent(doc, key); + }; + + return { + key, + label: raw.title, + unit: raw.function === "percent" ? "%" : "", + get, + desc: raw.desc, + }; + }, []); + + const essaysAscAll = useMemo(() => { + return [...essays].sort((a, b) => new Date(a.dateISO) - new Date(b.dateISO)); + }, [essays]); + + const baselineByMetric = useMemo(() => { + const out = {}; + for (const k of metrics) { + const def = metricByKey(k); + if (!def) continue; + const vals = essaysAscAll.map((e) => safeNum(def.get(e), 0)); + out[k] = { median: median(vals), sd: std(vals) }; + } + return out; + }, [essaysAscAll, metrics, metricByKey]); + + const essaysInRangeAsc = useMemo(() => { + const inRange = essaysAscAll.filter((e) => { + const d = new Date(e.dateISO); + const afterStart = !startDate || d >= new Date(startDate); + const beforeEnd = !endDate || d <= new Date(endDate); + return afterStart && beforeEnd; + }); + return inRange; + }, [essaysAscAll, startDate, endDate]); + + const getSeriesForMetric = useCallback( + (key) => { + const def = metricByKey(key); + if (!def) return []; + const base = baselineByMetric[key] || { median: 0, sd: 0 }; + + return essaysInRangeAsc.map((e, idx) => { + const raw = safeNum(def.get(e), 0); + const delta = raw - base.median; + const badge = + base.sd > 0 ? (delta > 0.75 * base.sd ? "β–²" : delta < -0.75 * base.sd ? "β–Ό" : "●") : "●"; + return { + idx, + label: e.date, + title: e.title, + date: e.date, + genre: e.tags[0], + raw, + value: delta, + delta, + badge, + unit: def.unit, + }; + }); + }, + [metricByKey, baselineByMetric, essaysInRangeAsc] + ); + + const getGenreSegments = () => { + const segs = []; + if (!essaysInRangeAsc.length) return segs; + let start = 0; + let current = essaysInRangeAsc[0].tags[0]; + for (let i = 1; i < essaysInRangeAsc.length; i++) { + const g = essaysInRangeAsc[i].tags[0]; + if (g !== current) { + segs.push({ x1: start, x2: i - 1, genre: current }); + current = g; + start = i; + } + } + segs.push({ x1: start, x2: essaysInRangeAsc.length - 1, genre: current }); + return segs; + }; + const genreSegments = useMemo(() => getGenreSegments(), [essaysInRangeAsc]); + + const filteredEssaysCompare = useMemo(() => { + const byTags = (e) => filterTags.length === 0 || filterTags.some((t) => (e.tags || []).includes(t)); + const bySearch = (e) => { + if (!search.trim()) return true; + const q = search.toLowerCase(); + return ( + (e.title || "").toLowerCase().includes(q) || + (e.preview || "").toLowerCase().includes(q) || + (e.tags || []).some((t) => t.toLowerCase().includes(q)) + ); + }; + + const sorted = [...essays].sort((a, b) => { + if (sortBy === "words") return safeNum(b.words) - safeNum(a.words); + if (sortBy === "title") return (a.title || "").localeCompare(b.title || ""); + return new Date(a.dateISO) < new Date(b.dateISO) ? 1 : -1; + }); + + return sorted.filter(byTags).filter(bySearch); + }, [essays, filterTags, sortBy, search]); + + const groupedEssays = useMemo(() => { + return filteredEssaysCompare.reduce((acc, essay) => { + if (!acc[essay.category]) acc[essay.category] = []; + acc[essay.category].push(essay); + return acc; + }, {}); + }, [filteredEssaysCompare]); + + const getGridCols = () => { + switch (cardsPerRow) { + case 1: + return "grid-cols-1"; + case 2: + return "grid-cols-2"; + case 3: + return "grid-cols-3"; + case 4: + return "grid-cols-4"; + case 5: + return "grid-cols-5"; + case 6: + return "grid-cols-6"; + default: + return "grid-cols-4"; + } + }; + + const tagRef = useRef(null); + useEffect(() => { + const onClick = (e) => { + if (tagRef.current && !tagRef.current.contains(e.target)) setTagOpen(false); + }; + window.addEventListener("click", onClick); + return () => window.removeEventListener("click", onClick); + }, []); + + const applyQuickRange = (key) => { + setActiveQuickRange(key); + + if (key === "all") { + setStartDate(""); + setEndDate(""); + return; + } + + if (!essaysAscAll.length) return; + + const last = new Date(essaysAscAll[essaysAscAll.length - 1].dateISO); + const end = new Date(last); + const start2 = new Date(last); + + if (key === "3mo") start2.setMonth(start2.getMonth() - 3); + if (key === "6mo") start2.setMonth(start2.getMonth() - 6); + if (key === "9mo") start2.setMonth(start2.getMonth() - 9); + + setStartDate(start2.toISOString().slice(0, 10)); + setEndDate(end.toISOString().slice(0, 10)); + }; + + const onStartDateChange = (v) => { + setStartDate(v); + setActiveQuickRange(""); + }; + const onEndDateChange = (v) => { + setEndDate(v); + setActiveQuickRange(""); + }; + + const clearFilters = () => { + setFilterTags([]); + setTagQuery(""); + setSearch(""); + }; + const isAnyFilter = filterTags.length > 0 || search.trim().length > 0; + + const handleEssaySelect = (essayId) => { + if (mode !== MODES.COMPARE) return; + setSelectedEssays((prev) => { + if (prev.includes(essayId)) return prev.filter((id) => id !== essayId); + if (prev.length >= 2) return prev; + return [...prev, essayId]; + }); + }; + + return ( +
+
+ + +
+
+
+

Essays in Portfolio

+ +
+

{documentIDS.length}

+

Available documents

+
+ + {/* TODO: Populate top summary cards with real student analytics data, then re-enable this section. */} + {false && ( + <> +
+
+

Most Improved

+ +
+

β€”

+

Uses selected growth metrics

+
+ +
+
+

Biggest Decline

+ +
+

β€”

+

Uses selected growth metrics

+
+ +
+
+

Avg. Time on Task

+ +
+

β€”

+

(Not available yet)

+
+ +
+
+

Writing Fluency

+ +
+

β€”

+

(Not available yet)

+
+ + )} +
+ +
+
+ + + +
+
+ + {mode === MODES.GROWTH && ( +
+
+
+ + onStartDateChange(e.target.value)} + className="border-0 text-sm focus:outline-none" + aria-label="Start date" + /> + – + onEndDateChange(e.target.value)} + className="border-0 text-sm focus:outline-none" + aria-label="End date" + /> +
+ +
+ + + + + + + +
+
+
+ )} +
+ +
+ {mode === MODES.GROWTH ? ( + + ) : ( + + )} +
+
+ ); +} diff --git a/modules/portfolio_diff/src/app/students/components/StudentsIndex.js b/modules/portfolio_diff/src/app/students/components/StudentsIndex.js new file mode 100644 index 000000000..009c1c583 --- /dev/null +++ b/modules/portfolio_diff/src/app/students/components/StudentsIndex.js @@ -0,0 +1,668 @@ +"use client"; + +import { + CheckCircle, + ChevronDown, + ChevronUp, + Clock, + Download, + FileText, + Search, + Users, + AlertTriangle, + TrendingUp, + Minus, + Loader, +} from "lucide-react"; +import Link from "next/link"; +import { useMemo, useState } from "react"; +import { + useLOConnectionDataManager, + LO_CONNECTION_STATUS, +} from "lo_event/lo_event/lo_assess/components/components.jsx"; +import { useCourseIdContext } from "@/app/providers/CourseIdProvider"; +import { getWsOriginFromWindow } from "@/app/utils/ws"; + + +const SEVEN_DAYS_SECS = 7 * 24 * 60 * 60; + +function deepMerge(a, b) { + const aObj = a && typeof a === "object" && !Array.isArray(a); + const bObj = b && typeof b === "object" && !Array.isArray(b); + if (aObj && bObj) { + const out = { ...a }; + for (const k of Object.keys(b)) out[k] = deepMerge(a[k], b[k]); + return out; + } + return b; +} + +function latestLastAccessSec(availableDocuments) { + if (!availableDocuments) return null; + const docs = Object.values(availableDocuments); + if (!docs.length) return null; + + let max = null; + for (const d of docs) { + const v = d?.last_access; + if (v == null) continue; + const n = Number(v); + if (Number.isNaN(n)) continue; + if (max == null || n > max) max = n; + } + return max; +} + +function formatLastActivity(lastAccessSec) { + if (!lastAccessSec) return "β€”"; + + const nowSec = Date.now() / 1000; + const diffSec = Math.max(0, nowSec - Number(lastAccessSec)); + + const mins = Math.floor(diffSec / 60); + const hrs = Math.floor(diffSec / 3600); + const days = Math.floor(diffSec / 86400); + + if (mins < 1) return "Just now"; + if (mins < 60) return `${mins} min ago`; + if (hrs < 24) return `${hrs} hr${hrs === 1 ? "" : "s"} ago`; + if (days <= 14) return `${days} day${days === 1 ? "" : "s"} ago`; + + try { + return new Date(Number(lastAccessSec) * 1000).toLocaleDateString(); + } catch { + return "β€”"; + } +} + +function riskBadgeClass(risk) { + if (risk === "At-Risk") return "text-amber-800 bg-amber-100 ring-1 ring-inset ring-amber-200"; + if (risk === "Top Growth") return "text-emerald-800 bg-emerald-100 ring-1 ring-inset ring-emerald-200"; + if (risk === "Under Review") return "text-gray-800 bg-gray-100 ring-1 ring-inset ring-gray-200"; + return "text-gray-700 bg-gray-100 ring-1 ring-inset ring-gray-200"; +} + +function riskIcon(risk) { + if (risk === "At-Risk") return ; + if (risk === "Top Growth") return ; + if (risk === "Under Review") return ; + return ; +} + +export default function WritingPortfolioDashboard() { + // table controls + const [query, setQuery] = useState(""); + const [focusFilter, setFocusFilter] = useState("All"); // All | At-Risk | Top Growth + const [pageSize, setPageSize] = useState(10); + const [page, setPage] = useState(1); + const [sortKey, setSortKey] = useState("id"); + const [sortDir, setSortDir] = useState("asc"); + const [selected, setSelected] = useState(new Set()); + const { courseId } = useCourseIdContext(); + + // ------ LO connection setup ------ + const decoded = {}; + decoded.course_id = courseId; + decoded.student_id = [{ user_id: "tc-testcase-Alberta" }]; + decoded.document = [{ doc_id: "fake-google-doc-id-1" }]; + decoded.nlp_options = ["academic_language"]; + + const dataScope = { + wo: { + execution_dag: "writing_observer", + target_exports: ["roster", "document_list"], + kwargs: decoded, + }, + }; + + const origin = + process.env.NEXT_PUBLIC_LO_WS_ORIGIN?.replace(/\/+$/, "") || + getWsOriginFromWindow() || + "ws://localhost:8888"; + + const { data, errors, connection } = useLOConnectionDataManager({ + url: `${origin}/wsapi/communication_protocol`, + dataScope, + }); + + // Merge roster + document_list if they are separate; otherwise use data.students if already merged. + const studentsMap = useMemo(() => { + if (!data) return {}; + + // In many LO setups, patches are already applied into `data.students` + const flatStudents = data?.students ?? {}; + if (flatStudents && Object.keys(flatStudents).length) return flatStudents; + + const rosterStudents = data?.wo?.roster?.students ?? {}; + const docsStudents = data?.wo?.document_list?.students ?? {}; + + const merged = { ...rosterStudents }; + for (const [k, v] of Object.entries(docsStudents)) { + merged[k] = deepMerge(merged[k] ?? {}, v ?? {}); + } + return merged; + }, [data]); + + // Build rows for the table + const DATA = useMemo(() => { + const list = Object.values(studentsMap || {}); + return list.map((s, idx) => { + const profile = s?.profile ?? {}; + const nameObj = profile?.name ?? {}; + const availableDocuments = s?.availableDocuments ?? {}; + const docCount = Object.keys(availableDocuments).length; + + const lastAccessSec = latestLastAccessSec(availableDocuments); + const lastActivity = formatLastActivity(lastAccessSec); + + const risk = "Under Review"; + + return { + id: s?.user_id ?? `student-${idx + 1}`, + firstname: nameObj?.given_name || `Student ${idx + 1}`, + lastname: nameObj?.family_name || "", + fullname: nameObj?.full_name || `Student ${idx + 1}`, + avatar: "", + documents: docCount, + lastAccessSec: lastAccessSec ?? null, // for sorting + lastActivity, + risk, + }; + }); + }, [studentsMap]); + + // Cards metrics + const metrics = useMemo(() => { + const totalStudents = DATA.length; + + let totalDocuments = 0; + let studentsWithDocs = 0; + let active7d = 0; + + const nowSec = Date.now() / 1000; + + for (const s of DATA) { + const docs = Number(s.documents) || 0; + totalDocuments += docs; + if (docs > 0) studentsWithDocs += 1; + + const last = s.lastAccessSec ? Number(s.lastAccessSec) : null; + if (last && nowSec - last <= SEVEN_DAYS_SECS) active7d += 1; + } + + const coverage = totalStudents === 0 ? 0 : Math.round((studentsWithDocs / totalStudents) * 100); + + return { totalStudents, totalDocuments, active7d, coverage }; + }, [DATA]); + + const toggleSort = (key) => { + if (key === sortKey) setSortDir((d) => (d === "asc" ? "desc" : "asc")); + else { + setSortKey(key); + setSortDir("asc"); + } + }; + + const filtered = useMemo(() => { + let rows = DATA.slice(); + + // search + if (query.trim()) { + const q = query.toLowerCase(); + rows = rows.filter((r) => { + const full = `${r.firstname || ""} ${r.lastname || ""}`.toLowerCase(); + return full.includes(q) || String(r.id).toLowerCase().includes(q); + }); + } + + // focus tabs now drive the Risk Level column and filtering + if (focusFilter === "At-Risk") rows = rows.filter((r) => r.risk === "At-Risk"); + if (focusFilter === "Top Growth") rows = rows.filter((r) => r.risk === "Top Growth"); + + // sorting + rows.sort((a, b) => { + if (sortKey === "lastActivity") { + const aT = a.lastAccessSec ?? -Infinity; + const bT = b.lastAccessSec ?? -Infinity; + if (aT < bT) return sortDir === "asc" ? -1 : 1; + if (aT > bT) return sortDir === "asc" ? 1 : -1; + return 0; + } + + const A = a[sortKey]; + const B = b[sortKey]; + + if (A < B) return sortDir === "asc" ? -1 : 1; + if (A > B) return sortDir === "asc" ? 1 : -1; + return 0; + }); + + return rows; + }, [DATA, query, focusFilter, sortKey, sortDir]); + + // pagination + const total = filtered.length; + const totalPages = Math.max(1, Math.ceil(total / pageSize)); + const safePage = Math.min(page, totalPages); + const start = (safePage - 1) * pageSize; + const rows = filtered.slice(start, start + pageSize); + + const allIdsOnPage = (pageRows) => pageRows.map((s) => s.id); + const allSelectedOnPage = rows.length > 0 && rows.every((r) => selected.has(r.id)); + + // Loading state (improved) + const isConnecting = + connection?.status === LO_CONNECTION_STATUS.CONNECTING || + connection?.status === LO_CONNECTION_STATUS.RECONNECTING; + + const hasErrors = errors && Object.keys(errors).length > 0; + const hasStudents = (DATA?.length ?? 0) > 0; + + const showLoading = !hasErrors && !hasStudents; + + console.log(hasErrors, isConnecting, hasStudents, data); + console.log(DATA); + + const renderLoading = () => ( +
+
+
+
+ {isConnecting ? "Connecting to data source…" : "Loading roster…"} +
+
+ +
+ {[0, 1, 2, 3].map((i) => ( +
+ ))} +
+ +
+
+ {[0, 1, 2, 3, 4].map((i) => ( +
+ ))} +
+
+ ); + + const renderError = () => ( +
+
Failed to load dashboard data
+
{JSON.stringify(errors, null, 2)}
+
+ ); + + // Export selected rows as CSV + const exportSelectedToCsv = () => { + const esc = (v) => { + const s = v == null ? "" : String(v); + const needsQuotes = /[",\n\r]/.test(s); + const out = s.replace(/"/g, '""'); + return needsQuotes ? `"${out}"` : out; + }; + + const headers = ["ID", "First Name", "Last Name", "Documents", "Last Activity", "Risk Level"]; + + const selectedRows = DATA.filter((r) => selected.has(r.id)); + const lines = [ + headers.map(esc).join(","), + ...selectedRows.map((r) => + [r.id, r.firstname, r.lastname, r.documents, r.lastActivity, r.risk].map(esc).join(",") + ), + ]; + + const csv = lines.join("\r\n"); + const blob = new Blob([csv], { type: "text/csv;charset=utf-8;" }); + const url = URL.createObjectURL(blob); + + const a = document.createElement("a"); + a.href = url; + a.download = `students_export_${new Date().toISOString().slice(0, 10)}.csv`; + document.body.appendChild(a); + a.click(); + a.remove(); + + URL.revokeObjectURL(url); + }; + + // UPDATED: bulk bar shows ONLY Export (and performs CSV download) + const renderBulkBar = () => ( +
+
+ {selected.size} selected +
+
+ +
+
+ ); + + const renderStatsRow = () => ( +
+ {/* Total Students */} +
+

Total Students

+
+

{metrics.totalStudents}

+
+ +
+
+
+ + {/* Total Documents */} +
+

Total Documents

+
+

{metrics.totalDocuments}

+
+ +
+
+
+ + {/* Active last 7 days */} +
+

Active (Last 7 Days)

+
+

{metrics.active7d}

+
+ +
+
+
+ + {/* Roster coverage */} +
+

Roster Coverage

+
+

{metrics.coverage}%

+
+ +
+
+
+
+ ); + + const renderHeaderBar = () => ( +
+
+
+

Student Overview & Signals

+

Search for student or use the filter tabs to focus on a subset and review students' writing activities.

+
+ + {/* Tabs mapped to risk */} +
+ {["All", "At-Risk", "Top Growth"].map((opt) => ( + + ))} +
+
+
+ ); + + const renderTable = () => ( +
+
+
+

Students

+ Total: {total.toLocaleString()} +
+ +
+
+ + { + setQuery(e.target.value); + setPage(1); + }} + onKeyDown={(e) => e.stopPropagation()} + onClick={(e) => e.stopPropagation()} + className="w-full md:w-64 pl-8 pr-3 py-2 text-sm bg-white border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-emerald-500" + placeholder="Search name or ID…" + autoComplete="off" + autoCorrect="off" + spellCheck={false} + /> +
+
+
+ + {selected.size > 0 && renderBulkBar()} + +
+ + + + + + {[ + { key: "id", label: "ID", align: "text-left" }, + { key: "fullname", label: "Name", align: "text-left" }, + { key: "documents", label: "Documents", align: "text-center" }, + { key: "lastActivity", label: "Last Activity", align: "text-left" }, + { key: "risk", label: "Risk Level", align: "text-left" }, + ].map((c) => ( + + ))} + + + + + + + {rows.map((student) => ( + + + + + + + + + + + + + + + + ))} + + {rows.length === 0 && ( + + + + )} + +
+ { + const next = new Set(selected); + if (e.target.checked) allIdsOnPage(rows).forEach((id) => next.add(id)); + else allIdsOnPage(rows).forEach((id) => next.delete(id)); + setSelected(next); + }} + /> + + + + Actions +
+ { + const next = new Set(selected); + if (e.target.checked) next.add(student.id); + else next.delete(student.id); + setSelected(next); + }} + /> + + {student.id} + + +
+
+ {student.avatar} +
+
{student.fullname}
+
+ +
+ {student.documents} + + {student.lastActivity} + + + {riskIcon(student.risk)} + {student.risk} + + + + + +
+ No students match your filters. +
+
+ +
+
+
+ + {total === 0 ? "0" : `${start + 1}–${Math.min(start + pageSize, total)} of ${total.toLocaleString()}`} + + β€’ + + +
+ +
+ + + Page {safePage} of {totalPages} + + +
+
+
+
+ ); + + return ( +
+ {/* Welcome header */} +
+
+
+
+

+ Welcome back! πŸ‘‹ +

+

A snapshot of your class writing activity and progress.

+ {/* TODO implement meaningful tags for the current course being shown */} + {false && ( +
+ πŸ“š English 10A + πŸ“… Fall 2025 +
+ )} +
+
+
+
+ + {/* Main content */} +
+ {hasErrors ? renderError() : showLoading ? renderLoading() : ( + <> + {renderStatsRow()} + {renderHeaderBar()} + {renderTable()} + + )} +
+
+ ); +} diff --git a/modules/portfolio_diff/src/app/students/page.js b/modules/portfolio_diff/src/app/students/page.js new file mode 100644 index 000000000..b39d86fb7 --- /dev/null +++ b/modules/portfolio_diff/src/app/students/page.js @@ -0,0 +1,23 @@ +// app/students/page.js +"use client"; + +import { Suspense } from "react"; +import { useSearchParams } from "next/navigation"; +import StudentsIndex from "./components/StudentsIndex"; +import StudentDetail from "./components/StudentDetail"; + +function StudentsPageContent() { + const sp = useSearchParams(); + const studentId = sp.get("student_id"); + + if (!studentId) return ; + return ; +} + +export default function StudentsPage() { + return ( + Loading…
}> + + + ); +} diff --git a/modules/portfolio_diff/src/app/utils/data.js b/modules/portfolio_diff/src/app/utils/data.js new file mode 100644 index 000000000..23027c4c0 --- /dev/null +++ b/modules/portfolio_diff/src/app/utils/data.js @@ -0,0 +1,359 @@ +export const students = [ + { + id: 1, + name: "Robert Fox", + period: "Period 1A", + avgScore: 85.3, + essays: 12, + mostImproved: "Grammar", + avatar: "RF", + gender: "Male", + age: 17, + class: "1A", + missingDays: 0, + }, + { + id: 2, + name: "Marvin McKinney", + period: "Period 1B", + avgScore: 92.1, + essays: 15, + mostImproved: "Argumentation", + avatar: "MM", + gender: "Male", + age: 6, + class: "1B", + missingDays: 0, + }, + { + id: 3, + name: "Darrell Steward", + period: "Period 4C", + avgScore: 78.9, + essays: 8, + mostImproved: "Structure", + avatar: "DS", + gender: "Female", + age: 10, + class: "4C", + missingDays: 6, + }, + { + id: 4, + name: "Savannah Nguyen", + period: "Period 4C", + avgScore: 88.7, + essays: 11, + mostImproved: "Transitions", + avatar: "SN", + gender: "Male", + age: 11, + class: "4C", + missingDays: 6, + }, + { + id: 5, + name: "Dianne Russell", + period: "Period 11B", + avgScore: 91.2, + essays: 14, + mostImproved: "Grammar", + avatar: "DR", + gender: "Female", + age: 16, + class: "11B", + missingDays: 10, + }, + { + id: 6, + name: "Cody Fisher", + period: "Period 4A", + avgScore: 82.4, + essays: 9, + mostImproved: "Structure", + avatar: "CF", + gender: "Female", + age: 11, + class: "4A", + missingDays: 20, + }, + { + id: 7, + name: "Leslie Alexander", + period: "Period 5A", + avgScore: 87.6, + essays: 13, + mostImproved: "Argumentation", + avatar: "LA", + gender: "Female", + age: 12, + class: "5A", + missingDays: 0, + }, + { + id: 8, + name: "Albert Flores", + period: "Period 7B", + avgScore: 84.1, + essays: 10, + mostImproved: "Grammar", + avatar: "AF", + gender: "Male", + age: 14, + class: "7B", + missingDays: 0, + }, + { + id: 9, + name: "Ralph Edwards", + period: "Period 11C", + avgScore: 89.5, + essays: 16, + mostImproved: "Transitions", + avatar: "RE", + gender: "Male", + age: 17, + class: "11C", + missingDays: 1, + }, + { + id: 10, + name: "Darlene Robertson", + period: "Period 1A", + avgScore: 86.8, + essays: 12, + mostImproved: "Structure", + avatar: "DR", + gender: "Female", + age: 18, + class: "1A", + missingDays: 0, + }, +]; + +export const assignments = [ + { + id: 1, + title: "Persuasive Essay", + submissions: "26/28", + avgScore: 84.3, + dueDate: "May 12", + status: "Graded", + statusColor: "text-green-600 bg-green-50", + }, + { + id: 2, + title: "Book Report #2", + submissions: "22/28", + avgScore: 79.6, + dueDate: "May 5", + status: "Grading", + statusColor: "text-orange-600 bg-orange-50", + }, + { + id: 3, + title: "Poetry Analysis", + submissions: "28/28", + avgScore: 88.9, + dueDate: "Apr 30", + status: "Graded", + statusColor: "text-green-600 bg-green-50", + }, + { + id: 4, + title: "Research Paper Draft", + submissions: "18/28", + avgScore: 76.2, + dueDate: "May 20", + status: "Open", + statusColor: "text-blue-600 bg-blue-50", + }, + { + id: 5, + title: "Creative Writing", + submissions: "24/28", + avgScore: 91.4, + dueDate: "Apr 25", + status: "Graded", + statusColor: "text-green-600 bg-green-50", + }, + { + id: 6, + title: "Compare & Contrast Essay", + submissions: "28/28", + avgScore: 82.7, + dueDate: "Apr 15", + status: "Graded", + statusColor: "text-green-600 bg-green-50", + }, +]; + +export const baseTags = [ + "Argumentative", + "Narrative", + "Personal", + "Analytical", + "Reflective", + "Opinion", + "Education", + "Descriptive", + "Experience", + "Economics", + "Research", +]; + +// genre colors for shading +export const GENRE_COLORS = { + Argumentative: "#ecfccb", + Narrative: "#e0e7ff", + Analytical: "#e0f2fe", + Reflective: "#fae8ff", + Descriptive: "#fef9c3", + Personal: "#fef3c7", + Opinion: "#f5f5f5", + Education: "#f0fdf4", + Experience: "#fff1f2", + Economics: "#eef2ff", + Research: "#f1f5f9", +}; + +// ---- Metric definitions (adds longPauseRatioPct + clearer labels) ---- +export const METRIC_DEFS = [ + // PRODUCT + { + key: "wordCount", + label: "Word Count", + category: "Product", + unit: "words", + get: (e) => e.words, + }, + { + key: "uniqueWords", + label: "Unique Words", + category: "Product", + unit: "words", + get: (e) => e.uniqueWords, + }, + { + key: "lexicalDiversity", + label: "Lexical Diversity", + category: "Product", + unit: "%", + get: (e) => e.lexicalDiversity, + }, + { + key: "avgSentenceLen", + label: "Avg Sentence Length", + category: "Product", + unit: "words/sent", + get: (e) => e.avgSentenceLen, + }, + // PROCESS + { + key: "wpm", + label: "Fluency (WPM in bursts)", + category: "Process", + unit: "wpm", + get: (e) => e.wpm, + }, + { + key: "burst", + label: "Burst Length", + category: "Process", + unit: "words", + get: (e) => Math.max(1, Math.round(e.burst / 1)), + }, + { + key: "avgPauseSec", + label: "Avg Pause (sec)", + category: "Process", + unit: "sec", + get: (e) => e.avgPauseSec, + }, + { + key: "longPauseRatioPct", + label: "Long-Pause Ratio (%)", + category: "Process", + unit: "%", + get: (e) => { + const total = Math.max(1, e.pauses + e.longPauses); + return Number(((e.longPauses / total) * 100).toFixed(1)); + }, + }, + // REVISION + { + key: "revisionDepth", + label: "Revision Depth (count)", + category: "Revision", + unit: "revisions", + get: (e) => e.revisionDepth, + }, + { + key: "deletions", + label: "Deletions", + category: "Revision", + unit: "ops", + get: (e) => e.deletions, + }, + { + key: "insertions", + label: "Insertions", + category: "Revision", + unit: "ops", + get: (e) => e.insertions, + }, + { + key: "editRatioPct", + label: "Revision Density (% per 100 words)", + category: "Revision", + unit: "%", + get: (e) => e.editRatioPct, + }, + // OUTCOME + { + key: "gradePct", + label: "Grade (%)", + category: "Quality", + unit: "%", + get: (e) => gradeToPct(e.grade), + }, +]; + +export const metricSections = [ + { + name: "Product Metrics", + keys: ["wordCount", "uniqueWords", "lexicalDiversity", "avgSentenceLen"], + }, + { + name: "Process Metrics", + keys: ["wpm", "burst", "avgPauseSec", "longPauseRatioPct"], + }, + { + name: "Revision Metrics", + keys: ["revisionDepth", "deletions", "insertions", "editRatioPct"], + }, + { name: "Quality & Outcomes", keys: ["gradePct"] }, +]; + +export const DEFAULT_METRICS = [ + "wpm", + "longPauseRatioPct", + "burst", + "editRatioPct", + "gradePct", +]; + +const gradeToPct = (g) => { + const map = { + "A+": 97, + A: 94, + "A-": 91, + "B+": 88, + B: 85, + "B-": 82, + "C+": 78, + C: 75, + "C-": 72, + }; + return map[g] ?? 80; +}; \ No newline at end of file diff --git a/modules/portfolio_diff/src/app/utils/navigation.js b/modules/portfolio_diff/src/app/utils/navigation.js new file mode 100644 index 000000000..5b687c67a --- /dev/null +++ b/modules/portfolio_diff/src/app/utils/navigation.js @@ -0,0 +1,22 @@ +/** + * Force a full page navigation to a route with query params. + * This intentionally avoids Next.js router to prevent `_rsc` fetches. + * + * @param {string} path - Relative path (e.g. "students/compare") + * @param {Object} queryParams - Key/value pairs for query string + */ +export function navigateTo(path, queryParams = {}) { + if (typeof window === "undefined") return; + + const query = Object.entries(queryParams) + .filter(([, value]) => value !== undefined && value !== null && value !== "") + .map(([key, value]) => { + return `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`; + }) + .join("&"); + + const url = query ? `${path}?${query}` : path; + + // Force full navigation (prevents Next.js from app-router data fetching) + window.location.assign("/wo_portfolio_diff/portfolio_diff/" + url); +} diff --git a/modules/portfolio_diff/src/app/utils/ws.js b/modules/portfolio_diff/src/app/utils/ws.js new file mode 100644 index 000000000..528bb49e7 --- /dev/null +++ b/modules/portfolio_diff/src/app/utils/ws.js @@ -0,0 +1,15 @@ +/** + * Returns the WebSocket origin based on the current browser window location. + * + * - Uses `wss:` if the page is loaded over HTTPS. + * - Uses `ws:` if the page is loaded over HTTP. + * - Returns `null` when executed in a non-browser environment (e.g., SSR or Node.js), + * where `window` is undefined. + */ + +export function getWsOriginFromWindow() { + if (typeof window === "undefined") return null; + + const proto = window.location.protocol === "https:" ? "wss:" : "ws:"; + return `${proto}//${window.location.host}`; +} \ No newline at end of file diff --git a/modules/wo_portfolio_diff/README.md b/modules/wo_portfolio_diff/README.md new file mode 100644 index 000000000..d45678607 --- /dev/null +++ b/modules/wo_portfolio_diff/README.md @@ -0,0 +1,56 @@ +# wo_portfolio_diff + +This package bundles the compiled frontend output of the `portfolio_diff` project as a Python package, allowing it to be installed and served as a static asset. + +## How It Works + +### 1. Build the Frontend + +The frontend lives in the `portfolio_diff` source directory. To produce a fresh build, run the following from that directory: + +```bash +rm -rf out/ +npm run build +``` + +This removes any previous build artifacts and compiles the project into an `out/` folder. + +### 2. Copy the Build Output + +Once the build is complete, the compiled assets are copied into this package: + +```bash +rm -rf ../wo_portfolio_diff/wo_portfolio_diff/portfolio_diff/ +mkdir ../wo_portfolio_diff/wo_portfolio_diff/portfolio_diff/ +cp -r out/ ../wo_portfolio_diff/wo_portfolio_diff/portfolio_diff/ +``` + +This replaces the previously bundled assets with the freshly built ones, placing the contents of `out/` under `wo_portfolio_diff/portfolio_diff/`. + +### 3. Install the Package + +With the build output in place, install the Python package in editable mode: + +```bash +pip install -e modules/wo_portfolio_diff +``` + +The `-e` flag installs the package in editable (development) mode, meaning changes to the package directory are immediately reflected without needing to reinstall. + +## Summary + +| Step | Command | Purpose | +|------|---------|---------| +| Clean | `rm -rf out/` | Remove stale build artifacts | +| Build | `npm run build` | Compile the frontend into `out/` | +| Sync | `cp -r out/ ../wo_portfolio_diff/...` | Update bundled assets in this package | +| Install | `pip install -e modules/wo_portfolio_diff` | Install the package locally | + +### ENV Variables + +To set environment variables at build time, add them to a `.env.production` file. The build command will load these in. + +| Env | Purpose | +|-----|---------| +| NEXT_PUBLIC_LO_WS_ORIGIN | Where to point communication protocol queries. | + diff --git a/modules/wo_portfolio_diff/VERSION b/modules/wo_portfolio_diff/VERSION new file mode 100644 index 000000000..d6b123109 --- /dev/null +++ b/modules/wo_portfolio_diff/VERSION @@ -0,0 +1 @@ +0.1.0+2026.02.27T21.15.41.540Z.01d7abed.berickson.20260220.dami.portfolio.pr diff --git a/modules/wo_portfolio_diff/pyproject.toml b/modules/wo_portfolio_diff/pyproject.toml new file mode 100644 index 000000000..8fe2f47af --- /dev/null +++ b/modules/wo_portfolio_diff/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["setuptools>=42", "wheel"] +build-backend = "setuptools.build_meta" diff --git a/modules/wo_portfolio_diff/setup.cfg b/modules/wo_portfolio_diff/setup.cfg new file mode 100644 index 000000000..c081ec603 --- /dev/null +++ b/modules/wo_portfolio_diff/setup.cfg @@ -0,0 +1,11 @@ +[metadata] +name = Writing Observer Portfolio Diff +version = file:VERSION +description = Use this as a base template for creating new modules on the Learning Observer. + +[options] +packages = wo_portfolio_diff + +[options.entry_points] +lo_modules = + wo_portfolio_diff = wo_portfolio_diff.module diff --git a/modules/wo_portfolio_diff/wo_portfolio_diff/__init__.py b/modules/wo_portfolio_diff/wo_portfolio_diff/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/modules/wo_portfolio_diff/wo_portfolio_diff/module.py b/modules/wo_portfolio_diff/wo_portfolio_diff/module.py new file mode 100644 index 000000000..a8b13d7c9 --- /dev/null +++ b/modules/wo_portfolio_diff/wo_portfolio_diff/module.py @@ -0,0 +1,31 @@ +''' +Writing Observer Portfolio Diff + +A writing observer module that shows the difference between the works of a student +''' + +# Name for the module +NAME = 'Writing Observer - Portfolio Diff' + + +''' +The Course Dashboards are used to populate the modules +on the home screen. + +Note the icon uses Font Awesome v5 +''' +COURSE_DASHBOARDS = [{ + 'name': NAME, + 'url': "/wo_portfolio_diff/portfolio_diff/", + "icon": { + "type": "fas", + "icon": "fa-play-circle" + } +}] + +''' +Built NextJS pages we want to serve. +''' +NEXTJS_PAGES = [ + {'path': 'portfolio_diff/'} +] \ No newline at end of file diff --git a/modules/writing_observer/VERSION b/modules/writing_observer/VERSION index e5c21f01e..3c8236e9f 100644 --- a/modules/writing_observer/VERSION +++ b/modules/writing_observer/VERSION @@ -1 +1 @@ -0.1.0+2026.02.20T12.50.18.986Z.f3ada3ba.berickson.20260210.dashboard.updates +0.1.0+2026.02.27T13.40.48.021Z.a86fc6ac.berickson.20260220.dami.portfolio.pr diff --git a/modules/writing_observer/writing_observer/document_timestamps.py b/modules/writing_observer/writing_observer/document_timestamps.py index ee8464eb3..4d6ec4449 100644 --- a/modules/writing_observer/writing_observer/document_timestamps.py +++ b/modules/writing_observer/writing_observer/document_timestamps.py @@ -85,3 +85,13 @@ async def fetch_doc_at_timestamp(overall_timestamps, kwargs=None): target_ts = sorted_ts[bisect_index] student['doc_id'] = timestamps[target_ts] yield student + + +@learning_observer.communication_protocol.integration.publish_function('writing_observer.single_student_from_roster') +def single_student_from_roster(roster, student_id): + ''' + Return the roster entry for a single student id. + + We use roster data as the source of profile truth for dashboards. + ''' + return [student for student in roster if student.get('user_id') == student_id] diff --git a/modules/writing_observer/writing_observer/module.py b/modules/writing_observer/writing_observer/module.py index 5036c853f..568bb1fd6 100644 --- a/modules/writing_observer/writing_observer/module.py +++ b/modules/writing_observer/writing_observer/module.py @@ -11,8 +11,11 @@ # files. import pmss +import learning_observer.communication_protocol.integration import learning_observer.communication_protocol.query as q +import learning_observer.constants as constants import learning_observer.settings +import learning_observer.rosters from learning_observer import downloads as d @@ -26,6 +29,16 @@ NAME = "The Writing Observer" +@learning_observer.communication_protocol.integration.publish_function('writing_observer.roster_with_provenance') +async def roster_with_provenance(roster, course_id): + """Fetch roster entries and add STUDENT provenance for protocol consumers.""" + for student in roster: + if constants.USER_ID not in student: + continue + student['provenance'] = {'STUDENT': {constants.USER_ID: student[constants.USER_ID]}} + return roster + + # things that process data versus things that interact with the environment # side-effects or not # course_id or rosters.course_id to distinguish or provide a default - how to specify selector @@ -43,6 +56,7 @@ document_by_title_text = q.call('writing_observer.fetch_doc_by_title_text') source_selector = q.call('source_selector') +single_student_from_roster = q.call('writing_observer.single_student_from_roster') # TODO each of these choices should come from an Enum pmss.parser('nlp_source', parent='string', choices=['nlp', 'nlp_sep_proc'], transform=None) @@ -95,9 +109,59 @@ EXECUTION_DAG = { "execution_dag": { "roster": course_roster(runtime=q.parameter("runtime"), course_id=q.parameter("course_id", required=True)), + "roster_with_provenance": q.call('writing_observer.roster_with_provenance')(roster=q.variable('roster'), course_id=q.parameter("course_id", required=True)), + # all documents for a student + 'student_with_docs': q.select( + q.keys( + 'writing_observer.document_list', + scope_fields={ + "student": q.parameter("student_id", required=True) + } + ), + fields={'docs': 'documents'} + ), + # a single document by explicit doc id + 'single_student_doc_by_id': q.select( + q.keys( + 'writing_observer.reconstruct', + scope_fields={ + "student": q.parameter("student_id", required=True), + "doc_id": q.parameter("doc_ids", default=[]) + } + ), + fields={'text': 'text'} + ), + 'single_student_profile': single_student_from_roster( + roster=q.variable('roster_with_provenance'), + student_id=q.parameter('student_id', required=True) + ), + "docs": q.select( + q.keys( + 'writing_observer.reconstruct', + scope_fields={ + "student": {"values": q.variable("roster"), "path": "user_id"}, + "doc_id": {"values": q.variable("update_docs"), "path": "doc_id"} + } + ), + fields={'text': 'text'} + ), + 'single_student_docs_by_ids': q.select( + q.keys( + 'writing_observer.reconstruct', + scope_fields={ + "student": q.parameter("student_id", required=True), + "doc_id": q.parameter("document", default=[]) + } + ), + fields={'text': 'text'} + ), + 'single_student_nlp': process_texts( + writing_data=q.variable('single_student_docs_by_ids'), + options=q.parameter('nlp_options', required=False, default=[]) + ), "doc_ids": q.select(q.keys('writing_observer.last_document', STUDENTS=q.variable("roster"), STUDENTS_path='user_id'), fields={'document_id': 'doc_id'}), 'update_docs': update_via_google(runtime=q.parameter("runtime"), doc_ids=q.variable('doc_sources')), - "docs": q.select(q.keys('writing_observer.reconstruct', STUDENTS=q.variable("roster"), STUDENTS_path='user_id', RESOURCES=q.variable("update_docs"), RESOURCES_path='doc_id'), fields={'text': 'text'}), + "docs_combined": q.join(LEFT=q.variable("docs"), RIGHT=q.variable("roster"), LEFT_ON='provenance.provenance.STUDENT.value.user_id', RIGHT_ON='user_id'), 'nlp': process_texts(writing_data=q.variable('docs'), options=q.parameter('nlp_options', required=False, default=[])), 'nlp_sep_proc': q.select(q.keys('writing_observer.nlp_components', STUDENTS=q.variable('roster'), STUDENTS_path='user_id', RESOURCES=q.variable("doc_ids"), RESOURCES_path='doc_id'), fields='All'), @@ -169,10 +233,35 @@ "output": "" }, "roster": { - "returns": "roster", + "returns": "roster_with_provenance", "parameters": ["course_id"], "output": "" }, + "student_with_docs": { + "returns": "student_with_docs", + # "parameters": ["student_id"], + "output": "" + }, + "single_student_doc_by_id": { + "returns": "single_student_doc_by_id", + "parameters": ["student_id", "doc_ids"], + "output": "" + }, + "single_student_profile": { + "returns": "single_student_profile", + "parameters": ["student_id"], + "output": "" + }, + "single_student_all_reconstruct": { + "returns": "single_student_all_reconstruct", + "parameters": ["student_id"], + "output": "" + }, + "single_student_docs_with_nlp_annotations": { + "returns": "single_student_nlp", + "parameters": ["student_id", "document", "nlp_options"], + "output": "" + }, "document_list": { "returns": "document_list", "parameters": ["course_id"], From 377c68ef2fc53b2dba290b36c1a0a3c6b9cc3f74 Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Fri, 6 Mar 2026 17:17:07 -0500 Subject: [PATCH 88/88] copy paste reducer with dashboard metrics * added copycut and paste redcuers * updated reducers to call funcs for default value * added pasted text into object * cleaned up copy paste utils file * added paste metric to dashboard * added more paste options and a copy option --------- Co-authored-by: saminur --- VERSION | 2 +- modules/wo_bulk_essay_analysis/VERSION | 2 +- .../wo_bulk_essay_analysis/assets/scripts.js | 74 +++- modules/wo_classroom_text_highlighter/VERSION | 2 +- .../assets/scripts.js | 21 +- .../wo_classroom_text_highlighter/options.py | 12 +- modules/writing_observer/VERSION | 2 +- .../writing_observer/copy_paste_utils.py | 322 ++++++++++++++++++ .../writing_observer/module.py | 25 ++ .../writing_observer/writing_analysis.py | 48 ++- 10 files changed, 489 insertions(+), 21 deletions(-) create mode 100644 modules/writing_observer/writing_observer/copy_paste_utils.py diff --git a/VERSION b/VERSION index 20ad29970..42ec10d5b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0+2026.02.27T21.25.37.849Z.3207a114.berickson.20260220.dami.portfolio.pr +0.1.0+2026.03.06T16.07.48.997Z.0bfff3a0.berickson.20260303.copy.paste.reducer diff --git a/modules/wo_bulk_essay_analysis/VERSION b/modules/wo_bulk_essay_analysis/VERSION index fff79c93a..42ec10d5b 100644 --- a/modules/wo_bulk_essay_analysis/VERSION +++ b/modules/wo_bulk_essay_analysis/VERSION @@ -1 +1 @@ -0.1.0+2026.02.20T19.02.56.547Z.85e344b1.berickson.20260210.dashboard.updates +0.1.0+2026.03.06T16.07.48.997Z.0bfff3a0.berickson.20260303.copy.paste.reducer diff --git a/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/assets/scripts.js b/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/assets/scripts.js index 8641af6ab..6ed6589c5 100644 --- a/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/assets/scripts.js +++ b/modules/wo_bulk_essay_analysis/wo_bulk_essay_analysis/assets/scripts.js @@ -18,6 +18,62 @@ function fetchSelectedItemsFromOptions (value, options, type) { }, []); } +function createPasteBadge (children, color = 'secondary', className = 'me-1') { + return createDashComponent( + DASH_BOOTSTRAP_COMPONENTS, + 'Badge', + { children, color, className } + ); +} + +function createBinnedPasteEventsBadge (document) { + const lengthBins = document?.length_bins ?? {}; + const smallPastes = lengthBins.short_1_20 ?? 0; + const mediumPastes = lengthBins.medium_21_200 ?? 0; + const largePastes = lengthBins.long_201_plus ?? 0; + const largeColor = largePastes > 0 ? 'warning' : 'secondary'; + const mediumColor = mediumPastes > 0 ? 'warning' : 'seconday'; + + return createDashComponent( + DASH_HTML_COMPONENTS, + 'Div', + { + children: [ + createPasteBadge(`${smallPastes} small (<20)`, 'secondary'), + createPasteBadge(`${mediumPastes} medium (21-200)`,mediumColor), + createPasteBadge(`${largePastes} large (201+)`, largeColor) + ], + className: 'd-inline' + } + ); +} + +function createPasteMetricComponent (document, metricId) { + const pasteCount = document?.pastes_with_length ?? 0; + const totalPasteChars = document?.total_paste_chars ?? 0; + + switch (metricId) { + case 'paste_events': + return createPasteBadge(`${pasteCount} pastes`, pasteCount > 0 ? 'secondary' : 'light', 'me-1'); + case 'paste_bins': + return createBinnedPasteEventsBadge(document); + case 'total_paste_chars': + const totalPastedColor = totalPasteChars > 500 ? 'danger' : totalPasteChars > 100 ? 'warning' : 'secondary'; + return createPasteBadge(`${totalPasteChars} pasted chars`, totalPastedColor, 'me-1'); + case 'paste': + return createPasteMetricComponent(document, 'paste_events'); + default: + return null; + } +} + +window.WOPasteMetricHelpers = { + createPasteBadge, + createBinnedPasteEventsBadge, + createPasteMetricComponent +}; + + function createProcessTags (document, metrics) { const children = metrics.map(metric => { switch (metric.id) { @@ -32,10 +88,22 @@ function createProcessTags (document, metrics) { DASH_BOOTSTRAP_COMPONENTS, 'Badge', { children: document[metric.id], color } ); + case 'paste_events': + case 'paste_bins': + case 'total_paste_chars': + case 'paste': + return window.WOPasteMetricHelpers.createPasteMetricComponent(document, metric.id); + case 'copy': + const copyCount = document?.copy_count ?? 0; + const copyColor = copyCount > 0 ? 'primary' : 'secondary' + return createDashComponent( + DASH_BOOTSTRAP_COMPONENTS, 'Badge', + { children: `${copyCount} copies`, color: copyColor } + ); default: break; } - }); + }).filter(Boolean); return createDashComponent(DASH_HTML_COMPONENTS, 'Div', { children, className: 'sticky-top' }); } @@ -214,7 +282,7 @@ const fileTextExtractors = { docx: extractDOCX }; -const AIAssistantLoadingQueries = ['gpt_bulk', 'time_on_task', 'activity']; +const AIAssistantLoadingQueries = ['gpt_bulk', 'time_on_task', 'activity', 'paste_metrics', 'copy_cut_metrics']; // ── Walkthrough step definitions ────────────────────────────────────── const BULK_WALKTHROUGH_STEPS = [ @@ -633,7 +701,7 @@ window.dash_clientside.bulk_essay_feedback = { const message = { wo: { execution_dag: 'writing_observer', - target_exports: ['gpt_bulk', 'document_list', 'document_sources', 'time_on_task', 'activity'], + target_exports: ['gpt_bulk', 'document_list', 'document_sources', 'time_on_task', 'activity', 'paste_metrics', 'copy_cut_metrics'], kwargs: decoded } }; diff --git a/modules/wo_classroom_text_highlighter/VERSION b/modules/wo_classroom_text_highlighter/VERSION index fff79c93a..42ec10d5b 100644 --- a/modules/wo_classroom_text_highlighter/VERSION +++ b/modules/wo_classroom_text_highlighter/VERSION @@ -1 +1 @@ -0.1.0+2026.02.20T19.02.56.547Z.85e344b1.berickson.20260210.dashboard.updates +0.1.0+2026.03.06T16.07.48.997Z.0bfff3a0.berickson.20260303.copy.paste.reducer diff --git a/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/assets/scripts.js b/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/assets/scripts.js index 7ffae744e..d05e6785b 100644 --- a/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/assets/scripts.js +++ b/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/assets/scripts.js @@ -113,12 +113,25 @@ function createProcessTags (document, metrics) { const color = document[metric.id] === 'active' ? 'success' : 'warning'; return createDashComponent( DASH_BOOTSTRAP_COMPONENTS, 'Badge', - { children: document[metric.id], color } + { children: document[metric.id], color, className: 'me-1' } + ); + case 'paste_events': + case 'paste_bins': + case 'total_paste_chars': + case 'paste': + if (!window.WOPasteMetricHelpers?.createPasteMetricComponent) { return null; } + return window.WOPasteMetricHelpers.createPasteMetricComponent(document, metric.id); + case 'copy': + const copyCount = document?.copy_count ?? 0; + const copyColor = copyCount > 0 ? 'primary' : 'secondary' + return createDashComponent( + DASH_BOOTSTRAP_COMPONENTS, 'Badge', + { children: `${copyCount} copies`, color: copyColor } ); default: break; } - }); + }).filter(Boolean); return createDashComponent(DASH_HTML_COMPONENTS, 'Div', { children, className: 'sticky-top' }); } @@ -140,7 +153,7 @@ function studentHasResponded (student, appliedHash) { return true; } -const ClassroomTextHighlightLoadingQueries = ['docs_with_nlp_annotations', 'time_on_task', 'activity']; +const ClassroomTextHighlightLoadingQueries = ['docs_with_nlp_annotations', 'time_on_task', 'activity', 'paste_metrics', 'copy_cut_metrics']; // ── Walkthrough step definitions ────────────────────────────────────── const WALKTHROUGH_STEPS = [ @@ -369,7 +382,7 @@ window.dash_clientside.wo_classroom_text_highlighter = { const outgoingMessage = { wo_classroom_text_highlighter_query: { execution_dag: 'writing_observer', - target_exports: ['docs_with_nlp_annotations', 'document_sources', 'document_list', 'time_on_task', 'activity'], + target_exports: ['docs_with_nlp_annotations', 'document_sources', 'document_list', 'time_on_task', 'activity', 'paste_metrics', 'copy_cut_metrics'], kwargs: decodedParams } }; diff --git a/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/options.py b/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/options.py index 07a9ab5aa..eefe5a52f 100644 --- a/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/options.py +++ b/modules/wo_classroom_text_highlighter/wo_classroom_text_highlighter/options.py @@ -4,7 +4,11 @@ PROCESS_OPTIONS = [ {'id': 'process_information', 'label': 'Process Information', 'parent': ''}, {'id': 'time_on_task', 'label': 'Time on Task', 'types': ['metric'], 'parent': 'process_information'}, - {'id': 'status', 'label': 'Status', 'types': ['metric'], 'parent': 'process_information'} + {'id': 'status', 'label': 'Status', 'types': ['metric'], 'parent': 'process_information'}, + {'id': 'paste_events', 'label': '# Paste Events', 'types': ['metric'], 'parent': 'process_information'}, + {'id': 'paste_bins', 'label': 'Binned Paste Events', 'types': ['metric'], 'parent': 'process_information'}, + {'id': 'total_paste_chars', 'label': 'Total Characters Pasted', 'types': ['metric'], 'parent': 'process_information'}, + {'id': 'copy', 'label': 'Copy', 'types': ['metric'], 'parent': 'process_information'} ] OPTIONS = PROCESS_OPTIONS + [ {'id': indicator['id'], 'types': ['highlight'], 'label': indicator['name'], 'parent': indicator['category']} @@ -16,7 +20,11 @@ DEFAULT_VALUE = { 'time_on_task': {'metric': {'value': True}}, - 'status': {'metric': {'value': True}} + 'status': {'metric': {'value': True}}, + 'paste_events': {'metric': {'value': True}}, + 'paste_bins': {'metric': {'value': True}}, + 'total_paste_chars': {'metric': {'value': True}}, + 'copy': {'metric': {'value': True}} } # Set of colors to use for highlighting with presets diff --git a/modules/writing_observer/VERSION b/modules/writing_observer/VERSION index 3c8236e9f..42ec10d5b 100644 --- a/modules/writing_observer/VERSION +++ b/modules/writing_observer/VERSION @@ -1 +1 @@ -0.1.0+2026.02.27T13.40.48.021Z.a86fc6ac.berickson.20260220.dami.portfolio.pr +0.1.0+2026.03.06T16.07.48.997Z.0bfff3a0.berickson.20260303.copy.paste.reducer diff --git a/modules/writing_observer/writing_observer/copy_paste_utils.py b/modules/writing_observer/writing_observer/copy_paste_utils.py new file mode 100644 index 000000000..c7f7b11d4 --- /dev/null +++ b/modules/writing_observer/writing_observer/copy_paste_utils.py @@ -0,0 +1,322 @@ +""" +Helpers for extracting copy, cut, and paste signals from writing analytics +events. +""" + +from __future__ import annotations + +import datetime as dt + +PASTE_WAIT_MS = 2500 +MENU_FLAG_MS = 1500 +DEDUP_MS = 750 +BIG_PASTE_THRESHOLD = 200 +MAX_RECENT_PASTE_TEXT_CHARS = 500 +MAX_RECURSION_DEPTH = 50 + + +class Actions: + COPY = frozenset({"copy", "clipboard_copy", "gdocs_copy", "menu_copy", "edit_copy"}) + CUT = frozenset({"cut", "clipboard_cut", "gdocs_cut", "menu_cut", "edit_cut"}) + PASTE = frozenset({"paste", "clipboard_paste", "gdocs_paste", "insert_from_clipboard"}) + MENU_PASTE = frozenset({"menu_paste", "edit_paste", "contextmenu_paste"}) + GDOCS_SAVE = "google_docs_save" + + +def unwrap_event(event): + if isinstance(event, dict) and isinstance(event.get("client"), dict): + return event["client"] + return event if isinstance(event, dict) else {} + + +def _get_event_time(event, client): + """Resolve the timestamp once per event, with fallback.""" + server_time = (event.get("server") or {}).get("time") + if server_time is not None: + return server_time + return client.get("timestamp") or (client.get("metadata") or {}).get("ts") + + +def event_action(client): + action = client.get("action") or client.get("event") or client.get("type") or client.get("event_type") or "" + if action: + return str(action).lower() + keystroke = client.get("keystroke", {}) if isinstance(client.get("keystroke"), dict) else {} + return str(keystroke.get("action") or keystroke.get("type") or "").lower() + + +def keys_info(client): + keystroke = client.get("keystroke", {}) if isinstance(client.get("keystroke"), dict) else {} + key = keystroke.get("key") or client.get("key") or "" + code = keystroke.get("code") or client.get("code") or "" + event_type = keystroke.get("type") or client.get("type") or "" + ctrl = bool(keystroke.get("ctrl") or client.get("ctrl") or keystroke.get("ctrlKey") or client.get("ctrlKey")) + meta = bool(keystroke.get("metaKey") or client.get("metaKey")) + key_code = keystroke.get("keyCode") or client.get("keyCode") or keystroke.get("which") or client.get("which") + return { + "key": str(key).lower() if key else "", + "code": str(code), + "event_type": str(event_type).lower(), + "ctrl_or_meta": bool(ctrl or meta), + "key_code": int(key_code) if isinstance(key_code, (int, float)) else None, + } + + +def _is_key_combo(info, key_char, code_str, key_code_int): + return info["event_type"] == "keydown" and info["ctrl_or_meta"] and ( + info["key"] == key_char or info["code"] == code_str or info["key_code"] == key_code_int + ) + + +def is_copy(client): + action = event_action(client) + if action in Actions.COPY: + return True + return _is_key_combo(keys_info(client), "c", "KeyC", 67) + + +def is_cut(client): + action = event_action(client) + if action in Actions.CUT: + return True + return _is_key_combo(keys_info(client), "x", "KeyX", 88) + + +def is_paste_keyboard(client): + action = event_action(client) + if action in Actions.PASTE: + return True + return _is_key_combo(keys_info(client), "v", "KeyV", 86) + + +def looks_like_menu_paste(client): + action = event_action(client) + if action in Actions.MENU_PASTE: + return True + return action == "contextmenu" + + +def timestamp_ms(event, client=None): + client = client or unwrap_event(event) + value = _get_event_time(event, client) + if isinstance(value, (int, float)): + return int(value * 1000) if value < 10**11 else int(value) + return int(dt.datetime.now(dt.timezone.utc).timestamp() * 1000) + + +def collect_inserted_text(command, output, _depth=0): + if not isinstance(command, dict) or _depth > MAX_RECURSION_DEPTH: + return + + if command.get("ty") == "is": + string_value = command.get("s") + if isinstance(string_value, str) and string_value: + output.append(string_value) + + if isinstance(command.get("nmc"), dict): + collect_inserted_text(command["nmc"], output, _depth + 1) + + for child in command.get("mts") or []: + collect_inserted_text(child, output, _depth + 1) + + +def extract_insert_from_gdocs_save(client): + parts = [] + for bundle in client.get("bundles") or []: + for command in (bundle or {}).get("commands") or []: + collect_inserted_text(command, parts) + return "".join(parts) + + +def paste_length_bin(length): + if length <= 0: + return "none" + if length <= 20: + return "short_1_20" + if length <= 200: + return "medium_21_200" + return "long_201_plus" + + +def append_recent(items, entry, limit=10): + result = list(items or []) + result.append(entry) + return result[-limit:] + + +def clip_recent_paste_text(text, limit=MAX_RECENT_PASTE_TEXT_CHARS): + if text is None: + return None, False + if len(text) <= limit: + return text, False + return text[:limit], True + + +def default_paste_state(): + return { + "paste_count": 0, + "pastes_with_length": 0, + "total_paste_chars": 0, + "max_paste_len": 0, + "last_paste_len": 0, + "big_pastes": 0, + "length_bins": { + "short_1_20": 0, + "medium_21_200": 0, + "long_201_plus": 0, + }, + "recent_pastes": [], + "awaiting_paste_until": 0, + "maybe_menu_paste_until": 0, + "last_paste_signal_ms": 0, + } + + +def default_copy_cut_state(): + return { + "copy_count": 0, + "cut_count": 0, + "last_copy_ts": 0, + "last_cut_ts": 0, + "recent_events": [], + } + + +def update_paste_state(event, state): + state = dict(default_paste_state() if state is None else state) + state["length_bins"] = dict(state.get("length_bins", {})) + state["recent_pastes"] = [dict(p) for p in state.get("recent_pastes", [])] + + client = unwrap_event(event) + ts_ms = timestamp_ms(event, client) + action = event_action(client) + + if is_paste_keyboard(client): + if ts_ms - state.get("last_paste_signal_ms", 0) <= DEDUP_MS: + return False + state["paste_count"] = state.get("paste_count", 0) + 1 + state["last_paste_signal_ms"] = ts_ms + state["awaiting_paste_until"] = ts_ms + PASTE_WAIT_MS + state["recent_pastes"] = append_recent( + state["recent_pastes"], + { + "timestamp_ms": ts_ms, + "length": None, + "source": "keyboard", + "text": None, + "text_truncated": False, + "resolved": False, + }, + ) + return state + + if looks_like_menu_paste(client): + state["maybe_menu_paste_until"] = ts_ms + MENU_FLAG_MS + return state + + if action != Actions.GDOCS_SAVE: + return False + + inserted_text = extract_insert_from_gdocs_save(client) + if not inserted_text: + return False + + paste_length = len(inserted_text) + awaiting_paste_until = state.get("awaiting_paste_until", 0) + maybe_menu_paste_until = state.get("maybe_menu_paste_until", 0) + counted_from_save = False + + if ts_ms <= maybe_menu_paste_until and ts_ms > awaiting_paste_until: + if ts_ms - state.get("last_paste_signal_ms", 0) <= DEDUP_MS: + return False + state["paste_count"] = state.get("paste_count", 0) + 1 + state["last_paste_signal_ms"] = ts_ms + counted_from_save = True + + if ts_ms > awaiting_paste_until and not counted_from_save: + return False + + state["pastes_with_length"] = state.get("pastes_with_length", 0) + 1 + state["total_paste_chars"] = state.get("total_paste_chars", 0) + paste_length + state["max_paste_len"] = max(state.get("max_paste_len", 0), paste_length) + state["last_paste_len"] = paste_length + if paste_length >= BIG_PASTE_THRESHOLD: + state["big_pastes"] = state.get("big_pastes", 0) + 1 + + bin_name = paste_length_bin(paste_length) + if bin_name != "none": + state["length_bins"][bin_name] = state["length_bins"].get(bin_name, 0) + 1 + + clipped_text, was_truncated = clip_recent_paste_text(inserted_text) + + # --- Coalesce: find the pending keyboard entry and enrich it --- + if not counted_from_save: + resolved = False + for entry in reversed(state["recent_pastes"]): + if not entry.get("resolved") and entry.get("source") == "keyboard": + entry["length"] = paste_length + entry["text"] = clipped_text + entry["text_truncated"] = was_truncated + entry["resolved"] = True + resolved = True + break + if not resolved: + # Defensive fallback: no pending entry found, append standalone + state["recent_pastes"] = append_recent( + state["recent_pastes"], + { + "timestamp_ms": ts_ms, + "length": paste_length, + "source": "keyboard", + "text": clipped_text, + "text_truncated": was_truncated, + "resolved": True, + }, + ) + else: + state["recent_pastes"] = append_recent( + state["recent_pastes"], + { + "timestamp_ms": ts_ms, + "length": paste_length, + "source": "menu", + "text": clipped_text, + "text_truncated": was_truncated, + "resolved": True, + }, + ) + + state["awaiting_paste_until"] = 0 + state["maybe_menu_paste_until"] = 0 + return state + + +def update_copy_cut_state(event, state): + state = dict(default_copy_cut_state() if state is None else state) + state["recent_events"] = list(state.get("recent_events", [])) + + client = unwrap_event(event) + ts_ms = timestamp_ms(event, client) + event_type = None + + if is_copy(client): + if ts_ms - state.get("last_copy_ts", 0) <= DEDUP_MS: + return False + state["copy_count"] = state.get("copy_count", 0) + 1 + state["last_copy_ts"] = ts_ms + event_type = "copy" + elif is_cut(client): + if ts_ms - state.get("last_cut_ts", 0) <= DEDUP_MS: + return False + state["cut_count"] = state.get("cut_count", 0) + 1 + state["last_cut_ts"] = ts_ms + event_type = "cut" + + if not event_type: + return False + + state["recent_events"] = append_recent( + state.get("recent_events"), + {"timestamp_ms": ts_ms, "event_type": event_type}, + ) + return state diff --git a/modules/writing_observer/writing_observer/module.py b/modules/writing_observer/writing_observer/module.py index 568bb1fd6..6da99bce0 100644 --- a/modules/writing_observer/writing_observer/module.py +++ b/modules/writing_observer/writing_observer/module.py @@ -24,6 +24,7 @@ import writing_observer.languagetool import writing_observer.tag_docs import writing_observer.document_timestamps +import writing_observer.copy_paste_utils from writing_observer.nlp_indicators import INDICATOR_JSONS @@ -163,6 +164,8 @@ async def roster_with_provenance(roster, course_id): 'update_docs': update_via_google(runtime=q.parameter("runtime"), doc_ids=q.variable('doc_sources')), "docs_combined": q.join(LEFT=q.variable("docs"), RIGHT=q.variable("roster"), LEFT_ON='provenance.provenance.STUDENT.value.user_id', RIGHT_ON='user_id'), + "paste_metrics": q.select(q.keys('writing_observer.lo_paste_reducer', STUDENTS=q.variable("roster"), STUDENTS_path='user_id', RESOURCES=q.variable("doc_sources"), RESOURCES_path='doc_id'), fields={'pastes_with_length': 'pastes_with_length', 'length_bins': 'length_bins', 'total_paste_chars': 'total_paste_chars'}), + "copy_cut_metrics": q.select(q.keys('writing_observer.lo_copy_cut_reducer', STUDENTS=q.variable("roster"), STUDENTS_path='user_id', RESOURCES=q.variable("doc_sources"), RESOURCES_path='doc_id'), fields={'copy_count': 'copy_count'}), 'nlp': process_texts(writing_data=q.variable('docs'), options=q.parameter('nlp_options', required=False, default=[])), 'nlp_sep_proc': q.select(q.keys('writing_observer.nlp_components', STUDENTS=q.variable('roster'), STUDENTS_path='user_id', RESOURCES=q.variable("doc_ids"), RESOURCES_path='doc_id'), fields='All'), 'nlp_combined': q.join(LEFT=q.variable(nlp_source), LEFT_ON='provenance.provenance.STUDENT.value.user_id', RIGHT=q.variable('roster'), RIGHT_ON='user_id'), @@ -232,6 +235,16 @@ async def roster_with_provenance(roster, course_id): "parameters": ["course_id"], "output": "" }, + "paste_metrics": { + "returns": "paste_metrics", + "parameters": ["course_id"], + "output": "" + }, + "copy_cut_metrics": { + "returns": "copy_cut_metrics", + "parameters": ["course_id"], + "output": "" + }, "roster": { "returns": "roster_with_provenance", "parameters": ["course_id"], @@ -353,6 +366,18 @@ async def roster_with_provenance(roster, course_id): # Incoming event APIs REDUCERS = [ + { + 'context': "org.mitros.writing_analytics", + 'scope': writing_observer.writing_analysis.gdoc_scope, + 'function': writing_observer.writing_analysis.lo_paste_reducer, + 'default': writing_observer.copy_paste_utils.default_paste_state() + }, + { + 'context': "org.mitros.writing_analytics", + 'scope': writing_observer.writing_analysis.gdoc_scope, + 'function': writing_observer.writing_analysis.lo_copy_cut_reducer, + 'default': writing_observer.copy_paste_utils.default_copy_cut_state() + }, { 'context': "org.mitros.writing_analytics", 'scope': writing_observer.writing_analysis.gdoc_scope, diff --git a/modules/writing_observer/writing_observer/writing_analysis.py b/modules/writing_observer/writing_observer/writing_analysis.py index 85dc87425..e5cde008a 100644 --- a/modules/writing_observer/writing_observer/writing_analysis.py +++ b/modules/writing_observer/writing_observer/writing_analysis.py @@ -11,6 +11,7 @@ import re import time +import writing_observer.copy_paste_utils import writing_observer.reconstruct_doc import learning_observer.adapters @@ -35,6 +36,21 @@ # (e.g. all the numbers would go up/down 20%, but behavior was # substantatively identical). +pmss.register_field( + name='time_on_task_threshold', + type=pmss.pmsstypes.TYPES.integer, + description='Maximum time to pass before marking a session as over. '\ + 'Should be 60-300 seconds in production, but 5 seconds is nice for '\ + 'debugging in a local deployment.', + default=60 +) +pmss.register_field( + name='binned_time_on_task_bin_size', + type=pmss.pmsstypes.TYPES.integer, + description='How large (in seconds) to make timestamp bins when '\ + 'recording binned time on task.', + default=600 +) pmss.register_field( name='activity_threshold', type=pmss.pmsstypes.TYPES.integer, @@ -152,6 +168,28 @@ async def event_count(event, internal_state): return state, state +@kvs_pipeline( + scope=gdoc_scope, + null_state=writing_observer.copy_paste_utils.default_paste_state() +) +async def lo_paste_reducer(event, internal_state): + state = writing_observer.copy_paste_utils.update_paste_state(event, internal_state) + if not state: + return False, False + return state, state + + +@kvs_pipeline( + scope=gdoc_scope, + null_state=writing_observer.copy_paste_utils.default_copy_cut_state() +) +async def lo_copy_cut_reducer(event, internal_state): + state = writing_observer.copy_paste_utils.update_copy_cut_state(event, internal_state) + if not state: + return False, False + return state, state + + @kvs_pipeline(scope=student_scope, null_state={}) async def student_profile(event, internal_state): '''Store profile information for a given id @@ -449,7 +487,7 @@ async def last_document(event, internal_state): # Document URls are as follows: # https://docs.google.com/document/d/18JAnmxzVD_lGSfa8t6Se66KLZm30YFrC_4M-D2zdYG4/edit -DOC_URL_re = re.compile("^https://docs.google.com/document/d/(?P[^/\s]+)/(?P[a-zA-Z]+)") # noqa: W605 \s is invalid escape +DOC_URL_re = re.compile("^https://docs.google.com/document/d/(?P[^/\\s]+)/(?P[a-zA-Z]+)") def get_doc_id(event): @@ -481,17 +519,10 @@ def get_doc_id(event): if doc_id: return doc_id - # Failing that pull out the url event. - # Object_value = event.get('client', {}).get('object', None) url = client.get('object', {}).get('url') if not url: return None - # Now test if the object has a URL and if that corresponds - # to a doc edit/review URL as opposed to their main page. - # if so return the id from it. In the off chance the id - # is still not present or is none then this will return - # none. url_match = DOC_URL_re.match(url) if not url_match: return None @@ -499,6 +530,7 @@ def get_doc_id(event): doc_id = client.get('object', {}).get('id') return doc_id + def document_link_to_doc_id(event): ''' Convert a document link to include a doc_id