From 99e8aa4a7ccd00d78fb17339af67c9557cd7a6c5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 29 Mar 2026 22:36:46 +0000 Subject: [PATCH 001/131] build(deps-dev): bump serialize-javascript from 7.0.4 to 7.0.5 Bumps [serialize-javascript](https://github.com/yahoo/serialize-javascript) from 7.0.4 to 7.0.5. - [Release notes](https://github.com/yahoo/serialize-javascript/releases) - [Commits](https://github.com/yahoo/serialize-javascript/compare/v7.0.4...v7.0.5) --- updated-dependencies: - dependency-name: serialize-javascript dependency-version: 7.0.5 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 185e4b3f9c..856f339d60 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7342,11 +7342,10 @@ } }, "node_modules/serialize-javascript": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-7.0.4.tgz", - "integrity": "sha512-DuGdB+Po43Q5Jxwpzt1lhyFSYKryqoNjQSA9M92tyw0lyHIOur+XCalOUe0KTJpyqzT8+fQ5A0Jf7vCx/NKmIg==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-7.0.5.tgz", + "integrity": "sha512-F4LcB0UqUl1zErq+1nYEEzSHJnIwb3AF2XWB94b+afhrekOUijwooAYqFyRbjYkm2PAKBabx6oYv/xDxNi8IBw==", "dev": true, - "license": "BSD-3-Clause", "engines": { "node": ">=20.0.0" } @@ -13344,9 +13343,9 @@ "devOptional": true }, "serialize-javascript": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-7.0.4.tgz", - "integrity": "sha512-DuGdB+Po43Q5Jxwpzt1lhyFSYKryqoNjQSA9M92tyw0lyHIOur+XCalOUe0KTJpyqzT8+fQ5A0Jf7vCx/NKmIg==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-7.0.5.tgz", + "integrity": "sha512-F4LcB0UqUl1zErq+1nYEEzSHJnIwb3AF2XWB94b+afhrekOUijwooAYqFyRbjYkm2PAKBabx6oYv/xDxNi8IBw==", "dev": true }, "set-function-length": { From f7da3348e2be139d64ccb1ad0a53c007f16798e9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 10 Apr 2026 03:25:38 +0000 Subject: [PATCH 002/131] build(deps): bump lodash from 4.17.23 to 4.18.1 Bumps [lodash](https://github.com/lodash/lodash) from 4.17.23 to 4.18.1. - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](https://github.com/lodash/lodash/compare/4.17.23...4.18.1) --- updated-dependencies: - dependency-name: lodash dependency-version: 4.18.1 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 185e4b3f9c..4a848f9db4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "html-to-text": "^9.0.5", "jquery": "^3.7.1", "js-cookie": "^3.0.5", - "lodash": "^4.17.23", + "lodash": "^4.18.1", "popper.js": "^1.16.1", "prop-types": "^15.7.2", "react": "^18.3.1", @@ -5913,9 +5913,9 @@ } }, "node_modules/lodash": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", - "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==" + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==" }, "node_modules/lodash.debounce": { "version": "4.0.8", @@ -12366,9 +12366,9 @@ } }, "lodash": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", - "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==" + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==" }, "lodash.debounce": { "version": "4.0.8", diff --git a/package.json b/package.json index b156cfc1f2..073d1dcd37 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "html-to-text": "^9.0.5", "jquery": "^3.7.1", "js-cookie": "^3.0.5", - "lodash": "^4.17.23", + "lodash": "^4.18.1", "popper.js": "^1.16.1", "prop-types": "^15.7.2", "react": "^18.3.1", From cad701ea40c032a591c368e32060f52cfe6b5e6d Mon Sep 17 00:00:00 2001 From: emmatekulova Date: Mon, 13 Apr 2026 15:06:22 +0200 Subject: [PATCH 003/131] Handle empty sections in AnswerTree --- rdmo/projects/answers.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rdmo/projects/answers.py b/rdmo/projects/answers.py index a85a0ce7c6..50c5c873be 100644 --- a/rdmo/projects/answers.py +++ b/rdmo/projects/answers.py @@ -79,7 +79,8 @@ def compute_element_node(self, element, parent_set=None): # find the first element if element_type in self.verbose: - element_node['first'] = element_node['elements'][0]['id'] + if element_node['elements']: + element_node['first'] = element_node['elements'][0]['id'] # aggregate count and total from the child elements element_node['count'] = sum(child_node['count'] for child_node in element_node['elements']) From 2c01e9ff775d6d141e7b7037a26ef2da91eb1c14 Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Thu, 16 Apr 2026 14:18:22 +0200 Subject: [PATCH 004/131] Use short_title in compute_navigation --- rdmo/projects/answers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rdmo/projects/answers.py b/rdmo/projects/answers.py index a85a0ce7c6..2eb7bc3a68 100644 --- a/rdmo/projects/answers.py +++ b/rdmo/projects/answers.py @@ -44,12 +44,12 @@ def compute_element_node(self, element, parent_set=None): if element_type in ['catalog', 'page', 'questionset']: element_node.update({ - 'title': markdown2html(element.title), + 'title': markdown2html(element.short_title) or markdown2html(element.title), 'help': markdown2html(element.help) }) elif element_type == 'section': element_node.update({ - 'title': markdown2html(element.title) + 'title': markdown2html(element.short_title) or markdown2html(element.title) }) elif element_type == 'question': element_node.update({ From b2e27b024f23a7d49ca94e7028f34ebc2ac7f6de Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Sat, 18 Apr 2026 14:01:59 +0200 Subject: [PATCH 005/131] Add a POST alternative for ProjectViewSet.resolve for bulk condition checking --- rdmo/projects/tests/test_viewset_project.py | 23 +++++++ rdmo/projects/viewsets.py | 71 +++++++++++++++++++++ 2 files changed, 94 insertions(+) diff --git a/rdmo/projects/tests/test_viewset_project.py b/rdmo/projects/tests/test_viewset_project.py index a4ff426a3e..19a9f42d0b 100644 --- a/rdmo/projects/tests/test_viewset_project.py +++ b/rdmo/projects/tests/test_viewset_project.py @@ -678,6 +678,29 @@ def test_resolve(db, client, username, password, project_id, condition_id): assert response.status_code == 401 +@pytest.mark.parametrize('username,password', users) +@pytest.mark.parametrize('project_id', projects) +@pytest.mark.parametrize('condition_id', conditions) +def test_resolve_post(db, client, username, password, project_id, condition_id): + client.login(username=username, password=password) + + url = reverse(urlnames['resolve'], args=[project_id]) + data = [{ + 'element_type': 'conditions', + 'element_id': condition_id + }] + response = client.post(url, data, content_type='application/json') + + if project_id in view_project_permission_map.get(username, []): + assert response.status_code == 200 + assert isinstance(response.json(), list) + else: + if password: + assert response.status_code == 404 + else: + assert response.status_code == 401 + + @pytest.mark.parametrize('username,password', users) def test_options(db, client, username, password): client.login(username=username, password=password) diff --git a/rdmo/projects/viewsets.py b/rdmo/projects/viewsets.py index d16ed7b90c..0ef81983ed 100644 --- a/rdmo/projects/viewsets.py +++ b/rdmo/projects/viewsets.py @@ -250,6 +250,77 @@ def resolve(self, request, pk=None): return Response({'result': False}) + @resolve.mapping.post + def resolve_post(self, request, pk=None): + snapshot_id = request.GET.get('snapshot') + + if not isinstance(request.data, list) or not all(isinstance(x, dict) for x in request.data): + raise ValidationError('Expected a list of objects.') + + values = self.get_object().values.filter(snapshot_id=snapshot_id).select_related('attribute', 'option') + + response_data = [] + for request_params in request.data: + # set the result to false by default + params = { + **request_params, + 'result': False + } + + set_prefix = params.get('set_prefix') + set_index = params.get('set_index') + + element_type = params.get('element_type') + element_id = params.get('element_id') + + if element_type == 'pages': + try: + page = Page.objects.get(id=element_id) + conditions = page.conditions.select_related('source', 'target_option') + if check_conditions(conditions, values, set_prefix, set_index): + params.update({'result': True}) + except Page.DoesNotExist: + pass + + elif element_type == 'questionsets': + try: + questionset = QuestionSet.objects.get(id=element_id) + conditions = questionset.conditions.select_related('source', 'target_option') + if check_conditions(conditions, values, set_prefix, set_index): + params.update({'result': True}) + except QuestionSet.DoesNotExist: + pass + + elif element_type == 'questions': + try: + question = Question.objects.get(id=element_id) + conditions = question.conditions.select_related('source', 'target_option') + if check_conditions(conditions, values, set_prefix, set_index): + params.update({'result': True}) + except Question.DoesNotExist: + pass + + elif element_type == 'optionsets': + try: + optionset = OptionSet.objects.get(id=element_id) + conditions = optionset.conditions.select_related('source', 'target_option') + if check_conditions(conditions, values, set_prefix, set_index): + params.update({'result': True}) + except OptionSet.DoesNotExist: + pass + + elif element_type == 'conditions': + try: + condition = Condition.objects.select_related('source', 'target_option').get(id=element_id) + if check_conditions([condition], values, set_prefix, set_index): + params.update({'result': True}) + except Condition.DoesNotExist: + pass + + response_data.append(params) + + return Response(response_data) + @action(detail=True, permission_classes=(HasModelPermission | HasProjectPermission, )) def options(self, request, pk=None): project = self.get_object() From 18ecaaa390204c4f07b284c678db49eef9aa07f0 Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Sat, 18 Apr 2026 14:07:21 +0200 Subject: [PATCH 006/131] Use bulk condition checking in interview --- .../js/interview/actions/actionTypes.js | 6 +- .../js/interview/actions/interviewActions.js | 77 +++++++++++-------- .../assets/js/interview/api/ProjectApi.js | 13 +--- .../js/interview/reducers/interviewReducer.js | 34 +++++--- 4 files changed, 73 insertions(+), 57 deletions(-) diff --git a/rdmo/projects/assets/js/interview/actions/actionTypes.js b/rdmo/projects/assets/js/interview/actions/actionTypes.js index 4972df8501..9f8a69c67a 100644 --- a/rdmo/projects/assets/js/interview/actions/actionTypes.js +++ b/rdmo/projects/assets/js/interview/actions/actionTypes.js @@ -28,9 +28,9 @@ export const FETCH_VALUES_INIT = 'FETCH_VALUES_INIT' export const FETCH_VALUES_SUCCESS = 'FETCH_VALUES_SUCCESS' export const FETCH_VALUES_ERROR = 'FETCH_VALUES_ERROR' -export const RESOLVE_CONDITION_INIT = 'RESOLVE_CONDITION_INIT' -export const RESOLVE_CONDITION_SUCCESS = 'RESOLVE_CONDITION_SUCCESS' -export const RESOLVE_CONDITION_ERROR = 'RESOLVE_CONDITION_ERROR' +export const RESOLVE_CONDITIONS_INIT = 'RESOLVE_CONDITIONS_INIT' +export const RESOLVE_CONDITIONS_SUCCESS = 'RESOLVE_CONDITIONS_SUCCESS' +export const RESOLVE_CONDITIONS_ERROR = 'RESOLVE_CONDITIONS_ERROR' export const CREATE_VALUE = 'CREATE_VALUE' export const UPDATE_VALUE = 'UPDATE_VALUE' diff --git a/rdmo/projects/assets/js/interview/actions/interviewActions.js b/rdmo/projects/assets/js/interview/actions/interviewActions.js index a066c88d2d..10ef610cc9 100644 --- a/rdmo/projects/assets/js/interview/actions/interviewActions.js +++ b/rdmo/projects/assets/js/interview/actions/interviewActions.js @@ -4,8 +4,6 @@ import PageApi from '../api/PageApi' import ProjectApi from '../api/ProjectApi' import ValueApi from '../api/ValueApi' -import { elementTypes } from 'rdmo/management/assets/js/constants/elements' - import { updateProgress } from './projectActions' import { updateLocation } from '../utils/location' @@ -33,9 +31,9 @@ import { FETCH_VALUES_INIT, FETCH_VALUES_SUCCESS, FETCH_VALUES_ERROR, - RESOLVE_CONDITION_INIT, - RESOLVE_CONDITION_SUCCESS, - RESOLVE_CONDITION_ERROR, + RESOLVE_CONDITIONS_INIT, + RESOLVE_CONDITIONS_SUCCESS, + RESOLVE_CONDITIONS_ERROR, CREATE_VALUE, UPDATE_VALUE, STORE_VALUE_INIT, @@ -221,51 +219,66 @@ export function fetchValuesError(error) { } export function resolveConditions(page, sets) { + const pendingId = 'resolveConditions' + return (dispatch) => { - // loop over set to evaluate conditions - sets.forEach((set) => { - page.questionsets.filter((questionset) => questionset.has_conditions) - .forEach((questionset) => dispatch(resolveCondition(questionset, set))) + dispatch(addToPending(pendingId)) + dispatch(resolveConditionsInit()) - page.questions.filter((question) => question.has_conditions) - .forEach((question) => dispatch(resolveCondition(question, set))) + // loop over sets to gather payload + const payload = [] - page.optionsets.filter((optionset) => optionset.has_conditions) - .forEach((optionset) => dispatch(resolveCondition(optionset, set))) + sets.forEach((set) => { + page.questionsets + .filter((questionset) => questionset.has_conditions) + .forEach((questionset) => payload.push({ + element_type: 'questionsets', + element_id: questionset.id, + set_prefix: set.set_prefix, + set_index: set.set_index, + })) + + page.questions + .filter((question) => question.has_conditions) + .forEach((question) => payload.push({ + element_type: 'questions', + element_id: question.id, + set_prefix: set.set_prefix, + set_index: set.set_index, + })) + + page.optionsets + .filter((optionset) => optionset.has_conditions) + .forEach((optionset) => payload.push({ + element_type: 'optionsets', + element_id: optionset.id, + set_prefix: set.set_prefix, + set_index: set.set_index, + })) }) - } -} - -export function resolveCondition(element, set) { - const pendingId = `resolveCondition/${element.model}/${element.id}/${set.set_prefix}/${set.set_index}` - - return (dispatch) => { - dispatch(addToPending(pendingId)) - dispatch(resolveConditionInit()) - return ProjectApi.resolveCondition(projectId, set, element) + return ProjectApi.resolveConditions(projectId, payload) .then((response) => { - const elementType = elementTypes[element.model] dispatch(removeFromPending(pendingId)) - dispatch(resolveConditionSuccess(set, elementType, element.id, response.result)) + dispatch(resolveConditionsSuccess(response)) }) .catch((error) => { dispatch(removeFromPending(pendingId)) - dispatch(resolveConditionError(error)) + dispatch(resolveConditionsError(error)) }) } } -export function resolveConditionInit() { - return {type: RESOLVE_CONDITION_INIT} +export function resolveConditionsInit() { + return {type: RESOLVE_CONDITIONS_INIT} } -export function resolveConditionSuccess(set, elementType, elementId, result) { - return {type: RESOLVE_CONDITION_SUCCESS, set, elementType, elementId, result} +export function resolveConditionsSuccess(results) { + return {type: RESOLVE_CONDITIONS_SUCCESS, results} } -export function resolveConditionError(error) { - return {type: RESOLVE_CONDITION_ERROR, error} +export function resolveConditionsError(error) { + return {type: RESOLVE_CONDITIONS_ERROR, error} } export function storeValue(value) { diff --git a/rdmo/projects/assets/js/interview/api/ProjectApi.js b/rdmo/projects/assets/js/interview/api/ProjectApi.js index bdd9efb77f..9404cbffc0 100644 --- a/rdmo/projects/assets/js/interview/api/ProjectApi.js +++ b/rdmo/projects/assets/js/interview/api/ProjectApi.js @@ -1,4 +1,4 @@ -import { isNil, last } from 'lodash' +import { isNil } from 'lodash' import { encodeParams } from 'rdmo/core/assets/js/utils/api' @@ -35,15 +35,8 @@ class ProjectsApi extends BaseApi { return this.get(`/api/v1/projects/projects/${projectId}/options/?${encodeParams(params)}`) } - static resolveCondition(projectId, set, element) { - const model = last(element.model.split('.')) - const params = { - set_prefix: set.set_prefix, - set_index: set.set_index, - [model]: element.id - } - - return this.get(`/api/v1/projects/projects/${projectId}/resolve/?${encodeParams(params)}`) + static resolveConditions(projectId, payload) { + return this.post(`/api/v1/projects/projects/${projectId}/resolve/`, payload) } } diff --git a/rdmo/projects/assets/js/interview/reducers/interviewReducer.js b/rdmo/projects/assets/js/interview/reducers/interviewReducer.js index 5d7d15c3e2..d0f72ceb9f 100644 --- a/rdmo/projects/assets/js/interview/reducers/interviewReducer.js +++ b/rdmo/projects/assets/js/interview/reducers/interviewReducer.js @@ -11,9 +11,9 @@ import { FETCH_VALUES_INIT, FETCH_VALUES_SUCCESS, FETCH_VALUES_ERROR, - RESOLVE_CONDITION_INIT, - RESOLVE_CONDITION_SUCCESS, - RESOLVE_CONDITION_ERROR, + RESOLVE_CONDITIONS_INIT, + RESOLVE_CONDITIONS_SUCCESS, + RESOLVE_CONDITIONS_ERROR, CREATE_VALUE, UPDATE_VALUE, STORE_VALUE_INIT, @@ -51,14 +51,24 @@ export default function interviewReducer(state = initialState, action) { return { ...state, values: action.values, sets: action.sets } case FETCH_OPTIONS_SUCCESS: return { ...state, page: action.page } - case RESOLVE_CONDITION_SUCCESS: + case RESOLVE_CONDITIONS_SUCCESS: return { ...state, sets: state.sets.map( - (set) => ( - (set.set_prefix == action.set.set_prefix) && - (set.set_index == action.set.set_index) - ) ? { - ...set, [action.elementType]: {...set[action.elementType], [action.elementId]: action.result} - } : set + (set) => { + // filter all results for the current set + const results = action.results.filter((result) => ( + result.set_prefix === set.set_prefix && + result.set_index === set.set_index + )) + + // apply the results + return results.reduce((acc, result) => ({ + ...acc, + [result.element_type]: { + ...acc[result.element_type], + [result.element_id]: result.result + }, + }), set) + } )} case CREATE_VALUE: return { ...state, values: [...state.values, action.value] } @@ -98,7 +108,7 @@ export default function interviewReducer(state = initialState, action) { case FETCH_NAVIGATION_INIT: case FETCH_OPTIONS_INIT: case FETCH_VALUES_INIT: - case RESOLVE_CONDITION_INIT: + case RESOLVE_CONDITIONS_INIT: return { ...state, errors: [] } case STORE_VALUE_INIT: return { @@ -121,7 +131,7 @@ export default function interviewReducer(state = initialState, action) { case FETCH_NAVIGATION_ERROR: case FETCH_OPTIONS_ERROR: case FETCH_VALUES_ERROR: - case RESOLVE_CONDITION_ERROR: + case RESOLVE_CONDITIONS_ERROR: return { ...state, errors: [...state.errors, { actionType: action.type, ...action.error }] } case STORE_VALUE_ERROR: if (action.valueIndex > -1) { From 011356a3e2d8289c06037127c42269d2b36a8660 Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Sat, 18 Apr 2026 20:44:39 +0200 Subject: [PATCH 007/131] Optimize database access and validation --- rdmo/projects/serializers/v1/__init__.py | 7 + rdmo/projects/tests/test_viewset_project.py | 2 + rdmo/projects/viewsets.py | 134 ++++++++++---------- 3 files changed, 76 insertions(+), 67 deletions(-) diff --git a/rdmo/projects/serializers/v1/__init__.py b/rdmo/projects/serializers/v1/__init__.py index 790fc18994..4e805048e6 100644 --- a/rdmo/projects/serializers/v1/__init__.py +++ b/rdmo/projects/serializers/v1/__init__.py @@ -117,6 +117,13 @@ class Meta: read_only_fields = ProjectSerializer.Meta.read_only_fields +class ProjectResolveSerializer(serializers.Serializer): + set_prefix = serializers.CharField(allow_blank=True) + set_index = serializers.IntegerField() + element_type = serializers.ChoiceField(choices=['pages', 'questionsets', 'questions', 'optionsets', 'conditions']) + element_id = serializers.IntegerField() + + class ProjectVisibilitySerializer(serializers.ModelSerializer): class Meta: diff --git a/rdmo/projects/tests/test_viewset_project.py b/rdmo/projects/tests/test_viewset_project.py index 19a9f42d0b..508e072cba 100644 --- a/rdmo/projects/tests/test_viewset_project.py +++ b/rdmo/projects/tests/test_viewset_project.py @@ -686,6 +686,8 @@ def test_resolve_post(db, client, username, password, project_id, condition_id): url = reverse(urlnames['resolve'], args=[project_id]) data = [{ + 'set_prefix': '', + 'set_index': 0, 'element_type': 'conditions', 'element_id': condition_id }] diff --git a/rdmo/projects/viewsets.py b/rdmo/projects/viewsets.py index 0ef81983ed..762b40afd7 100644 --- a/rdmo/projects/viewsets.py +++ b/rdmo/projects/viewsets.py @@ -1,3 +1,5 @@ +from collections import defaultdict + from django.conf import settings from django.contrib.sites.shortcuts import get_current_site from django.core.exceptions import ObjectDoesNotExist @@ -64,6 +66,7 @@ ProjectIssueSerializer, ProjectMembershipSerializer, ProjectMembershipUpdateSerializer, + ProjectResolveSerializer, ProjectSerializer, ProjectSnapshotSerializer, ProjectValueSerializer, @@ -252,74 +255,71 @@ def resolve(self, request, pk=None): @resolve.mapping.post def resolve_post(self, request, pk=None): - snapshot_id = request.GET.get('snapshot') - - if not isinstance(request.data, list) or not all(isinstance(x, dict) for x in request.data): - raise ValidationError('Expected a list of objects.') - - values = self.get_object().values.filter(snapshot_id=snapshot_id).select_related('attribute', 'option') + project = self.get_object() - response_data = [] - for request_params in request.data: - # set the result to false by default - params = { - **request_params, - 'result': False - } - - set_prefix = params.get('set_prefix') - set_index = params.get('set_index') - - element_type = params.get('element_type') - element_id = params.get('element_id') - - if element_type == 'pages': - try: - page = Page.objects.get(id=element_id) - conditions = page.conditions.select_related('source', 'target_option') - if check_conditions(conditions, values, set_prefix, set_index): - params.update({'result': True}) - except Page.DoesNotExist: - pass - - elif element_type == 'questionsets': - try: - questionset = QuestionSet.objects.get(id=element_id) - conditions = questionset.conditions.select_related('source', 'target_option') - if check_conditions(conditions, values, set_prefix, set_index): - params.update({'result': True}) - except QuestionSet.DoesNotExist: - pass - - elif element_type == 'questions': - try: - question = Question.objects.get(id=element_id) - conditions = question.conditions.select_related('source', 'target_option') - if check_conditions(conditions, values, set_prefix, set_index): - params.update({'result': True}) - except Question.DoesNotExist: - pass - - elif element_type == 'optionsets': - try: - optionset = OptionSet.objects.get(id=element_id) - conditions = optionset.conditions.select_related('source', 'target_option') - if check_conditions(conditions, values, set_prefix, set_index): - params.update({'result': True}) - except OptionSet.DoesNotExist: - pass - - elif element_type == 'conditions': - try: - condition = Condition.objects.select_related('source', 'target_option').get(id=element_id) - if check_conditions([condition], values, set_prefix, set_index): - params.update({'result': True}) - except Condition.DoesNotExist: - pass - - response_data.append(params) - - return Response(response_data) + serializer = ProjectResolveSerializer(data=request.data, many=True) + serializer.is_valid(raise_exception=True) + validated_data = serializer.validated_data + + # gather element_ids + element_ids = defaultdict(set) + for params in validated_data: + element_ids[params['element_type']].add(params['element_id']) + + elements = defaultdict(lambda: defaultdict(set)) + + # gather conditions for pages + if 'pages' in element_ids: + queryset = Page.conditions.through.objects.filter(page_id__in=element_ids['pages']) + for page_id, condition_id in queryset.values_list('page_id', 'condition_id'): + elements['pages'][page_id].add(condition_id) + + # gather conditions for questionsets + if 'questionsets' in element_ids: + queryset = QuestionSet.conditions.through.objects.filter(questionset_id__in=element_ids['questionsets']) + for questionset_id, condition_id in queryset.values_list('questionset_id', 'condition_id'): + elements['questionsets'][questionset_id].add(condition_id) + + # gather conditions for questions + if 'questions' in element_ids: + queryset = Question.conditions.through.objects.filter(question_id__in=element_ids['questions']) + for question_id, condition_id in queryset.values_list('question_id', 'condition_id'): + elements['questions'][question_id].add(condition_id) + + # gather conditions for optionsets + if 'optionsets' in element_ids: + queryset = OptionSet.conditions.through.objects.filter(optionset_id__in=element_ids['optionsets']) + for optionset_id, condition_id in queryset.values_list('optionset_id', 'condition_id'): + elements['optionsets'][optionset_id].add(condition_id) + + # gather conditions + if 'conditions' in element_ids: + # construct a similar structure for conditions + for condition_id in element_ids['conditions']: + elements['conditions'][condition_id] = {condition_id} + + # query conditions + condition_ids = set().union(*( + conditions_set + for element_dict in elements.values() + for conditions_set in element_dict.values() + )) + conditions = Condition.objects.select_related('source', 'target_option').in_bulk(condition_ids) + + # get all values of the project + values = project.values.filter(snapshot=None).select_related('attribute', 'option') + + # second pass: resolve conditions + for params in validated_data: + set_prefix = params['set_prefix'] + set_index = params['set_index'] + element_type = params['element_type'] + element_id = params['element_id'] + + element_conditions = [conditions[condition_id] for condition_id in elements[element_type][element_id]] + params['result'] = check_conditions(element_conditions, values, set_prefix, set_index) + + return Response(validated_data) @action(detail=True, permission_classes=(HasModelPermission | HasProjectPermission, )) def options(self, request, pk=None): From a2c2f263047a0c5055c6618931710990563f3db2 Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Sun, 19 Apr 2026 11:37:59 +0200 Subject: [PATCH 008/131] Add test_viewset_project_resolve.py --- .../tests/test_viewset_project_resolve.py | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 rdmo/projects/tests/test_viewset_project_resolve.py diff --git a/rdmo/projects/tests/test_viewset_project_resolve.py b/rdmo/projects/tests/test_viewset_project_resolve.py new file mode 100644 index 0000000000..cd5b08fa0a --- /dev/null +++ b/rdmo/projects/tests/test_viewset_project_resolve.py @@ -0,0 +1,99 @@ +import pytest + +from django.urls import reverse + +from ..models import Value + +urlnames = { + 'resolve': 'v1-projects:project-resolve', +} + +project_id = 1 + +@pytest.mark.parametrize('results', [ + [ + # from: http://example.com/terms/questions/catalog/conditions/set + {'set_prefix': '', 'set_index': 0, 'element_type': 'questions', 'element_id': 104, 'result': True}, + {'set_prefix': '', 'set_index': 1, 'element_type': 'questions', 'element_id': 104, 'result': False}, + ], + [ + # from: http://example.com/terms/questions/catalog/conditions/set_set + {'set_prefix': '', 'set_index': 0, 'element_type': 'questionsets', 'element_id': 94, 'result': True}, + {'set_prefix': '', 'set_index': 1, 'element_type': 'questionsets', 'element_id': 94, 'result': False}, + ], + [ + # from: http://example.com/terms/questions/catalog/conditions/set_set_question + {'set_prefix': '', 'set_index': 0, 'element_type': 'questions', 'element_id': 127, 'result': True}, + {'set_prefix': '', 'set_index': 1, 'element_type': 'questions', 'element_id': 127, 'result': False}, + ] +]) +def test_resolve_set(db, client, results): + client.login(username='author', password='author') + + attribute_id = 114 # http://example.com/terms/domain/conditions/set/bool + + # TODO: maybe move this into the fixture + for result in results: + if result['result']: + Value.objects.update_or_create( + project_id=project_id, + snapshot_id=None, + attribute_id=attribute_id, + set_prefix=result['set_prefix'], + set_index=result['set_index'], + defaults={ + 'text': '1' + } + ) + + url = reverse(urlnames['resolve'], args=[project_id]) + data = [ + {k: v for k, v in result.items() if k != 'result'} + for result in results + ] + response = client.post(url, data, content_type='application/json') + + assert response.status_code == 200, response.content + for response_result, result in zip(response.json(), results, strict=True): + assert response_result == result + + +@pytest.mark.parametrize('results', [ + [ + # http://example.com/terms/questions/catalog/conditions/optionset + {'set_prefix': '', 'set_index': 0, 'element_type': 'optionsets', 'element_id': 3, 'result': True}, + ], + [ + # http://example.com/terms/questions/catalog/conditions/optionset + {'set_prefix': '', 'set_index': 0, 'element_type': 'optionsets', 'element_id': 3, 'result': False}, + ] +]) +def test_resolve_optionset(db, client, results): + client.login(username='author', password='author') + + attribute_id = 120 # http://example.com/terms/domain/conditions/optionset/bool + + # TODO: maybe move this into the fixture + for result in results: + if result['result']: + Value.objects.update_or_create( + project_id=project_id, + snapshot_id=None, + attribute_id=attribute_id, + set_prefix=result['set_prefix'], + set_index=result['set_index'], + defaults={ + 'text': '1' + } + ) + + url = reverse(urlnames['resolve'], args=[project_id]) + data = [ + {k: v for k, v in result.items() if k != 'result'} + for result in results + ] + response = client.post(url, data, content_type='application/json') + + assert response.status_code == 200, response.content + for response_result, result in zip(response.json(), results, strict=True): + assert response_result == result From f1af913682322c526a6369e8d336ea29cd950ddc Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Sun, 19 Apr 2026 13:10:06 +0200 Subject: [PATCH 009/131] Fix value_file.html template and deprecate Pandoc 1 --- rdmo/core/pandoc.py | 17 ++++++++--------- rdmo/core/tests/test_pandoc.py | 8 -------- rdmo/views/templates/views/tags/value_file.html | 2 +- 3 files changed, 9 insertions(+), 18 deletions(-) diff --git a/rdmo/core/pandoc.py b/rdmo/core/pandoc.py index 4ea86cc98e..8b10d13ee5 100644 --- a/rdmo/core/pandoc.py +++ b/rdmo/core/pandoc.py @@ -75,6 +75,9 @@ def get_pandoc_args(export_format, context): pandoc_version = get_pandoc_version() pandoc_args = list(settings.EXPORT_PANDOC_ARGS.get(export_format, [])) # without list(), settings would be changed + if pandoc_version < Version('2'): + raise RuntimeError(f'pandoc >= 2.0 is required, but found {pandoc_version}.') + if export_format == 'pdf': # we used xelatex before pandoc 3 if pandoc_version < Version('3'): @@ -87,17 +90,13 @@ def get_pandoc_args(export_format, context): # find and add a possible reference document reference_document = get_pandoc_reference_document(export_format, context) if reference_document: - if pandoc_version >= Version('2'): - pandoc_args.append(f'--reference-doc={reference_document}') - else: - pandoc_args.append(f'--reference-{export_format}={reference_document}') + pandoc_args.append(f'--reference-doc={reference_document}') # add STATIC_ROOT and possible additional resource paths - if pandoc_version >= Version('2'): - pandoc_args.append(f'--resource-path={settings.STATIC_ROOT}') - if 'resource_path' in context: - resource_path = Path(settings.MEDIA_ROOT) / context['resource_path'] - pandoc_args.append(f'--resource-path={resource_path}') + pandoc_args.append(f'--resource-path={settings.STATIC_ROOT}') + if 'resource_path' in context: + resource_path = Path(settings.MEDIA_ROOT) / context['resource_path'] + pandoc_args.append(f'--resource-path={resource_path}') return pandoc_args diff --git a/rdmo/core/tests/test_pandoc.py b/rdmo/core/tests/test_pandoc.py index 60ac6ac381..a986d28403 100644 --- a/rdmo/core/tests/test_pandoc.py +++ b/rdmo/core/tests/test_pandoc.py @@ -19,7 +19,6 @@ testing_path = rdmo_path.parent / 'testing' pandoc_versions = [ - '1.9.0', '2.0.0', '3.0.0', '3.5.0' @@ -36,13 +35,6 @@ ] pandoc_args_map = { - '1.9.0': { - 'pdf': ['-V', 'geometry:a4paper, margin=1in', '--pdf-engine=xelatex'], - 'rtf': ['--standalone'], - 'docx': [f'--reference-docx={rdmo_path}/share/reference.docx'], - 'odt': [f'--reference-odt={rdmo_path}/share/reference.odt'], - 'other': [] - }, '2.0.0': { 'pdf': ['-V', 'geometry:a4paper, margin=1in', '--pdf-engine=xelatex', f'--resource-path={testing_path}/static_root'], diff --git a/rdmo/views/templates/views/tags/value_file.html b/rdmo/views/templates/views/tags/value_file.html index 1513ad9e3e..fc5a9950a0 100644 --- a/rdmo/views/templates/views/tags/value_file.html +++ b/rdmo/views/templates/views/tags/value_file.html @@ -5,7 +5,7 @@ {% if format == 'docx' or format == 'odt' or format == 'rtf' or format == 'pdf' %} - {% if value.file_type|startswith:'image/' and pandoc_version > 1 %} + {% if value.file_type|startswith:'image/' %} {{ value.file_name }} {% else %} [{% trans 'file' %}: {{ value.file_name }}] From 204996be9278915ed3e32b04f7c9688b21e4babe Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Sun, 19 Apr 2026 13:11:18 +0200 Subject: [PATCH 010/131] Fix pandoc test --- rdmo/core/tests/test_pandoc.py | 5 +- testing/media/resources/favicon.png | Bin 0 -> 1129 bytes testing/media/resources/rdmo-logo.svg | 260 ++++++++++++++++++++++++++ 3 files changed, 262 insertions(+), 3 deletions(-) create mode 100644 testing/media/resources/favicon.png create mode 100644 testing/media/resources/rdmo-logo.svg diff --git a/rdmo/core/tests/test_pandoc.py b/rdmo/core/tests/test_pandoc.py index a986d28403..98614aaba2 100644 --- a/rdmo/core/tests/test_pandoc.py +++ b/rdmo/core/tests/test_pandoc.py @@ -169,17 +169,16 @@ def test_get_pandoc_reference_documents_settings(settings, export_format): @pytest.mark.parametrize('export_format', export_formats) -def test_get_pandoc_content(settings, export_format): +def test_get_pandoc_content(settings, files, export_format): html_path = settings.BASE_DIR / 'export' / 'project.html' html = html_path.read_text() metadata = { 'title': 'this is a very nice title', 'author': ['author one', 'author two'], - 'keywords': ['nothing', 'something', 'whatever'] } - assert len(get_pandoc_content(html, metadata, export_format, {})) > 0 + assert len(get_pandoc_content(html, metadata, export_format, {'resource_path': 'resources'})) > 0 @pytest.mark.parametrize('export_format', export_formats) diff --git a/testing/media/resources/favicon.png b/testing/media/resources/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..042bcf2bbb2fdbc5100253d19cdda66fc2b434a2 GIT binary patch literal 1129 zcmV-v1eW`WP)mSr5qfB*Y=-jBWSX7g3I{6g2}Hir_rOl72{g@PG=pfMtZBIu-(CshAs4r{nb;dYS8eDabeT97PLq7;iyNK)u#}r~A@PQwVCw*h=9uTHj zzyze-*MX@+gpjbx*Txbi{8C`T6<~D84g@-62lRj50TcEV1QS!>oWt6`!$>KG$iJTh zOo~@ZkUO$u^4#-Od2cP&I+PMTrT)uTHRV{tS$@C?#~A8^9RDoY~dPOjY9h z3K5~Z=T8d7fOsrIV?!-U30KnB$rnn9lu4T#?**W{cMz=&O%1gyYN+MXVvO*t&r2o0m~4Y4*Rri}`a?WHO^X^VDiquc*gp!#jJov$m~~v9SUb z@d$6dzJ+a@myyerD3uILn&XXZrSliafPsc8BC4HS}%xBe#dOkYvE4>$o_~i4GtZc0#7V`#CEExiAQCb@&mo=7TBF5fVpWxtEr#XKi zgR|u(w9XM|gE1EC9L8D#V{s0S{c@4TjkDRYwUvX1P80Dxw6^7Et;1+L!C`R|?@n^w z77zsGbtOH_D;^p6SN~WkEfC8R{0yq-I7H z>HZO%b7*boAGm>aj*57MVo_5O_Yn~;UmeCyi12FWJ<_4IB{eIX@fF+{&6CX*xikEvvGuhn zxe7Vw?h32Bg6RLxakv7)zLJIB1I~rYk0NBK@cv0aDX~gK(xEAE{H8=?Uug93g;&?K vp);E+j)+JKWZqQRF33p{dAF*v;=R8B{JACOzfO+{00000NkvXXu0mjfoc$N7 literal 0 HcmV?d00001 diff --git a/testing/media/resources/rdmo-logo.svg b/testing/media/resources/rdmo-logo.svg new file mode 100644 index 0000000000..93fc2eae24 --- /dev/null +++ b/testing/media/resources/rdmo-logo.svg @@ -0,0 +1,260 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + RDMO + + From c94951a1cf17325c48651274da3a7904a5f4249a Mon Sep 17 00:00:00 2001 From: David Wallace Date: Mon, 27 Apr 2026 17:20:43 +0200 Subject: [PATCH 011/131] Test navigation short title for section and page #1589 Signed-off-by: David Wallace --- rdmo/projects/tests/test_navigation.py | 29 ++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/rdmo/projects/tests/test_navigation.py b/rdmo/projects/tests/test_navigation.py index 9352971fd5..7ec729cb28 100644 --- a/rdmo/projects/tests/test_navigation.py +++ b/rdmo/projects/tests/test_navigation.py @@ -86,3 +86,32 @@ def test_compute_navigation(db, section_uri): assert page['total'] == total, page['uri'] assert page['show'] == show, page['uri'] assert 'title' in page # test for verbose, help is filtered out + +def test_compute_navigation_uses_short_title_as_title(db): + project = Project.objects.get(id=1) + project.catalog.prefetch_elements() + + # ARRANGE: pick a section and its first page + section = project.catalog.elements[0] + page = section.elements[0] + + # ARRANGE: clear the normal titles and set short titles + section.title_lang1 = "" + section.short_title_lang1 = 'SectionShorty' + section.save() + page.title_lang1 = "" + page.short_title_lang1 = 'PageShorty' + page.save() + + # ACT: compute navigation for this section + section = project.catalog.sections.get(id=section.id) + navigation = compute_navigation(project, section) + + # ASSERT: find the section title and compare to its short_title + section_nav = next((s for s in navigation if s['id'] == section.id), None) + assert section_nav.get('title') == 'SectionShorty' + + # ASSERT: find the page title and compare to its short_title + pages = section_nav.get('pages', []) + page_nav = next((p for p in pages if p['id'] == page.id), None) + assert page_nav.get('title') == 'PageShorty' From 497601cfcd0fa5d84387d9856fa3e8d2f635dc9a Mon Sep 17 00:00:00 2001 From: David Wallace Date: Mon, 27 Apr 2026 17:57:57 +0200 Subject: [PATCH 012/131] Scope translation overrides to tests make non-leaky Signed-off-by: David Wallace --- rdmo/core/tests/test_tags.py | 22 ++++++++++------------ rdmo/core/tests/test_utils.py | 6 +++--- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/rdmo/core/tests/test_tags.py b/rdmo/core/tests/test_tags.py index 0aa74a73fa..f099c521e0 100644 --- a/rdmo/core/tests/test_tags.py +++ b/rdmo/core/tests/test_tags.py @@ -10,15 +10,13 @@ def test_i18n_switcher(rf): # create a fake template with a name template = "{% load core_tags %}{% i18n_switcher %}" - # set a language - translation.activate(settings.LANGUAGES[0][0]) - - # render the link - request = rf.get(reverse('home')) - context = RequestContext(request, {}) - rendered_template = Template(template).render(context) - for language in settings.LANGUAGES: - if language == settings.LANGUAGES[0]: - assert '{}'.format(*language) in rendered_template - else: - assert'{}'.format(*language) in rendered_template + with translation.override(settings.LANGUAGES[0][0]): + # render the link + request = rf.get(reverse('home')) + context = RequestContext(request, {}) + rendered_template = Template(template).render(context) + for language in settings.LANGUAGES: + if language == settings.LANGUAGES[0]: + assert '{}'.format(*language) in rendered_template + else: + assert '{}'.format(*language) in rendered_template diff --git a/rdmo/core/tests/test_utils.py b/rdmo/core/tests/test_utils.py index 2c446d5192..5943f6c940 100644 --- a/rdmo/core/tests/test_utils.py +++ b/rdmo/core/tests/test_utils.py @@ -2,7 +2,7 @@ import pytest -from django.utils.translation import activate +from django.utils import translation from rdmo.core.utils import ( human2bytes, @@ -85,8 +85,8 @@ def test_human2bytes(human: str | None, bytes: float): @pytest.mark.parametrize("locale, date_string, expected_date", valid_date_strings) def test_parse_date_from_string_valid_formats(settings, locale, date_string, expected_date): - activate(locale) - assert parse_date_from_string(date_string) == expected_date + with translation.override(locale): + assert parse_date_from_string(date_string) == expected_date @pytest.mark.parametrize("invalid_date, error_msg", invalid_date_strings) From c740edaf8a82c16bde6f18a4adefcab719acbb88 Mon Sep 17 00:00:00 2001 From: David Wallace Date: Wed, 29 Apr 2026 14:58:29 +0200 Subject: [PATCH 013/131] style: make changes minimal Signed-off-by: David Wallace --- rdmo/core/tests/test_tags.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rdmo/core/tests/test_tags.py b/rdmo/core/tests/test_tags.py index f099c521e0..73ace32691 100644 --- a/rdmo/core/tests/test_tags.py +++ b/rdmo/core/tests/test_tags.py @@ -10,7 +10,9 @@ def test_i18n_switcher(rf): # create a fake template with a name template = "{% load core_tags %}{% i18n_switcher %}" + # set a language with translation.override(settings.LANGUAGES[0][0]): + # render the link request = rf.get(reverse('home')) context = RequestContext(request, {}) From a66fad3845b611e08019dede49691f50a1c2a042 Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Thu, 16 Apr 2026 13:45:21 +0200 Subject: [PATCH 014/131] Add tests for navigation for a catalog without sections or pages --- rdmo/projects/tests/test_viewset_project.py | 33 +++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/rdmo/projects/tests/test_viewset_project.py b/rdmo/projects/tests/test_viewset_project.py index a4ff426a3e..b8f7c6f8b7 100644 --- a/rdmo/projects/tests/test_viewset_project.py +++ b/rdmo/projects/tests/test_viewset_project.py @@ -659,6 +659,39 @@ def test_navigation_section(db, client, username, password, project_id): assert response.status_code == 401 +def test_navigation_without_sections(db, client): + from rdmo.questions.models.section import Section + project_id = 1 + username = 'owner' + Section.objects.all().delete() + + client.login(username=username, password=username) + + url = reverse(urlnames['navigation'], args=[project_id]) + response = client.get(url) + + assert response.status_code == 200 + assert response.json() == [] + + +def test_navigation_without_pages(db, client): + from rdmo.questions.models.page import Page + project_id = 1 + username = 'owner' + Page.objects.all().delete() + + client.login(username=username, password=username) + + url = reverse(urlnames['navigation'], args=[project_id]) + response = client.get(url) + response_data = response.json() + + assert response.status_code == 200 + for data in response_data: + assert data['count'] == 0 + assert data['first'] is None + + @pytest.mark.parametrize('username,password', users) @pytest.mark.parametrize('project_id', projects) @pytest.mark.parametrize('condition_id', conditions) From e3e3dfa24c4b573c99c1ff69e3887ecbad47ef16 Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Thu, 30 Apr 2026 17:36:16 +0200 Subject: [PATCH 015/131] Remove slack and add matrix and mastodon to README and pyproject.toml --- README.md | 4 ++-- pyproject.toml | 13 +++++++------ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 4084440ed4..a613cb7ee5 100644 --- a/README.md +++ b/README.md @@ -22,8 +22,8 @@ RDMO is a tool to support the systematic planning, organisation and implementati
https://rdmo.readthedocs.io
Mailing list
https://www.listserv.dfn.de/sympa/subscribe/rdmo
-
Slack
-
https://rdmo.slack.com
+
Matrix
+
https://matrix.to/#/#rdmo:matrix.org
Demo
https://rdmo.aip.de
diff --git a/pyproject.toml b/pyproject.toml index a2db2477da..409a41fb89 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -107,12 +107,13 @@ pytest = [ ] [project.urls] -changelog = "https://github.com/rdmorganiser/rdmo/blob/main/CHANGELOG.md" -documentation = "https://rdmo.readthedocs.io" -homepage = "https://rdmorganiser.github.io" -issues = "https://github.com/rdmorganiser/rdmo/issues" -repository = "https://github.com/rdmorganiser/rdmo.git" -slack = "https://rdmo.slack.com" +Homepage = "https://rdmorganiser.github.io" +Repository = "https://github.com/rdmorganiser/rdmo" +Issues = "https://github.com/rdmorganiser/rdmo/issues" +Changelog = "https://github.com/rdmorganiser/rdmo/blob/main/CHANGELOG.md" +Documentation = "https://rdmo.readthedocs.io" +Mastodon = "https://openbiblio.social/@rdmo" +Matrix = "https://matrix.to/#/#rdmo:matrix.org" [project.scripts] rdmo-admin = "rdmo.__main__:main" From b8bfc61a7a2ca4f02488c220854d628ea518a46e Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Thu, 30 Apr 2026 19:27:15 +0200 Subject: [PATCH 016/131] Use os.pathsep to seperate --resource-path for pandoc --- rdmo/core/pandoc.py | 6 ++--- rdmo/core/tests/test_pandoc.py | 47 ++++++++++++++++++---------------- 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/rdmo/core/pandoc.py b/rdmo/core/pandoc.py index 8b10d13ee5..7fa96cffd2 100644 --- a/rdmo/core/pandoc.py +++ b/rdmo/core/pandoc.py @@ -93,10 +93,10 @@ def get_pandoc_args(export_format, context): pandoc_args.append(f'--reference-doc={reference_document}') # add STATIC_ROOT and possible additional resource paths - pandoc_args.append(f'--resource-path={settings.STATIC_ROOT}') + resource_paths = [settings.STATIC_ROOT] if 'resource_path' in context: - resource_path = Path(settings.MEDIA_ROOT) / context['resource_path'] - pandoc_args.append(f'--resource-path={resource_path}') + resource_paths.append(Path(settings.MEDIA_ROOT) / context['resource_path']) + pandoc_args.append(f'--resource-path={os.pathsep.join(map(str, resource_paths))}') return pandoc_args diff --git a/rdmo/core/tests/test_pandoc.py b/rdmo/core/tests/test_pandoc.py index 98614aaba2..c2746a8b88 100644 --- a/rdmo/core/tests/test_pandoc.py +++ b/rdmo/core/tests/test_pandoc.py @@ -1,3 +1,4 @@ +import os from pathlib import Path import pytest @@ -16,7 +17,12 @@ ) rdmo_path = Path(apps.get_app_config('rdmo').path) + testing_path = rdmo_path.parent / 'testing' +reference_docx_path = rdmo_path / 'share' / 'reference.docx' +reference_odt_path = rdmo_path / 'share' / 'reference.odt' +static_root_path = testing_path / 'static_root' +media_root_test_path = testing_path / 'media_root' / 'test' pandoc_versions = [ '2.0.0', @@ -36,28 +42,25 @@ pandoc_args_map = { '2.0.0': { - 'pdf': ['-V', 'geometry:a4paper, margin=1in', '--pdf-engine=xelatex', - f'--resource-path={testing_path}/static_root'], - 'rtf': ['--standalone', f'--resource-path={testing_path}/static_root'], - 'docx': [f'--reference-doc={rdmo_path}/share/reference.docx', f'--resource-path={testing_path}/static_root'], - 'odt': [f'--reference-doc={rdmo_path}/share/reference.odt', f'--resource-path={testing_path}/static_root'], - 'other': [f'--resource-path={testing_path}/static_root'] + 'pdf': ['-V', 'geometry:a4paper, margin=1in', '--pdf-engine=xelatex', f'--resource-path={static_root_path}'], + 'rtf': ['--standalone', f'--resource-path={static_root_path}'], + 'docx': [f'--reference-doc={reference_docx_path}', f'--resource-path={static_root_path}'], + 'odt': [f'--reference-doc={reference_odt_path}', f'--resource-path={static_root_path}'], + 'other': [f'--resource-path={static_root_path}'] }, '3.0.0': { - 'pdf': ['-V', 'geometry:a4paper, margin=1in', '--pdf-engine=lualatex', - f'--resource-path={testing_path}/static_root'], - 'rtf': ['--standalone', f'--resource-path={testing_path}/static_root'], - 'docx': [f'--reference-doc={rdmo_path}/share/reference.docx', f'--resource-path={testing_path}/static_root'], - 'odt': [f'--reference-doc={rdmo_path}/share/reference.odt', f'--resource-path={testing_path}/static_root'], - 'other': [f'--resource-path={testing_path}/static_root'] + 'pdf': ['-V', 'geometry:a4paper, margin=1in', '--pdf-engine=lualatex', f'--resource-path={static_root_path}'], + 'rtf': ['--standalone', f'--resource-path={static_root_path}'], + 'docx': [f'--reference-doc={reference_docx_path}', f'--resource-path={static_root_path}'], + 'odt': [f'--reference-doc={reference_odt_path}', f'--resource-path={static_root_path}'], + 'other': [f'--resource-path={static_root_path}'] }, '3.5.0': { - 'pdf': ['-V', 'geometry:a4paper, margin=1in', '--pdf-engine=lualatex', - f'--resource-path={testing_path}/static_root'], - 'rtf': ['--standalone', f'--resource-path={testing_path}/static_root'], - 'docx': [f'--reference-doc={rdmo_path}/share/reference.docx', f'--resource-path={testing_path}/static_root'], - 'odt': [f'--reference-doc={rdmo_path}/share/reference.odt', f'--resource-path={testing_path}/static_root'], - 'other': [f'--resource-path={testing_path}/static_root'] + 'pdf': ['-V', 'geometry:a4paper, margin=1in', '--pdf-engine=lualatex', f'--resource-path={static_root_path}'], + 'rtf': ['--standalone', f'--resource-path={static_root_path}'], + 'docx': [f'--reference-doc={reference_docx_path}', f'--resource-path={static_root_path}'], + 'odt': [f'--reference-doc={reference_odt_path}', f'--resource-path={static_root_path}'], + 'other': [f'--resource-path={static_root_path}'] } } @@ -86,10 +89,10 @@ def test_get_pandoc_args_resource_path(settings, mocker, pandoc_version, export_ mocker.patch('pypandoc.get_pandoc_version', return_value=pandoc_version) pandoc_args = pandoc_args_map[pandoc_version].get(export_format, pandoc_args_map[pandoc_version]['other']).copy() - if Version(pandoc_version) >= Version('2'): - pandoc_args.append(f'--resource-path={testing_path}/media_root/test') - - assert get_pandoc_args(export_format, {'resource_path': 'test'}) == pandoc_args + assert get_pandoc_args(export_format, {'resource_path': 'test'}) == [ + pandoc_arg + os.pathsep + str(media_root_test_path) if pandoc_arg.startswith('--resource-path=') else pandoc_arg + for pandoc_arg in pandoc_args + ] def test_get_pandoc_reference_document(mocker): From e3acb620ef68c06f9cebd718c0558d1788dcecb8 Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Fri, 1 May 2026 21:16:49 +0200 Subject: [PATCH 017/131] Add pypi-release to GitHub workflow --- .github/workflows/ci.yml | 44 +++++++++++++++++++++++++++++++--------- .github/zizmor.yml | 4 ++++ 2 files changed, 38 insertions(+), 10 deletions(-) create mode 100644 .github/zizmor.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4b8b6ebbbb..69728a8481 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,6 +4,8 @@ on: push: branches: - main + tags: + - '*' pull_request: # run CI only if files in these whitelisted paths are changed paths: @@ -31,26 +33,26 @@ permissions: {} jobs: - build-wheel: - name: Build python wheel + build: + name: Build python package runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v6 with: persist-credentials: false fetch-depth: 0 # important for setuptools_scm - # build the webpack bundle - uses: actions/setup-node@v6 with: node-version: 22.22 cache: npm - - run: npm ci && npm run build:dist + - name: build front end + run: npm ci && npm run build:dist - name: Build and inspect package uses: hynek/build-and-inspect-python-package@efb823f52190ad02594531168b7a2d5790e66516 # v2.14.0 test: name: "Test (Python: ${{ matrix.python-version }}, DB: ${{ matrix.db-backend }})" - needs: build-wheel + needs: build runs-on: ubuntu-24.04 strategy: matrix: @@ -110,7 +112,7 @@ jobs: test-e2e: name: "End-to-end Test (Python: ${{ matrix.python-version }}, DB: ${{ matrix.db-backend }})" - needs: build-wheel + needs: build runs-on: ubuntu-24.04 strategy: matrix: @@ -125,7 +127,7 @@ jobs: with: python-version: ${{ matrix.python-version }} cache: pip - - name: Download wheel + - name: Download package uses: actions/download-artifact@v6 with: name: Packages @@ -191,7 +193,7 @@ jobs: dependencies: name: Test installation of all dependencies - needs: build-wheel + needs: build runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v6 @@ -201,7 +203,7 @@ jobs: with: python-version: "3.13" cache: pip - - name: Download wheel + - name: Download package uses: actions/download-artifact@v6 with: name: Packages @@ -244,10 +246,32 @@ jobs: echo -e "\`\`\`\n" } >> $GITHUB_STEP_SUMMARY + pypi-release: + name: Publish distribution to PyPI + if: startsWith(github.ref, 'refs/tags/') + needs: + - build + - test + - test-e2e + runs-on: ubuntu-24.04 + environment: + name: pypi + url: https://pypi.org/p/rdmo + permissions: + id-token: write + steps: + - name: Download package + uses: actions/download-artifact@v6 + with: + name: Packages + path: dist + - name: Publish to PyPI + uses: pypa/gh-action-pypi-publish@6733eb7d741f0b11ec6a39b58540dab7590f9b7d # v1.14.0 + required-checks-pass: if: always() needs: - - build-wheel + - build - test - coveralls - test-e2e diff --git a/.github/zizmor.yml b/.github/zizmor.yml new file mode 100644 index 0000000000..b58972fc33 --- /dev/null +++ b/.github/zizmor.yml @@ -0,0 +1,4 @@ +rules: + cache-poisoning: + ignore: + - ci.yml From 0f64ea3e10d7aee0701d000edb13223ad4f636cf Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Sat, 2 May 2026 09:21:15 +0200 Subject: [PATCH 018/131] Disable cache for the build step in the GitHub workflow on tags --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 69728a8481..740865fc0a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,7 +44,7 @@ jobs: - uses: actions/setup-node@v6 with: node-version: 22.22 - cache: npm + cache: ${{ startsWith(github.ref, 'refs/tags/') && '' || 'npm' }} # disable cache on tags - name: build front end run: npm ci && npm run build:dist - name: Build and inspect package From aa71f3aa7f1160cb083a581def17d813bf12a20f Mon Sep 17 00:00:00 2001 From: David Wallace Date: Thu, 7 May 2026 09:13:02 +0200 Subject: [PATCH 019/131] Add select_related for Attribute queryset and split for index,nested and exports Signed-off-by: David Wallace --- rdmo/domain/viewsets.py | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/rdmo/domain/viewsets.py b/rdmo/domain/viewsets.py index e25efe7734..29981a9c47 100644 --- a/rdmo/domain/viewsets.py +++ b/rdmo/domain/viewsets.py @@ -24,11 +24,6 @@ class AttributeViewSet(ModelViewSet): permission_classes = (HasModelPermission | HasObjectPermission, ) - queryset = Attribute.objects.annotate(values_count=models.Count('values')) \ - .annotate(projects_count=models.Count('values__project', distinct=True)) \ - .prefetch_related('conditions', 'pages', 'questionsets', 'questions', - 'tasks_as_start', 'tasks_as_end', 'editors') \ - .order_by('path') filter_backends = (SearchFilter, DjangoFilterBackend) search_fields = ('uri', ) @@ -40,6 +35,27 @@ class AttributeViewSet(ModelViewSet): 'parent' ) + def get_queryset(self): + queryset = Attribute.objects.all().order_by('path') + if self.action in ['index']: + return queryset + elif self.action in ('nested', 'export', 'detail_export'): + return queryset.select_related('parent') + else: + return queryset.annotate( + values_count=models.Count('values') + ).annotate( + projects_count=models.Count('values__project', distinct=True) + ).prefetch_related( + 'conditions', + 'pages', + 'questionsets', + 'questions', + 'tasks_as_start', + 'tasks_as_end', + 'editors' + ).select_related('parent') + def get_serializer_class(self): return AttributeListSerializer if self.action == 'list' else AttributeSerializer From f5582c38b5e148f69d74bb372fc48cee61ba29b6 Mon Sep 17 00:00:00 2001 From: David Wallace Date: Thu, 7 May 2026 09:15:59 +0200 Subject: [PATCH 020/131] Add default_option to questions models prefetch and do not annotate for catalog index Signed-off-by: David Wallace --- rdmo/questions/models/catalog.py | 2 ++ rdmo/questions/models/page.py | 2 ++ rdmo/questions/models/question.py | 3 ++- rdmo/questions/models/questionset.py | 1 + rdmo/questions/models/section.py | 2 ++ rdmo/questions/viewsets.py | 10 ++++++---- 6 files changed, 15 insertions(+), 5 deletions(-) diff --git a/rdmo/questions/models/catalog.py b/rdmo/questions/models/catalog.py index 20b2e6848f..844ce90724 100644 --- a/rdmo/questions/models/catalog.py +++ b/rdmo/questions/models/catalog.py @@ -23,11 +23,13 @@ class Catalog(Model, TranslationMixin): 'catalog_sections__section__section_pages__page__page_questions__question__attribute', 'catalog_sections__section__section_pages__page__page_questions__question__conditions', 'catalog_sections__section__section_pages__page__page_questions__question__optionsets', + 'catalog_sections__section__section_pages__page__page_questions__question__default_option', 'catalog_sections__section__section_pages__page__page_questionsets__questionset__attribute', 'catalog_sections__section__section_pages__page__page_questionsets__questionset__conditions', 'catalog_sections__section__section_pages__page__page_questionsets__questionset__questionset_questions__question__attribute', 'catalog_sections__section__section_pages__page__page_questionsets__questionset__questionset_questions__question__conditions', 'catalog_sections__section__section_pages__page__page_questionsets__questionset__questionset_questions__question__optionsets', + 'catalog_sections__section__section_pages__page__page_questionsets__questionset__questionset_questions__question__default_option', 'catalog_sections__section__section_pages__page__page_questionsets__questionset__questionset_questionsets__questionset__attribute', 'catalog_sections__section__section_pages__page__page_questionsets__questionset__questionset_questionsets__questionset__conditions' ) diff --git a/rdmo/questions/models/page.py b/rdmo/questions/models/page.py index 06cf582d21..921e1b58f5 100644 --- a/rdmo/questions/models/page.py +++ b/rdmo/questions/models/page.py @@ -21,11 +21,13 @@ class Page(Model, TranslationMixin): 'page_questions__question__attribute', 'page_questions__question__conditions', 'page_questions__question__optionsets', + 'page_questions__question__default_option', 'page_questionsets__questionset__attribute', 'page_questionsets__questionset__conditions', 'page_questionsets__questionset__questionset_questions__question__attribute', 'page_questionsets__questionset__questionset_questions__question__conditions', 'page_questionsets__questionset__questionset_questions__question__optionsets', + 'page_questionsets__questionset__questionset_questions__question__default_option', 'page_questionsets__questionset__questionset_questionsets__questionset__attribute', 'page_questionsets__questionset__questionset_questionsets__questionset__conditions' ) diff --git a/rdmo/questions/models/question.py b/rdmo/questions/models/question.py index 32a283e673..d9073e330d 100644 --- a/rdmo/questions/models/question.py +++ b/rdmo/questions/models/question.py @@ -19,7 +19,8 @@ class Question(Model, TranslationMixin): prefetch_lookups = ( 'conditions', - 'optionsets' + 'optionsets', + 'default_option' ) uri = models.URLField( diff --git a/rdmo/questions/models/questionset.py b/rdmo/questions/models/questionset.py index d8204ca4cb..835fcdf753 100644 --- a/rdmo/questions/models/questionset.py +++ b/rdmo/questions/models/questionset.py @@ -21,6 +21,7 @@ class QuestionSet(Model, TranslationMixin): 'questionset_questions__question__attribute', 'questionset_questions__question__conditions', 'questionset_questions__question__optionsets', + 'questionset_questions__question__default_option', 'questionset_questionsets__questionset__attribute', 'questionset_questionsets__questionset__conditions' ) diff --git a/rdmo/questions/models/section.py b/rdmo/questions/models/section.py index 432ad4d9ad..94557146c1 100644 --- a/rdmo/questions/models/section.py +++ b/rdmo/questions/models/section.py @@ -20,11 +20,13 @@ class Section(Model, TranslationMixin): 'section_pages__page__page_questions__question__attribute', 'section_pages__page__page_questions__question__conditions', 'section_pages__page__page_questions__question__optionsets', + 'section_pages__page__page_questions__question__default_option', 'section_pages__page__page_questionsets__questionset__attribute', 'section_pages__page__page_questionsets__questionset__conditions', 'section_pages__page__page_questionsets__questionset__questionset_questions__question__attribute', 'section_pages__page__page_questionsets__questionset__questionset_questions__question__conditions', 'section_pages__page__page_questionsets__questionset__questionset_questions__question__optionsets', + 'section_pages__page__page_questionsets__questionset__questionset_questions__question__default_option', 'section_pages__page__page_questionsets__questionset__questionset_questionsets__questionset__attribute', 'section_pages__page__page_questionsets__questionset__questionset_questionsets__questionset__conditions' ) diff --git a/rdmo/questions/viewsets.py b/rdmo/questions/viewsets.py index 7457e8a4da..82bb94bfbb 100644 --- a/rdmo/questions/viewsets.py +++ b/rdmo/questions/viewsets.py @@ -58,10 +58,12 @@ class CatalogViewSet(ElementToggleCurrentSiteViewSetMixin, ModelViewSet): ) def get_queryset(self): - queryset = Catalog.objects.annotate(projects_count=models.Count('projects')) + queryset = Catalog.objects.all() if self.action in ['index']: return queryset - elif self.action in ('nested', 'export', 'detail_export'): + + queryset = Catalog.objects.annotate(projects_count=models.Count('projects')) + if self.action in ('nested', 'export', 'detail_export'): return queryset.prefetch_elements() else: return queryset.prefetch_related('sites', 'editors', 'groups', 'catalog_sections__section') @@ -375,7 +377,7 @@ def get_queryset(self): if self.action in ['index']: return queryset elif self.action in ('nested', 'export', 'detail_export'): - return queryset.prefetch_elements().select_related('attribute') + return queryset.prefetch_elements().select_related('attribute', 'default_option') else: return queryset.prefetch_related( 'conditions', @@ -383,7 +385,7 @@ def get_queryset(self): 'pages', 'questionsets', 'editors' - ).select_related('attribute') + ).select_related('attribute', 'default_option') @action(detail=False) def index(self, request): From 75ccdfbe8cc4686a5d22a3efd1ab08e8214d09b7 Mon Sep 17 00:00:00 2001 From: David Wallace Date: Thu, 7 May 2026 09:44:34 +0200 Subject: [PATCH 021/131] Add CustomSiteAdmin and add id to list display Signed-off-by: David Wallace --- rdmo/core/admin.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/rdmo/core/admin.py b/rdmo/core/admin.py index d132d83279..eb2d12079c 100644 --- a/rdmo/core/admin.py +++ b/rdmo/core/admin.py @@ -1,4 +1,7 @@ from django import forms +from django.contrib import admin +from django.contrib.sites.admin import SiteAdmin +from django.contrib.sites.models import Site class ElementAdminForm(forms.ModelForm): @@ -6,3 +9,13 @@ class ElementAdminForm(forms.ModelForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields['uri_path'].required = True + + +if admin.site.is_registered(Site): + admin.site.unregister(Site) + + +@admin.register(Site) +class CustomSiteAdmin(SiteAdmin): + list_display = ('id', 'domain', 'name') + search_fields = ('domain', 'name') From ad813f254efe72e62f3bc50be5f8cf496231f9c6 Mon Sep 17 00:00:00 2001 From: David Wallace Date: Thu, 7 May 2026 14:23:30 +0200 Subject: [PATCH 022/131] Do not prefetch for the index action on conditions,options and optionsets Signed-off-by: David Wallace --- rdmo/conditions/viewsets.py | 20 +++++++++++++++++--- rdmo/options/viewsets.py | 36 +++++++++++++++++++++++++++--------- 2 files changed, 44 insertions(+), 12 deletions(-) diff --git a/rdmo/conditions/viewsets.py b/rdmo/conditions/viewsets.py index ec10620e58..35a382b55e 100644 --- a/rdmo/conditions/viewsets.py +++ b/rdmo/conditions/viewsets.py @@ -20,9 +20,6 @@ class ConditionViewSet(ModelViewSet): permission_classes = (HasModelPermission | HasObjectPermission, ) serializer_class = ConditionSerializer - queryset = Condition.objects.select_related('source', 'target_option') \ - .prefetch_related('optionsets', 'pages', 'questionsets', - 'questions', 'tasks', 'editors') filter_backends = (SearchFilter, DjangoFilterBackend) search_fields = ('uri', ) @@ -36,6 +33,23 @@ class ConditionViewSet(ModelViewSet): 'target_option' ) + def get_queryset(self): + queryset = Condition.objects.all() + if self.action in ['index']: + return queryset + else: + return queryset.select_related( + 'source', + 'target_option' + ).prefetch_related( + 'optionsets', + 'pages', + 'questionsets', + 'questions', + 'tasks', + 'editors' + ) + @action(detail=False) def index(self, request): queryset = self.filter_queryset(self.get_queryset()) diff --git a/rdmo/options/viewsets.py b/rdmo/options/viewsets.py index b0eaffd2d4..0062a4d01f 100644 --- a/rdmo/options/viewsets.py +++ b/rdmo/options/viewsets.py @@ -29,12 +29,6 @@ class OptionSetViewSet(ModelViewSet): permission_classes = (HasModelPermission | HasObjectPermission, ) serializer_class = OptionSetSerializer - queryset = OptionSet.objects.prefetch_related( - 'optionset_options__option', - 'conditions', - 'questions', - 'editors' - ) filter_backends = (SearchFilter, DjangoFilterBackend) search_fields = ('uri', ) @@ -45,6 +39,18 @@ class OptionSetViewSet(ModelViewSet): 'comment' ) + def get_queryset(self): + queryset = OptionSet.objects.all() + if self.action in ['index']: + return queryset + else: + return queryset.prefetch_related( + 'optionset_options__option', + 'conditions', + 'questions', + 'editors' + ) + @action(detail=False) def index(self, request): queryset = self.filter_queryset(self.get_queryset()) @@ -93,9 +99,6 @@ def get_export_renderer_context(self, request): class OptionViewSet(ModelViewSet): permission_classes = (HasModelPermission | HasObjectPermission, ) serializer_class = OptionSerializer - queryset = Option.objects.annotate(values_count=models.Count('values')) \ - .annotate(projects_count=models.Count('values__project', distinct=True)) \ - .prefetch_related('optionsets', 'conditions', 'editors') filter_backends = (SearchFilter, DjangoFilterBackend) search_fields = ('uri', 'text') @@ -109,6 +112,21 @@ class OptionViewSet(ModelViewSet): 'comment' ) + def get_queryset(self): + queryset = Option.objects.all() + if self.action in ['index']: + return queryset + else: + return queryset.annotate( + values_count=models.Count('values') + ).annotate( + projects_count=models.Count('values__project', distinct=True) + ).prefetch_related( + 'optionsets', + 'conditions', + 'editors' + ) + @action(detail=False) def index(self, request): queryset = self.filter_queryset(self.get_queryset()) From f1ec6c9ec7f1f518a016139865affbce3383fdf5 Mon Sep 17 00:00:00 2001 From: David Wallace Date: Thu, 7 May 2026 15:07:19 +0200 Subject: [PATCH 023/131] Also do not prefetch for the index action on views and tasks Signed-off-by: David Wallace --- rdmo/tasks/viewsets.py | 22 ++++++++++++++++++---- rdmo/views/viewsets.py | 17 ++++++++++++++--- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/rdmo/tasks/viewsets.py b/rdmo/tasks/viewsets.py index 1ddc4f80ea..9cf564b245 100644 --- a/rdmo/tasks/viewsets.py +++ b/rdmo/tasks/viewsets.py @@ -21,10 +21,6 @@ class TaskViewSet(ElementToggleCurrentSiteViewSetMixin, ModelViewSet): permission_classes = (HasModelPermission | HasObjectPermission, ) serializer_class = TaskSerializer - queryset = Task.objects.select_related('start_attribute', 'end_attribute') \ - .prefetch_related('catalogs', 'sites', 'editors', 'groups', 'conditions') \ - .annotate(projects_count=models.Count('projects')) \ - .order_by('uri') filter_backends = (SearchFilter, DjangoFilterBackend) search_fields = ('uri', 'title') @@ -37,6 +33,24 @@ class TaskViewSet(ElementToggleCurrentSiteViewSetMixin, ModelViewSet): 'editors' ) + def get_queryset(self): + queryset = Task.objects.all().order_by('uri') + if self.action in ['index']: + return queryset + else: + return queryset.select_related( + 'start_attribute', + 'end_attribute' + ).prefetch_related( + 'catalogs', + 'sites', + 'editors', + 'groups', + 'conditions' + ).annotate( + projects_count=models.Count('projects') + ) + @action(detail=False) def index(self, request): queryset = self.filter_queryset(self.get_queryset()) diff --git a/rdmo/views/viewsets.py b/rdmo/views/viewsets.py index d0abca7c86..e14aa0a81c 100644 --- a/rdmo/views/viewsets.py +++ b/rdmo/views/viewsets.py @@ -21,9 +21,6 @@ class ViewViewSet(ElementToggleCurrentSiteViewSetMixin, ModelViewSet): permission_classes = (HasModelPermission | HasObjectPermission, ) serializer_class = ViewSerializer - queryset = View.objects.prefetch_related('catalogs', 'sites', 'editors', 'groups') \ - .annotate(projects_count=models.Count('projects')) \ - .order_by('uri') filter_backends = (SearchFilter, DjangoFilterBackend) search_fields = ('uri', 'title') @@ -36,6 +33,20 @@ class ViewViewSet(ElementToggleCurrentSiteViewSetMixin, ModelViewSet): 'editors' ) + def get_queryset(self): + queryset = View.objects.all().order_by('uri') + if self.action in ['index']: + return queryset + else: + return queryset.prefetch_related( + 'catalogs', + 'sites', + 'editors', + 'groups' + ).annotate( + projects_count=models.Count('projects') + ) + @action(detail=False) def index(self, request): queryset = self.filter_queryset(self.get_queryset()) From 1a38bf6747d43468a3e73e5b41f91e86a7393b8d Mon Sep 17 00:00:00 2001 From: David Wallace Date: Thu, 7 May 2026 15:10:36 +0200 Subject: [PATCH 024/131] Add simple performance tests for query counts on api actions Signed-off-by: David Wallace --- pyproject.toml | 1 + .../tests/test_management_api_performance.py | 181 ++++++++++++++++++ 2 files changed, 182 insertions(+) create mode 100644 rdmo/management/tests/test_management_api_performance.py diff --git a/pyproject.toml b/pyproject.toml index a2db2477da..749a853811 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -201,6 +201,7 @@ pythonpath = [".", "testing"] addopts = ["-ra", "-p no:randomly", "-m", "not e2e"] markers = [ "e2e: marks tests as end-to-end tests using playwright (deselect with '-m \"not e2e\"')", + "performance: marks query-count regression tests", ] filterwarnings = [ # fail on RemovedInDjango50Warning exception diff --git a/rdmo/management/tests/test_management_api_performance.py b/rdmo/management/tests/test_management_api_performance.py new file mode 100644 index 0000000000..d25a68a16a --- /dev/null +++ b/rdmo/management/tests/test_management_api_performance.py @@ -0,0 +1,181 @@ +import pytest + +from rdmo.management.constants import RDMO_MODEL_PATH_MAPPER + +pytestmark = pytest.mark.django_db + + +MANAGEMENT_ENDPOINTS = { + "conditions.condition": { + "url": "/api/v1/conditions/conditions/", + "queries": { + "index": 3, + "list": 9, + "detail": 9, + }, + }, + "domain.attribute": { + "url": "/api/v1/domain/attributes/", + "queries": { + "index": 3, + "list": 10, + "detail": 11, + }, + }, + "options.optionset": { + "url": "/api/v1/options/optionsets/", + "queries": { + "index": 3, + "list": 8, + "detail": 8, + }, + }, + "options.option": { + "url": "/api/v1/options/options/", + "queries": { + "index": 3, + "list": 10, + "detail": 10, + }, + }, + "questions.catalog": { + "url": "/api/v1/questions/catalogs/", + "queries": { + "index": 3, + "list": 8, + "detail": 8, + }, + }, + "questions.section": { + "url": "/api/v1/questions/sections/", + "queries": { + "index": 3, + "list": 8, + "detail": 8, + }, + }, + "questions.page": { + "url": "/api/v1/questions/pages/", + "queries": { + "index": 3, + "list": 10, + "detail": 10, + }, + }, + "questions.questionset": { + "url": "/api/v1/questions/questionsets/", + "queries": { + "index": 3, + "list": 11, + "detail": 10, + }, + }, + "questions.question": { + "url": "/api/v1/questions/questions/", + "queries": { + "index": 3, + "list": 10, + "detail": 10, + }, + }, + "tasks.task": { + "url": "/api/v1/tasks/tasks/", + "queries": { + "index": 3, + "list": 10, + "detail": 10, + }, + }, + "views.view": { + "url": "/api/v1/views/views/", + "queries": { + "index": 3, + "list": 8, + "detail": 8, + }, + }, +} + + +def get_params(action): + params = [] + + for model_path, config in MANAGEMENT_ENDPOINTS.items(): + max_queries = config["queries"][action] + + if action == "index": + url = f'{config["url"]}index/' + else: + url = config["url"] + + params.append(pytest.param( + model_path, + url, + max_queries, + id=f"{model_path}-{action}", + )) + + return params + + +@pytest.mark.performance +@pytest.mark.parametrize( + "model_path,url,max_queries", + get_params("index"), +) +def test_management_index_endpoint_query_counts( + admin_client, + django_assert_max_num_queries, + model_path, + url, + max_queries, +): + assert model_path in RDMO_MODEL_PATH_MAPPER + + with django_assert_max_num_queries(max_queries): + response = admin_client.get(url) + + assert response.status_code == 200 + + +@pytest.mark.performance +@pytest.mark.parametrize( + "model_path,url,max_queries", + get_params("list"), +) +def test_management_list_endpoint_query_counts( + admin_client, + django_assert_max_num_queries, + model_path, + url, + max_queries, +): + assert model_path in RDMO_MODEL_PATH_MAPPER + + with django_assert_max_num_queries(max_queries): + response = admin_client.get(url) + + assert response.status_code == 200 + + +@pytest.mark.performance +@pytest.mark.parametrize( + "model_path,url,max_queries", + get_params("detail"), +) +def test_management_detail_endpoint_query_counts( + admin_client, + django_assert_max_num_queries, + model_path, + url, + max_queries, +): + model = RDMO_MODEL_PATH_MAPPER[model_path] + obj = model.objects.first() + + assert obj is not None, f"No test object exists for {model_path}." + + with django_assert_max_num_queries(max_queries): + response = admin_client.get(f"{url}{obj.pk}/") + + assert response.status_code == 200 From a55a41f231195d649de0206bb57dac4e0339c9a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Heinz-Alexander=20F=C3=BCtterer?= <35225576+afuetterer@users.noreply.github.com> Date: Tue, 10 Mar 2026 11:56:13 +0100 Subject: [PATCH 025/131] ci: switch to smaller ubuntu-slim runner image --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4b8b6ebbbb..3da3d9d528 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -163,7 +163,7 @@ jobs: name: Indicate completion to coveralls needs: test if: ${{ always() }} - runs-on: ubuntu-24.04 + runs-on: ubuntu-slim steps: - name: Run Coveralls finish uses: coverallsapp/github-action@5cbfd81b66ca5d10c19b062c04de0199c215fb6e # v2.3.7 @@ -253,7 +253,7 @@ jobs: - test-e2e - dev-setup - dependencies - runs-on: ubuntu-24.04 + runs-on: ubuntu-slim steps: - uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe # v1.2.2 with: From 3edd5497f53e8b0ff2ced12b50c55d1f19c34baf Mon Sep 17 00:00:00 2001 From: David Wallace Date: Fri, 8 May 2026 07:49:30 +0200 Subject: [PATCH 026/131] Rename import to DjangoSiteAdmin, add link and search and order by id Signed-off-by: David Wallace --- rdmo/core/admin.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/rdmo/core/admin.py b/rdmo/core/admin.py index eb2d12079c..ead4a9c4ff 100644 --- a/rdmo/core/admin.py +++ b/rdmo/core/admin.py @@ -1,6 +1,6 @@ from django import forms from django.contrib import admin -from django.contrib.sites.admin import SiteAdmin +from django.contrib.sites.admin import SiteAdmin as DjangoSiteAdmin from django.contrib.sites.models import Site @@ -10,12 +10,14 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields['uri_path'].required = True - +# the original Django SiteAdmin needs to be unregistered first if admin.site.is_registered(Site): admin.site.unregister(Site) @admin.register(Site) -class CustomSiteAdmin(SiteAdmin): +class SiteAdmin(DjangoSiteAdmin): list_display = ('id', 'domain', 'name') - search_fields = ('domain', 'name') + list_display_links = ('domain',) + search_fields = ('id', 'domain', 'name') + ordering = ('id',) From 7da6be9ec69e12f40c4c4444c632f8f53f429400 Mon Sep 17 00:00:00 2001 From: David Wallace Date: Fri, 8 May 2026 07:56:10 +0200 Subject: [PATCH 027/131] Add test for new SiteAdmin Signed-off-by: David Wallace --- rdmo/core/tests/test_admin.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 rdmo/core/tests/test_admin.py diff --git a/rdmo/core/tests/test_admin.py b/rdmo/core/tests/test_admin.py new file mode 100644 index 0000000000..7b99dcbfae --- /dev/null +++ b/rdmo/core/tests/test_admin.py @@ -0,0 +1,18 @@ +import pytest + +from django.urls import reverse + +sites = ( + (1, 'example.com'), + (2, 'foo.com'), + (3, 'bar.com'), +) + + +@pytest.mark.parametrize('site_id, domain', sites) +def test_admin_sites_site(admin_client, site_id, domain): + url = reverse('admin:sites_site_changelist') + f'?q={domain}' + response = admin_client.get(url) + + assert response.status_code == 200 + assert reverse('admin:sites_site_change', args=[site_id]).encode() in response.content From 86edd494a84ffbea7845556f07b0baaa4e408c38 Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Fri, 8 May 2026 23:25:24 +0200 Subject: [PATCH 028/131] Add openapi urls to core urls (conditionally) --- rdmo/core/urls/v1/__init__.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/rdmo/core/urls/v1/__init__.py b/rdmo/core/urls/v1/__init__.py index 31119717b6..95e75fe568 100644 --- a/rdmo/core/urls/v1/__init__.py +++ b/rdmo/core/urls/v1/__init__.py @@ -1,3 +1,4 @@ +from django.apps import apps from django.urls import include, path urlpatterns = [ @@ -14,3 +15,8 @@ path('core/', include('rdmo.core.urls.v1.core')), ] + +if apps.is_installed('drf_spectacular'): + urlpatterns += [ + path('', include('rdmo.core.urls.v1.openapi')), + ] From 318d5fbb29139b858da6ae75046b7cc461ef7ac1 Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Sat, 9 May 2026 00:03:37 +0200 Subject: [PATCH 029/131] Use DRF token or session authentication for the schema --- rdmo/core/tests/test_openapi.py | 27 +++++++++++++++++++++++++-- rdmo/core/urls/v1/openapi.py | 13 +++++++++++-- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/rdmo/core/tests/test_openapi.py b/rdmo/core/tests/test_openapi.py index a12f319eff..4aa8b31d98 100644 --- a/rdmo/core/tests/test_openapi.py +++ b/rdmo/core/tests/test_openapi.py @@ -1,5 +1,9 @@ import pytest +from django.contrib.auth.models import User + +from rest_framework.authtoken.models import Token + import yaml pytestmark = pytest.mark.django_db @@ -12,7 +16,7 @@ @pytest.mark.parametrize('username', users) -def test_openapi_schema(db, client, login, settings, username): +def test_openapi_schema(db, client, login, username): login(username) response = client.get('/api/v1/schema/') @@ -24,7 +28,26 @@ def test_openapi_schema(db, client, login, settings, username): assert len(schema['paths']) > 120 assert len(schema['paths']) < 140 else: - assert response.status_code == 302 + assert response.status_code == 403 + + +def test_openapi_schema_token(db, client): + user = User.objects.get(username='user') + token, _ = Token.objects.get_or_create(user=user) + + response = client.get('/api/v1/schema/', headers={ + 'Authorization': f'Token {token}' + }) + + assert response.status_code == 200 + + +def test_openapi_schema_token_forbidden(db, client, login): + response = client.get('/api/v1/schema/', headers={ + 'Authorization': 'Token wrong' + }) + + assert response.status_code == 403 @pytest.mark.parametrize('username', users) diff --git a/rdmo/core/urls/v1/openapi.py b/rdmo/core/urls/v1/openapi.py index 81af0b569c..970af37a05 100644 --- a/rdmo/core/urls/v1/openapi.py +++ b/rdmo/core/urls/v1/openapi.py @@ -2,13 +2,22 @@ from django.urls import path from django.views.generic.base import RedirectView +from rest_framework.authentication import SessionAuthentication, TokenAuthentication +from rest_framework.permissions import IsAuthenticated + from drf_spectacular.views import SpectacularAPIView, SpectacularRedocView, SpectacularSwaggerView app_name = 'v1-openapi' + +class AuthenticatedSpectacularAPIView(SpectacularAPIView): + authentication_classes = [SessionAuthentication, TokenAuthentication] + permission_classes = [IsAuthenticated] + + urlpatterns = [ - path('schema/', login_required(SpectacularAPIView.as_view()), name='schema'), + path('schema/', AuthenticatedSpectacularAPIView.as_view(), name='schema'), path('swagger/', login_required(SpectacularSwaggerView.as_view(url_name='v1-openapi:schema')), name='swagger'), path('redoc/', login_required(SpectacularRedocView.as_view(url_name='v1-openapi:schema')), name='redoc'), - path('', RedirectView.as_view(pattern_name='api', permanent=False)) + path('', RedirectView.as_view(pattern_name='api', permanent=False)), ] From 6d0dedd2cb43524bc71383a39a9cf3eac8254134 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 9 May 2026 16:13:52 +0000 Subject: [PATCH 030/131] build(deps-dev): bump @babel/plugin-transform-modules-systemjs Bumps [@babel/plugin-transform-modules-systemjs](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-modules-systemjs) from 7.27.1 to 7.29.4. - [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.29.4/packages/babel-plugin-transform-modules-systemjs) --- updated-dependencies: - dependency-name: "@babel/plugin-transform-modules-systemjs" dependency-version: 7.29.4 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | 225 +++++++++++++++++++++++----------------------- 1 file changed, 110 insertions(+), 115 deletions(-) diff --git a/package-lock.json b/package-lock.json index 185e4b3f9c..bad89f09c3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -117,12 +117,11 @@ "dev": true }, "node_modules/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", - "license": "MIT", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" }, @@ -176,12 +175,12 @@ "devOptional": true }, "node_modules/@babel/generator": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.0.tgz", - "integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==", + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", "dependencies": { - "@babel/parser": "^7.28.0", - "@babel/types": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -321,27 +320,26 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", - "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", - "license": "MIT", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", - "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", "devOptional": true, "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.27.3" + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -364,11 +362,10 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", - "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -433,10 +430,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", - "license": "MIT", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "engines": { "node": ">=6.9.0" } @@ -480,11 +476,11 @@ } }, "node_modules/@babel/parser": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", - "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz", + "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==", "dependencies": { - "@babel/types": "^7.28.0" + "@babel/types": "^7.29.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -1070,16 +1066,15 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.27.1.tgz", - "integrity": "sha512-w5N1XzsRbc0PQStASMksmUeqECuzKuTJer7kFagK8AXgpCMkeDMO5S+aaFb7A51ZYDF7XI34qsTX+fkHiIm5yA==", + "version": "7.29.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.4.tgz", + "integrity": "sha512-N7QmZ0xRZfjHOfZeQLJjwgX2zS9pdGHSVl/cjSGlo4dXMqvurfxXDMKY4RqEKzPozV78VMcd0lxyG13mlbKc4w==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.27.1" + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.29.0" }, "engines": { "node": ">=6.9.0" @@ -1713,29 +1708,29 @@ } }, "node_modules/@babel/template": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.0.tgz", - "integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.0", + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.0", - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", "debug": "^4.3.1" }, "engines": { @@ -1743,12 +1738,12 @@ } }, "node_modules/@babel/types": { - "version": "7.28.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", - "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", "dependencies": { "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" + "@babel/helper-validator-identifier": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -8333,11 +8328,11 @@ } }, "@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", "requires": { - "@babel/helper-validator-identifier": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } @@ -8380,12 +8375,12 @@ } }, "@babel/generator": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.0.tgz", - "integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==", + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", "requires": { - "@babel/parser": "^7.28.0", - "@babel/types": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -8496,23 +8491,23 @@ } }, "@babel/helper-module-imports": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", - "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", "requires": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" } }, "@babel/helper-module-transforms": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", - "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", "devOptional": true, "requires": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.27.3" + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" } }, "@babel/helper-optimise-call-expression": { @@ -8525,9 +8520,9 @@ } }, "@babel/helper-plugin-utils": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", - "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", "dev": true }, "@babel/helper-remap-async-to-generator": { @@ -8568,9 +8563,9 @@ "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==" }, "@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==" + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==" }, "@babel/helper-validator-option": { "version": "7.27.1", @@ -8600,11 +8595,11 @@ } }, "@babel/parser": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", - "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz", + "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==", "requires": { - "@babel/types": "^7.28.0" + "@babel/types": "^7.29.0" } }, "@babel/plugin-bugfix-firefox-class-in-computed-class-key": { @@ -8947,15 +8942,15 @@ } }, "@babel/plugin-transform-modules-systemjs": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.27.1.tgz", - "integrity": "sha512-w5N1XzsRbc0PQStASMksmUeqECuzKuTJer7kFagK8AXgpCMkeDMO5S+aaFb7A51ZYDF7XI34qsTX+fkHiIm5yA==", + "version": "7.29.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.4.tgz", + "integrity": "sha512-N7QmZ0xRZfjHOfZeQLJjwgX2zS9pdGHSVl/cjSGlo4dXMqvurfxXDMKY4RqEKzPozV78VMcd0lxyG13mlbKc4w==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.27.1" + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.29.0" } }, "@babel/plugin-transform-modules-umd": { @@ -9361,36 +9356,36 @@ } }, "@babel/template": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", "requires": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" } }, "@babel/traverse": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.0.tgz", - "integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", "requires": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.0", + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.0", - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", "debug": "^4.3.1" } }, "@babel/types": { - "version": "7.28.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", - "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", "requires": { "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" + "@babel/helper-validator-identifier": "^7.28.5" } }, "@codemirror/autocomplete": { From 757e139a1a234a515a1930a5218632358e6a30eb Mon Sep 17 00:00:00 2001 From: David Wallace Date: Mon, 11 May 2026 15:30:56 +0200 Subject: [PATCH 031/131] Remove editors from AttributeViewSet prefetch and do not use select_related Signed-off-by: David Wallace --- rdmo/domain/viewsets.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/rdmo/domain/viewsets.py b/rdmo/domain/viewsets.py index 29981a9c47..a0846635d6 100644 --- a/rdmo/domain/viewsets.py +++ b/rdmo/domain/viewsets.py @@ -37,10 +37,8 @@ class AttributeViewSet(ModelViewSet): def get_queryset(self): queryset = Attribute.objects.all().order_by('path') - if self.action in ['index']: + if self.action in ('index','nested', 'export', 'detail_export'): return queryset - elif self.action in ('nested', 'export', 'detail_export'): - return queryset.select_related('parent') else: return queryset.annotate( values_count=models.Count('values') @@ -53,8 +51,7 @@ def get_queryset(self): 'questions', 'tasks_as_start', 'tasks_as_end', - 'editors' - ).select_related('parent') + ) def get_serializer_class(self): return AttributeListSerializer if self.action == 'list' else AttributeSerializer From 248e5bb056b08ec506910da07b09e51dee9baacc Mon Sep 17 00:00:00 2001 From: David Wallace Date: Wed, 13 May 2026 10:30:00 +0200 Subject: [PATCH 032/131] Add tests for api performance of all elements Signed-off-by: David Wallace --- rdmo/conditions/tests/test_api_performance.py | 32 ++++ .../tests/test_api_performance_condition.py | 36 ++++ rdmo/domain/tests/test_api_performance.py | 37 ++++ .../tests/test_api_performance_attribute.py | 31 +++ .../tests/test_management_api_performance.py | 181 ------------------ .../tests/test_api_performance_option.py | 33 ++++ .../tests/test_api_performance_options.py | 63 ++++++ .../tests/test_api_performance_optionset.py | 33 ++++ .../tests/test_api_performance_optionsets.py | 63 ++++++ rdmo/questions/tests/test_api_performance.py | 57 ++++++ .../tests/test_api_performance_catalog.py | 36 ++++ .../tests/test_api_performance_page.py | 36 ++++ .../tests/test_api_performance_question.py | 36 ++++ .../tests/test_api_performance_questionset.py | 36 ++++ .../tests/test_api_performance_section.py | 36 ++++ rdmo/tasks/tests/test_api_performance.py | 37 ++++ rdmo/tasks/tests/test_api_performance_task.py | 34 ++++ rdmo/views/tests/test_api_performance.py | 39 ++++ rdmo/views/tests/test_api_performance_view.py | 36 ++++ 19 files changed, 711 insertions(+), 181 deletions(-) create mode 100644 rdmo/conditions/tests/test_api_performance.py create mode 100644 rdmo/conditions/tests/test_api_performance_condition.py create mode 100644 rdmo/domain/tests/test_api_performance.py create mode 100644 rdmo/domain/tests/test_api_performance_attribute.py delete mode 100644 rdmo/management/tests/test_management_api_performance.py create mode 100644 rdmo/options/tests/test_api_performance_option.py create mode 100644 rdmo/options/tests/test_api_performance_options.py create mode 100644 rdmo/options/tests/test_api_performance_optionset.py create mode 100644 rdmo/options/tests/test_api_performance_optionsets.py create mode 100644 rdmo/questions/tests/test_api_performance.py create mode 100644 rdmo/questions/tests/test_api_performance_catalog.py create mode 100644 rdmo/questions/tests/test_api_performance_page.py create mode 100644 rdmo/questions/tests/test_api_performance_question.py create mode 100644 rdmo/questions/tests/test_api_performance_questionset.py create mode 100644 rdmo/questions/tests/test_api_performance_section.py create mode 100644 rdmo/tasks/tests/test_api_performance.py create mode 100644 rdmo/tasks/tests/test_api_performance_task.py create mode 100644 rdmo/views/tests/test_api_performance.py create mode 100644 rdmo/views/tests/test_api_performance_view.py diff --git a/rdmo/conditions/tests/test_api_performance.py b/rdmo/conditions/tests/test_api_performance.py new file mode 100644 index 0000000000..35209decaa --- /dev/null +++ b/rdmo/conditions/tests/test_api_performance.py @@ -0,0 +1,32 @@ +import pytest + +from django.urls import reverse + +max_query_map = { + # action: url_name url_kwargs max_query_counts + 'list': ('v1-conditions:condition-list', {}, 9), + 'index': ('v1-conditions:condition-index', {}, 3), + 'export': ('v1-conditions:condition-export', {'export_format': 'xml'}, 11), + 'detail': ('v1-conditions:condition-detail', {'pk': 1}, 9), + 'detail_export': ('v1-conditions:condition-detail-export', {'pk': 1, 'export_format': 'xml'}, 9), +} + + +@pytest.mark.performance +@pytest.mark.parametrize( + 'url_name,url_kwargs,max_queries', + [ + (url_name, url_kwargs, max_queries) + for action, (url_name, url_kwargs, max_queries) in max_query_map.items() + ], +) +def test_conditions_endpoints_query_counts( + db, admin_client,django_assert_max_num_queries, + url_name,url_kwargs,max_queries, +): + url = reverse(url_name, kwargs=url_kwargs) + + with django_assert_max_num_queries(max_queries): + response = admin_client.get(url) + + assert response.status_code == 200 diff --git a/rdmo/conditions/tests/test_api_performance_condition.py b/rdmo/conditions/tests/test_api_performance_condition.py new file mode 100644 index 0000000000..ca347aa1ce --- /dev/null +++ b/rdmo/conditions/tests/test_api_performance_condition.py @@ -0,0 +1,36 @@ +import pytest + +from django.urls import reverse + +from .test_viewset_condition import urlnames + +max_query_map = { + # action: max_queries url_kwargs + 'list': {'max_queries': 9}, + 'index': {'max_queries': 3}, + 'export': {'max_queries': 11, 'url_kwargs': {'export_format': 'xml'}}, + 'detail': {'max_queries': 9, 'url_kwargs': {'pk': 1}}, + 'detail_export': { + 'max_queries': 9, 'url_kwargs': {'pk': 1, 'export_format': 'xml'}, + }, +} + + +@pytest.mark.performance +@pytest.mark.parametrize( + 'action,max_queries,url_kwargs', + [ + (action, case['max_queries'], case.get('url_kwargs')) + for action, case in max_query_map.items() + ], +) +def test_actions_max_query_counts( + db, admin_client, django_assert_max_num_queries, + action, max_queries, url_kwargs, +): + url = reverse(urlnames[action], kwargs=url_kwargs) + + with django_assert_max_num_queries(max_queries): + response = admin_client.get(url) + + assert response.status_code == 200 diff --git a/rdmo/domain/tests/test_api_performance.py b/rdmo/domain/tests/test_api_performance.py new file mode 100644 index 0000000000..aeabfced8c --- /dev/null +++ b/rdmo/domain/tests/test_api_performance.py @@ -0,0 +1,37 @@ +import pytest + +from django.urls import reverse + +max_query_map = { + 'list': ('v1-domain:attribute-list', {}, 10), + 'index': ('v1-domain:attribute-index', {}, 3), + 'export': ('v1-domain:attribute-export', {'export_format': 'xml'}, 12), + 'detail': ('v1-domain:attribute-detail', {'pk': 1}, 11), + 'detail_export': ( + 'v1-domain:attribute-detail-export', + {'pk': 1, 'export_format': 'xml'}, + 12, + ), +} + +@pytest.mark.performance +@pytest.mark.parametrize( + 'url_name,url_kwargs,max_queries', + [ + (url_name, url_kwargs, max_queries) + for _, (url_name, url_kwargs, max_queries) in max_query_map.items() + ], +) +def test_domain_endpoints_query_counts( + db, + admin_client, + django_assert_max_num_queries, + url_name, + url_kwargs, + max_queries, +): + url = reverse(url_name, kwargs=url_kwargs) + + with django_assert_max_num_queries(max_queries): + response = admin_client.get(url) + assert response.status_code == 200 diff --git a/rdmo/domain/tests/test_api_performance_attribute.py b/rdmo/domain/tests/test_api_performance_attribute.py new file mode 100644 index 0000000000..45203e7e7d --- /dev/null +++ b/rdmo/domain/tests/test_api_performance_attribute.py @@ -0,0 +1,31 @@ +import pytest + +from django.urls import reverse + +from .test_viewset_attribute import urlnames + +max_query_map = { + 'list': {'max_queries': 10}, + 'index': {'max_queries': 3}, + 'export': {'max_queries': 12, 'url_kwargs': {'export_format': 'xml'}}, + 'detail': {'max_queries': 11, 'url_kwargs': {'pk': 1}}, + 'detail_export': {'max_queries': 12, 'url_kwargs': {'pk': 1, 'export_format': 'xml'}}, +} + +@pytest.mark.performance +@pytest.mark.parametrize( + 'action,max_queries,url_kwargs', + [ + (action, case['max_queries'], case.get('url_kwargs')) + for action, case in max_query_map.items() + ], +) +def test_domain_endpoints_query_counts( + db, admin_client, django_assert_max_num_queries, + action, max_queries, url_kwargs, +): + url = reverse(urlnames[action], kwargs=url_kwargs) + + with django_assert_max_num_queries(max_queries): + response = admin_client.get(url) + assert response.status_code == 200 diff --git a/rdmo/management/tests/test_management_api_performance.py b/rdmo/management/tests/test_management_api_performance.py deleted file mode 100644 index d25a68a16a..0000000000 --- a/rdmo/management/tests/test_management_api_performance.py +++ /dev/null @@ -1,181 +0,0 @@ -import pytest - -from rdmo.management.constants import RDMO_MODEL_PATH_MAPPER - -pytestmark = pytest.mark.django_db - - -MANAGEMENT_ENDPOINTS = { - "conditions.condition": { - "url": "/api/v1/conditions/conditions/", - "queries": { - "index": 3, - "list": 9, - "detail": 9, - }, - }, - "domain.attribute": { - "url": "/api/v1/domain/attributes/", - "queries": { - "index": 3, - "list": 10, - "detail": 11, - }, - }, - "options.optionset": { - "url": "/api/v1/options/optionsets/", - "queries": { - "index": 3, - "list": 8, - "detail": 8, - }, - }, - "options.option": { - "url": "/api/v1/options/options/", - "queries": { - "index": 3, - "list": 10, - "detail": 10, - }, - }, - "questions.catalog": { - "url": "/api/v1/questions/catalogs/", - "queries": { - "index": 3, - "list": 8, - "detail": 8, - }, - }, - "questions.section": { - "url": "/api/v1/questions/sections/", - "queries": { - "index": 3, - "list": 8, - "detail": 8, - }, - }, - "questions.page": { - "url": "/api/v1/questions/pages/", - "queries": { - "index": 3, - "list": 10, - "detail": 10, - }, - }, - "questions.questionset": { - "url": "/api/v1/questions/questionsets/", - "queries": { - "index": 3, - "list": 11, - "detail": 10, - }, - }, - "questions.question": { - "url": "/api/v1/questions/questions/", - "queries": { - "index": 3, - "list": 10, - "detail": 10, - }, - }, - "tasks.task": { - "url": "/api/v1/tasks/tasks/", - "queries": { - "index": 3, - "list": 10, - "detail": 10, - }, - }, - "views.view": { - "url": "/api/v1/views/views/", - "queries": { - "index": 3, - "list": 8, - "detail": 8, - }, - }, -} - - -def get_params(action): - params = [] - - for model_path, config in MANAGEMENT_ENDPOINTS.items(): - max_queries = config["queries"][action] - - if action == "index": - url = f'{config["url"]}index/' - else: - url = config["url"] - - params.append(pytest.param( - model_path, - url, - max_queries, - id=f"{model_path}-{action}", - )) - - return params - - -@pytest.mark.performance -@pytest.mark.parametrize( - "model_path,url,max_queries", - get_params("index"), -) -def test_management_index_endpoint_query_counts( - admin_client, - django_assert_max_num_queries, - model_path, - url, - max_queries, -): - assert model_path in RDMO_MODEL_PATH_MAPPER - - with django_assert_max_num_queries(max_queries): - response = admin_client.get(url) - - assert response.status_code == 200 - - -@pytest.mark.performance -@pytest.mark.parametrize( - "model_path,url,max_queries", - get_params("list"), -) -def test_management_list_endpoint_query_counts( - admin_client, - django_assert_max_num_queries, - model_path, - url, - max_queries, -): - assert model_path in RDMO_MODEL_PATH_MAPPER - - with django_assert_max_num_queries(max_queries): - response = admin_client.get(url) - - assert response.status_code == 200 - - -@pytest.mark.performance -@pytest.mark.parametrize( - "model_path,url,max_queries", - get_params("detail"), -) -def test_management_detail_endpoint_query_counts( - admin_client, - django_assert_max_num_queries, - model_path, - url, - max_queries, -): - model = RDMO_MODEL_PATH_MAPPER[model_path] - obj = model.objects.first() - - assert obj is not None, f"No test object exists for {model_path}." - - with django_assert_max_num_queries(max_queries): - response = admin_client.get(f"{url}{obj.pk}/") - - assert response.status_code == 200 diff --git a/rdmo/options/tests/test_api_performance_option.py b/rdmo/options/tests/test_api_performance_option.py new file mode 100644 index 0000000000..f5c6db19f2 --- /dev/null +++ b/rdmo/options/tests/test_api_performance_option.py @@ -0,0 +1,33 @@ +import pytest + +from django.urls import reverse + +from .test_viewset_options import urlnames + +max_query_map_option = { + 'list': {'max_queries': 10}, + 'index': {'max_queries': 3}, + 'export': {'max_queries': 12, 'url_kwargs': {'export_format': 'xml'}}, + 'detail': {'max_queries': 10, 'url_kwargs': {'pk': 1}}, + 'detail_export': {'max_queries': 12, 'url_kwargs': {'pk': 1, 'export_format': 'xml'}}, +} + +@pytest.mark.performance +@pytest.mark.parametrize( + 'action,max_queries,url_kwargs', + [ + (action, case['max_queries'], case.get('url_kwargs')) + for action, case in max_query_map_option.items() + ], +) +def test_option_endpoints_query_counts( + admin_client, + django_assert_max_num_queries, + action, + max_queries, + url_kwargs, +): + url = reverse(urlnames[action], kwargs=url_kwargs) + with django_assert_max_num_queries(max_queries): + response = admin_client.get(url) + assert response.status_code == 200 diff --git a/rdmo/options/tests/test_api_performance_options.py b/rdmo/options/tests/test_api_performance_options.py new file mode 100644 index 0000000000..650f5f1ad5 --- /dev/null +++ b/rdmo/options/tests/test_api_performance_options.py @@ -0,0 +1,63 @@ +import pytest + +from django.urls import reverse + +from .test_viewset_options import urlnames as option_urlnames +from .test_viewset_optionsets import urlnames as optionset_urlnames + +max_query_map_optionset = { + 'list': {'max_queries': 8}, + 'index': {'max_queries': 3}, + 'export': {'max_queries': 11, 'url_kwargs': {'export_format': 'xml'}}, + 'detail': {'max_queries': 8, 'url_kwargs': {'pk': 1}}, + 'detail_export': {'max_queries': 10, 'url_kwargs': {'pk': 1, 'export_format': 'xml'}}, +} + +max_query_map_option = { + 'list': {'max_queries': 10}, + 'index': {'max_queries': 3}, + 'export': {'max_queries': 12, 'url_kwargs': {'export_format': 'xml'}}, + 'detail': {'max_queries': 10, 'url_kwargs': {'pk': 1}}, + 'detail_export': {'max_queries': 12, 'url_kwargs': {'pk': 1, 'export_format': 'xml'}}, +} + +@pytest.mark.performance +@pytest.mark.parametrize( + 'action,max_queries,url_kwargs', + [ + (action, case['max_queries'], case.get('url_kwargs')) + for action, case in max_query_map_optionset.items() + ], +) +def test_optionset_endpoints_query_counts( + admin_client, + django_assert_max_num_queries, + action, + max_queries, + url_kwargs, +): + url = reverse(optionset_urlnames[action], kwargs=url_kwargs) + with django_assert_max_num_queries(max_queries): + response = admin_client.get(url) + assert response.status_code == 200 + + +@pytest.mark.performance +@pytest.mark.parametrize( + 'action,max_queries,url_kwargs', + [ + (action, case['max_queries'], case.get('url_kwargs')) + for action, case in max_query_map_option.items() + ], +) +def test_option_endpoints_query_counts( + admin_client, + django_assert_max_num_queries, + action, + max_queries, + url_kwargs, +): + url = reverse(option_urlnames[action], kwargs=url_kwargs) + with django_assert_max_num_queries(max_queries): + response = admin_client.get(url) + assert response.status_code == 200 diff --git a/rdmo/options/tests/test_api_performance_optionset.py b/rdmo/options/tests/test_api_performance_optionset.py new file mode 100644 index 0000000000..23036721fb --- /dev/null +++ b/rdmo/options/tests/test_api_performance_optionset.py @@ -0,0 +1,33 @@ +import pytest + +from django.urls import reverse + +from .test_viewset_optionsets import urlnames + +max_query_map_optionset = { + 'list': {'max_queries': 8}, + 'index': {'max_queries': 3}, + 'export': {'max_queries': 11, 'url_kwargs': {'export_format': 'xml'}}, + 'detail': {'max_queries': 8, 'url_kwargs': {'pk': 1}}, + 'detail_export': {'max_queries': 10, 'url_kwargs': {'pk': 1, 'export_format': 'xml'}}, +} + +@pytest.mark.performance +@pytest.mark.parametrize( + 'action,max_queries,url_kwargs', + [ + (action, case['max_queries'], case.get('url_kwargs')) + for action, case in max_query_map_optionset.items() + ], +) +def test_optionset_endpoints_query_counts( + admin_client, + django_assert_max_num_queries, + action, + max_queries, + url_kwargs, +): + url = reverse(urlnames[action], kwargs=url_kwargs) + with django_assert_max_num_queries(max_queries): + response = admin_client.get(url) + assert response.status_code == 200 diff --git a/rdmo/options/tests/test_api_performance_optionsets.py b/rdmo/options/tests/test_api_performance_optionsets.py new file mode 100644 index 0000000000..650f5f1ad5 --- /dev/null +++ b/rdmo/options/tests/test_api_performance_optionsets.py @@ -0,0 +1,63 @@ +import pytest + +from django.urls import reverse + +from .test_viewset_options import urlnames as option_urlnames +from .test_viewset_optionsets import urlnames as optionset_urlnames + +max_query_map_optionset = { + 'list': {'max_queries': 8}, + 'index': {'max_queries': 3}, + 'export': {'max_queries': 11, 'url_kwargs': {'export_format': 'xml'}}, + 'detail': {'max_queries': 8, 'url_kwargs': {'pk': 1}}, + 'detail_export': {'max_queries': 10, 'url_kwargs': {'pk': 1, 'export_format': 'xml'}}, +} + +max_query_map_option = { + 'list': {'max_queries': 10}, + 'index': {'max_queries': 3}, + 'export': {'max_queries': 12, 'url_kwargs': {'export_format': 'xml'}}, + 'detail': {'max_queries': 10, 'url_kwargs': {'pk': 1}}, + 'detail_export': {'max_queries': 12, 'url_kwargs': {'pk': 1, 'export_format': 'xml'}}, +} + +@pytest.mark.performance +@pytest.mark.parametrize( + 'action,max_queries,url_kwargs', + [ + (action, case['max_queries'], case.get('url_kwargs')) + for action, case in max_query_map_optionset.items() + ], +) +def test_optionset_endpoints_query_counts( + admin_client, + django_assert_max_num_queries, + action, + max_queries, + url_kwargs, +): + url = reverse(optionset_urlnames[action], kwargs=url_kwargs) + with django_assert_max_num_queries(max_queries): + response = admin_client.get(url) + assert response.status_code == 200 + + +@pytest.mark.performance +@pytest.mark.parametrize( + 'action,max_queries,url_kwargs', + [ + (action, case['max_queries'], case.get('url_kwargs')) + for action, case in max_query_map_option.items() + ], +) +def test_option_endpoints_query_counts( + admin_client, + django_assert_max_num_queries, + action, + max_queries, + url_kwargs, +): + url = reverse(option_urlnames[action], kwargs=url_kwargs) + with django_assert_max_num_queries(max_queries): + response = admin_client.get(url) + assert response.status_code == 200 diff --git a/rdmo/questions/tests/test_api_performance.py b/rdmo/questions/tests/test_api_performance.py new file mode 100644 index 0000000000..32dc4c702d --- /dev/null +++ b/rdmo/questions/tests/test_api_performance.py @@ -0,0 +1,57 @@ +import pytest + +from django.urls import reverse + +# (action, url_name, kwargs, max_queries) +max_queries_mapping = [ + # Catalog endpoints + ('catalog_list', 'v1-questions:catalog-list', {}, 8), + ('catalog_index', 'v1-questions:catalog-index', {}, 3), + ('catalog_export', 'v1-questions:catalog-export', {'export_format': 'xml'}, 30), + ('catalog_detail', 'v1-questions:catalog-detail', {'pk': 1}, 8), + ('catalog_detail_export', 'v1-questions:catalog-detail-export', + {'pk': 1, 'export_format': 'xml'}, 10), + + # Section endpoints + ('section_list', 'v1-questions:section-list', {}, 8), + ('section_index', 'v1-questions:section-index', {}, 3), + ('section_export', 'v1-questions:section-export', {'export_format': 'xml'}, 10), + ('section_detail', 'v1-questions:section-detail', {'pk': 1}, 8), + ('section_detail_export', 'v1-questions:section-detail-export', + {'pk': 1, 'export_format': 'xml'}, 10), + + # Page endpoints + ('page_list', 'v1-questions:page-list', {}, 10), + ('page_index', 'v1-questions:page-index', {}, 3), + ('page_export', 'v1-questions:page-export', {'export_format': 'xml'}, 12), + ('page_detail', 'v1-questions:page-detail', {'pk': 1}, 10), + ('page_detail_export', 'v1-questions:page-detail-export', + {'pk': 1, 'export_format': 'xml'}, 13), + + # Questionset endpoints + ('questionset_list', 'v1-questions:questionset-list', {}, 11), + ('questionset_index', 'v1-questions:questionset-index', {}, 3), + ('questionset_export', 'v1-questions:questionset-export', {'export_format': 'xml'}, 13), + ('questionset_detail', 'v1-questions:questionset-detail', {'pk': 89}, 10), + ('questionset_detail_export', 'v1-questions:questionset-detail-export', + {'pk': 89, 'export_format': 'xml'}, 13), + + # Question endpoints + ('question_list', 'v1-questions:question-list', {}, 10), + ('question_index', 'v1-questions:question-index', {}, 3), + ('question_export', 'v1-questions:question-export', {'export_format': 'xml'}, 12), + ('question_detail', 'v1-questions:question-detail', {'pk': 1}, 10), + ('question_detail_export', 'v1-questions:question-detail-export', + {'pk': 1, 'export_format': 'xml'}, 12), +] + +@pytest.mark.performance +@pytest.mark.parametrize('action,url_name,url_kwargs,max_queries', max_queries_mapping) +def test_questions_endpoints_query_counts(db, admin_client, django_assert_max_num_queries, + action, url_name, url_kwargs, max_queries): + url = reverse(url_name, kwargs=url_kwargs) + + with django_assert_max_num_queries(max_queries): + response = admin_client.get(url) + + assert response.status_code == 200 diff --git a/rdmo/questions/tests/test_api_performance_catalog.py b/rdmo/questions/tests/test_api_performance_catalog.py new file mode 100644 index 0000000000..8b65d40d02 --- /dev/null +++ b/rdmo/questions/tests/test_api_performance_catalog.py @@ -0,0 +1,36 @@ +import pytest + +from django.urls import reverse + +from .test_viewset_catalog import urlnames + +max_query_map = { + # action: max_queries url_kwargs + 'list': {'max_queries': 8}, + 'index': {'max_queries': 3}, + 'export': {'max_queries': 30, 'url_kwargs': {'export_format': 'xml'}}, + 'detail': {'max_queries': 8, 'url_kwargs': {'pk': 1}}, + 'detail_export': { + 'max_queries': 29, 'url_kwargs': {'pk': 1, 'export_format': 'xml'}, + }, +} + + +@pytest.mark.performance +@pytest.mark.parametrize( + 'action,max_queries,url_kwargs', + [ + (action, case['max_queries'], case.get('url_kwargs')) + for action, case in max_query_map.items() + ], +) +def test_catalog_endpoints_query_counts( + db, admin_client, django_assert_max_num_queries, + action, max_queries, url_kwargs, +): + url = reverse(urlnames[action], kwargs=url_kwargs) + + with django_assert_max_num_queries(max_queries): + response = admin_client.get(url) + + assert response.status_code == 200 diff --git a/rdmo/questions/tests/test_api_performance_page.py b/rdmo/questions/tests/test_api_performance_page.py new file mode 100644 index 0000000000..d44a31d9d8 --- /dev/null +++ b/rdmo/questions/tests/test_api_performance_page.py @@ -0,0 +1,36 @@ +import pytest + +from django.urls import reverse + +from .test_viewset_page import urlnames + +max_query_map = { + # action: max_queries url_kwargs + 'list': {'max_queries': 10}, + 'index': {'max_queries': 3}, + 'export': {'max_queries': 25, 'url_kwargs': {'export_format': 'xml'}}, + 'detail': {'max_queries': 10, 'url_kwargs': {'pk': 1}}, + 'detail_export': { + 'max_queries': 13, 'url_kwargs': {'pk': 1, 'export_format': 'xml'}, + }, +} + + +@pytest.mark.performance +@pytest.mark.parametrize( + 'action,max_queries,url_kwargs', + [ + (action, case['max_queries'], case.get('url_kwargs')) + for action, case in max_query_map.items() + ], +) +def test_page_endpoints_query_counts( + db, admin_client, django_assert_max_num_queries, + action, max_queries, url_kwargs, +): + url = reverse(urlnames[action], kwargs=url_kwargs) + + with django_assert_max_num_queries(max_queries): + response = admin_client.get(url) + + assert response.status_code == 200 diff --git a/rdmo/questions/tests/test_api_performance_question.py b/rdmo/questions/tests/test_api_performance_question.py new file mode 100644 index 0000000000..77d7021188 --- /dev/null +++ b/rdmo/questions/tests/test_api_performance_question.py @@ -0,0 +1,36 @@ +import pytest + +from django.urls import reverse + +from .test_viewset_question import urlnames + +max_query_map = { + # action: max_queries url_kwargs + 'list': {'max_queries': 10}, + 'index': {'max_queries': 3}, + 'export': {'max_queries': 12, 'url_kwargs': {'export_format': 'xml'}}, + 'detail': {'max_queries': 10, 'url_kwargs': {'pk': 1}}, + 'detail_export': { + 'max_queries': 12, 'url_kwargs': {'pk': 1, 'export_format': 'xml'}, + }, +} + + +@pytest.mark.performance +@pytest.mark.parametrize( + 'action,max_queries,url_kwargs', + [ + (action, case['max_queries'], case.get('url_kwargs')) + for action, case in max_query_map.items() + ], +) +def test_question_endpoints_query_counts( + db, admin_client, django_assert_max_num_queries, + action, max_queries, url_kwargs, +): + url = reverse(urlnames[action], kwargs=url_kwargs) + + with django_assert_max_num_queries(max_queries): + response = admin_client.get(url) + + assert response.status_code == 200 diff --git a/rdmo/questions/tests/test_api_performance_questionset.py b/rdmo/questions/tests/test_api_performance_questionset.py new file mode 100644 index 0000000000..5513e7255f --- /dev/null +++ b/rdmo/questions/tests/test_api_performance_questionset.py @@ -0,0 +1,36 @@ +import pytest + +from django.urls import reverse + +from .test_viewset_questionset import urlnames + +max_query_map = { + # action: max_queries url_kwargs + 'list': {'max_queries': 11}, + 'index': {'max_queries': 3}, + 'export': {'max_queries': 17, 'url_kwargs': {'export_format': 'xml'}}, + 'detail': {'max_queries': 10, 'url_kwargs': {'pk': 89}}, + 'detail_export': { + 'max_queries': 13, 'url_kwargs': {'pk': 89, 'export_format': 'xml'}, + }, +} + + +@pytest.mark.performance +@pytest.mark.parametrize( + 'action,max_queries,url_kwargs', + [ + (action, case['max_queries'], case.get('url_kwargs')) + for action, case in max_query_map.items() + ], +) +def test_questionset_endpoints_query_counts( + db, admin_client, django_assert_max_num_queries, + action, max_queries, url_kwargs, +): + url = reverse(urlnames[action], kwargs=url_kwargs) + + with django_assert_max_num_queries(max_queries): + response = admin_client.get(url) + + assert response.status_code == 200 diff --git a/rdmo/questions/tests/test_api_performance_section.py b/rdmo/questions/tests/test_api_performance_section.py new file mode 100644 index 0000000000..13c5d8ab9d --- /dev/null +++ b/rdmo/questions/tests/test_api_performance_section.py @@ -0,0 +1,36 @@ +import pytest + +from django.urls import reverse + +from .test_viewset_section import urlnames + +max_query_map = { + # action: max_queries url_kwargs + 'list': {'max_queries': 8}, + 'index': {'max_queries': 3}, + 'export': {'max_queries': 27, 'url_kwargs': {'export_format': 'xml'}}, + 'detail': {'max_queries': 8, 'url_kwargs': {'pk': 1}}, + 'detail_export': { + 'max_queries': 12, 'url_kwargs': {'pk': 1, 'export_format': 'xml'}, + }, +} + + +@pytest.mark.performance +@pytest.mark.parametrize( + 'action,max_queries,url_kwargs', + [ + (action, case['max_queries'], case.get('url_kwargs')) + for action, case in max_query_map.items() + ], +) +def test_section_endpoints_query_counts( + db, admin_client, django_assert_max_num_queries, + action, max_queries, url_kwargs, +): + url = reverse(urlnames[action], kwargs=url_kwargs) + + with django_assert_max_num_queries(max_queries): + response = admin_client.get(url) + + assert response.status_code == 200 diff --git a/rdmo/tasks/tests/test_api_performance.py b/rdmo/tasks/tests/test_api_performance.py new file mode 100644 index 0000000000..775daf190f --- /dev/null +++ b/rdmo/tasks/tests/test_api_performance.py @@ -0,0 +1,37 @@ +import pytest + +from django.urls import reverse + +max_query_map = { + 'list': ('v1-tasks:task-list', {}, 10), + 'index': ('v1-tasks:task-index', {}, 3), + 'export': ('v1-tasks:task-export', {'export_format': 'xml'}, 12), + 'detail': ('v1-tasks:task-detail', {'pk': 1}, 10), + 'detail_export': ( + 'v1-tasks:task-detail-export', + {'pk': 1, 'export_format': 'xml'}, + 12, + ), +} + +@pytest.mark.performance +@pytest.mark.parametrize( + 'action,url_name,url_kwargs,max_queries', + [ + (action, url_name, url_kwargs, max_queries) + for action, (url_name, url_kwargs, max_queries) in max_query_map.items() + ], +) +def test_tasks_endpoints_query_counts( + db, + admin_client, + django_assert_max_num_queries, + action, + url_name, + url_kwargs, + max_queries, +): + url = reverse(url_name, kwargs=url_kwargs) + with django_assert_max_num_queries(max_queries): + response = admin_client.get(url) + assert response.status_code == 200 diff --git a/rdmo/tasks/tests/test_api_performance_task.py b/rdmo/tasks/tests/test_api_performance_task.py new file mode 100644 index 0000000000..e51523cc6f --- /dev/null +++ b/rdmo/tasks/tests/test_api_performance_task.py @@ -0,0 +1,34 @@ +import pytest + +from django.urls import reverse + +from .test_viewset_task import urlnames + +max_query_map = { + 'list': {'max_queries': 10}, + 'index': {'max_queries': 3}, + 'export': {'max_queries': 12, 'url_kwargs': {'export_format': 'xml'}}, + 'detail': {'max_queries': 10, 'url_kwargs': {'pk': 1}}, + 'detail_export': {'max_queries': 12, 'url_kwargs': {'pk': 1, 'export_format': 'xml'}}, +} + +@pytest.mark.performance +@pytest.mark.parametrize( + 'action,max_queries,url_kwargs', + [ + (action, case['max_queries'], case.get('url_kwargs')) + for action, case in max_query_map.items() + ], +) +def test_tasks_endpoints_query_counts( + db, + admin_client, + django_assert_max_num_queries, + action, + max_queries, + url_kwargs, +): + url = reverse(urlnames[action], kwargs=url_kwargs) + with django_assert_max_num_queries(max_queries): + response = admin_client.get(url) + assert response.status_code == 200 diff --git a/rdmo/views/tests/test_api_performance.py b/rdmo/views/tests/test_api_performance.py new file mode 100644 index 0000000000..676e067529 --- /dev/null +++ b/rdmo/views/tests/test_api_performance.py @@ -0,0 +1,39 @@ +import pytest + +from django.urls import reverse + +max_query_map = { + 'list': ('v1-views:view-list', {}, 8), + 'index': ('v1-views:view-index', {}, 3), + 'export': ('v1-views:view-export', {'export_format': 'xml'}, 10), + 'detail': ('v1-views:view-detail', {'pk': 1}, 8), + 'detail_export': ( + 'v1-views:view-detail-export', + {'pk': 1, 'export_format': 'xml'}, + 10, + ), +} + +@pytest.mark.performance +@pytest.mark.parametrize( + 'action,url_name,url_kwargs,max_queries', + [ + (action, url_name, url_kwargs, max_queries) + for action, (url_name, url_kwargs, max_queries) in max_query_map.items() + ], +) +def test_views_endpoints_query_counts( + db, + admin_client, + django_assert_max_num_queries, + action, + url_name, + url_kwargs, + max_queries, +): + url = reverse(url_name, kwargs=url_kwargs) + + with django_assert_max_num_queries(max_queries): + response = admin_client.get(url) + + assert response.status_code == 200 diff --git a/rdmo/views/tests/test_api_performance_view.py b/rdmo/views/tests/test_api_performance_view.py new file mode 100644 index 0000000000..4bf4918e4e --- /dev/null +++ b/rdmo/views/tests/test_api_performance_view.py @@ -0,0 +1,36 @@ +import pytest + +from django.urls import reverse + +from .test_viewset_view import urlnames + +max_query_map = { + 'list': {'max_queries': 8}, + 'index': {'max_queries': 3}, + 'export': {'max_queries': 10, 'url_kwargs': {'export_format': 'xml'}}, + 'detail': {'max_queries': 8, 'url_kwargs': {'pk': 1}}, + 'detail_export': {'max_queries': 10, 'url_kwargs': {'pk': 1, 'export_format': 'xml'}}, +} + +@pytest.mark.performance +@pytest.mark.parametrize( + 'action,max_queries,url_kwargs', + [ + (action, case['max_queries'], case.get('url_kwargs')) + for action, case in max_query_map.items() + ], +) +def test_views_endpoints_query_counts( + db, + admin_client, + django_assert_max_num_queries, + action, + max_queries, + url_kwargs, +): + url = reverse(urlnames[action], kwargs=url_kwargs) + + with django_assert_max_num_queries(max_queries): + response = admin_client.get(url) + + assert response.status_code == 200 From 4bf0ba3e58826afbfc9f780352e847c8a5b6e88b Mon Sep 17 00:00:00 2001 From: David Wallace Date: Wed, 13 May 2026 10:34:21 +0200 Subject: [PATCH 033/131] Clean up tests Signed-off-by: David Wallace --- rdmo/conditions/tests/test_api_performance.py | 32 ---------- rdmo/domain/tests/test_api_performance.py | 37 ----------- .../tests/test_api_performance_options.py | 63 ------------------- .../tests/test_api_performance_optionsets.py | 63 ------------------- rdmo/questions/tests/test_api_performance.py | 57 ----------------- rdmo/tasks/tests/test_api_performance.py | 37 ----------- rdmo/views/tests/test_api_performance.py | 39 ------------ 7 files changed, 328 deletions(-) delete mode 100644 rdmo/conditions/tests/test_api_performance.py delete mode 100644 rdmo/domain/tests/test_api_performance.py delete mode 100644 rdmo/options/tests/test_api_performance_options.py delete mode 100644 rdmo/options/tests/test_api_performance_optionsets.py delete mode 100644 rdmo/questions/tests/test_api_performance.py delete mode 100644 rdmo/tasks/tests/test_api_performance.py delete mode 100644 rdmo/views/tests/test_api_performance.py diff --git a/rdmo/conditions/tests/test_api_performance.py b/rdmo/conditions/tests/test_api_performance.py deleted file mode 100644 index 35209decaa..0000000000 --- a/rdmo/conditions/tests/test_api_performance.py +++ /dev/null @@ -1,32 +0,0 @@ -import pytest - -from django.urls import reverse - -max_query_map = { - # action: url_name url_kwargs max_query_counts - 'list': ('v1-conditions:condition-list', {}, 9), - 'index': ('v1-conditions:condition-index', {}, 3), - 'export': ('v1-conditions:condition-export', {'export_format': 'xml'}, 11), - 'detail': ('v1-conditions:condition-detail', {'pk': 1}, 9), - 'detail_export': ('v1-conditions:condition-detail-export', {'pk': 1, 'export_format': 'xml'}, 9), -} - - -@pytest.mark.performance -@pytest.mark.parametrize( - 'url_name,url_kwargs,max_queries', - [ - (url_name, url_kwargs, max_queries) - for action, (url_name, url_kwargs, max_queries) in max_query_map.items() - ], -) -def test_conditions_endpoints_query_counts( - db, admin_client,django_assert_max_num_queries, - url_name,url_kwargs,max_queries, -): - url = reverse(url_name, kwargs=url_kwargs) - - with django_assert_max_num_queries(max_queries): - response = admin_client.get(url) - - assert response.status_code == 200 diff --git a/rdmo/domain/tests/test_api_performance.py b/rdmo/domain/tests/test_api_performance.py deleted file mode 100644 index aeabfced8c..0000000000 --- a/rdmo/domain/tests/test_api_performance.py +++ /dev/null @@ -1,37 +0,0 @@ -import pytest - -from django.urls import reverse - -max_query_map = { - 'list': ('v1-domain:attribute-list', {}, 10), - 'index': ('v1-domain:attribute-index', {}, 3), - 'export': ('v1-domain:attribute-export', {'export_format': 'xml'}, 12), - 'detail': ('v1-domain:attribute-detail', {'pk': 1}, 11), - 'detail_export': ( - 'v1-domain:attribute-detail-export', - {'pk': 1, 'export_format': 'xml'}, - 12, - ), -} - -@pytest.mark.performance -@pytest.mark.parametrize( - 'url_name,url_kwargs,max_queries', - [ - (url_name, url_kwargs, max_queries) - for _, (url_name, url_kwargs, max_queries) in max_query_map.items() - ], -) -def test_domain_endpoints_query_counts( - db, - admin_client, - django_assert_max_num_queries, - url_name, - url_kwargs, - max_queries, -): - url = reverse(url_name, kwargs=url_kwargs) - - with django_assert_max_num_queries(max_queries): - response = admin_client.get(url) - assert response.status_code == 200 diff --git a/rdmo/options/tests/test_api_performance_options.py b/rdmo/options/tests/test_api_performance_options.py deleted file mode 100644 index 650f5f1ad5..0000000000 --- a/rdmo/options/tests/test_api_performance_options.py +++ /dev/null @@ -1,63 +0,0 @@ -import pytest - -from django.urls import reverse - -from .test_viewset_options import urlnames as option_urlnames -from .test_viewset_optionsets import urlnames as optionset_urlnames - -max_query_map_optionset = { - 'list': {'max_queries': 8}, - 'index': {'max_queries': 3}, - 'export': {'max_queries': 11, 'url_kwargs': {'export_format': 'xml'}}, - 'detail': {'max_queries': 8, 'url_kwargs': {'pk': 1}}, - 'detail_export': {'max_queries': 10, 'url_kwargs': {'pk': 1, 'export_format': 'xml'}}, -} - -max_query_map_option = { - 'list': {'max_queries': 10}, - 'index': {'max_queries': 3}, - 'export': {'max_queries': 12, 'url_kwargs': {'export_format': 'xml'}}, - 'detail': {'max_queries': 10, 'url_kwargs': {'pk': 1}}, - 'detail_export': {'max_queries': 12, 'url_kwargs': {'pk': 1, 'export_format': 'xml'}}, -} - -@pytest.mark.performance -@pytest.mark.parametrize( - 'action,max_queries,url_kwargs', - [ - (action, case['max_queries'], case.get('url_kwargs')) - for action, case in max_query_map_optionset.items() - ], -) -def test_optionset_endpoints_query_counts( - admin_client, - django_assert_max_num_queries, - action, - max_queries, - url_kwargs, -): - url = reverse(optionset_urlnames[action], kwargs=url_kwargs) - with django_assert_max_num_queries(max_queries): - response = admin_client.get(url) - assert response.status_code == 200 - - -@pytest.mark.performance -@pytest.mark.parametrize( - 'action,max_queries,url_kwargs', - [ - (action, case['max_queries'], case.get('url_kwargs')) - for action, case in max_query_map_option.items() - ], -) -def test_option_endpoints_query_counts( - admin_client, - django_assert_max_num_queries, - action, - max_queries, - url_kwargs, -): - url = reverse(option_urlnames[action], kwargs=url_kwargs) - with django_assert_max_num_queries(max_queries): - response = admin_client.get(url) - assert response.status_code == 200 diff --git a/rdmo/options/tests/test_api_performance_optionsets.py b/rdmo/options/tests/test_api_performance_optionsets.py deleted file mode 100644 index 650f5f1ad5..0000000000 --- a/rdmo/options/tests/test_api_performance_optionsets.py +++ /dev/null @@ -1,63 +0,0 @@ -import pytest - -from django.urls import reverse - -from .test_viewset_options import urlnames as option_urlnames -from .test_viewset_optionsets import urlnames as optionset_urlnames - -max_query_map_optionset = { - 'list': {'max_queries': 8}, - 'index': {'max_queries': 3}, - 'export': {'max_queries': 11, 'url_kwargs': {'export_format': 'xml'}}, - 'detail': {'max_queries': 8, 'url_kwargs': {'pk': 1}}, - 'detail_export': {'max_queries': 10, 'url_kwargs': {'pk': 1, 'export_format': 'xml'}}, -} - -max_query_map_option = { - 'list': {'max_queries': 10}, - 'index': {'max_queries': 3}, - 'export': {'max_queries': 12, 'url_kwargs': {'export_format': 'xml'}}, - 'detail': {'max_queries': 10, 'url_kwargs': {'pk': 1}}, - 'detail_export': {'max_queries': 12, 'url_kwargs': {'pk': 1, 'export_format': 'xml'}}, -} - -@pytest.mark.performance -@pytest.mark.parametrize( - 'action,max_queries,url_kwargs', - [ - (action, case['max_queries'], case.get('url_kwargs')) - for action, case in max_query_map_optionset.items() - ], -) -def test_optionset_endpoints_query_counts( - admin_client, - django_assert_max_num_queries, - action, - max_queries, - url_kwargs, -): - url = reverse(optionset_urlnames[action], kwargs=url_kwargs) - with django_assert_max_num_queries(max_queries): - response = admin_client.get(url) - assert response.status_code == 200 - - -@pytest.mark.performance -@pytest.mark.parametrize( - 'action,max_queries,url_kwargs', - [ - (action, case['max_queries'], case.get('url_kwargs')) - for action, case in max_query_map_option.items() - ], -) -def test_option_endpoints_query_counts( - admin_client, - django_assert_max_num_queries, - action, - max_queries, - url_kwargs, -): - url = reverse(option_urlnames[action], kwargs=url_kwargs) - with django_assert_max_num_queries(max_queries): - response = admin_client.get(url) - assert response.status_code == 200 diff --git a/rdmo/questions/tests/test_api_performance.py b/rdmo/questions/tests/test_api_performance.py deleted file mode 100644 index 32dc4c702d..0000000000 --- a/rdmo/questions/tests/test_api_performance.py +++ /dev/null @@ -1,57 +0,0 @@ -import pytest - -from django.urls import reverse - -# (action, url_name, kwargs, max_queries) -max_queries_mapping = [ - # Catalog endpoints - ('catalog_list', 'v1-questions:catalog-list', {}, 8), - ('catalog_index', 'v1-questions:catalog-index', {}, 3), - ('catalog_export', 'v1-questions:catalog-export', {'export_format': 'xml'}, 30), - ('catalog_detail', 'v1-questions:catalog-detail', {'pk': 1}, 8), - ('catalog_detail_export', 'v1-questions:catalog-detail-export', - {'pk': 1, 'export_format': 'xml'}, 10), - - # Section endpoints - ('section_list', 'v1-questions:section-list', {}, 8), - ('section_index', 'v1-questions:section-index', {}, 3), - ('section_export', 'v1-questions:section-export', {'export_format': 'xml'}, 10), - ('section_detail', 'v1-questions:section-detail', {'pk': 1}, 8), - ('section_detail_export', 'v1-questions:section-detail-export', - {'pk': 1, 'export_format': 'xml'}, 10), - - # Page endpoints - ('page_list', 'v1-questions:page-list', {}, 10), - ('page_index', 'v1-questions:page-index', {}, 3), - ('page_export', 'v1-questions:page-export', {'export_format': 'xml'}, 12), - ('page_detail', 'v1-questions:page-detail', {'pk': 1}, 10), - ('page_detail_export', 'v1-questions:page-detail-export', - {'pk': 1, 'export_format': 'xml'}, 13), - - # Questionset endpoints - ('questionset_list', 'v1-questions:questionset-list', {}, 11), - ('questionset_index', 'v1-questions:questionset-index', {}, 3), - ('questionset_export', 'v1-questions:questionset-export', {'export_format': 'xml'}, 13), - ('questionset_detail', 'v1-questions:questionset-detail', {'pk': 89}, 10), - ('questionset_detail_export', 'v1-questions:questionset-detail-export', - {'pk': 89, 'export_format': 'xml'}, 13), - - # Question endpoints - ('question_list', 'v1-questions:question-list', {}, 10), - ('question_index', 'v1-questions:question-index', {}, 3), - ('question_export', 'v1-questions:question-export', {'export_format': 'xml'}, 12), - ('question_detail', 'v1-questions:question-detail', {'pk': 1}, 10), - ('question_detail_export', 'v1-questions:question-detail-export', - {'pk': 1, 'export_format': 'xml'}, 12), -] - -@pytest.mark.performance -@pytest.mark.parametrize('action,url_name,url_kwargs,max_queries', max_queries_mapping) -def test_questions_endpoints_query_counts(db, admin_client, django_assert_max_num_queries, - action, url_name, url_kwargs, max_queries): - url = reverse(url_name, kwargs=url_kwargs) - - with django_assert_max_num_queries(max_queries): - response = admin_client.get(url) - - assert response.status_code == 200 diff --git a/rdmo/tasks/tests/test_api_performance.py b/rdmo/tasks/tests/test_api_performance.py deleted file mode 100644 index 775daf190f..0000000000 --- a/rdmo/tasks/tests/test_api_performance.py +++ /dev/null @@ -1,37 +0,0 @@ -import pytest - -from django.urls import reverse - -max_query_map = { - 'list': ('v1-tasks:task-list', {}, 10), - 'index': ('v1-tasks:task-index', {}, 3), - 'export': ('v1-tasks:task-export', {'export_format': 'xml'}, 12), - 'detail': ('v1-tasks:task-detail', {'pk': 1}, 10), - 'detail_export': ( - 'v1-tasks:task-detail-export', - {'pk': 1, 'export_format': 'xml'}, - 12, - ), -} - -@pytest.mark.performance -@pytest.mark.parametrize( - 'action,url_name,url_kwargs,max_queries', - [ - (action, url_name, url_kwargs, max_queries) - for action, (url_name, url_kwargs, max_queries) in max_query_map.items() - ], -) -def test_tasks_endpoints_query_counts( - db, - admin_client, - django_assert_max_num_queries, - action, - url_name, - url_kwargs, - max_queries, -): - url = reverse(url_name, kwargs=url_kwargs) - with django_assert_max_num_queries(max_queries): - response = admin_client.get(url) - assert response.status_code == 200 diff --git a/rdmo/views/tests/test_api_performance.py b/rdmo/views/tests/test_api_performance.py deleted file mode 100644 index 676e067529..0000000000 --- a/rdmo/views/tests/test_api_performance.py +++ /dev/null @@ -1,39 +0,0 @@ -import pytest - -from django.urls import reverse - -max_query_map = { - 'list': ('v1-views:view-list', {}, 8), - 'index': ('v1-views:view-index', {}, 3), - 'export': ('v1-views:view-export', {'export_format': 'xml'}, 10), - 'detail': ('v1-views:view-detail', {'pk': 1}, 8), - 'detail_export': ( - 'v1-views:view-detail-export', - {'pk': 1, 'export_format': 'xml'}, - 10, - ), -} - -@pytest.mark.performance -@pytest.mark.parametrize( - 'action,url_name,url_kwargs,max_queries', - [ - (action, url_name, url_kwargs, max_queries) - for action, (url_name, url_kwargs, max_queries) in max_query_map.items() - ], -) -def test_views_endpoints_query_counts( - db, - admin_client, - django_assert_max_num_queries, - action, - url_name, - url_kwargs, - max_queries, -): - url = reverse(url_name, kwargs=url_kwargs) - - with django_assert_max_num_queries(max_queries): - response = admin_client.get(url) - - assert response.status_code == 200 From 0ef1b748800941951b8fdc384335d62c31cce780 Mon Sep 17 00:00:00 2001 From: David Wallace Date: Wed, 13 May 2026 11:03:27 +0200 Subject: [PATCH 034/131] Optimize queries for export and detail_export actions on elements Signed-off-by: David Wallace --- rdmo/conditions/viewsets.py | 48 +++++++++--- rdmo/domain/serializers/export.py | 36 ++++++++- rdmo/domain/serializers/v1.py | 5 +- rdmo/domain/viewsets.py | 20 +++-- rdmo/options/viewsets.py | 18 +++-- rdmo/questions/serializers/export.py | 42 +++++++++-- rdmo/questions/viewsets.py | 108 +++++++++++++++++---------- rdmo/tasks/viewsets.py | 9 ++- rdmo/views/viewsets.py | 9 ++- 9 files changed, 210 insertions(+), 85 deletions(-) diff --git a/rdmo/conditions/viewsets.py b/rdmo/conditions/viewsets.py index 35a382b55e..40769fed49 100644 --- a/rdmo/conditions/viewsets.py +++ b/rdmo/conditions/viewsets.py @@ -10,6 +10,7 @@ from rdmo.core.permissions import HasModelPermission, HasObjectPermission from rdmo.core.utils import is_truthy, render_to_format from rdmo.core.views import ChoicesViewSet +from rdmo.domain.models import Attribute from .models import Condition from .renderers import ConditionRenderer @@ -17,6 +18,11 @@ from .serializers.v1 import ConditionIndexSerializer, ConditionSerializer +def get_attributes_by_id(): + attributes = list(Attribute.objects.all()) + return {attribute.pk: attribute for attribute in attributes} + + class ConditionViewSet(ModelViewSet): permission_classes = (HasModelPermission | HasObjectPermission, ) serializer_class = ConditionSerializer @@ -37,6 +43,19 @@ def get_queryset(self): queryset = Condition.objects.all() if self.action in ['index']: return queryset + elif self.action in ['export', 'detail_export']: + return queryset.select_related( + 'source', + 'source__parent', + 'target_option' + ).prefetch_related( + 'optionsets', + 'pages', + 'questionsets', + 'questions', + 'tasks', + 'editors' + ) else: return queryset.select_related( 'source', @@ -60,33 +79,40 @@ def index(self, request): def export(self, request, export_format='xml'): queryset = self.filter_queryset(self.get_queryset()) if export_format == 'xml': - serializer = ConditionExportSerializer(queryset, many=True) - xml = ConditionRenderer().render(serializer.data, context=self.get_export_renderer_context(request)) + conditions = list(queryset) + context = self.get_export_renderer_context(request, attributes_by_id=True) + serializer = ConditionExportSerializer(conditions, many=True, context=context) + xml = ConditionRenderer().render(serializer.data, context=context) return XMLResponse(xml, name='conditions') else: - return render_to_format(self.request, export_format, 'tasks', 'conditions/export/conditions.html', { + return render_to_format(self.request, export_format, 'conditions', 'conditions/export/conditions.html', { 'conditions': queryset }) @action(detail=True, url_path='export(?:/(?P[a-z]+))?') def detail_export(self, request, pk=None, export_format='xml'): + condition = self.get_object() if export_format == 'xml': - serializer = ConditionExportSerializer(self.get_object()) - xml = ConditionRenderer().render([serializer.data], context=self.get_export_renderer_context(request)) - return XMLResponse(xml, name=self.get_object().uri_path) + context = self.get_export_renderer_context(request) + serializer = ConditionExportSerializer(condition, context=context) + xml = ConditionRenderer().render([serializer.data], context=context) + return XMLResponse(xml, name=condition.uri_path) else: return render_to_format( - self.request, export_format, self.get_object().uri_path, 'conditions/export/conditions.html', { - 'conditions': [self.get_object()] + self.request, export_format, condition.uri_path, 'conditions/export/conditions.html', { + 'conditions': [condition] } ) - def get_export_renderer_context(self, request): + def get_export_renderer_context(self, request, attributes_by_id=False): full = is_truthy(request.GET.get('full')) - return { + context = { 'attributes': full or is_truthy(request.GET.get('attributes')), - 'options': full or is_truthy(request.GET.get('options')) + 'options': full or is_truthy(request.GET.get('options')), } + if attributes_by_id: + context['attributes_by_id'] = get_attributes_by_id() + return context class RelationViewSet(ChoicesViewSet): diff --git a/rdmo/domain/serializers/export.py b/rdmo/domain/serializers/export.py index 7071482140..05a1be41d4 100644 --- a/rdmo/domain/serializers/export.py +++ b/rdmo/domain/serializers/export.py @@ -5,7 +5,7 @@ class AttributeExportSerializer(serializers.ModelSerializer): - parent = serializers.CharField(source='parent.uri', default=None, read_only=True) + parent = serializers.SerializerMethodField() parent_data = serializers.SerializerMethodField() class Meta: @@ -17,9 +17,37 @@ class Meta: 'path', 'comment', 'parent', - 'parent_data' + 'parent_data', ) + def get_parent(self, obj): + parent = self.get_parent_object_from_context(obj) + return parent.uri if parent is not None else None + def get_parent_data(self, obj): - if obj.parent is not None: - return AttributeExportSerializer(obj.parent).data + parent = self.get_parent_object_from_context(obj) + + if parent is None: + return None + + cache = self.context.setdefault('parent_data_cache', {}) + + if parent.pk not in cache: + cache[parent.pk] = AttributeExportSerializer( + parent, + context=self.context, + ).data + + return cache[parent.pk] + + def get_parent_object_from_context(self, obj): + if obj.parent_id is None: + return None + + attributes_by_id = self.context.get('attributes_by_id', {}) + + if obj.parent_id in attributes_by_id: + return attributes_by_id[obj.parent_id] + + # fallback for edge cases, but this can query + return obj.parent diff --git a/rdmo/domain/serializers/v1.py b/rdmo/domain/serializers/v1.py index b3f65f57c3..01bd74effc 100644 --- a/rdmo/domain/serializers/v1.py +++ b/rdmo/domain/serializers/v1.py @@ -29,7 +29,6 @@ class Meta: 'comment', 'locked', 'read_only', - 'editors', 'parent' ) @@ -58,7 +57,8 @@ class Meta(BaseAttributeSerializer.Meta): 'tasks', 'attributes', 'values_count', - 'projects_count' + 'projects_count', + 'editors', ) extra_kwargs = { 'key': {'required': True} @@ -84,7 +84,6 @@ class Meta(BaseAttributeSerializer.Meta): 'is_leaf_node' ) - class AttributeNestedSerializer(AttributeListSerializer): elements = serializers.SerializerMethodField() diff --git a/rdmo/domain/viewsets.py b/rdmo/domain/viewsets.py index a0846635d6..c3d76d95db 100644 --- a/rdmo/domain/viewsets.py +++ b/rdmo/domain/viewsets.py @@ -71,7 +71,12 @@ def nested(self, request, pk): def export(self, request, export_format='xml'): queryset = self.filter_queryset(self.get_queryset()) if export_format == 'xml': - serializer = AttributeExportSerializer(queryset, many=True) + attributes = list(queryset) + serializer = AttributeExportSerializer( + attributes, many=True, context={ + 'attributes_by_id': { attribute.pk: attribute for attribute in attributes} + }, + ) xml = AttributeRenderer().render(serializer.data) return XMLResponse(xml, name='attributes') elif export_format[:3] == 'csv': @@ -85,18 +90,23 @@ def export(self, request, export_format='xml'): @action(detail=True, url_path='export(?:/(?P[a-z]+))?') def detail_export(self, request, pk=None, export_format='xml'): - attributes = self.get_object().get_descendants(include_self=True) + instance = self.get_object() + attributes = instance.get_descendants(include_self=True) if export_format == 'xml': - serializer = AttributeExportSerializer(attributes, many=True) + serializer = AttributeExportSerializer( + attributes, many=True, context={ + 'attributes_by_id': {attribute.pk: attribute for attribute in attributes} + }, + ) xml = AttributeRenderer().render(serializer.data) - return XMLResponse(xml, name=self.get_object().key) + return XMLResponse(xml, name=instance.key) elif export_format[:3] == 'csv': rows = [(attribute.key, attribute.comment, attribute.uri) for attribute in attributes] delimiter = ',' if export_format == 'csvcomma' else ';' return render_to_csv('domain', rows, delimiter) else: return render_to_format( - self.request, export_format, self.get_object().key, 'domain/export/attributes.html', { + self.request, export_format, instance.key, 'domain/export/attributes.html', { 'attributes': attributes } ) diff --git a/rdmo/options/viewsets.py b/rdmo/options/viewsets.py index 0062a4d01f..e9a8b80028 100644 --- a/rdmo/options/viewsets.py +++ b/rdmo/options/viewsets.py @@ -76,14 +76,15 @@ def export(self, request, export_format='xml'): @action(detail=True, url_path='export(?:/(?P[a-z]+))?') def detail_export(self, request, pk=None, export_format='xml'): + instance = self.get_object() if export_format == 'xml': - serializer = OptionSetExportSerializer(self.get_object()) + serializer = OptionSetExportSerializer(instance) xml = OptionSetRenderer().render([serializer.data], context=self.get_export_renderer_context(request)) - return XMLResponse(xml, name=self.get_object().uri_path) + return XMLResponse(xml, name=instance.uri_path) else: return render_to_format( - self.request, export_format, self.get_object().uri_path, 'options/export/optionsets.html', { - 'optionsets': [self.get_object()] + self.request, export_format, instance.uri_path, 'options/export/optionsets.html', { + 'optionsets': [instance] } ) @@ -147,14 +148,15 @@ def export(self, request, export_format='xml'): @action(detail=True, url_path='export(?:/(?P[a-z]+))?') def detail_export(self, request, pk=None, export_format='xml'): + instance = self.get_object() if export_format == 'xml': - serializer = OptionExportSerializer(self.get_object()) + serializer = OptionExportSerializer(instance) xml = OptionRenderer().render([serializer.data]) - return XMLResponse(xml, name=self.get_object().uri_path) + return XMLResponse(xml, name=instance.uri_path) else: return render_to_format( - self.request, export_format, self.get_object().uri_path, 'options/export/options.html', { - 'options': [self.get_object()] + self.request, export_format, instance.uri_path, 'options/export/options.html', { + 'options': [instance] } ) diff --git a/rdmo/questions/serializers/export.py b/rdmo/questions/serializers/export.py index b659ffdc7c..d07f2aeacf 100644 --- a/rdmo/questions/serializers/export.py +++ b/rdmo/questions/serializers/export.py @@ -24,8 +24,8 @@ class QuestionExportSerializer(TranslationSerializerMixin, serializers.ModelSeri attribute = AttributeExportSerializer() default_option = serializers.CharField(source='default_option.uri', default=None, read_only=True) - optionsets = OptionSetExportSerializer(many=True) - conditions = ConditionExportSerializer(many=True) + optionsets = serializers.SerializerMethodField() + conditions = serializers.SerializerMethodField() class Meta: model = Question @@ -56,6 +56,22 @@ class Meta: 'verbose_name' ) + def get_optionsets(self, obj): + optionsets = obj.optionsets.all() + + if self.context.get('optionsets'): + return OptionSetExportSerializer(optionsets, many=True, context=self.context).data + + return [{'uri': optionset.uri} for optionset in optionsets] + + def get_conditions(self, obj): + conditions = obj.conditions.all() + + if self.context.get('conditions'): + return ConditionExportSerializer(conditions, many=True, context=self.context).data + + return [{'uri': condition.uri} for condition in conditions] + class QuestionSetQuestionSetExportSerializer(serializers.ModelSerializer): @@ -69,7 +85,7 @@ class Meta: ) def get_questionset(self, obj): - return QuestionSetExportSerializer(obj.questionset).data + return QuestionSetExportSerializer(obj.questionset, context=self.context).data class QuestionSetQuestionExportSerializer(serializers.ModelSerializer): @@ -87,7 +103,7 @@ class Meta: class QuestionSetExportSerializer(TranslationSerializerMixin, serializers.ModelSerializer): attribute = AttributeExportSerializer() - conditions = ConditionExportSerializer(many=True) + conditions = serializers.SerializerMethodField() questionset_questionsets = QuestionSetQuestionSetExportSerializer(many=True) questionset_questions = QuestionSetQuestionExportSerializer(many=True) @@ -111,6 +127,14 @@ class Meta: 'verbose_name' ) + def get_conditions(self, obj): + conditions = obj.conditions.all() + + if self.context.get('conditions'): + return ConditionExportSerializer(conditions, many=True, context=self.context).data + + return [{'uri': condition.uri} for condition in conditions] + class PageQuestionSetExportSerializer(serializers.ModelSerializer): @@ -139,7 +163,7 @@ class Meta: class PageExportSerializer(TranslationSerializerMixin, serializers.ModelSerializer): attribute = AttributeExportSerializer() - conditions = ConditionExportSerializer(many=True) + conditions = serializers.SerializerMethodField() page_questionsets = PageQuestionSetExportSerializer(many=True) page_questions = PageQuestionExportSerializer(many=True) @@ -164,6 +188,14 @@ class Meta: 'verbose_name' ) + def get_conditions(self, obj): + conditions = obj.conditions.all() + + if self.context.get('conditions'): + return ConditionExportSerializer(conditions, many=True, context=self.context).data + + return [{'uri': condition.uri} for condition in conditions] + class SectionPageExportSerializer(serializers.ModelSerializer): diff --git a/rdmo/questions/viewsets.py b/rdmo/questions/viewsets.py index 82bb94bfbb..283bc3a0c9 100644 --- a/rdmo/questions/viewsets.py +++ b/rdmo/questions/viewsets.py @@ -13,6 +13,7 @@ from rdmo.core.permissions import HasModelPermission, HasObjectPermission from rdmo.core.utils import is_truthy, render_to_format from rdmo.core.views import ChoicesViewSet +from rdmo.domain.models import Attribute from rdmo.management.viewsets import ElementToggleCurrentSiteViewSetMixin from .constants import WIDGET_TYPE_CHOICES @@ -43,6 +44,11 @@ ) +def get_attributes_by_id(): + attributes = list(Attribute.objects.all()) + return {attribute.pk: attribute for attribute in attributes} + + class CatalogViewSet(ElementToggleCurrentSiteViewSetMixin, ModelViewSet): permission_classes = (HasModelPermission | HasObjectPermission,) serializer_class = CatalogSerializer @@ -83,8 +89,9 @@ def index(self, request): def export(self, request, export_format='xml'): queryset = self.filter_queryset(self.get_queryset()) if export_format == 'xml': - serializer = CatalogExportSerializer(queryset, many=True) - xml = CatalogRenderer().render(serializer.data, context=self.get_export_renderer_context(request)) + context = self.get_export_renderer_context(request) + serializer = CatalogExportSerializer(queryset, many=True, context=context) + xml = CatalogRenderer().render(serializer.data, context=context) return XMLResponse(xml, name='catalogs') else: return render_to_format( @@ -95,14 +102,16 @@ def export(self, request, export_format='xml'): @action(detail=True, url_path='export(?:/(?P[a-z]+))?') def detail_export(self, request, pk=None, export_format='xml'): + instance = self.get_object() if export_format == 'xml': - serializer = CatalogExportSerializer(self.get_object()) - xml = CatalogRenderer().render([serializer.data], context=self.get_export_renderer_context(request)) - return XMLResponse(xml, name=self.get_object().uri_path) + context = self.get_export_renderer_context(request) + serializer = CatalogExportSerializer(instance, context=context) + xml = CatalogRenderer().render([serializer.data], context=context) + return XMLResponse(xml, name=instance.uri_path) else: return render_to_format( - self.request, export_format, self.get_object().uri_path, 'questions/export/catalogs.html', { - 'catalogs': [self.get_object()] + self.request, export_format, instance.uri_path, 'questions/export/catalogs.html', { + 'catalogs': [instance] } ) @@ -116,7 +125,8 @@ def get_export_renderer_context(self, request): 'attributes': full or is_truthy(request.GET.get('attributes')), 'optionsets': full or is_truthy(request.GET.get('optionsets')), 'options': full or is_truthy(request.GET.get('options')), - 'conditions': full or is_truthy(request.GET.get('conditions')) + 'conditions': full or is_truthy(request.GET.get('conditions')), + 'attributes_by_id': get_attributes_by_id(), } @@ -157,8 +167,9 @@ def index(self, request): def export(self, request, export_format='xml'): queryset = self.filter_queryset(self.get_queryset()) if export_format == 'xml': - serializer = SectionExportSerializer(queryset, many=True) - xml = SectionRenderer().render(serializer.data, context=self.get_export_renderer_context(request)) + context = self.get_export_renderer_context(request) + serializer = SectionExportSerializer(queryset, many=True, context=context) + xml = SectionRenderer().render(serializer.data, context=context) return XMLResponse(xml, name='sections') else: return render_to_format( @@ -169,14 +180,16 @@ def export(self, request, export_format='xml'): @action(detail=True, url_path='export(?:/(?P[a-z]+))?') def detail_export(self, request, pk=None, export_format='xml'): + instance = self.get_object() if export_format == 'xml': - serializer = SectionExportSerializer(self.get_object()) - xml = SectionRenderer().render([serializer.data], context=self.get_export_renderer_context(request)) - return XMLResponse(xml, name=self.get_object().uri_path) + context = self.get_export_renderer_context(request) + serializer = SectionExportSerializer(instance, context=context) + xml = SectionRenderer().render([serializer.data], context=context) + return XMLResponse(xml, name=instance.uri_path) else: return render_to_format( - self.request, export_format, self.get_object().uri_path, 'questions/export/sections.html', { - 'sections': [self.get_object()] + self.request, export_format, instance.uri_path, 'questions/export/sections.html', { + 'sections': [instance] } ) @@ -189,7 +202,8 @@ def get_export_renderer_context(self, request): 'attributes': full or is_truthy(request.GET.get('attributes')), 'optionsets': full or is_truthy(request.GET.get('optionsets')), 'options': full or is_truthy(request.GET.get('options')), - 'conditions': full or is_truthy(request.GET.get('conditions')) + 'conditions': full or is_truthy(request.GET.get('conditions')), + 'attributes_by_id': get_attributes_by_id(), } @@ -238,8 +252,9 @@ def index(self, request): def export(self, request, export_format='xml'): queryset = self.filter_queryset(self.get_queryset()) if export_format == 'xml': - serializer = PageExportSerializer(queryset, many=True) - xml = PageRenderer().render(serializer.data, context=self.get_export_renderer_context(request)) + context = self.get_export_renderer_context(request) + serializer = PageExportSerializer(queryset, many=True, context=context) + xml = PageRenderer().render(serializer.data, context=context) return XMLResponse(xml, name='pages') else: return render_to_format( @@ -250,14 +265,16 @@ def export(self, request, export_format='xml'): @action(detail=True, url_path='export(?:/(?P[a-z]+))?') def detail_export(self, request, pk=None, export_format='xml'): + instance = self.get_object() if export_format == 'xml': - serializer = PageExportSerializer(self.get_object()) - xml = PageRenderer().render([serializer.data], context=self.get_export_renderer_context(request)) - return XMLResponse(xml, name=self.get_object().uri_path) + context = self.get_export_renderer_context(request) + serializer = PageExportSerializer(instance, context=context) + xml = PageRenderer().render([serializer.data], context=context) + return XMLResponse(xml, name=instance.uri_path) else: return render_to_format( - self.request, export_format, self.get_object().uri_path, 'questions/export/pages.html', { - 'pages': [self.get_object()] + self.request, export_format, instance.uri_path, 'questions/export/pages.html', { + 'pages': [instance] } ) @@ -269,7 +286,8 @@ def get_export_renderer_context(self, request): 'attributes': full or is_truthy(request.GET.get('attributes')), 'optionsets': full or is_truthy(request.GET.get('optionsets')), 'options': full or is_truthy(request.GET.get('options')), - 'conditions': full or is_truthy(request.GET.get('conditions')) + 'conditions': full or is_truthy(request.GET.get('conditions')), + 'attributes_by_id': get_attributes_by_id(), } @@ -319,8 +337,9 @@ def index(self, request): def export(self, request, export_format='xml'): queryset = self.filter_queryset(self.get_queryset()) if export_format == 'xml': - serializer = QuestionSetExportSerializer(queryset, many=True) - xml = QuestionSetRenderer().render(serializer.data, context=self.get_export_renderer_context(request)) + context = self.get_export_renderer_context(request) + serializer = QuestionSetExportSerializer(queryset, many=True, context=context) + xml = QuestionSetRenderer().render(serializer.data, context=context) return XMLResponse(xml, name='questionsets') else: return render_to_format( @@ -331,14 +350,16 @@ def export(self, request, export_format='xml'): @action(detail=True, url_path='export(?:/(?P[a-z]+))?') def detail_export(self, request, pk=None, export_format='xml'): + instance = self.get_object() if export_format == 'xml': - serializer = QuestionSetExportSerializer(self.get_object()) - xml = QuestionSetRenderer().render([serializer.data], context=self.get_export_renderer_context(request)) - return XMLResponse(xml, name=self.get_object().uri_path) + context = self.get_export_renderer_context(request) + serializer = QuestionSetExportSerializer(instance, context=context) + xml = QuestionSetRenderer().render([serializer.data], context=context) + return XMLResponse(xml, name=instance.uri_path) else: return render_to_format( - self.request, export_format, self.get_object().uri_path, 'questions/export/questionsets.html', { - 'questionsets': [self.get_object()] + self.request, export_format, instance.uri_path, 'questions/export/questionsets.html', { + 'questionsets': [instance] } ) @@ -350,7 +371,8 @@ def get_export_renderer_context(self, request): 'attributes': full or is_truthy(request.GET.get('attributes')), 'optionsets': full or is_truthy(request.GET.get('optionsets')), 'options': full or is_truthy(request.GET.get('options')), - 'conditions': full or is_truthy(request.GET.get('conditions')) + 'conditions': full or is_truthy(request.GET.get('conditions')), + 'attributes_by_id': get_attributes_by_id(), } @@ -385,7 +407,7 @@ def get_queryset(self): 'pages', 'questionsets', 'editors' - ).select_related('attribute', 'default_option') + ).select_related('attribute') @action(detail=False) def index(self, request): @@ -397,8 +419,9 @@ def index(self, request): def export(self, request, export_format='xml'): queryset = self.filter_queryset(self.get_queryset()) if export_format == 'xml': - serializer = QuestionExportSerializer(queryset, many=True) - xml = QuestionRenderer().render(serializer.data, context=self.get_export_renderer_context(request)) + context = self.get_export_renderer_context(request) + serializer = QuestionExportSerializer(queryset, many=True, context=context) + xml = QuestionRenderer().render(serializer.data, context=context) return XMLResponse(xml, name='questions') else: return render_to_format( @@ -409,14 +432,16 @@ def export(self, request, export_format='xml'): @action(detail=True, url_path='export(?:/(?P[a-z]+))?') def detail_export(self, request, pk=None, export_format='xml'): + instance = self.get_object() if export_format == 'xml': - serializer = QuestionExportSerializer(self.get_object()) - xml = QuestionRenderer().render([serializer.data], context=self.get_export_renderer_context(request)) - return XMLResponse(xml, name=self.get_object().uri_path) + context = self.get_export_renderer_context(request) + serializer = QuestionExportSerializer(instance, context=context) + xml = QuestionRenderer().render([serializer.data], context=context) + return XMLResponse(xml, name=instance.uri_path) else: return render_to_format( - self.request, export_format, self.get_object().uri_path, 'questions/export/questions.html', { - 'questions': [self.get_object()] + self.request, export_format, instance.uri_path, 'questions/export/questions.html', { + 'questions': [instance] } ) @@ -426,7 +451,8 @@ def get_export_renderer_context(self, request): 'attributes': full or is_truthy(request.GET.get('attributes')), 'optionsets': full or is_truthy(request.GET.get('optionsets')), 'options': full or is_truthy(request.GET.get('options')), - 'conditions': full or is_truthy(request.GET.get('conditions')) + 'conditions': full or is_truthy(request.GET.get('conditions')), + 'attributes_by_id': get_attributes_by_id(), } diff --git a/rdmo/tasks/viewsets.py b/rdmo/tasks/viewsets.py index 9cf564b245..5487f4f650 100644 --- a/rdmo/tasks/viewsets.py +++ b/rdmo/tasks/viewsets.py @@ -71,14 +71,15 @@ def export(self, request, export_format='xml'): @action(detail=True, url_path='export(?:/(?P[a-z]+))?') def detail_export(self, request, pk=None, export_format='xml'): + instance = self.get_object() if export_format == 'xml': - serializer = TaskExportSerializer(self.get_object()) + serializer = TaskExportSerializer(instance) xml = TaskRenderer().render([serializer.data], context=self.get_export_renderer_context(request)) - return XMLResponse(xml, name=self.get_object().uri_path) + return XMLResponse(xml, name=instance.uri_path) else: return render_to_format( - self.request, export_format, self.get_object().uri_path, 'tasks/export/tasks.html', { - 'tasks': [self.get_object()] + self.request, export_format, instance.uri_path, 'tasks/export/tasks.html', { + 'tasks': [instance] } ) diff --git a/rdmo/views/viewsets.py b/rdmo/views/viewsets.py index e14aa0a81c..96d09e7b14 100644 --- a/rdmo/views/viewsets.py +++ b/rdmo/views/viewsets.py @@ -67,13 +67,14 @@ def export(self, request, export_format='xml'): @action(detail=True, url_path=r'export(?:/(?P[a-z]+))?') def detail_export(self, request, pk=None, export_format='xml'): + instance = self.get_object() if export_format == 'xml': - serializer = ViewExportSerializer(self.get_object()) + serializer = ViewExportSerializer(instance) xml = ViewRenderer().render([serializer.data]) - return XMLResponse(xml, name=self.get_object().uri_path) + return XMLResponse(xml, name=instance.uri_path) else: return render_to_format( - self.request, export_format, self.get_object().uri_path, 'views/export/views.html', { - 'views': [self.get_object()] + self.request, export_format, instance.uri_path, 'views/export/views.html', { + 'views': [instance] } ) From 5f4c0a8b68c42b09649fb37c758c2aee3c832bdb Mon Sep 17 00:00:00 2001 From: David Wallace Date: Wed, 13 May 2026 11:04:27 +0200 Subject: [PATCH 035/131] Add test for index attributes action Signed-off-by: David Wallace --- rdmo/domain/tests/test_viewset_attribute.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/rdmo/domain/tests/test_viewset_attribute.py b/rdmo/domain/tests/test_viewset_attribute.py index 752298e23a..9809d0082b 100644 --- a/rdmo/domain/tests/test_viewset_attribute.py +++ b/rdmo/domain/tests/test_viewset_attribute.py @@ -33,6 +33,7 @@ } urlnames = { 'list': 'v1-domain:attribute-list', + 'index': 'v1-domain:attribute-index', 'nested': 'v1-domain:attribute-nested', 'export': 'v1-domain:attribute-export', 'detail': 'v1-domain:attribute-detail', @@ -52,6 +53,15 @@ def test_list(db, client, username, password): assert response.status_code == status_map['list'][username], response.json() +@pytest.mark.parametrize('username,password', users) +def test_index(db, client, username, password): + client.login(username=username, password=password) + + url = reverse(urlnames['index']) + response = client.get(url) + assert response.status_code == status_map['list'][username], response.json() + + @pytest.mark.parametrize('username,password', users) @pytest.mark.parametrize('export_format', export_formats) def test_export(db, client, username, password, export_format): From 1140b532f7eedc0df3ab47fda38aa168f346adf3 Mon Sep 17 00:00:00 2001 From: David Wallace Date: Wed, 13 May 2026 11:07:02 +0200 Subject: [PATCH 036/131] Refactor and optimize prefetch lookups for questions with recursive functinos and select_related calls Signed-off-by: David Wallace --- rdmo/questions/models/catalog.py | 101 ++++++++++++++++++++++----- rdmo/questions/models/page.py | 75 +++++++++++++++----- rdmo/questions/models/question.py | 21 ++++-- rdmo/questions/models/questionset.py | 53 +++++++++++--- rdmo/questions/models/section.py | 90 +++++++++++++++++++----- 5 files changed, 273 insertions(+), 67 deletions(-) diff --git a/rdmo/questions/models/catalog.py b/rdmo/questions/models/catalog.py index 844ce90724..e2ff811e94 100644 --- a/rdmo/questions/models/catalog.py +++ b/rdmo/questions/models/catalog.py @@ -2,6 +2,7 @@ from django.contrib.auth.models import Group from django.contrib.sites.models import Site from django.db import models +from django.db.models import Prefetch from django.utils.functional import cached_property from django.utils.translation import gettext_lazy as _ @@ -16,24 +17,6 @@ class Catalog(Model, TranslationMixin): objects = CatalogManager() - prefetch_lookups = ( - 'catalog_sections__section', - 'catalog_sections__section__section_pages__page__attribute', - 'catalog_sections__section__section_pages__page__conditions', - 'catalog_sections__section__section_pages__page__page_questions__question__attribute', - 'catalog_sections__section__section_pages__page__page_questions__question__conditions', - 'catalog_sections__section__section_pages__page__page_questions__question__optionsets', - 'catalog_sections__section__section_pages__page__page_questions__question__default_option', - 'catalog_sections__section__section_pages__page__page_questionsets__questionset__attribute', - 'catalog_sections__section__section_pages__page__page_questionsets__questionset__conditions', - 'catalog_sections__section__section_pages__page__page_questionsets__questionset__questionset_questions__question__attribute', - 'catalog_sections__section__section_pages__page__page_questionsets__questionset__questionset_questions__question__conditions', - 'catalog_sections__section__section_pages__page__page_questionsets__questionset__questionset_questions__question__optionsets', - 'catalog_sections__section__section_pages__page__page_questionsets__questionset__questionset_questions__question__default_option', - 'catalog_sections__section__section_pages__page__page_questionsets__questionset__questionset_questionsets__questionset__attribute', - 'catalog_sections__section__section_pages__page__page_questionsets__questionset__questionset_questionsets__questionset__conditions' - ) - uri = models.URLField( max_length=800, blank=True, verbose_name=_('URI'), @@ -272,3 +255,85 @@ def build_uri(cls, uri_prefix, uri_path): if not uri_path: raise RuntimeError('uri_path is missing') return join_url(uri_prefix or settings.DEFAULT_URI_PREFIX, '/questions/', uri_path) + + +def condition_prefetch(path): + return Prefetch( + path, + queryset=Condition.objects.select_related('source', 'source__parent', 'target_option') + ) + + +def question_prefetch(path): + from .question import Question + + return Prefetch( + path, + queryset=Question.objects.select_related( + 'attribute', + 'default_option', + ).prefetch_related( + condition_prefetch('conditions'), + 'optionsets', + ) + ) + + +def child_questionset_prefetch(path): + from .questionset import QuestionSet + + return Prefetch( + path, + queryset=QuestionSet.objects.select_related( + 'attribute', + ).prefetch_related( + condition_prefetch('conditions'), + question_prefetch('questionset_questions__question'), + ) + ) + + +def questionset_prefetch(path): + from .questionset import QuestionSet + + return Prefetch( + path, + queryset=QuestionSet.objects.select_related( + 'attribute', + ).prefetch_related( + condition_prefetch('conditions'), + question_prefetch('questionset_questions__question'), + child_questionset_prefetch('questionset_questionsets__questionset'), + ) + ) + + +def page_prefetch(path): + from .page import Page + + return Prefetch( + path, + queryset=Page.objects.select_related( + 'attribute', + ).prefetch_related( + condition_prefetch('conditions'), + question_prefetch('page_questions__question'), + questionset_prefetch('page_questionsets__questionset'), + ) + ) + + +def section_prefetch(path): + from .section import Section + + return Prefetch( + path, + queryset=Section.objects.prefetch_related( + page_prefetch('section_pages__page'), + ) + ) + + +Catalog.prefetch_lookups = ( + section_prefetch('catalog_sections__section'), +) diff --git a/rdmo/questions/models/page.py b/rdmo/questions/models/page.py index 921e1b58f5..9861cfcc92 100644 --- a/rdmo/questions/models/page.py +++ b/rdmo/questions/models/page.py @@ -1,6 +1,7 @@ from django.conf import settings from django.contrib.sites.models import Site from django.db import models +from django.db.models import Prefetch from django.utils.functional import cached_property from django.utils.translation import gettext_lazy as _ @@ -16,22 +17,6 @@ class Page(Model, TranslationMixin): objects = PageManager() - prefetch_lookups = ( - 'conditions', - 'page_questions__question__attribute', - 'page_questions__question__conditions', - 'page_questions__question__optionsets', - 'page_questions__question__default_option', - 'page_questionsets__questionset__attribute', - 'page_questionsets__questionset__conditions', - 'page_questionsets__questionset__questionset_questions__question__attribute', - 'page_questionsets__questionset__questionset_questions__question__conditions', - 'page_questionsets__questionset__questionset_questions__question__optionsets', - 'page_questionsets__questionset__questionset_questions__question__default_option', - 'page_questionsets__questionset__questionset_questionsets__questionset__attribute', - 'page_questionsets__questionset__questionset_questionsets__questionset__conditions' - ) - uri = models.URLField( max_length=800, blank=True, verbose_name=_('URI'), @@ -257,3 +242,61 @@ def build_uri(cls, uri_prefix, uri_path): if not uri_path: raise RuntimeError('uri_path is missing') return join_url(uri_prefix or settings.DEFAULT_URI_PREFIX, '/questions/', uri_path) + + +def condition_prefetch(path): + return Prefetch( + path, + queryset=Condition.objects.select_related('source', 'source__parent', 'target_option') + ) + + +def question_prefetch(path): + from .question import Question + + return Prefetch( + path, + queryset=Question.objects.select_related( + 'attribute', + 'default_option', + ).prefetch_related( + condition_prefetch('conditions'), + 'optionsets', + ) + ) + + +def child_questionset_prefetch(path): + from .questionset import QuestionSet + + return Prefetch( + path, + queryset=QuestionSet.objects.select_related( + 'attribute', + ).prefetch_related( + condition_prefetch('conditions'), + question_prefetch('questionset_questions__question'), + ) + ) + + +def questionset_prefetch(path): + from .questionset import QuestionSet + + return Prefetch( + path, + queryset=QuestionSet.objects.select_related( + 'attribute', + ).prefetch_related( + condition_prefetch('conditions'), + question_prefetch('questionset_questions__question'), + child_questionset_prefetch('questionset_questionsets__questionset'), + ) + ) + + +Page.prefetch_lookups = ( + condition_prefetch('conditions'), + question_prefetch('page_questions__question'), + questionset_prefetch('page_questionsets__questionset'), +) diff --git a/rdmo/questions/models/question.py b/rdmo/questions/models/question.py index d9073e330d..467b3a550b 100644 --- a/rdmo/questions/models/question.py +++ b/rdmo/questions/models/question.py @@ -1,6 +1,7 @@ from django.conf import settings from django.contrib.sites.models import Site from django.db import models +from django.db.models import Prefetch from django.utils.functional import cached_property from django.utils.translation import gettext_lazy as _ @@ -17,12 +18,6 @@ class Question(Model, TranslationMixin): - prefetch_lookups = ( - 'conditions', - 'optionsets', - 'default_option' - ) - uri = models.URLField( max_length=800, blank=True, default="", verbose_name=_('URI'), @@ -293,3 +288,17 @@ def build_uri(cls, uri_prefix, uri_path): if not uri_path: raise RuntimeError('uri_path is missing') return join_url(uri_prefix or settings.DEFAULT_URI_PREFIX, '/questions/', uri_path) + + +def condition_prefetch(path): + return Prefetch( + path, + queryset=Condition.objects.select_related('source', 'source__parent', 'target_option') + ) + + +Question.prefetch_lookups = ( + condition_prefetch('conditions'), + 'optionsets', + 'default_option', +) diff --git a/rdmo/questions/models/questionset.py b/rdmo/questions/models/questionset.py index 835fcdf753..12fe7d54b6 100644 --- a/rdmo/questions/models/questionset.py +++ b/rdmo/questions/models/questionset.py @@ -1,6 +1,7 @@ from django.conf import settings from django.contrib.sites.models import Site from django.db import models +from django.db.models import Prefetch from django.utils.functional import cached_property from django.utils.translation import gettext_lazy as _ @@ -16,16 +17,6 @@ class QuestionSet(Model, TranslationMixin): objects = QuestionSetManager() - prefetch_lookups = ( - 'conditions', - 'questionset_questions__question__attribute', - 'questionset_questions__question__conditions', - 'questionset_questions__question__optionsets', - 'questionset_questions__question__default_option', - 'questionset_questionsets__questionset__attribute', - 'questionset_questionsets__questionset__conditions' - ) - uri = models.URLField( max_length=800, blank=True, verbose_name=_('URI'), @@ -226,3 +217,45 @@ def build_uri(cls, uri_prefix, uri_path): if not uri_path: raise RuntimeError('uri_path is missing') return join_url(uri_prefix or settings.DEFAULT_URI_PREFIX, '/questions/', uri_path) + + +def condition_prefetch(path): + return Prefetch( + path, + queryset=Condition.objects.select_related('source', 'source__parent', 'target_option') + ) + + +def question_prefetch(path): + from .question import Question + + return Prefetch( + path, + queryset=Question.objects.select_related( + 'attribute', + 'default_option', + ).prefetch_related( + condition_prefetch('conditions'), + 'optionsets', + ) + ) + + +def child_questionset_prefetch(path): + return Prefetch( + path, + queryset=QuestionSet.objects.select_related( + 'attribute', + ).prefetch_related( + condition_prefetch('conditions'), + question_prefetch('questionset_questions__question'), + 'questionset_questionsets', + ) + ) + + +QuestionSet.prefetch_lookups = ( + condition_prefetch('conditions'), + question_prefetch('questionset_questions__question'), + child_questionset_prefetch('questionset_questionsets__questionset'), +) diff --git a/rdmo/questions/models/section.py b/rdmo/questions/models/section.py index 94557146c1..aacb9ba421 100644 --- a/rdmo/questions/models/section.py +++ b/rdmo/questions/models/section.py @@ -1,9 +1,11 @@ from django.conf import settings from django.contrib.sites.models import Site from django.db import models +from django.db.models import Prefetch from django.utils.functional import cached_property from django.utils.translation import gettext_lazy as _ +from rdmo.conditions.models import Condition from rdmo.core.models import Model, TranslationMixin from rdmo.core.utils import join_url @@ -14,23 +16,6 @@ class Section(Model, TranslationMixin): objects = SectionManager() - prefetch_lookups = ( - 'section_pages__page__attribute', - 'section_pages__page__conditions', - 'section_pages__page__page_questions__question__attribute', - 'section_pages__page__page_questions__question__conditions', - 'section_pages__page__page_questions__question__optionsets', - 'section_pages__page__page_questions__question__default_option', - 'section_pages__page__page_questionsets__questionset__attribute', - 'section_pages__page__page_questionsets__questionset__conditions', - 'section_pages__page__page_questionsets__questionset__questionset_questions__question__attribute', - 'section_pages__page__page_questionsets__questionset__questionset_questions__question__conditions', - 'section_pages__page__page_questionsets__questionset__questionset_questions__question__optionsets', - 'section_pages__page__page_questionsets__questionset__questionset_questions__question__default_option', - 'section_pages__page__page_questionsets__questionset__questionset_questionsets__questionset__attribute', - 'section_pages__page__page_questionsets__questionset__questionset_questionsets__questionset__conditions' - ) - uri = models.URLField( max_length=800, blank=True, verbose_name=_('URI'), @@ -172,3 +157,74 @@ def build_uri(cls, uri_prefix, uri_path): if not uri_path: raise RuntimeError('uri_path is missing') return join_url(uri_prefix or settings.DEFAULT_URI_PREFIX, '/questions/', uri_path) + + +def condition_prefetch(path): + return Prefetch( + path, + queryset=Condition.objects.select_related('source', 'source__parent', 'target_option') + ) + + +def question_prefetch(path): + from .question import Question + + return Prefetch( + path, + queryset=Question.objects.select_related( + 'attribute', + 'default_option', + ).prefetch_related( + condition_prefetch('conditions'), + 'optionsets', + ) + ) + + +def child_questionset_prefetch(path): + from .questionset import QuestionSet + + return Prefetch( + path, + queryset=QuestionSet.objects.select_related( + 'attribute', + ).prefetch_related( + condition_prefetch('conditions'), + question_prefetch('questionset_questions__question'), + ) + ) + + +def questionset_prefetch(path): + from .questionset import QuestionSet + + return Prefetch( + path, + queryset=QuestionSet.objects.select_related( + 'attribute', + ).prefetch_related( + condition_prefetch('conditions'), + question_prefetch('questionset_questions__question'), + child_questionset_prefetch('questionset_questionsets__questionset'), + ) + ) + + +def page_prefetch(path): + from .page import Page + + return Prefetch( + path, + queryset=Page.objects.select_related( + 'attribute', + ).prefetch_related( + condition_prefetch('conditions'), + question_prefetch('page_questions__question'), + questionset_prefetch('page_questionsets__questionset'), + ) + ) + + +Section.prefetch_lookups = ( + page_prefetch('section_pages__page'), +) From 66d6167c63b7038f5c9e36a9b96b440e6712e067 Mon Sep 17 00:00:00 2001 From: David Wallace Date: Wed, 13 May 2026 20:55:04 +0200 Subject: [PATCH 037/131] Apply simple fix to exportParams for not nested action #1467 Signed-off-by: David Wallace --- .../js/components/sidebar/ElementsSidebar.js | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/rdmo/management/assets/js/components/sidebar/ElementsSidebar.js b/rdmo/management/assets/js/components/sidebar/ElementsSidebar.js index c451ebf1f2..c7ffdcc6d5 100644 --- a/rdmo/management/assets/js/components/sidebar/ElementsSidebar.js +++ b/rdmo/management/assets/js/components/sidebar/ElementsSidebar.js @@ -13,12 +13,14 @@ import Link from 'rdmo/core/assets/js/components/Link' import { UploadForm } from '../common/Forms' const ElementsSidebar = ({ config, elements, elementActions, importActions }) => { - const { elementType, elementId } = elements + const { elementType, elementId, elementAction } = elements const model = invert(elementTypes)[elementType] const exportUrl = isNil(elementId) ? buildApiPath(elementModules[model], elementType, 'export') : buildApiPath(elementModules[model], elementType, elementId, 'export') - const exportParams = isNil(config.filter) ? '' : getExportParams(config.filter[elementType]) + const exportParams = (isNil(elementId) && elementAction != 'nested' && !isNil(config.filter)) + ? getExportParams(config.filter[elementType]) + : '' return (
@@ -79,7 +81,7 @@ const ElementsSidebar = ({ config, elements, elementActions, importActions }) =>
  • - {gettext('XML')} + {gettext('XML')}
  • { [ @@ -93,7 +95,9 @@ const ElementsSidebar = ({ config, elements, elementActions, importActions }) => 'tasks' ].includes(elementType) && (
  • - {gettext('XML (full)')} + + {gettext('XML (full)')} +
  • ) } @@ -103,12 +107,12 @@ const ElementsSidebar = ({ config, elements, elementActions, importActions }) => { elementType == 'attributes' && <>
  • - + {gettext('CSV comma separated')}
  • - + {gettext('CSV semicolon separated')}
  • @@ -117,7 +121,7 @@ const ElementsSidebar = ({ config, elements, elementActions, importActions }) => { config.settings.export_formats && config.settings.export_formats.map(([key, label], index) =>
  • - {label}
  • ) From 964f42e20784bf28378a458c7e39707c9af40368 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 4 May 2026 19:10:33 +0000 Subject: [PATCH 038/131] build(pre-commit): pre-commit autoupdate by ci MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.14.7 → v0.15.12](https://github.com/astral-sh/ruff-pre-commit/compare/v0.14.7...v0.15.12) - [github.com/pre-commit/mirrors-eslint: v9.39.1 → v10.3.0](https://github.com/pre-commit/mirrors-eslint/compare/v9.39.1...v10.3.0) - [github.com/crate-ci/typos: v1.40.0 → v1](https://github.com/crate-ci/typos/compare/v1.40.0...v1) - [github.com/zizmorcore/zizmor-pre-commit: v1.18.0 → v1.24.1](https://github.com/zizmorcore/zizmor-pre-commit/compare/v1.18.0...v1.24.1) --- .pre-commit-config.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index de0884ed08..ed892222fb 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -22,12 +22,12 @@ repos: exclude: \.dot$ - id: debug-statements - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.14.7 + rev: v0.15.12 hooks: - id: ruff-check args: [--fix, --exit-non-zero-on-fix] - repo: https://github.com/pre-commit/mirrors-eslint - rev: v9.39.1 + rev: v10.3.0 hooks: - id: eslint args: [--fix, --color] @@ -36,7 +36,7 @@ repos: - eslint-plugin-react@7.37.0 - react@18.3.1 - repo: https://github.com/crate-ci/typos - rev: v1.40.0 + rev: v1 hooks: - id: typos exclude: | @@ -50,7 +50,7 @@ repos: testing/.*.xml )$ - repo: https://github.com/zizmorcore/zizmor-pre-commit - rev: v1.18.0 + rev: v1.24.1 hooks: - id: zizmor args: [--fix, --offline] From c62e7d10d742f61c3922a2a2ccccb76374fc2898 Mon Sep 17 00:00:00 2001 From: David Wallace Date: Wed, 13 May 2026 21:30:18 +0200 Subject: [PATCH 039/131] style: fix RUF061 Use context-manager form of pytest.raises Signed-off-by: David Wallace --- rdmo/accounts/tests/test_views.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/rdmo/accounts/tests/test_views.py b/rdmo/accounts/tests/test_views.py index 4393fab7b1..cf06cbac92 100644 --- a/rdmo/accounts/tests/test_views.py +++ b/rdmo/accounts/tests/test_views.py @@ -239,7 +239,8 @@ def test_password_change_get(db, client, settings, account): response = client.get(url) assert response.status_code == 200 else: - pytest.raises(NoReverseMatch, reverse, 'account_change_password') + with pytest.raises(NoReverseMatch): + reverse('account_change_password') @pytest.mark.parametrize('account', boolean_toggle) @@ -262,7 +263,8 @@ def test_password_change_post(db, client, settings, account): response = client.post(url, data) assert response.status_code == 200 else: - pytest.raises(NoReverseMatch, reverse, 'account_change_password') + with pytest.raises(NoReverseMatch): + reverse('account_change_password') @pytest.mark.parametrize('account', boolean_toggle) @@ -278,7 +280,8 @@ def test_password_reset_get(db, client, settings, account): response = client.get(url) assert response.status_code == 200 else: - pytest.raises(NoReverseMatch, reverse, 'account_reset_password') + with pytest.raises(NoReverseMatch): + reverse('account_reset_password') @pytest.mark.parametrize('account', boolean_toggle) @@ -297,7 +300,8 @@ def test_password_reset_post_invalid(db, client, settings, account): assert response.status_code == 200 assert len(mail.outbox) == 0 else: - pytest.raises(NoReverseMatch, reverse, 'account_reset_password') + with pytest.raises(NoReverseMatch): + reverse('account_reset_password') @pytest.mark.urls('rdmo.accounts.urls') @@ -328,7 +332,8 @@ def test_password_reset_post_valid(db, client, settings, account): assert response.status_code == 302 assert response.url == reverse('account_reset_password_from_key', args=['4', 'set-password']) else: - pytest.raises(NoReverseMatch, reverse, 'account_reset_password') + with pytest.raises(NoReverseMatch): + reverse('account_reset_password') @pytest.mark.parametrize('profile_delete', boolean_toggle) @@ -364,7 +369,8 @@ def test_remove_user_post(db, client, settings, django_user_model, profile_delet assert response.status_code == 200 if settings.PROFILE_DELETE: assertTemplateUsed(response, 'profile/profile_remove_success.html') - pytest.raises(ObjectDoesNotExist, django_user_model.objects.get, username='user') + with pytest.raises(ObjectDoesNotExist): + django_user_model.objects.get(username='user') else: assertTemplateUsed(response, 'profile/profile_remove_closed.html') assert django_user_model.objects.get(username='user') @@ -473,7 +479,8 @@ def test_remove_a_user_without_usable_password_post(db, client, settings, django assert response.status_code == 200 if settings.PROFILE_DELETE: assertTemplateUsed(response, 'profile/profile_remove_success.html') - pytest.raises(ObjectDoesNotExist, django_user_model.objects.get, pk=user.pk) + with pytest.raises(ObjectDoesNotExist): + django_user_model.objects.get(pk=user.pk) else: assertTemplateUsed(response, 'profile/profile_remove_closed.html') client.logout() From 7a9e4eca74f55cbcb946530a0e5c4742d1055cde Mon Sep 17 00:00:00 2001 From: David Wallace Date: Wed, 13 May 2026 21:53:13 +0200 Subject: [PATCH 040/131] ci: pin actions to full release commit SHAs Signed-off-by: David Wallace --- .github/workflows/ci.yml | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cc140a8c24..b195f702c4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,11 +37,11 @@ jobs: name: Build python package runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false fetch-depth: 0 # important for setuptools_scm - - uses: actions/setup-node@v6 + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: 22.22 cache: ${{ startsWith(github.ref, 'refs/tags/') && '' || 'npm' }} # disable cache on tags @@ -59,16 +59,16 @@ jobs: python-version: ['3.10', '3.13'] db-backend: [mysql, postgres] steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v6 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: ${{ matrix.python-version }} cache: pip - name: Download wheel - uses: actions/download-artifact@v6 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: Packages path: dist @@ -119,16 +119,16 @@ jobs: python-version: ['3.13'] db-backend: [postgres] steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v6 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: ${{ matrix.python-version }} cache: pip - name: Download package - uses: actions/download-artifact@v6 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: Packages path: dist @@ -156,7 +156,7 @@ jobs: env: DJANGO_DEBUG: True GITHUB_DB_BACKEND: ${{ matrix.db-backend }} - - uses: actions/upload-artifact@v5 + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: screenshots path: screenshots/**/*.png @@ -181,10 +181,10 @@ jobs: matrix: os: [ubuntu-latest, windows-latest, macos-latest] steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - - uses: actions/setup-python@v6 + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: "3.13" cache: pip @@ -196,15 +196,15 @@ jobs: needs: build runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - - uses: actions/setup-python@v6 + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: "3.13" cache: pip - name: Download package - uses: actions/download-artifact@v6 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: Packages path: dist @@ -215,7 +215,7 @@ jobs: run: python -m pip install --no-compile "$(ls dist/*.whl)[all]" - name: Verify installed packages have compatible dependencies run: python -m pip check - - uses: actions/setup-node@v6 + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: 22 cache: npm @@ -261,7 +261,7 @@ jobs: id-token: write steps: - name: Download package - uses: actions/download-artifact@v6 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: Packages path: dist From d280f6e67e17a2eabbf3b2e6265f363a63ee398f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 13 May 2026 20:02:27 +0000 Subject: [PATCH 041/131] build(deps-dev): bump fast-uri from 3.1.0 to 3.1.2 Bumps [fast-uri](https://github.com/fastify/fast-uri) from 3.1.0 to 3.1.2. - [Release notes](https://github.com/fastify/fast-uri/releases) - [Commits](https://github.com/fastify/fast-uri/compare/v3.1.0...v3.1.2) --- updated-dependencies: - dependency-name: fast-uri dependency-version: 3.1.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 185e4b3f9c..f4a841734b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4718,9 +4718,9 @@ "dev": true }, "node_modules/fast-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", - "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", + "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", "dev": true, "funding": [ { @@ -4731,8 +4731,7 @@ "type": "opencollective", "url": "https://opencollective.com/fastify" } - ], - "license": "BSD-3-Clause" + ] }, "node_modules/fastest-levenshtein": { "version": "1.0.12", @@ -11539,9 +11538,9 @@ "dev": true }, "fast-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", - "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", + "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", "dev": true }, "fastest-levenshtein": { From e76676a52efd13cdb30bfc3e9eb099e614600c29 Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Fri, 15 May 2026 12:05:31 +0200 Subject: [PATCH 042/131] Refactor tests --- .../tests/test_api_performance_condition.py | 31 ++++++------------- .../tests/test_api_performance_attribute.py | 24 ++++++-------- .../tests/test_api_performance_option.py | 24 ++++++-------- .../tests/test_api_performance_optionset.py | 24 ++++++-------- .../tests/test_api_performance_page.py | 26 ++++++---------- .../tests/test_api_performance_question.py | 26 ++++++---------- .../tests/test_api_performance_questionset.py | 26 ++++++---------- .../tests/test_api_performance_section.py | 26 ++++++---------- rdmo/tasks/tests/test_api_performance_task.py | 24 ++++++-------- rdmo/views/tests/test_api_performance_view.py | 23 ++++++-------- 10 files changed, 95 insertions(+), 159 deletions(-) diff --git a/rdmo/conditions/tests/test_api_performance_condition.py b/rdmo/conditions/tests/test_api_performance_condition.py index ca347aa1ce..62f5af1170 100644 --- a/rdmo/conditions/tests/test_api_performance_condition.py +++ b/rdmo/conditions/tests/test_api_performance_condition.py @@ -4,30 +4,19 @@ from .test_viewset_condition import urlnames -max_query_map = { - # action: max_queries url_kwargs - 'list': {'max_queries': 9}, - 'index': {'max_queries': 3}, - 'export': {'max_queries': 11, 'url_kwargs': {'export_format': 'xml'}}, - 'detail': {'max_queries': 9, 'url_kwargs': {'pk': 1}}, - 'detail_export': { - 'max_queries': 9, 'url_kwargs': {'pk': 1, 'export_format': 'xml'}, - }, -} +max_queries = [ + # action, max_queries, url_kwargs + ('list', 9, {}), + ('index', 3, {}), + ('export', 11, {'export_format': 'xml'}), + ('detail', 9, {'pk': 1}), + ('detail_export', 9, {'pk': 1, 'export_format': 'xml'}), +] @pytest.mark.performance -@pytest.mark.parametrize( - 'action,max_queries,url_kwargs', - [ - (action, case['max_queries'], case.get('url_kwargs')) - for action, case in max_query_map.items() - ], -) -def test_actions_max_query_counts( - db, admin_client, django_assert_max_num_queries, - action, max_queries, url_kwargs, -): +@pytest.mark.parametrize('action,max_queries,url_kwargs', max_queries) +def test_actions_max_query_counts(db, admin_client, django_assert_max_num_queries, action, max_queries, url_kwargs): url = reverse(urlnames[action], kwargs=url_kwargs) with django_assert_max_num_queries(max_queries): diff --git a/rdmo/domain/tests/test_api_performance_attribute.py b/rdmo/domain/tests/test_api_performance_attribute.py index 45203e7e7d..d95b59b369 100644 --- a/rdmo/domain/tests/test_api_performance_attribute.py +++ b/rdmo/domain/tests/test_api_performance_attribute.py @@ -4,22 +4,18 @@ from .test_viewset_attribute import urlnames -max_query_map = { - 'list': {'max_queries': 10}, - 'index': {'max_queries': 3}, - 'export': {'max_queries': 12, 'url_kwargs': {'export_format': 'xml'}}, - 'detail': {'max_queries': 11, 'url_kwargs': {'pk': 1}}, - 'detail_export': {'max_queries': 12, 'url_kwargs': {'pk': 1, 'export_format': 'xml'}}, -} +max_queries = [ + # action, max_queries, url_kwargs + ('list', 10, {}), + ('index', 3, {}), + ('export', 12, {'export_format': 'xml'}), + ('detail', 11, {'pk': 1}), + ('detail_export', 12, {'pk': 1, 'export_format': 'xml'}), +] + @pytest.mark.performance -@pytest.mark.parametrize( - 'action,max_queries,url_kwargs', - [ - (action, case['max_queries'], case.get('url_kwargs')) - for action, case in max_query_map.items() - ], -) +@pytest.mark.parametrize('action,max_queries,url_kwargs', max_queries) def test_domain_endpoints_query_counts( db, admin_client, django_assert_max_num_queries, action, max_queries, url_kwargs, diff --git a/rdmo/options/tests/test_api_performance_option.py b/rdmo/options/tests/test_api_performance_option.py index f5c6db19f2..f9026e5e5f 100644 --- a/rdmo/options/tests/test_api_performance_option.py +++ b/rdmo/options/tests/test_api_performance_option.py @@ -4,22 +4,18 @@ from .test_viewset_options import urlnames -max_query_map_option = { - 'list': {'max_queries': 10}, - 'index': {'max_queries': 3}, - 'export': {'max_queries': 12, 'url_kwargs': {'export_format': 'xml'}}, - 'detail': {'max_queries': 10, 'url_kwargs': {'pk': 1}}, - 'detail_export': {'max_queries': 12, 'url_kwargs': {'pk': 1, 'export_format': 'xml'}}, -} +max_queries = [ + # action, max_queries, url_kwargs + ('list', 10, {}), + ('index', 3, {}), + ('export', 12, {'export_format': 'xml'}), + ('detail', 10, {'pk': 1}), + ('detail_export', 12, {'pk': 1, 'export_format': 'xml'}), +] + @pytest.mark.performance -@pytest.mark.parametrize( - 'action,max_queries,url_kwargs', - [ - (action, case['max_queries'], case.get('url_kwargs')) - for action, case in max_query_map_option.items() - ], -) +@pytest.mark.parametrize('action,max_queries,url_kwargs', max_queries) def test_option_endpoints_query_counts( admin_client, django_assert_max_num_queries, diff --git a/rdmo/options/tests/test_api_performance_optionset.py b/rdmo/options/tests/test_api_performance_optionset.py index 23036721fb..9660e59164 100644 --- a/rdmo/options/tests/test_api_performance_optionset.py +++ b/rdmo/options/tests/test_api_performance_optionset.py @@ -4,22 +4,18 @@ from .test_viewset_optionsets import urlnames -max_query_map_optionset = { - 'list': {'max_queries': 8}, - 'index': {'max_queries': 3}, - 'export': {'max_queries': 11, 'url_kwargs': {'export_format': 'xml'}}, - 'detail': {'max_queries': 8, 'url_kwargs': {'pk': 1}}, - 'detail_export': {'max_queries': 10, 'url_kwargs': {'pk': 1, 'export_format': 'xml'}}, -} +max_queries = [ + # action, max_queries, url_kwargs + ('list', 8, {}), + ('index', 3, {}), + ('export', 11, {'export_format': 'xml'}), + ('detail', 8, {'pk': 1}), + ('detail_export', 10, {'pk': 1, 'export_format': 'xml'}), +] + @pytest.mark.performance -@pytest.mark.parametrize( - 'action,max_queries,url_kwargs', - [ - (action, case['max_queries'], case.get('url_kwargs')) - for action, case in max_query_map_optionset.items() - ], -) +@pytest.mark.parametrize('action,max_queries,url_kwargs', max_queries) def test_optionset_endpoints_query_counts( admin_client, django_assert_max_num_queries, diff --git a/rdmo/questions/tests/test_api_performance_page.py b/rdmo/questions/tests/test_api_performance_page.py index d44a31d9d8..dd969441d5 100644 --- a/rdmo/questions/tests/test_api_performance_page.py +++ b/rdmo/questions/tests/test_api_performance_page.py @@ -4,26 +4,18 @@ from .test_viewset_page import urlnames -max_query_map = { - # action: max_queries url_kwargs - 'list': {'max_queries': 10}, - 'index': {'max_queries': 3}, - 'export': {'max_queries': 25, 'url_kwargs': {'export_format': 'xml'}}, - 'detail': {'max_queries': 10, 'url_kwargs': {'pk': 1}}, - 'detail_export': { - 'max_queries': 13, 'url_kwargs': {'pk': 1, 'export_format': 'xml'}, - }, -} +max_queries = [ + # action, max_queries, url_kwargs + ('list', 10, {}), + ('index', 3, {}), + ('export', 25, {'export_format': 'xml'}), + ('detail', 10, {'pk': 1}), + ('detail_export', 13, {'pk': 1, 'export_format': 'xml'}), +] @pytest.mark.performance -@pytest.mark.parametrize( - 'action,max_queries,url_kwargs', - [ - (action, case['max_queries'], case.get('url_kwargs')) - for action, case in max_query_map.items() - ], -) +@pytest.mark.parametrize('action,max_queries,url_kwargs', max_queries) def test_page_endpoints_query_counts( db, admin_client, django_assert_max_num_queries, action, max_queries, url_kwargs, diff --git a/rdmo/questions/tests/test_api_performance_question.py b/rdmo/questions/tests/test_api_performance_question.py index 77d7021188..2c29595820 100644 --- a/rdmo/questions/tests/test_api_performance_question.py +++ b/rdmo/questions/tests/test_api_performance_question.py @@ -4,26 +4,18 @@ from .test_viewset_question import urlnames -max_query_map = { - # action: max_queries url_kwargs - 'list': {'max_queries': 10}, - 'index': {'max_queries': 3}, - 'export': {'max_queries': 12, 'url_kwargs': {'export_format': 'xml'}}, - 'detail': {'max_queries': 10, 'url_kwargs': {'pk': 1}}, - 'detail_export': { - 'max_queries': 12, 'url_kwargs': {'pk': 1, 'export_format': 'xml'}, - }, -} +max_queries = [ + # action, max_queries, url_kwargs + ('list', 10, {}), + ('index', 3, {}), + ('export', 12, {'export_format': 'xml'}), + ('detail', 10, {'pk': 1}), + ('detail_export', 12, {'pk': 1, 'export_format': 'xml'}), +] @pytest.mark.performance -@pytest.mark.parametrize( - 'action,max_queries,url_kwargs', - [ - (action, case['max_queries'], case.get('url_kwargs')) - for action, case in max_query_map.items() - ], -) +@pytest.mark.parametrize('action,max_queries,url_kwargs', max_queries) def test_question_endpoints_query_counts( db, admin_client, django_assert_max_num_queries, action, max_queries, url_kwargs, diff --git a/rdmo/questions/tests/test_api_performance_questionset.py b/rdmo/questions/tests/test_api_performance_questionset.py index 5513e7255f..90dd9b4c31 100644 --- a/rdmo/questions/tests/test_api_performance_questionset.py +++ b/rdmo/questions/tests/test_api_performance_questionset.py @@ -4,26 +4,18 @@ from .test_viewset_questionset import urlnames -max_query_map = { - # action: max_queries url_kwargs - 'list': {'max_queries': 11}, - 'index': {'max_queries': 3}, - 'export': {'max_queries': 17, 'url_kwargs': {'export_format': 'xml'}}, - 'detail': {'max_queries': 10, 'url_kwargs': {'pk': 89}}, - 'detail_export': { - 'max_queries': 13, 'url_kwargs': {'pk': 89, 'export_format': 'xml'}, - }, -} +max_queries = [ + # action, max_queries, url_kwargs + ('list', 11, {}), + ('index', 3, {}), + ('export', 17, {'export_format': 'xml'}), + ('detail', 10, {'pk': 89}), + ('detail_export', 13, {'pk': 89, 'export_format': 'xml'}), +] @pytest.mark.performance -@pytest.mark.parametrize( - 'action,max_queries,url_kwargs', - [ - (action, case['max_queries'], case.get('url_kwargs')) - for action, case in max_query_map.items() - ], -) +@pytest.mark.parametrize('action,max_queries,url_kwargs', max_queries) def test_questionset_endpoints_query_counts( db, admin_client, django_assert_max_num_queries, action, max_queries, url_kwargs, diff --git a/rdmo/questions/tests/test_api_performance_section.py b/rdmo/questions/tests/test_api_performance_section.py index 13c5d8ab9d..d9ea7ff07b 100644 --- a/rdmo/questions/tests/test_api_performance_section.py +++ b/rdmo/questions/tests/test_api_performance_section.py @@ -4,26 +4,18 @@ from .test_viewset_section import urlnames -max_query_map = { - # action: max_queries url_kwargs - 'list': {'max_queries': 8}, - 'index': {'max_queries': 3}, - 'export': {'max_queries': 27, 'url_kwargs': {'export_format': 'xml'}}, - 'detail': {'max_queries': 8, 'url_kwargs': {'pk': 1}}, - 'detail_export': { - 'max_queries': 12, 'url_kwargs': {'pk': 1, 'export_format': 'xml'}, - }, -} +max_queries = [ + # action, max_queries, url_kwargs + ('list', 8, {}), + ('index', 3, {}), + ('export', 27, {'export_format': 'xml'}), + ('detail', 8, {'pk': 1}), + ('detail_export', 12, {'pk': 1, 'export_format': 'xml'}), +] @pytest.mark.performance -@pytest.mark.parametrize( - 'action,max_queries,url_kwargs', - [ - (action, case['max_queries'], case.get('url_kwargs')) - for action, case in max_query_map.items() - ], -) +@pytest.mark.parametrize('action,max_queries,url_kwargs', max_queries) def test_section_endpoints_query_counts( db, admin_client, django_assert_max_num_queries, action, max_queries, url_kwargs, diff --git a/rdmo/tasks/tests/test_api_performance_task.py b/rdmo/tasks/tests/test_api_performance_task.py index e51523cc6f..8af150fb32 100644 --- a/rdmo/tasks/tests/test_api_performance_task.py +++ b/rdmo/tasks/tests/test_api_performance_task.py @@ -4,22 +4,18 @@ from .test_viewset_task import urlnames -max_query_map = { - 'list': {'max_queries': 10}, - 'index': {'max_queries': 3}, - 'export': {'max_queries': 12, 'url_kwargs': {'export_format': 'xml'}}, - 'detail': {'max_queries': 10, 'url_kwargs': {'pk': 1}}, - 'detail_export': {'max_queries': 12, 'url_kwargs': {'pk': 1, 'export_format': 'xml'}}, -} +max_queries = [ + # action, max_queries, url_kwargs + ('list', 10, {}), + ('index', 3, {}), + ('export', 12, {'export_format': 'xml'}), + ('detail', 10, {'pk': 1}), + ('detail_export', 12, {'pk': 1, 'export_format': 'xml'}), +] + @pytest.mark.performance -@pytest.mark.parametrize( - 'action,max_queries,url_kwargs', - [ - (action, case['max_queries'], case.get('url_kwargs')) - for action, case in max_query_map.items() - ], -) +@pytest.mark.parametrize('action,max_queries,url_kwargs', max_queries) def test_tasks_endpoints_query_counts( db, admin_client, diff --git a/rdmo/views/tests/test_api_performance_view.py b/rdmo/views/tests/test_api_performance_view.py index 4bf4918e4e..9d28eb5d6f 100644 --- a/rdmo/views/tests/test_api_performance_view.py +++ b/rdmo/views/tests/test_api_performance_view.py @@ -4,22 +4,17 @@ from .test_viewset_view import urlnames -max_query_map = { - 'list': {'max_queries': 8}, - 'index': {'max_queries': 3}, - 'export': {'max_queries': 10, 'url_kwargs': {'export_format': 'xml'}}, - 'detail': {'max_queries': 8, 'url_kwargs': {'pk': 1}}, - 'detail_export': {'max_queries': 10, 'url_kwargs': {'pk': 1, 'export_format': 'xml'}}, -} +max_queries = [ + # action, max_queries, url_kwargs + ('list', 8, {}), + ('index', 3, {}), + ('export', 10, {'export_format': 'xml'}), + ('detail', 8, {'pk': 1}), + ('detail_export', 10, {'pk': 1, 'export_format': 'xml'}), +] @pytest.mark.performance -@pytest.mark.parametrize( - 'action,max_queries,url_kwargs', - [ - (action, case['max_queries'], case.get('url_kwargs')) - for action, case in max_query_map.items() - ], -) +@pytest.mark.parametrize('action,max_queries,url_kwargs', max_queries) def test_views_endpoints_query_counts( db, admin_client, From 733ba22dbc540ac5c14ca681b38112061fabef4f Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Fri, 15 May 2026 14:47:40 +0200 Subject: [PATCH 043/131] Refactor Attribute exports --- rdmo/conditions/serializers/export.py | 7 +++- rdmo/conditions/viewsets.py | 54 +++++++++++++-------------- rdmo/domain/renderers/mixins.py | 9 +++-- rdmo/domain/serializers/export.py | 35 ++--------------- rdmo/domain/viewsets.py | 22 +++++++---- rdmo/options/viewsets.py | 31 +++++++++++++-- rdmo/questions/viewsets.py | 11 ------ rdmo/tasks/renderers/mixins.py | 8 +++- rdmo/tasks/serializers/export.py | 15 +++++++- rdmo/tasks/viewsets.py | 34 ++++++++++++++++- 10 files changed, 133 insertions(+), 93 deletions(-) diff --git a/rdmo/conditions/serializers/export.py b/rdmo/conditions/serializers/export.py index f03dd055bc..2f6d411952 100644 --- a/rdmo/conditions/serializers/export.py +++ b/rdmo/conditions/serializers/export.py @@ -7,7 +7,7 @@ class ConditionExportSerializer(serializers.ModelSerializer): - source = AttributeExportSerializer() + source = serializers.SerializerMethodField() target_option = serializers.SerializerMethodField() class Meta: @@ -23,6 +23,11 @@ class Meta: 'target_option' ) + def get_source(self, obj): + source = self.context.get('attribute_map', {}).get(obj.source_id) + if source: + return AttributeExportSerializer(source).data + def get_target_option(self, obj): if obj.target_option is not None: from rdmo.options.serializers.export import OptionExportSerializer diff --git a/rdmo/conditions/viewsets.py b/rdmo/conditions/viewsets.py index 40769fed49..7776f0dcb6 100644 --- a/rdmo/conditions/viewsets.py +++ b/rdmo/conditions/viewsets.py @@ -18,11 +18,6 @@ from .serializers.v1 import ConditionIndexSerializer, ConditionSerializer -def get_attributes_by_id(): - attributes = list(Attribute.objects.all()) - return {attribute.pk: attribute for attribute in attributes} - - class ConditionViewSet(ModelViewSet): permission_classes = (HasModelPermission | HasObjectPermission, ) serializer_class = ConditionSerializer @@ -46,15 +41,7 @@ def get_queryset(self): elif self.action in ['export', 'detail_export']: return queryset.select_related( 'source', - 'source__parent', 'target_option' - ).prefetch_related( - 'optionsets', - 'pages', - 'questionsets', - 'questions', - 'tasks', - 'editors' ) else: return queryset.select_related( @@ -79,10 +66,12 @@ def index(self, request): def export(self, request, export_format='xml'): queryset = self.filter_queryset(self.get_queryset()) if export_format == 'xml': - conditions = list(queryset) - context = self.get_export_renderer_context(request, attributes_by_id=True) - serializer = ConditionExportSerializer(conditions, many=True, context=context) - xml = ConditionRenderer().render(serializer.data, context=context) + serializer = ConditionExportSerializer( + queryset, + many=True, + context=self.get_export_serializer_context(queryset), + ) + xml = ConditionRenderer().render(serializer.data, context=self.get_export_renderer_context(request)) return XMLResponse(xml, name='conditions') else: return render_to_format(self.request, export_format, 'conditions', 'conditions/export/conditions.html', { @@ -91,28 +80,35 @@ def export(self, request, export_format='xml'): @action(detail=True, url_path='export(?:/(?P[a-z]+))?') def detail_export(self, request, pk=None, export_format='xml'): - condition = self.get_object() + instance = self.get_object() if export_format == 'xml': - context = self.get_export_renderer_context(request) - serializer = ConditionExportSerializer(condition, context=context) - xml = ConditionRenderer().render([serializer.data], context=context) - return XMLResponse(xml, name=condition.uri_path) + serializer = ConditionExportSerializer( + instance, + context=self.get_export_serializer_context([instance]), + ) + xml = ConditionRenderer().render([serializer.data], context=self.get_export_renderer_context(request)) + return XMLResponse(xml, name=instance.uri_path) else: return render_to_format( - self.request, export_format, condition.uri_path, 'conditions/export/conditions.html', { - 'conditions': [condition] + self.request, export_format, instance.uri_path, 'conditions/export/conditions.html', { + 'conditions': [instance] } ) - def get_export_renderer_context(self, request, attributes_by_id=False): + def get_export_serializer_context(self, conditions): + return { + 'attribute_map': Attribute.objects.get_queryset_ancestors( + Attribute.objects.filter(id__in=[condition.source_id for condition in conditions]), + include_self=True + ).in_bulk() + } + + def get_export_renderer_context(self, request): full = is_truthy(request.GET.get('full')) - context = { + return { 'attributes': full or is_truthy(request.GET.get('attributes')), 'options': full or is_truthy(request.GET.get('options')), } - if attributes_by_id: - context['attributes_by_id'] = get_attributes_by_id() - return context class RelationViewSet(ChoicesViewSet): diff --git a/rdmo/domain/renderers/mixins.py b/rdmo/domain/renderers/mixins.py index 45899ba86e..2a87c49896 100644 --- a/rdmo/domain/renderers/mixins.py +++ b/rdmo/domain/renderers/mixins.py @@ -9,13 +9,14 @@ def render_attribute(self, xml, attribute, include_children=True): self.render_text_element(xml, 'key', {}, attribute['key']) self.render_text_element(xml, 'path', {}, attribute['path']) self.render_text_element(xml, 'dc:comment', {}, attribute['comment']) - self.render_text_element(xml, 'parent', {'dc:uri': attribute['parent']}, None) + self.render_text_element(xml, 'parent', { + 'dc:uri': attribute['parent']['uri'] if attribute['parent'] is not None else None + }, None) xml.endElement('attribute') if include_children: for child in attribute.get('children', []): self.render_attribute(xml, child) - parent_data = attribute.get('parent_data') - if parent_data: - self.render_attribute(xml, parent_data, include_children=False) + if attribute['parent']: + self.render_attribute(xml, attribute['parent'], include_children=False) diff --git a/rdmo/domain/serializers/export.py b/rdmo/domain/serializers/export.py index 05a1be41d4..7f3379eec0 100644 --- a/rdmo/domain/serializers/export.py +++ b/rdmo/domain/serializers/export.py @@ -6,7 +6,6 @@ class AttributeExportSerializer(serializers.ModelSerializer): parent = serializers.SerializerMethodField() - parent_data = serializers.SerializerMethodField() class Meta: model = Attribute @@ -17,37 +16,9 @@ class Meta: 'path', 'comment', 'parent', - 'parent_data', ) def get_parent(self, obj): - parent = self.get_parent_object_from_context(obj) - return parent.uri if parent is not None else None - - def get_parent_data(self, obj): - parent = self.get_parent_object_from_context(obj) - - if parent is None: - return None - - cache = self.context.setdefault('parent_data_cache', {}) - - if parent.pk not in cache: - cache[parent.pk] = AttributeExportSerializer( - parent, - context=self.context, - ).data - - return cache[parent.pk] - - def get_parent_object_from_context(self, obj): - if obj.parent_id is None: - return None - - attributes_by_id = self.context.get('attributes_by_id', {}) - - if obj.parent_id in attributes_by_id: - return attributes_by_id[obj.parent_id] - - # fallback for edge cases, but this can query - return obj.parent + parent = self.context.get('attribute_map', {}).get(obj.parent_id) + if parent: + return AttributeExportSerializer(parent).data diff --git a/rdmo/domain/viewsets.py b/rdmo/domain/viewsets.py index c3d76d95db..f05e18a081 100644 --- a/rdmo/domain/viewsets.py +++ b/rdmo/domain/viewsets.py @@ -37,7 +37,7 @@ class AttributeViewSet(ModelViewSet): def get_queryset(self): queryset = Attribute.objects.all().order_by('path') - if self.action in ('index','nested', 'export', 'detail_export'): + if self.action in ('index', 'nested', 'export', 'detail_export'): return queryset else: return queryset.annotate( @@ -51,6 +51,7 @@ def get_queryset(self): 'questions', 'tasks_as_start', 'tasks_as_end', + 'editors', ) def get_serializer_class(self): @@ -73,9 +74,9 @@ def export(self, request, export_format='xml'): if export_format == 'xml': attributes = list(queryset) serializer = AttributeExportSerializer( - attributes, many=True, context={ - 'attributes_by_id': { attribute.pk: attribute for attribute in attributes} - }, + queryset, + many=True, + context=self.get_export_serializer_context(attributes), ) xml = AttributeRenderer().render(serializer.data) return XMLResponse(xml, name='attributes') @@ -94,9 +95,9 @@ def detail_export(self, request, pk=None, export_format='xml'): attributes = instance.get_descendants(include_self=True) if export_format == 'xml': serializer = AttributeExportSerializer( - attributes, many=True, context={ - 'attributes_by_id': {attribute.pk: attribute for attribute in attributes} - }, + attributes, + many=True, + context=self.get_export_serializer_context(attributes), ) xml = AttributeRenderer().render(serializer.data) return XMLResponse(xml, name=instance.key) @@ -110,3 +111,10 @@ def detail_export(self, request, pk=None, export_format='xml'): 'attributes': attributes } ) + + def get_export_serializer_context(self, attributes): + return { + 'attribute_map': { + attribute.pk: attribute for attribute in attributes + } + } diff --git a/rdmo/options/viewsets.py b/rdmo/options/viewsets.py index e9a8b80028..5ea8ecb239 100644 --- a/rdmo/options/viewsets.py +++ b/rdmo/options/viewsets.py @@ -13,6 +13,7 @@ from rdmo.core.permissions import HasModelPermission, HasObjectPermission from rdmo.core.utils import is_truthy, render_to_format from rdmo.core.views import ChoicesViewSet +from rdmo.domain.models import Attribute from .models import Option, OptionSet from .renderers import OptionRenderer, OptionSetRenderer @@ -43,12 +44,17 @@ def get_queryset(self): queryset = OptionSet.objects.all() if self.action in ['index']: return queryset + elif self.action in ['export', 'detail_export']: + return queryset.prefetch_related( + 'optionset_options__option', + 'conditions', + ) else: return queryset.prefetch_related( 'optionset_options__option', 'conditions', 'questions', - 'editors' + 'editors', ) @action(detail=False) @@ -66,7 +72,11 @@ def nested(self, request, pk): def export(self, request, export_format='xml'): queryset = self.filter_queryset(self.get_queryset()) if export_format == 'xml': - serializer = OptionSetExportSerializer(queryset, many=True) + serializer = OptionSetExportSerializer( + queryset, + many=True, + context=self.get_export_serializer_context(queryset), + ) xml = OptionSetRenderer().render(serializer.data, context=self.get_export_renderer_context(request)) return XMLResponse(xml, name='optionsets') else: @@ -78,7 +88,10 @@ def export(self, request, export_format='xml'): def detail_export(self, request, pk=None, export_format='xml'): instance = self.get_object() if export_format == 'xml': - serializer = OptionSetExportSerializer(instance) + serializer = OptionSetExportSerializer( + instance, + context=self.get_export_serializer_context([instance]), + ) xml = OptionSetRenderer().render([serializer.data], context=self.get_export_renderer_context(request)) return XMLResponse(xml, name=instance.uri_path) else: @@ -88,6 +101,18 @@ def detail_export(self, request, pk=None, export_format='xml'): } ) + def get_export_serializer_context(self, optionsets): + return { + 'attribute_map': Attribute.objects.get_queryset_ancestors( + Attribute.objects.filter(id__in={ + condition.source_id + for optionset in optionsets + for condition in optionset.conditions.all() + }), + include_self=True + ).in_bulk() + } + def get_export_renderer_context(self, request): full = is_truthy(request.GET.get('full')) return { diff --git a/rdmo/questions/viewsets.py b/rdmo/questions/viewsets.py index 283bc3a0c9..c6dd11b260 100644 --- a/rdmo/questions/viewsets.py +++ b/rdmo/questions/viewsets.py @@ -13,7 +13,6 @@ from rdmo.core.permissions import HasModelPermission, HasObjectPermission from rdmo.core.utils import is_truthy, render_to_format from rdmo.core.views import ChoicesViewSet -from rdmo.domain.models import Attribute from rdmo.management.viewsets import ElementToggleCurrentSiteViewSetMixin from .constants import WIDGET_TYPE_CHOICES @@ -44,11 +43,6 @@ ) -def get_attributes_by_id(): - attributes = list(Attribute.objects.all()) - return {attribute.pk: attribute for attribute in attributes} - - class CatalogViewSet(ElementToggleCurrentSiteViewSetMixin, ModelViewSet): permission_classes = (HasModelPermission | HasObjectPermission,) serializer_class = CatalogSerializer @@ -126,7 +120,6 @@ def get_export_renderer_context(self, request): 'optionsets': full or is_truthy(request.GET.get('optionsets')), 'options': full or is_truthy(request.GET.get('options')), 'conditions': full or is_truthy(request.GET.get('conditions')), - 'attributes_by_id': get_attributes_by_id(), } @@ -203,7 +196,6 @@ def get_export_renderer_context(self, request): 'optionsets': full or is_truthy(request.GET.get('optionsets')), 'options': full or is_truthy(request.GET.get('options')), 'conditions': full or is_truthy(request.GET.get('conditions')), - 'attributes_by_id': get_attributes_by_id(), } @@ -287,7 +279,6 @@ def get_export_renderer_context(self, request): 'optionsets': full or is_truthy(request.GET.get('optionsets')), 'options': full or is_truthy(request.GET.get('options')), 'conditions': full or is_truthy(request.GET.get('conditions')), - 'attributes_by_id': get_attributes_by_id(), } @@ -372,7 +363,6 @@ def get_export_renderer_context(self, request): 'optionsets': full or is_truthy(request.GET.get('optionsets')), 'options': full or is_truthy(request.GET.get('options')), 'conditions': full or is_truthy(request.GET.get('conditions')), - 'attributes_by_id': get_attributes_by_id(), } @@ -452,7 +442,6 @@ def get_export_renderer_context(self, request): 'optionsets': full or is_truthy(request.GET.get('optionsets')), 'options': full or is_truthy(request.GET.get('options')), 'conditions': full or is_truthy(request.GET.get('conditions')), - 'attributes_by_id': get_attributes_by_id(), } diff --git a/rdmo/tasks/renderers/mixins.py b/rdmo/tasks/renderers/mixins.py index 73fbc4b4b5..a957c24e27 100644 --- a/rdmo/tasks/renderers/mixins.py +++ b/rdmo/tasks/renderers/mixins.py @@ -17,8 +17,12 @@ def render_task(self, xml, task): self.render_text_element(xml, 'title', {'lang': lang_code}, task[f'title_{lang_code}']) self.render_text_element(xml, 'text', {'lang': lang_code}, task[f'text_{lang_code}']) - self.render_text_element(xml, 'start_attribute', {'dc:uri': task['start_attribute']}, None) - self.render_text_element(xml, 'end_attribute', {'dc:uri': task['end_attribute']}, None) + self.render_text_element(xml, 'start_attribute', { + 'dc:uri': task['start_attribute']['uri'] if task['start_attribute'] is not None else None + }, None) + self.render_text_element(xml, 'end_attribute', { + 'dc:uri': task['end_attribute']['uri'] if task['end_attribute'] is not None else None + }, None) self.render_text_element(xml, 'days_before', {}, task['days_before']) self.render_text_element(xml, 'days_after', {}, task['days_after']) diff --git a/rdmo/tasks/serializers/export.py b/rdmo/tasks/serializers/export.py index 8aa3a327a1..e504c306ba 100644 --- a/rdmo/tasks/serializers/export.py +++ b/rdmo/tasks/serializers/export.py @@ -2,14 +2,15 @@ from rdmo.conditions.serializers.export import ConditionExportSerializer from rdmo.core.serializers import TranslationSerializerMixin +from rdmo.domain.serializers.export import AttributeExportSerializer from ..models import Task class TaskExportSerializer(TranslationSerializerMixin, serializers.ModelSerializer): - start_attribute = serializers.CharField(source='start_attribute.uri', default=None, read_only=True) - end_attribute = serializers.CharField(source='end_attribute.uri', default=None, read_only=True) + start_attribute = serializers.SerializerMethodField() + end_attribute = serializers.SerializerMethodField() conditions = ConditionExportSerializer(many=True) catalogs = serializers.SerializerMethodField() @@ -33,5 +34,15 @@ class Meta: 'text' ) + def get_start_attribute(self, obj): + start_attribute = self.context.get('attribute_map', {}).get(obj.start_attribute_id) + if start_attribute: + return AttributeExportSerializer(start_attribute).data + + def get_end_attribute(self, obj): + end_attribute = self.context.get('attribute_map', {}).get(obj.end_attribute_id) + if end_attribute: + return AttributeExportSerializer(end_attribute).data + def get_catalogs(self, obj): return [catalog.uri for catalog in obj.catalogs.all()] diff --git a/rdmo/tasks/viewsets.py b/rdmo/tasks/viewsets.py index 5487f4f650..cdcc009864 100644 --- a/rdmo/tasks/viewsets.py +++ b/rdmo/tasks/viewsets.py @@ -10,6 +10,7 @@ from rdmo.core.filters import SearchFilter from rdmo.core.permissions import HasModelPermission, HasObjectPermission from rdmo.core.utils import is_truthy, render_to_format +from rdmo.domain.models import Attribute from rdmo.management.viewsets import ElementToggleCurrentSiteViewSetMixin from .models import Task @@ -37,6 +38,11 @@ def get_queryset(self): queryset = Task.objects.all().order_by('uri') if self.action in ['index']: return queryset + elif self.action in ['export', 'detail_export']: + return queryset.prefetch_related( + 'catalogs', + 'conditions', + ) else: return queryset.select_related( 'start_attribute', @@ -61,7 +67,11 @@ def index(self, request): def export(self, request, export_format='xml'): queryset = self.filter_queryset(self.get_queryset()) if export_format == 'xml': - serializer = TaskExportSerializer(queryset, many=True) + serializer = TaskExportSerializer( + queryset, + many=True, + context=self.get_export_serializer_context(queryset), + ) xml = TaskRenderer().render(serializer.data, context=self.get_export_renderer_context(request)) return XMLResponse(xml, name='tasks') else: @@ -73,7 +83,10 @@ def export(self, request, export_format='xml'): def detail_export(self, request, pk=None, export_format='xml'): instance = self.get_object() if export_format == 'xml': - serializer = TaskExportSerializer(instance) + serializer = TaskExportSerializer( + instance, + context=self.get_export_serializer_context([instance]), + ) xml = TaskRenderer().render([serializer.data], context=self.get_export_renderer_context(request)) return XMLResponse(xml, name=instance.uri_path) else: @@ -83,6 +96,23 @@ def detail_export(self, request, pk=None, export_format='xml'): } ) + def get_export_serializer_context(self, tasks): + attribute_ids = set() + for task in tasks: + if task.start_attribute: + attribute_ids.add(task.start_attribute_id) + if task.end_attribute: + attribute_ids.add(task.end_attribute_id) + for condition in task.conditions.all(): + attribute_ids.add(condition.source_id) + + return { + 'attribute_map': Attribute.objects.get_queryset_ancestors( + Attribute.objects.filter(id__in=attribute_ids), + include_self=True + ).in_bulk() + } + def get_export_renderer_context(self, request): full = is_truthy(request.GET.get('full')) return { From 0b5100e44f659487771682cd42aceda31da2054f Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Fri, 15 May 2026 16:04:10 +0200 Subject: [PATCH 044/131] Use get_supported_language_variant in TranslationMixin --- rdmo/core/models.py | 4 ++-- rdmo/core/tests/test_tags.py | 22 +++++++++------------- rdmo/projects/tests/test_navigation.py | 1 + 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/rdmo/core/models.py b/rdmo/core/models.py index 9a1c849348..a8bf298c53 100644 --- a/rdmo/core/models.py +++ b/rdmo/core/models.py @@ -3,7 +3,7 @@ from django.conf import settings from django.db import models from django.utils.timezone import now -from django.utils.translation import get_language +from django.utils.translation import get_language, get_supported_language_variant from django.utils.translation import gettext_lazy as _ from rdmo.core.utils import get_languages @@ -31,7 +31,7 @@ def save(self, *args, **kwargs): class TranslationMixin: def trans(self, field): - current_language = get_language() + current_language = get_supported_language_variant(get_language()) languages = get_languages() for lang_code, _lang_string, lang_field in languages: diff --git a/rdmo/core/tests/test_tags.py b/rdmo/core/tests/test_tags.py index 73ace32691..c6778e2704 100644 --- a/rdmo/core/tests/test_tags.py +++ b/rdmo/core/tests/test_tags.py @@ -1,7 +1,6 @@ from django.conf import settings from django.template import RequestContext, Template from django.urls import reverse -from django.utils import translation def test_i18n_switcher(rf): @@ -10,15 +9,12 @@ def test_i18n_switcher(rf): # create a fake template with a name template = "{% load core_tags %}{% i18n_switcher %}" - # set a language - with translation.override(settings.LANGUAGES[0][0]): - - # render the link - request = rf.get(reverse('home')) - context = RequestContext(request, {}) - rendered_template = Template(template).render(context) - for language in settings.LANGUAGES: - if language == settings.LANGUAGES[0]: - assert '{}'.format(*language) in rendered_template - else: - assert '{}'.format(*language) in rendered_template + # render the link + request = rf.get(reverse('home')) + context = RequestContext(request, {}) + rendered_template = Template(template).render(context) + for language in settings.LANGUAGES: + if language == settings.LANGUAGES[0]: + assert '{}'.format(*language) in rendered_template + else: + assert '{}'.format(*language) in rendered_template diff --git a/rdmo/projects/tests/test_navigation.py b/rdmo/projects/tests/test_navigation.py index 7ec729cb28..3f33a72ab1 100644 --- a/rdmo/projects/tests/test_navigation.py +++ b/rdmo/projects/tests/test_navigation.py @@ -87,6 +87,7 @@ def test_compute_navigation(db, section_uri): assert page['show'] == show, page['uri'] assert 'title' in page # test for verbose, help is filtered out + def test_compute_navigation_uses_short_title_as_title(db): project = Project.objects.get(id=1) project.catalog.prefetch_elements() From 62b0bc8b9821a32a699a0bcfb79ea9174e1dc04c Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Fri, 15 May 2026 16:59:26 +0200 Subject: [PATCH 045/131] Fix QuestionSetQuestionSetValidator --- .../test_validator_questionset_questionset.py | 46 +++++++++++++++++ .../tests/test_viewset_questionset.py | 49 +++++++++++++++++++ rdmo/questions/validators.py | 5 +- 3 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 rdmo/questions/tests/test_validator_questionset_questionset.py diff --git a/rdmo/questions/tests/test_validator_questionset_questionset.py b/rdmo/questions/tests/test_validator_questionset_questionset.py new file mode 100644 index 0000000000..639e8bb5b2 --- /dev/null +++ b/rdmo/questions/tests/test_validator_questionset_questionset.py @@ -0,0 +1,46 @@ +from contextlib import nullcontext + +import pytest + +from django.core.exceptions import ValidationError + +from rest_framework.exceptions import ValidationError as RestFrameworkValidationError + +from ..models import QuestionSet +from ..serializers.v1 import QuestionSetSerializer +from ..validators import QuestionSetQuestionSetValidator + + +@pytest.mark.parametrize('questionset_uri,success', [ + ('http://example.com/terms/questions/catalog/blocks/single/block', True), + ('http://example.com/terms/questions/catalog/blocks/set/block/block', False), + ('http://example.com/terms/questions/catalog/blocks/set/block', False), +]) +def test_update(db, questionset_uri, success): + instance = QuestionSet.objects.get(uri='http://example.com/terms/questions/catalog/blocks/set/block/block') + + with nullcontext() if success else pytest.raises(ValidationError): + QuestionSetQuestionSetValidator(instance)({ + 'questionsets': [ + QuestionSet.objects.get(uri=questionset_uri) + ] + }) + + +@pytest.mark.parametrize('questionset_uri,success', [ + ('http://example.com/terms/questions/catalog/blocks/single/block', True), + ('http://example.com/terms/questions/catalog/blocks/set/block/block', False), + ('http://example.com/terms/questions/catalog/blocks/set/block', False), +]) +def test_serializer_update(db, questionset_uri, success): + instance = QuestionSet.objects.get(uri='http://example.com/terms/questions/catalog/blocks/set/block/block') + + validator = QuestionSetQuestionSetValidator() + serializer = QuestionSetSerializer(instance=instance) + + with nullcontext() if success else pytest.raises(RestFrameworkValidationError): + validator({ + 'questionsets': [ + QuestionSet.objects.get(uri=questionset_uri) + ] + }, serializer) diff --git a/rdmo/questions/tests/test_viewset_questionset.py b/rdmo/questions/tests/test_viewset_questionset.py index 9d2ff14168..e8d2759a28 100644 --- a/rdmo/questions/tests/test_viewset_questionset.py +++ b/rdmo/questions/tests/test_viewset_questionset.py @@ -386,3 +386,52 @@ def test_detail_export_full(db, client): assert 'http://example.com/terms/domain/blocks' in uris assert 'http://example.com/terms/options/one_two_three' in uris assert 'http://example.com/terms/options/one_two_three/one' in uris + + + +@pytest.mark.parametrize('questionset_uri,status_code', [ + ('http://example.com/terms/questions/catalog/blocks/single/block', 200), + ('http://example.com/terms/questions/catalog/blocks/set/block/block', 400), + ('http://example.com/terms/questions/catalog/blocks/set/block', 400), +]) +def test_update_questionset(db, client, questionset_uri, status_code): + client.login(username='editor', password='editor') + + instance = QuestionSet.objects.get(uri='http://example.com/terms/questions/catalog/blocks/set/block/block') + + questionset = QuestionSet.objects.get(uri=questionset_uri) + questionsets = [{ + 'questionset': questionset.id, + 'order': 99 + }] + questions = [{ + 'question': questionset_question.question.id, + 'order': questionset_question.order + } for questionset_question in instance.questionset_questions.all()[:1]] + conditions = [condition.pk for condition in instance.conditions.all()[:1]] + + url = reverse(urlnames['detail'], args=[instance.pk]) + data = { + 'uri_prefix': instance.uri_prefix, + 'uri_path': instance.uri_path, + 'comment': instance.comment, + 'attribute': instance.attribute.pk if instance.attribute else None, + 'is_collection': instance.is_collection, + 'title_en': instance.title_lang1, + 'title_de': instance.title_lang2, + 'help_en': instance.help_lang1, + 'help_de': instance.help_lang2, + 'verbose_name_en': instance.verbose_name_lang1, + 'verbose_name_de': instance.verbose_name_lang2, + 'questionsets': questionsets, + 'questions': questions, + 'conditions': conditions + } + + response = client.put(url, data, content_type='application/json') + assert response.status_code == status_code + + if response.status_code == 400: + assert response.json().get('questionsets') == [ + 'A question set may not be a child of itself or one of its descendants.' + ] diff --git a/rdmo/questions/validators.py b/rdmo/questions/validators.py index 961d205f23..1668169679 100644 --- a/rdmo/questions/validators.py +++ b/rdmo/questions/validators.py @@ -40,7 +40,10 @@ class QuestionSetQuestionSetValidator(InstanceValidator): def __call__(self, data, serializer=None): super().__call__(data, serializer) - questionsets = data.get('questionsets') + questionsets = data.get('questionsets') or [ + questionset_questionset.get('questionset') + for questionset_questionset in data.get('questionset_questionsets', []) + ] if not questionsets: return From f68677f36e182ea59e4670741152043025fb85c8 Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Fri, 15 May 2026 17:08:23 +0200 Subject: [PATCH 046/131] Update tests --- .../test_validator_questionset_questionset.py | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/rdmo/questions/tests/test_validator_questionset_questionset.py b/rdmo/questions/tests/test_validator_questionset_questionset.py index 639e8bb5b2..714240354c 100644 --- a/rdmo/questions/tests/test_validator_questionset_questionset.py +++ b/rdmo/questions/tests/test_validator_questionset_questionset.py @@ -10,13 +10,21 @@ from ..serializers.v1 import QuestionSetSerializer from ..validators import QuestionSetQuestionSetValidator +fields = [ + 'questionsets', + 'questionsets_questionsets', +] -@pytest.mark.parametrize('questionset_uri,success', [ +questionsets = [ ('http://example.com/terms/questions/catalog/blocks/single/block', True), ('http://example.com/terms/questions/catalog/blocks/set/block/block', False), ('http://example.com/terms/questions/catalog/blocks/set/block', False), -]) -def test_update(db, questionset_uri, success): +] + + +@pytest.mark.parametrize('field', fields) +@pytest.mark.parametrize('questionset_uri,success', questionsets) +def test_update(db, field, questionset_uri, success): instance = QuestionSet.objects.get(uri='http://example.com/terms/questions/catalog/blocks/set/block/block') with nullcontext() if success else pytest.raises(ValidationError): @@ -27,12 +35,9 @@ def test_update(db, questionset_uri, success): }) -@pytest.mark.parametrize('questionset_uri,success', [ - ('http://example.com/terms/questions/catalog/blocks/single/block', True), - ('http://example.com/terms/questions/catalog/blocks/set/block/block', False), - ('http://example.com/terms/questions/catalog/blocks/set/block', False), -]) -def test_serializer_update(db, questionset_uri, success): +@pytest.mark.parametrize('field', fields) +@pytest.mark.parametrize('questionset_uri,success', questionsets) +def test_serializer_update(db, field, questionset_uri, success): instance = QuestionSet.objects.get(uri='http://example.com/terms/questions/catalog/blocks/set/block/block') validator = QuestionSetQuestionSetValidator() From a844b1d900a5fe0e0032fa0da01a94d5e3bb6824 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 May 2026 08:47:35 +0000 Subject: [PATCH 047/131] build(deps): bump yaml from 1.10.2 to 1.10.3 Bumps [yaml](https://github.com/eemeli/yaml) from 1.10.2 to 1.10.3. - [Release notes](https://github.com/eemeli/yaml/releases) - [Commits](https://github.com/eemeli/yaml/compare/v1.10.2...v1.10.3) --- updated-dependencies: - dependency-name: yaml dependency-version: 1.10.3 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 185e4b3f9c..c827ed6aba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8270,9 +8270,9 @@ "dev": true }, "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz", + "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==", "engines": { "node": ">= 6" } @@ -13980,9 +13980,9 @@ "dev": true }, "yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz", + "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==" }, "yocto-queue": { "version": "0.1.0", From 43d1dd089f76cd6d85e487ffc8ae5e87164f84f5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 May 2026 08:47:41 +0000 Subject: [PATCH 048/131] build(deps-dev): bump flatted from 3.4.1 to 3.4.2 Bumps [flatted](https://github.com/WebReflection/flatted) from 3.4.1 to 3.4.2. - [Commits](https://github.com/WebReflection/flatted/compare/v3.4.1...v3.4.2) --- updated-dependencies: - dependency-name: flatted dependency-version: 3.4.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 185e4b3f9c..4e9fa24459 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4864,11 +4864,10 @@ } }, "node_modules/flatted": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.1.tgz", - "integrity": "sha512-IxfVbRFVlV8V/yRaGzk0UVIcsKKHMSfYw66T/u4nTwlWteQePsxe//LjudR1AMX4tZW3WFCh3Zqa/sjlqpbURQ==", - "dev": true, - "license": "ISC" + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true }, "node_modules/font-awesome": { "version": "4.7.0", @@ -11641,9 +11640,9 @@ } }, "flatted": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.1.tgz", - "integrity": "sha512-IxfVbRFVlV8V/yRaGzk0UVIcsKKHMSfYw66T/u4nTwlWteQePsxe//LjudR1AMX4tZW3WFCh3Zqa/sjlqpbURQ==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", "dev": true }, "font-awesome": { From 4b08be4fb56cb95aac36ea80ff4aebc444722c59 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 May 2026 08:48:27 +0000 Subject: [PATCH 049/131] build(deps): bump picomatch Bumps and [picomatch](https://github.com/micromatch/picomatch). These dependencies needed to be updated together. Updates `picomatch` from 2.3.1 to 2.3.2 - [Release notes](https://github.com/micromatch/picomatch/releases) - [Changelog](https://github.com/micromatch/picomatch/blob/master/CHANGELOG.md) - [Commits](https://github.com/micromatch/picomatch/compare/2.3.1...2.3.2) Updates `picomatch` from 4.0.2 to 4.0.4 - [Release notes](https://github.com/micromatch/picomatch/releases) - [Changelog](https://github.com/micromatch/picomatch/blob/master/CHANGELOG.md) - [Commits](https://github.com/micromatch/picomatch/compare/2.3.1...2.3.2) --- updated-dependencies: - dependency-name: picomatch dependency-version: 2.3.2 dependency-type: indirect - dependency-name: picomatch dependency-version: 4.0.4 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/package-lock.json b/package-lock.json index 185e4b3f9c..9a79302295 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6360,9 +6360,9 @@ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, "optional": true, "engines": { @@ -7732,11 +7732,10 @@ } }, "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, - "license": "MIT", "engines": { "node": ">=12" }, @@ -12692,9 +12691,9 @@ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" }, "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, "optional": true }, @@ -13609,9 +13608,9 @@ "requires": {} }, "picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true } } From 5dd350d907544f8ea9c6c4fef6c752f796cc2d8c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Mar 2026 00:43:39 +0000 Subject: [PATCH 050/131] build(deps): update packaging requirement Updates the requirements on [packaging](https://github.com/pypa/packaging) to permit the latest version. - [Release notes](https://github.com/pypa/packaging/releases) - [Changelog](https://github.com/pypa/packaging/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pypa/packaging/compare/23.2...26.0) --- updated-dependencies: - dependency-name: packaging dependency-version: '26.0' dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 409a41fb89..1b84c86033 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -56,7 +56,7 @@ dependencies = [ "drf-extensions>=0.7.1,<1.0", "iso8601>=2.0,<3.0", "markdown>=3.4,<4.0", - "packaging>=23.2,<26.0", + "packaging>=23.2,<27.0", "pypandoc>=1.11,<2.0", "requests-toolbelt>=1.0,<2.0", "rules>=3.4,<4.0", From fe1a620ed604ab69ba44c8f4ddd9a60bd5e8ee4f Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Tue, 19 May 2026 09:20:18 +0200 Subject: [PATCH 051/131] Simplify tests --- .../test_validator_questionset_questionset.py | 35 ++++++++++++------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/rdmo/questions/tests/test_validator_questionset_questionset.py b/rdmo/questions/tests/test_validator_questionset_questionset.py index 714240354c..049987c024 100644 --- a/rdmo/questions/tests/test_validator_questionset_questionset.py +++ b/rdmo/questions/tests/test_validator_questionset_questionset.py @@ -1,4 +1,3 @@ -from contextlib import nullcontext import pytest @@ -27,12 +26,17 @@ def test_update(db, field, questionset_uri, success): instance = QuestionSet.objects.get(uri='http://example.com/terms/questions/catalog/blocks/set/block/block') - with nullcontext() if success else pytest.raises(ValidationError): - QuestionSetQuestionSetValidator(instance)({ - 'questionsets': [ - QuestionSet.objects.get(uri=questionset_uri) - ] - }) + data = { + 'questionsets': [ + QuestionSet.objects.get(uri=questionset_uri) + ] + } + + if success: + QuestionSetQuestionSetValidator(instance)(data) + else: + with pytest.raises(ValidationError): + QuestionSetQuestionSetValidator(instance)(data) @pytest.mark.parametrize('field', fields) @@ -43,9 +47,14 @@ def test_serializer_update(db, field, questionset_uri, success): validator = QuestionSetQuestionSetValidator() serializer = QuestionSetSerializer(instance=instance) - with nullcontext() if success else pytest.raises(RestFrameworkValidationError): - validator({ - 'questionsets': [ - QuestionSet.objects.get(uri=questionset_uri) - ] - }, serializer) + data = { + 'questionsets': [ + QuestionSet.objects.get(uri=questionset_uri) + ] + } + + if success: + validator(data, serializer) + else: + with pytest.raises(RestFrameworkValidationError): + validator(data, serializer) From 0b4a51f9e699410c5bbb82134aef5b522ddec1e1 Mon Sep 17 00:00:00 2001 From: Claudia Malzer Date: Tue, 12 May 2026 14:39:08 +0200 Subject: [PATCH 052/131] Add PROJECT_INVITE_USE_PROJECT_SITE setting and use it in get_invite_email_project_path (#1596) --- rdmo/core/settings.py | 1 + rdmo/projects/utils.py | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/rdmo/core/settings.py b/rdmo/core/settings.py index cd015c38b8..14bb9920d3 100644 --- a/rdmo/core/settings.py +++ b/rdmo/core/settings.py @@ -364,6 +364,7 @@ PROJECT_SEND_ISSUE = False PROJECT_INVITE_TIMEOUT = None +PROJECT_INVITE_USE_PROJECT_SITE = False PROJECT_SEND_INVITE = True diff --git a/rdmo/projects/utils.py b/rdmo/projects/utils.py index 4526b64481..e3063752ed 100644 --- a/rdmo/projects/utils.py +++ b/rdmo/projects/utils.py @@ -237,21 +237,21 @@ def save_import_views(project, views): project.views.add(view) -def get_invite_email_project_path(invite) -> str: +def get_invite_email_project_path(invite, scheme='http') -> str: project_invite_path = reverse('project_join', args=[invite.token]) # check if the invited user exists and the multisite environment is enabled - if invite.user is not None and settings.MULTISITE: + if invite.user is not None and settings.MULTISITE and not settings.PROJECT_INVITE_USE_PROJECT_SITE: # do nothing if user is a member of the current site current_site = Site.objects.get_current() if not invite.user.role.member.filter(id=current_site.id).exists(): # else take first site invited_user_member_domain = invite.user.role.member.first().domain - project_invite_path = 'http://' + invited_user_member_domain + project_invite_path + project_invite_path = f'{scheme}://' + invited_user_member_domain + project_invite_path return project_invite_path def send_invite_email(request, invite): - project_invite_path = get_invite_email_project_path(invite) + project_invite_path = get_invite_email_project_path(invite, request.scheme) context = { 'invite_url': request.build_absolute_uri(project_invite_path), 'invite_user': invite.user, From f1461cdfcb8e61acddbed429facf7f959d7744c8 Mon Sep 17 00:00:00 2001 From: Claudia Malzer Date: Tue, 12 May 2026 14:50:25 +0200 Subject: [PATCH 053/131] Add test --- .../tests/test_view_membership_multisite.py | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/rdmo/projects/tests/test_view_membership_multisite.py b/rdmo/projects/tests/test_view_membership_multisite.py index 50c1aea729..060834f218 100644 --- a/rdmo/projects/tests/test_view_membership_multisite.py +++ b/rdmo/projects/tests/test_view_membership_multisite.py @@ -61,6 +61,28 @@ def test_get_invite_email_project_path_function(db, client, settings, username, assert invite_email_project_path.startswith('http://' + site_domain + '/projects') +def test_get_invite_email_project_path_project_site(db, settings): + settings.MULTISITE = True + settings.PROJECT_INVITE_USE_PROJECT_SITE = True + + foo_site, _ = Site.objects.get_or_create(domain='foo.com', name='foo.com') + foo_user, _ = get_user_model().objects.get_or_create( + username='foo-user', + email='foo-user@foo.com', + password='foo-user', + ) + foo_user.role.member.set([foo_site]) + + project = Project.objects.get(pk=1) + invite = Invite(project=project, user=foo_user, role='owner') + invite.make_token() + invite.save() + + invite_email_project_path = get_invite_email_project_path(invite) + + assert invite_email_project_path == reverse('project_join', args=[invite.token]) + + @pytest.mark.parametrize('username,password', users) @pytest.mark.parametrize('project_id', projects) @pytest.mark.parametrize('membership_role', membership_roles) From 34502f1e4d263502d33b12b59a0937b9030e4160 Mon Sep 17 00:00:00 2001 From: Claudia Malzer Date: Tue, 12 May 2026 16:08:58 +0200 Subject: [PATCH 054/131] Change test --- .../tests/test_view_membership_multisite.py | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/rdmo/projects/tests/test_view_membership_multisite.py b/rdmo/projects/tests/test_view_membership_multisite.py index 060834f218..e3100b1ddf 100644 --- a/rdmo/projects/tests/test_view_membership_multisite.py +++ b/rdmo/projects/tests/test_view_membership_multisite.py @@ -65,22 +65,9 @@ def test_get_invite_email_project_path_project_site(db, settings): settings.MULTISITE = True settings.PROJECT_INVITE_USE_PROJECT_SITE = True - foo_site, _ = Site.objects.get_or_create(domain='foo.com', name='foo.com') - foo_user, _ = get_user_model().objects.get_or_create( - username='foo-user', - email='foo-user@foo.com', - password='foo-user', - ) - foo_user.role.member.set([foo_site]) - - project = Project.objects.get(pk=1) - invite = Invite(project=project, user=foo_user, role='owner') - invite.make_token() - invite.save() - - invite_email_project_path = get_invite_email_project_path(invite) + invite = Invite.objects.get(pk=1) - assert invite_email_project_path == reverse('project_join', args=[invite.token]) + assert get_invite_email_project_path(invite) == reverse('project_join', args=[invite.token]) @pytest.mark.parametrize('username,password', users) From 274710efeeb894e8421cc2ef8bb72d874407af8e Mon Sep 17 00:00:00 2001 From: Claudia Malzer Date: Tue, 12 May 2026 17:27:26 +0200 Subject: [PATCH 055/131] Change test back and fix it --- rdmo/projects/tests/test_view_membership_multisite.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/rdmo/projects/tests/test_view_membership_multisite.py b/rdmo/projects/tests/test_view_membership_multisite.py index e3100b1ddf..183228ffc0 100644 --- a/rdmo/projects/tests/test_view_membership_multisite.py +++ b/rdmo/projects/tests/test_view_membership_multisite.py @@ -65,7 +65,12 @@ def test_get_invite_email_project_path_project_site(db, settings): settings.MULTISITE = True settings.PROJECT_INVITE_USE_PROJECT_SITE = True - invite = Invite.objects.get(pk=1) + foo_user = get_user_model().objects.get(username='foo-user') + + project = Project.objects.get(pk=1) # example.com + invite = Invite(project=project, user=foo_user, role='owner') + invite.make_token() + invite.save() assert get_invite_email_project_path(invite) == reverse('project_join', args=[invite.token]) From 81bf755c399bdb5a98ec2eccdedacfa8c96f27e5 Mon Sep 17 00:00:00 2001 From: David Wallace Date: Wed, 20 May 2026 09:51:33 +0200 Subject: [PATCH 056/131] Add context to AttributeExportSerializer calls Signed-off-by: David Wallace --- rdmo/conditions/serializers/export.py | 2 +- rdmo/domain/serializers/export.py | 2 +- rdmo/tasks/serializers/export.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/rdmo/conditions/serializers/export.py b/rdmo/conditions/serializers/export.py index 2f6d411952..67f6001730 100644 --- a/rdmo/conditions/serializers/export.py +++ b/rdmo/conditions/serializers/export.py @@ -26,7 +26,7 @@ class Meta: def get_source(self, obj): source = self.context.get('attribute_map', {}).get(obj.source_id) if source: - return AttributeExportSerializer(source).data + return AttributeExportSerializer(source, context=self.context).data def get_target_option(self, obj): if obj.target_option is not None: diff --git a/rdmo/domain/serializers/export.py b/rdmo/domain/serializers/export.py index 7f3379eec0..0eb1101f3e 100644 --- a/rdmo/domain/serializers/export.py +++ b/rdmo/domain/serializers/export.py @@ -21,4 +21,4 @@ class Meta: def get_parent(self, obj): parent = self.context.get('attribute_map', {}).get(obj.parent_id) if parent: - return AttributeExportSerializer(parent).data + return AttributeExportSerializer(parent, context=self.context).data diff --git a/rdmo/tasks/serializers/export.py b/rdmo/tasks/serializers/export.py index e504c306ba..6a420f1c7c 100644 --- a/rdmo/tasks/serializers/export.py +++ b/rdmo/tasks/serializers/export.py @@ -37,12 +37,12 @@ class Meta: def get_start_attribute(self, obj): start_attribute = self.context.get('attribute_map', {}).get(obj.start_attribute_id) if start_attribute: - return AttributeExportSerializer(start_attribute).data + return AttributeExportSerializer(start_attribute, context=self.context).data def get_end_attribute(self, obj): end_attribute = self.context.get('attribute_map', {}).get(obj.end_attribute_id) if end_attribute: - return AttributeExportSerializer(end_attribute).data + return AttributeExportSerializer(end_attribute, context=self.context).data def get_catalogs(self, obj): return [catalog.uri for catalog in obj.catalogs.all()] From b4ee4cf32152d712b74b9b9b8cb543b355e0c783 Mon Sep 17 00:00:00 2001 From: David Wallace Date: Wed, 20 May 2026 09:52:29 +0200 Subject: [PATCH 057/131] Add export serializer context to questions export actions Signed-off-by: David Wallace --- rdmo/questions/utils.py | 47 +++++++++++++++++ rdmo/questions/viewsets.py | 101 ++++++++++++++++++++++++++----------- 2 files changed, 118 insertions(+), 30 deletions(-) create mode 100644 rdmo/questions/utils.py diff --git a/rdmo/questions/utils.py b/rdmo/questions/utils.py new file mode 100644 index 0000000000..0e5ac0440b --- /dev/null +++ b/rdmo/questions/utils.py @@ -0,0 +1,47 @@ +from rdmo.conditions.models import Condition +from rdmo.domain.models import Attribute +from rdmo.questions.models import Page, Question, QuestionSet + + +def get_export_serializer_context(elements, renderer_context): + if not any( + renderer_context.get(key) + for key in ('attributes', 'conditions', 'optionsets') + ): + return {'attribute_map': {}} + + attribute_ids = set() + question_ids = set() + + for element in elements: + for descendant in get_element_descendants(element): + if isinstance(descendant, (Page, QuestionSet, Question)) and descendant.attribute_id: + attribute_ids.add(descendant.attribute_id) + + if isinstance(descendant, Question): + question_ids.add(descendant.id) + + if isinstance(descendant, (Page, QuestionSet, Question)): + attribute_ids.update( + condition.source_id + for condition in descendant.conditions.all() + ) + + if renderer_context.get('optionsets') and question_ids: + attribute_ids.update( + Condition.objects.filter( + optionsets__questions__id__in=question_ids + ).values_list('source_id', flat=True) + ) + + return { + 'attribute_map': Attribute.objects.get_queryset_ancestors( + Attribute.objects.filter(id__in=attribute_ids), + include_self=True + ).in_bulk() + } + + +def get_element_descendants(element): + yield element + yield from getattr(element, 'descendants', []) diff --git a/rdmo/questions/viewsets.py b/rdmo/questions/viewsets.py index c6dd11b260..ebf1cb8308 100644 --- a/rdmo/questions/viewsets.py +++ b/rdmo/questions/viewsets.py @@ -41,6 +41,7 @@ SectionNestedSerializer, SectionSerializer, ) +from .utils import get_export_serializer_context class CatalogViewSet(ElementToggleCurrentSiteViewSetMixin, ModelViewSet): @@ -83,9 +84,13 @@ def index(self, request): def export(self, request, export_format='xml'): queryset = self.filter_queryset(self.get_queryset()) if export_format == 'xml': - context = self.get_export_renderer_context(request) - serializer = CatalogExportSerializer(queryset, many=True, context=context) - xml = CatalogRenderer().render(serializer.data, context=context) + renderer_context = self.get_export_renderer_context(request) + serializer_context = { + **renderer_context, + **get_export_serializer_context(queryset, renderer_context), + } + serializer = CatalogExportSerializer(queryset, many=True, context=serializer_context) + xml = CatalogRenderer().render(serializer.data, context=renderer_context) return XMLResponse(xml, name='catalogs') else: return render_to_format( @@ -98,9 +103,13 @@ def export(self, request, export_format='xml'): def detail_export(self, request, pk=None, export_format='xml'): instance = self.get_object() if export_format == 'xml': - context = self.get_export_renderer_context(request) - serializer = CatalogExportSerializer(instance, context=context) - xml = CatalogRenderer().render([serializer.data], context=context) + renderer_context = self.get_export_renderer_context(request) + serializer_context = { + **renderer_context, + **get_export_serializer_context([instance], renderer_context), + } + serializer = CatalogExportSerializer(instance, context=serializer_context) + xml = CatalogRenderer().render([serializer.data], context=renderer_context) return XMLResponse(xml, name=instance.uri_path) else: return render_to_format( @@ -160,9 +169,13 @@ def index(self, request): def export(self, request, export_format='xml'): queryset = self.filter_queryset(self.get_queryset()) if export_format == 'xml': - context = self.get_export_renderer_context(request) - serializer = SectionExportSerializer(queryset, many=True, context=context) - xml = SectionRenderer().render(serializer.data, context=context) + renderer_context = self.get_export_renderer_context(request) + serializer_context = { + **renderer_context, + **get_export_serializer_context(queryset, renderer_context), + } + serializer = SectionExportSerializer(queryset, many=True, context=serializer_context) + xml = SectionRenderer().render(serializer.data, context=renderer_context) return XMLResponse(xml, name='sections') else: return render_to_format( @@ -175,9 +188,13 @@ def export(self, request, export_format='xml'): def detail_export(self, request, pk=None, export_format='xml'): instance = self.get_object() if export_format == 'xml': - context = self.get_export_renderer_context(request) - serializer = SectionExportSerializer(instance, context=context) - xml = SectionRenderer().render([serializer.data], context=context) + renderer_context = self.get_export_renderer_context(request) + serializer_context = { + **renderer_context, + **get_export_serializer_context([instance], renderer_context), + } + serializer = SectionExportSerializer(instance, context=serializer_context) + xml = SectionRenderer().render([serializer.data], context=renderer_context) return XMLResponse(xml, name=instance.uri_path) else: return render_to_format( @@ -244,9 +261,13 @@ def index(self, request): def export(self, request, export_format='xml'): queryset = self.filter_queryset(self.get_queryset()) if export_format == 'xml': - context = self.get_export_renderer_context(request) - serializer = PageExportSerializer(queryset, many=True, context=context) - xml = PageRenderer().render(serializer.data, context=context) + renderer_context = self.get_export_renderer_context(request) + serializer_context = { + **renderer_context, + **get_export_serializer_context(queryset, renderer_context), + } + serializer = PageExportSerializer(queryset, many=True, context=serializer_context) + xml = PageRenderer().render(serializer.data, context=renderer_context) return XMLResponse(xml, name='pages') else: return render_to_format( @@ -259,9 +280,13 @@ def export(self, request, export_format='xml'): def detail_export(self, request, pk=None, export_format='xml'): instance = self.get_object() if export_format == 'xml': - context = self.get_export_renderer_context(request) - serializer = PageExportSerializer(instance, context=context) - xml = PageRenderer().render([serializer.data], context=context) + renderer_context = self.get_export_renderer_context(request) + serializer_context = { + **renderer_context, + **get_export_serializer_context([instance], renderer_context), + } + serializer = PageExportSerializer(instance, context=serializer_context) + xml = PageRenderer().render([serializer.data], context=renderer_context) return XMLResponse(xml, name=instance.uri_path) else: return render_to_format( @@ -328,9 +353,13 @@ def index(self, request): def export(self, request, export_format='xml'): queryset = self.filter_queryset(self.get_queryset()) if export_format == 'xml': - context = self.get_export_renderer_context(request) - serializer = QuestionSetExportSerializer(queryset, many=True, context=context) - xml = QuestionSetRenderer().render(serializer.data, context=context) + renderer_context = self.get_export_renderer_context(request) + serializer_context = { + **renderer_context, + **get_export_serializer_context(queryset, renderer_context), + } + serializer = QuestionSetExportSerializer(queryset, many=True, context=serializer_context) + xml = QuestionSetRenderer().render(serializer.data, context=renderer_context) return XMLResponse(xml, name='questionsets') else: return render_to_format( @@ -343,9 +372,13 @@ def export(self, request, export_format='xml'): def detail_export(self, request, pk=None, export_format='xml'): instance = self.get_object() if export_format == 'xml': - context = self.get_export_renderer_context(request) - serializer = QuestionSetExportSerializer(instance, context=context) - xml = QuestionSetRenderer().render([serializer.data], context=context) + renderer_context = self.get_export_renderer_context(request) + serializer_context = { + **renderer_context, + **get_export_serializer_context([instance], renderer_context), + } + serializer = QuestionSetExportSerializer(instance, context=serializer_context) + xml = QuestionSetRenderer().render([serializer.data], context=renderer_context) return XMLResponse(xml, name=instance.uri_path) else: return render_to_format( @@ -409,9 +442,13 @@ def index(self, request): def export(self, request, export_format='xml'): queryset = self.filter_queryset(self.get_queryset()) if export_format == 'xml': - context = self.get_export_renderer_context(request) - serializer = QuestionExportSerializer(queryset, many=True, context=context) - xml = QuestionRenderer().render(serializer.data, context=context) + renderer_context = self.get_export_renderer_context(request) + serializer_context = { + **renderer_context, + **get_export_serializer_context(queryset, renderer_context), + } + serializer = QuestionExportSerializer(queryset, many=True, context=serializer_context) + xml = QuestionRenderer().render(serializer.data, context=renderer_context) return XMLResponse(xml, name='questions') else: return render_to_format( @@ -424,9 +461,13 @@ def export(self, request, export_format='xml'): def detail_export(self, request, pk=None, export_format='xml'): instance = self.get_object() if export_format == 'xml': - context = self.get_export_renderer_context(request) - serializer = QuestionExportSerializer(instance, context=context) - xml = QuestionRenderer().render([serializer.data], context=context) + renderer_context = self.get_export_renderer_context(request) + serializer_context = { + **renderer_context, + **get_export_serializer_context([instance], renderer_context), + } + serializer = QuestionExportSerializer(instance, context=serializer_context) + xml = QuestionRenderer().render([serializer.data], context=renderer_context) return XMLResponse(xml, name=instance.uri_path) else: return render_to_format( From 1d95756d03de5f076dbd896b5ea945a25c34485a Mon Sep 17 00:00:00 2001 From: David Wallace Date: Wed, 20 May 2026 10:37:25 +0200 Subject: [PATCH 058/131] Also refactor catalog performance test Signed-off-by: David Wallace --- .../tests/test_api_performance_catalog.py | 26 +++++++------------ 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/rdmo/questions/tests/test_api_performance_catalog.py b/rdmo/questions/tests/test_api_performance_catalog.py index 8b65d40d02..0c9c52818f 100644 --- a/rdmo/questions/tests/test_api_performance_catalog.py +++ b/rdmo/questions/tests/test_api_performance_catalog.py @@ -4,26 +4,18 @@ from .test_viewset_catalog import urlnames -max_query_map = { - # action: max_queries url_kwargs - 'list': {'max_queries': 8}, - 'index': {'max_queries': 3}, - 'export': {'max_queries': 30, 'url_kwargs': {'export_format': 'xml'}}, - 'detail': {'max_queries': 8, 'url_kwargs': {'pk': 1}}, - 'detail_export': { - 'max_queries': 29, 'url_kwargs': {'pk': 1, 'export_format': 'xml'}, - }, -} +max_queries = [ + # action, max_queries url_kwargs + ('list', 8, {}), + ('index', 3, {}), + ('export', 30, {'export_format': 'xml'}), + ('detail', 8, {'pk': 1}), + ('detail_export', 29, {'pk': 1, 'export_format': 'xml'}), +] @pytest.mark.performance -@pytest.mark.parametrize( - 'action,max_queries,url_kwargs', - [ - (action, case['max_queries'], case.get('url_kwargs')) - for action, case in max_query_map.items() - ], -) +@pytest.mark.parametrize('action,max_queries,url_kwargs', max_queries) def test_catalog_endpoints_query_counts( db, admin_client, django_assert_max_num_queries, action, max_queries, url_kwargs, From 2d4903ac0dc86c17909becaae1dbcbd9711108d2 Mon Sep 17 00:00:00 2001 From: David Wallace Date: Wed, 20 May 2026 10:40:58 +0200 Subject: [PATCH 059/131] Refactor lookups into questions/prefetch.py and add a lookup for project page Signed-off-by: David Wallace --- rdmo/projects/viewsets.py | 8 +- rdmo/questions/managers.py | 18 +++- rdmo/questions/models/catalog.py | 86 +---------------- rdmo/questions/models/page.py | 62 +----------- rdmo/questions/models/question.py | 18 +--- rdmo/questions/models/questionset.py | 46 +-------- rdmo/questions/models/section.py | 76 +-------------- rdmo/questions/prefetch.py | 136 +++++++++++++++++++++++++++ 8 files changed, 162 insertions(+), 288 deletions(-) create mode 100644 rdmo/questions/prefetch.py diff --git a/rdmo/projects/viewsets.py b/rdmo/projects/viewsets.py index d16ed7b90c..d5082ceed6 100644 --- a/rdmo/projects/viewsets.py +++ b/rdmo/projects/viewsets.py @@ -25,6 +25,7 @@ from rdmo.core.utils import human2bytes, is_truthy, return_file_response from rdmo.options.models import OptionSet from rdmo.questions.models import Catalog, Page, Question, QuestionSet +from rdmo.questions.prefetch import project_page_prefetch_lookups from rdmo.tasks.models import Task from rdmo.views.models import View @@ -696,12 +697,9 @@ class ProjectPageViewSet(ProjectNestedViewSetMixin, RetrieveModelMixin, GenericV def get_queryset(self): self.project.catalog.prefetch_elements() - page = Page.objects.filter_by_catalog(self.project.catalog).prefetch_related( - *Page.prefetch_lookups, - 'page_questions__question__optionsets__optionset_options__option', - 'page_questionsets__questionset__questionset_questions__question__optionsets__optionset_options__option', + return Page.objects.filter_by_catalog(self.project.catalog).prefetch_related( + *project_page_prefetch_lookups() ) - return page def get_serializer_context(self): context = super().get_serializer_context() diff --git a/rdmo/questions/managers.py b/rdmo/questions/managers.py index 2f6608da4f..d9524e22f4 100644 --- a/rdmo/questions/managers.py +++ b/rdmo/questions/managers.py @@ -9,6 +9,14 @@ GroupsQuerySetMixin, ) +from .prefetch import ( + catalog_prefetch_lookups, + page_prefetch_lookups, + question_prefetch_lookups, + questionset_prefetch_lookups, + section_prefetch_lookups, +) + class CatalogQuerySet(CurrentSiteQuerySetMixin, GroupsQuerySetMixin, AvailabilityQuerySetMixin, models.QuerySet): @@ -16,7 +24,7 @@ def filter_catalog(self, catalog): return self.filter(models.Q(catalogs=None) | models.Q(catalogs=catalog)) def prefetch_elements(self): - return self.prefetch_related(*self.model.prefetch_lookups) + return self.prefetch_related(*catalog_prefetch_lookups()) def filter_for_user(self, user): return ( @@ -45,7 +53,7 @@ def filter_for_user(self, user): class SectionQuerySet(models.QuerySet): def prefetch_elements(self): - return self.prefetch_related(*self.model.prefetch_lookups) + return self.prefetch_related(*section_prefetch_lookups()) class SectionManager(models.Manager): @@ -60,7 +68,7 @@ def prefetch_elements(self): class PageQuerySet(models.QuerySet): def prefetch_elements(self): - return self.prefetch_related(*self.model.prefetch_lookups) + return self.prefetch_related(*page_prefetch_lookups()) def filter_by_catalog(self, catalog): ids = [descendant.id for descendant in catalog.descendants if isinstance(descendant, self.model)] @@ -82,7 +90,7 @@ def prefetch_elements(self): class QuestionSetQuerySet(models.QuerySet): def prefetch_elements(self): - return self.prefetch_related(*self.model.prefetch_lookups) + return self.prefetch_related(*questionset_prefetch_lookups()) def filter_by_catalog(self, catalog): ids = [descendant.id for descendant in catalog.descendants if isinstance(descendant, self.model)] @@ -101,7 +109,7 @@ def filter_by_catalog(self, catalog): class QuestionQuerySet(models.QuerySet): def prefetch_elements(self): - return self.prefetch_related(*self.model.prefetch_lookups) + return self.prefetch_related(*question_prefetch_lookups()) def filter_by_catalog(self, catalog): ids = [descendant.id for descendant in catalog.descendants if isinstance(descendant, self.model)] diff --git a/rdmo/questions/models/catalog.py b/rdmo/questions/models/catalog.py index e2ff811e94..9c24dc111b 100644 --- a/rdmo/questions/models/catalog.py +++ b/rdmo/questions/models/catalog.py @@ -2,7 +2,6 @@ from django.contrib.auth.models import Group from django.contrib.sites.models import Site from django.db import models -from django.db.models import Prefetch from django.utils.functional import cached_property from django.utils.translation import gettext_lazy as _ @@ -11,6 +10,7 @@ from rdmo.core.utils import join_url from ..managers import CatalogManager +from ..prefetch import catalog_prefetch_lookups class Catalog(Model, TranslationMixin): @@ -198,7 +198,7 @@ def optional_questions(self): return list(filter(lambda q: q.is_optional, self.questions)) def prefetch_elements(self): - models.prefetch_related_objects([self], *self.prefetch_lookups) + models.prefetch_related_objects([self], *catalog_prefetch_lookups()) def to_dict(self): elements = [element.to_dict() for element in self.elements] @@ -255,85 +255,3 @@ def build_uri(cls, uri_prefix, uri_path): if not uri_path: raise RuntimeError('uri_path is missing') return join_url(uri_prefix or settings.DEFAULT_URI_PREFIX, '/questions/', uri_path) - - -def condition_prefetch(path): - return Prefetch( - path, - queryset=Condition.objects.select_related('source', 'source__parent', 'target_option') - ) - - -def question_prefetch(path): - from .question import Question - - return Prefetch( - path, - queryset=Question.objects.select_related( - 'attribute', - 'default_option', - ).prefetch_related( - condition_prefetch('conditions'), - 'optionsets', - ) - ) - - -def child_questionset_prefetch(path): - from .questionset import QuestionSet - - return Prefetch( - path, - queryset=QuestionSet.objects.select_related( - 'attribute', - ).prefetch_related( - condition_prefetch('conditions'), - question_prefetch('questionset_questions__question'), - ) - ) - - -def questionset_prefetch(path): - from .questionset import QuestionSet - - return Prefetch( - path, - queryset=QuestionSet.objects.select_related( - 'attribute', - ).prefetch_related( - condition_prefetch('conditions'), - question_prefetch('questionset_questions__question'), - child_questionset_prefetch('questionset_questionsets__questionset'), - ) - ) - - -def page_prefetch(path): - from .page import Page - - return Prefetch( - path, - queryset=Page.objects.select_related( - 'attribute', - ).prefetch_related( - condition_prefetch('conditions'), - question_prefetch('page_questions__question'), - questionset_prefetch('page_questionsets__questionset'), - ) - ) - - -def section_prefetch(path): - from .section import Section - - return Prefetch( - path, - queryset=Section.objects.prefetch_related( - page_prefetch('section_pages__page'), - ) - ) - - -Catalog.prefetch_lookups = ( - section_prefetch('catalog_sections__section'), -) diff --git a/rdmo/questions/models/page.py b/rdmo/questions/models/page.py index 9861cfcc92..ba0e8586f2 100644 --- a/rdmo/questions/models/page.py +++ b/rdmo/questions/models/page.py @@ -1,7 +1,6 @@ from django.conf import settings from django.contrib.sites.models import Site from django.db import models -from django.db.models import Prefetch from django.utils.functional import cached_property from django.utils.translation import gettext_lazy as _ @@ -11,6 +10,7 @@ from rdmo.domain.models import Attribute from ..managers import PageManager +from ..prefetch import page_prefetch_lookups class Page(Model, TranslationMixin): @@ -223,7 +223,7 @@ def descendants(self) -> list: return descendants def prefetch_elements(self): - models.prefetch_related_objects([self], *self.prefetch_lookups) + models.prefetch_related_objects([self], *page_prefetch_lookups()) def to_dict(self): return { @@ -242,61 +242,3 @@ def build_uri(cls, uri_prefix, uri_path): if not uri_path: raise RuntimeError('uri_path is missing') return join_url(uri_prefix or settings.DEFAULT_URI_PREFIX, '/questions/', uri_path) - - -def condition_prefetch(path): - return Prefetch( - path, - queryset=Condition.objects.select_related('source', 'source__parent', 'target_option') - ) - - -def question_prefetch(path): - from .question import Question - - return Prefetch( - path, - queryset=Question.objects.select_related( - 'attribute', - 'default_option', - ).prefetch_related( - condition_prefetch('conditions'), - 'optionsets', - ) - ) - - -def child_questionset_prefetch(path): - from .questionset import QuestionSet - - return Prefetch( - path, - queryset=QuestionSet.objects.select_related( - 'attribute', - ).prefetch_related( - condition_prefetch('conditions'), - question_prefetch('questionset_questions__question'), - ) - ) - - -def questionset_prefetch(path): - from .questionset import QuestionSet - - return Prefetch( - path, - queryset=QuestionSet.objects.select_related( - 'attribute', - ).prefetch_related( - condition_prefetch('conditions'), - question_prefetch('questionset_questions__question'), - child_questionset_prefetch('questionset_questionsets__questionset'), - ) - ) - - -Page.prefetch_lookups = ( - condition_prefetch('conditions'), - question_prefetch('page_questions__question'), - questionset_prefetch('page_questionsets__questionset'), -) diff --git a/rdmo/questions/models/question.py b/rdmo/questions/models/question.py index 467b3a550b..66c87058b7 100644 --- a/rdmo/questions/models/question.py +++ b/rdmo/questions/models/question.py @@ -1,7 +1,6 @@ from django.conf import settings from django.contrib.sites.models import Site from django.db import models -from django.db.models import Prefetch from django.utils.functional import cached_property from django.utils.translation import gettext_lazy as _ @@ -14,6 +13,7 @@ from ..constants import WIDGET_TYPE_CHOICES from ..managers import QuestionManager +from ..prefetch import question_prefetch_lookups class Question(Model, TranslationMixin): @@ -264,7 +264,7 @@ def descendants(self) -> list: return [] def prefetch_elements(self): - models.prefetch_related_objects([self], *self.prefetch_lookups) + models.prefetch_related_objects([self], *question_prefetch_lookups()) def to_dict(self, *ancestors): return { @@ -288,17 +288,3 @@ def build_uri(cls, uri_prefix, uri_path): if not uri_path: raise RuntimeError('uri_path is missing') return join_url(uri_prefix or settings.DEFAULT_URI_PREFIX, '/questions/', uri_path) - - -def condition_prefetch(path): - return Prefetch( - path, - queryset=Condition.objects.select_related('source', 'source__parent', 'target_option') - ) - - -Question.prefetch_lookups = ( - condition_prefetch('conditions'), - 'optionsets', - 'default_option', -) diff --git a/rdmo/questions/models/questionset.py b/rdmo/questions/models/questionset.py index 12fe7d54b6..e3f0284e90 100644 --- a/rdmo/questions/models/questionset.py +++ b/rdmo/questions/models/questionset.py @@ -1,7 +1,6 @@ from django.conf import settings from django.contrib.sites.models import Site from django.db import models -from django.db.models import Prefetch from django.utils.functional import cached_property from django.utils.translation import gettext_lazy as _ @@ -11,6 +10,7 @@ from rdmo.domain.models import Attribute from ..managers import QuestionSetManager +from ..prefetch import questionset_prefetch_lookups class QuestionSet(Model, TranslationMixin): @@ -199,7 +199,7 @@ def descendants(self) -> list: return descendants def prefetch_elements(self): - models.prefetch_related_objects([self], *self.prefetch_lookups) + models.prefetch_related_objects([self], *questionset_prefetch_lookups()) def to_dict(self, *ancestors): return { @@ -217,45 +217,3 @@ def build_uri(cls, uri_prefix, uri_path): if not uri_path: raise RuntimeError('uri_path is missing') return join_url(uri_prefix or settings.DEFAULT_URI_PREFIX, '/questions/', uri_path) - - -def condition_prefetch(path): - return Prefetch( - path, - queryset=Condition.objects.select_related('source', 'source__parent', 'target_option') - ) - - -def question_prefetch(path): - from .question import Question - - return Prefetch( - path, - queryset=Question.objects.select_related( - 'attribute', - 'default_option', - ).prefetch_related( - condition_prefetch('conditions'), - 'optionsets', - ) - ) - - -def child_questionset_prefetch(path): - return Prefetch( - path, - queryset=QuestionSet.objects.select_related( - 'attribute', - ).prefetch_related( - condition_prefetch('conditions'), - question_prefetch('questionset_questions__question'), - 'questionset_questionsets', - ) - ) - - -QuestionSet.prefetch_lookups = ( - condition_prefetch('conditions'), - question_prefetch('questionset_questions__question'), - child_questionset_prefetch('questionset_questionsets__questionset'), -) diff --git a/rdmo/questions/models/section.py b/rdmo/questions/models/section.py index aacb9ba421..4b1ed425be 100644 --- a/rdmo/questions/models/section.py +++ b/rdmo/questions/models/section.py @@ -1,15 +1,14 @@ from django.conf import settings from django.contrib.sites.models import Site from django.db import models -from django.db.models import Prefetch from django.utils.functional import cached_property from django.utils.translation import gettext_lazy as _ -from rdmo.conditions.models import Condition from rdmo.core.models import Model, TranslationMixin from rdmo.core.utils import join_url from ..managers import SectionManager +from ..prefetch import section_prefetch_lookups class Section(Model, TranslationMixin): @@ -139,7 +138,7 @@ def descendants(self) -> list: return descendants def prefetch_elements(self): - models.prefetch_related_objects([self], *self.prefetch_lookups) + models.prefetch_related_objects([self], *section_prefetch_lookups()) def to_dict(self): elements = [element.to_dict() for element in self.elements] @@ -157,74 +156,3 @@ def build_uri(cls, uri_prefix, uri_path): if not uri_path: raise RuntimeError('uri_path is missing') return join_url(uri_prefix or settings.DEFAULT_URI_PREFIX, '/questions/', uri_path) - - -def condition_prefetch(path): - return Prefetch( - path, - queryset=Condition.objects.select_related('source', 'source__parent', 'target_option') - ) - - -def question_prefetch(path): - from .question import Question - - return Prefetch( - path, - queryset=Question.objects.select_related( - 'attribute', - 'default_option', - ).prefetch_related( - condition_prefetch('conditions'), - 'optionsets', - ) - ) - - -def child_questionset_prefetch(path): - from .questionset import QuestionSet - - return Prefetch( - path, - queryset=QuestionSet.objects.select_related( - 'attribute', - ).prefetch_related( - condition_prefetch('conditions'), - question_prefetch('questionset_questions__question'), - ) - ) - - -def questionset_prefetch(path): - from .questionset import QuestionSet - - return Prefetch( - path, - queryset=QuestionSet.objects.select_related( - 'attribute', - ).prefetch_related( - condition_prefetch('conditions'), - question_prefetch('questionset_questions__question'), - child_questionset_prefetch('questionset_questionsets__questionset'), - ) - ) - - -def page_prefetch(path): - from .page import Page - - return Prefetch( - path, - queryset=Page.objects.select_related( - 'attribute', - ).prefetch_related( - condition_prefetch('conditions'), - question_prefetch('page_questions__question'), - questionset_prefetch('page_questionsets__questionset'), - ) - ) - - -Section.prefetch_lookups = ( - page_prefetch('section_pages__page'), -) diff --git a/rdmo/questions/prefetch.py b/rdmo/questions/prefetch.py new file mode 100644 index 0000000000..103a70246c --- /dev/null +++ b/rdmo/questions/prefetch.py @@ -0,0 +1,136 @@ +from django.db.models import Prefetch + +from rdmo.conditions.models import Condition + + +def condition_prefetch(path): + return Prefetch( + path, + queryset=Condition.objects.select_related('source', 'source__parent', 'target_option') + ) + + +def question_options_prefetch_lookups(): + return ( + 'optionsets', + 'optionsets__optionset_options__option', + ) + + +def question_prefetch(path, include_options=False): + from .models import Question + + optionset_lookups = question_options_prefetch_lookups() if include_options else ('optionsets',) + + return Prefetch( + path, + queryset=Question.objects.select_related( + 'attribute', + 'default_option', + ).prefetch_related( + condition_prefetch('conditions'), + *optionset_lookups, + ) + ) + + +def questionset_questionset_prefetch(path): + from .models import QuestionSet + + return Prefetch( + path, + queryset=QuestionSet.objects.select_related( + 'attribute', + ).prefetch_related( + condition_prefetch('conditions'), + question_prefetch('questionset_questions__question'), + ) + ) + + +def questionset_prefetch(path, include_question_options=False): + from .models import QuestionSet + + return Prefetch( + path, + queryset=QuestionSet.objects.select_related( + 'attribute', + ).prefetch_related( + condition_prefetch('conditions'), + question_prefetch( + 'questionset_questions__question', + include_options=include_question_options + ), + questionset_questionset_prefetch('questionset_questionsets__questionset'), + ) + ) + + +def page_prefetch(path): + from .models import Page + + return Prefetch( + path, + queryset=Page.objects.select_related( + 'attribute', + ).prefetch_related( + condition_prefetch('conditions'), + question_prefetch('page_questions__question'), + questionset_prefetch('page_questionsets__questionset'), + ) + ) + + +def section_prefetch(path): + from .models import Section + + return Prefetch( + path, + queryset=Section.objects.prefetch_related( + page_prefetch('section_pages__page'), + ) + ) + + +def catalog_prefetch_lookups(): + return ( + section_prefetch('catalog_sections__section'), + ) + + +def section_prefetch_lookups(): + return ( + page_prefetch('section_pages__page'), + ) + + +def page_prefetch_lookups(): + return ( + condition_prefetch('conditions'), + question_prefetch('page_questions__question'), + questionset_prefetch('page_questionsets__questionset'), + ) + + +def project_page_prefetch_lookups(): + return ( + condition_prefetch('conditions'), + question_prefetch('page_questions__question', include_options=True), + questionset_prefetch('page_questionsets__questionset', include_question_options=True), + ) + + +def questionset_prefetch_lookups(): + return ( + condition_prefetch('conditions'), + question_prefetch('questionset_questions__question'), + questionset_questionset_prefetch('questionset_questionsets__questionset'), + ) + + +def question_prefetch_lookups(): + return ( + condition_prefetch('conditions'), + 'optionsets', + 'default_option', + ) From 8797201d2d93c3f96c304fb6c673b3ceaea890e6 Mon Sep 17 00:00:00 2001 From: David Wallace Date: Wed, 20 May 2026 10:42:00 +0200 Subject: [PATCH 060/131] Add api performance test for project answers Signed-off-by: David Wallace --- .../tests/test_api_performance_project.py | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 rdmo/projects/tests/test_api_performance_project.py diff --git a/rdmo/projects/tests/test_api_performance_project.py b/rdmo/projects/tests/test_api_performance_project.py new file mode 100644 index 0000000000..494e9b5632 --- /dev/null +++ b/rdmo/projects/tests/test_api_performance_project.py @@ -0,0 +1,28 @@ +import pytest + +from django.urls import reverse + +max_queries = [ + # urlname, max_queries, url_args + ('project_answers', 41, [1]), + ('project_answers_export', 34, [1, 'html']), +] + + +@pytest.mark.performance +@pytest.mark.parametrize('urlname,max_queries,url_args', max_queries) +def test_project_answer_endpoints_query_counts( + db, + client, + django_assert_max_num_queries, + urlname, + max_queries, + url_args, +): + client.login(username='owner', password='owner') + url = reverse(urlname, args=url_args) + + with django_assert_max_num_queries(max_queries): + response = client.get(url) + + assert response.status_code == 200 From f3fcf22b7e9398e7fad3cda1bfcee15fa2e2ca6e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 May 2026 09:21:01 +0000 Subject: [PATCH 061/131] build(deps-dev): bump the dev-dependencies group across 1 directory with 8 updates Bumps the dev-dependencies group with 8 updates in the / directory: | Package | From | To | | --- | --- | --- | | [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) | `7.28.0` | `7.29.0` | | [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) | `7.28.0` | `7.29.5` | | [babel-loader](https://github.com/babel/babel-loader) | `10.0.0` | `10.1.1` | | [eslint](https://github.com/eslint/eslint) | `8.56.0` | `10.3.0` | | [mini-css-extract-plugin](https://github.com/webpack/mini-css-extract-plugin) | `2.9.0` | `2.10.2` | | [sass](https://github.com/sass/dart-sass) | `1.94.2` | `1.99.0` | | [webpack](https://github.com/webpack/webpack) | `5.105.0` | `5.106.2` | | [webpack-cli](https://github.com/webpack/webpack-cli) | `6.0.1` | `7.0.2` | Updates `@babel/core` from 7.28.0 to 7.29.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.29.0/packages/babel-core) Updates `@babel/preset-env` from 7.28.0 to 7.29.5 - [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.29.5/packages/babel-preset-env) Updates `babel-loader` from 10.0.0 to 10.1.1 - [Release notes](https://github.com/babel/babel-loader/releases) - [Changelog](https://github.com/babel/babel-loader/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel-loader/compare/v10.0.0...v10.1.1) Updates `eslint` from 8.56.0 to 10.3.0 - [Release notes](https://github.com/eslint/eslint/releases) - [Commits](https://github.com/eslint/eslint/compare/v8.56.0...v10.3.0) Updates `mini-css-extract-plugin` from 2.9.0 to 2.10.2 - [Release notes](https://github.com/webpack/mini-css-extract-plugin/releases) - [Changelog](https://github.com/webpack/mini-css-extract-plugin/blob/main/CHANGELOG.md) - [Commits](https://github.com/webpack/mini-css-extract-plugin/compare/v2.9.0...v2.10.2) Updates `sass` from 1.94.2 to 1.99.0 - [Release notes](https://github.com/sass/dart-sass/releases) - [Changelog](https://github.com/sass/dart-sass/blob/main/CHANGELOG.md) - [Commits](https://github.com/sass/dart-sass/compare/1.94.2...1.99.0) Updates `webpack` from 5.105.0 to 5.106.2 - [Release notes](https://github.com/webpack/webpack/releases) - [Changelog](https://github.com/webpack/webpack/blob/main/CHANGELOG.md) - [Commits](https://github.com/webpack/webpack/compare/v5.105.0...v5.106.2) Updates `webpack-cli` from 6.0.1 to 7.0.2 - [Release notes](https://github.com/webpack/webpack-cli/releases) - [Changelog](https://github.com/webpack/webpack-cli/blob/main/CHANGELOG.md) - [Commits](https://github.com/webpack/webpack-cli/compare/webpack-cli@6.0.1...webpack-cli@7.0.2) --- updated-dependencies: - dependency-name: "@babel/core" dependency-version: 7.29.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: dev-dependencies - dependency-name: "@babel/preset-env" dependency-version: 7.29.5 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: dev-dependencies - dependency-name: babel-loader dependency-version: 10.1.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: dev-dependencies - dependency-name: eslint dependency-version: 10.3.0 dependency-type: direct:development update-type: version-update:semver-major dependency-group: dev-dependencies - dependency-name: mini-css-extract-plugin dependency-version: 2.10.2 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: dev-dependencies - dependency-name: sass dependency-version: 1.99.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: dev-dependencies - dependency-name: webpack dependency-version: 5.106.2 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: dev-dependencies - dependency-name: webpack-cli dependency-version: 7.0.2 dependency-type: direct:development update-type: version-update:semver-major dependency-group: dev-dependencies ... Signed-off-by: dependabot[bot] --- package-lock.json | 2859 +++++++++++++++++++-------------------------- package.json | 16 +- 2 files changed, 1228 insertions(+), 1647 deletions(-) diff --git a/package-lock.json b/package-lock.json index 054cbb7c70..65e6f4a18b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,20 +38,20 @@ }, "devDependencies": { "@babel/cli": "^7.28.0", - "@babel/core": "^7.28.0", - "@babel/preset-env": "^7.28.0", + "@babel/core": "^7.29.0", + "@babel/preset-env": "^7.29.5", "@babel/preset-react": "^7.28.5", - "babel-loader": "^10.0.0", + "babel-loader": "^10.1.1", "copy-webpack-plugin": "^14.0.0", "css-loader": "^7.1.1", - "eslint": "~8.56.0", + "eslint": "~10.3.0", "eslint-plugin-react": "^7.37.2", "file-loader": "^6.2.0", - "mini-css-extract-plugin": "^2.9.0", - "sass": "^1.94.2", + "mini-css-extract-plugin": "^2.10.2", + "sass": "^1.99.0", "sass-loader": "^16.0.0", - "webpack": "^5.105.0", - "webpack-cli": "^6.0.1", + "webpack": "^5.106.2", + "webpack-cli": "^7.0.2", "webpack-merge": "6.0.1" }, "engines": { @@ -68,19 +68,6 @@ "node": ">=0.10.0" } }, - "node_modules/@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", - "devOptional": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.1.0", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@babel/cli": { "version": "7.28.0", "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.28.0.tgz", @@ -130,30 +117,30 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz", - "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.3.tgz", + "integrity": "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==", "devOptional": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz", - "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "devOptional": true, "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.0", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-module-transforms": "^7.27.3", - "@babel/helpers": "^7.27.6", - "@babel/parser": "^7.28.0", - "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.0", - "@babel/types": "^7.28.0", + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -189,15 +176,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.12", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", - "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, "node_modules/@babel/helper-annotate-as-pure": { "version": "7.27.3", "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", @@ -211,12 +189,12 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", - "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", "devOptional": true, "dependencies": { - "@babel/compat-data": "^7.27.2", + "@babel/compat-data": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", @@ -242,18 +220,17 @@ "devOptional": true }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.27.1.tgz", - "integrity": "sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A==", + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.29.3.tgz", + "integrity": "sha512-RpLYy2sb51oNLjuu1iD3bwBqCBWUzjO0ocp+iaCP/lJtb2CPLcnC2Fftw+4sAzaMELGeWTgExSKADbdo0GFVzA==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1", + "@babel/helper-replace-supers": "^7.28.6", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/traverse": "^7.27.1", + "@babel/traverse": "^7.29.0", "semver": "^6.3.1" }, "engines": { @@ -264,14 +241,13 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.27.1.tgz", - "integrity": "sha512-uVDC72XVf8UbrH5qQTc18Agb8emwjTiZrQE11Nv3CuBEZmVvTwwE9CBUEvHku06gQCAyYf8Nv6ja1IN+6LMbxQ==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.28.5.tgz", + "integrity": "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "regexpu-core": "^6.2.0", + "@babel/helper-annotate-as-pure": "^7.27.3", + "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "engines": { @@ -282,16 +258,16 @@ } }, "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.5.tgz", - "integrity": "sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg==", + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.8.tgz", + "integrity": "sha512-47UwBLPpQi1NoWzLuHNjRoHlYXMwIJoBf7MFou6viC/sIHWYygpvr0B6IAyh5sBdA2nr2LPIRww8lfaUVQINBA==", "dev": true, "dependencies": { - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-plugin-utils": "^7.27.1", - "debug": "^4.4.1", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "debug": "^4.4.3", "lodash.debounce": "^4.0.8", - "resolve": "^1.22.10" + "resolve": "^1.22.11" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" @@ -306,14 +282,13 @@ } }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz", - "integrity": "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz", + "integrity": "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -375,7 +350,6 @@ "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz", "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-wrap-function": "^7.27.1", @@ -389,15 +363,14 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", - "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.28.6.tgz", + "integrity": "sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/traverse": "^7.27.1" + "@babel/traverse": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -448,28 +421,27 @@ } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.27.1.tgz", - "integrity": "sha512-NFJK2sHUvrjo8wAU/nQTWU890/zB2jj0qBcCbZbbf+005cAsv6tMjXz31fBign6M5ov1o0Bllu+9nbqkfsjjJQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.28.6.tgz", + "integrity": "sha512-z+PwLziMNBeSQJonizz2AGnndLsP2DeGHIxDAn+wdHOGuo4Fo1x1HBPPXeE9TAOPHNNWQKCSlA2VZyYyyibDnQ==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/template": "^7.27.1", - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.28.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.2.tgz", - "integrity": "sha512-/V9771t+EgXz62aCcyofnQhGM8DQACbRhvzKFsXKC9QM+5MadF8ZmIm0crDMaz3+o0h0zXfJnd4EhbYbxsrcFw==", + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", "devOptional": true, "dependencies": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.2" + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" }, "engines": { "node": ">=6.9.0" @@ -490,14 +462,13 @@ } }, "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.27.1.tgz", - "integrity": "sha512-QPG3C9cCVRQLxAVwmefEmwdTanECuUBMQZ/ym5kiw3XKCGA7qkuQLcjWWHcrD/GKbn/WmJwaezfuuAOcyKlRPA==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.28.5.tgz", + "integrity": "sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.27.1" + "@babel/traverse": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -538,6 +509,22 @@ "@babel/core": "^7.0.0" } }, + "node_modules/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": { + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array/-/plugin-bugfix-safari-rest-destructuring-rhs-array-7.29.3.tgz", + "integrity": "sha512-SRS46DFR4HqzUzCVgi90/xMoL+zeBDBvWdKYXSEzh79kXswNFEglUpMKxR04//dPqwYXWUBJ3mpUd933ru9Kmg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz", @@ -557,14 +544,13 @@ } }, "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.27.1.tgz", - "integrity": "sha512-6BpaYGDavZqkI6yT+KSPdpZFfpnd68UKXbcjI9pJ13pvHhPrCKWOOLp+ysvMeA+DxnhuPpgIaRpxRxo5A9t5jw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.6.tgz", + "integrity": "sha512-a0aBScVTlNaiUe35UtfxAN7A/tehvvG4/ByO6+46VPKTRSlfnAFsgKy0FUh+qAkQrDTmhDkT+IBOKlOoMUxQ0g==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/traverse": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -586,13 +572,12 @@ } }, "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.27.1.tgz", - "integrity": "sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.28.6.tgz", + "integrity": "sha512-pSJUpFHdx9z5nqTSirOCMtYVP2wFgoWhP0p3g8ONK/4IHhLIBd0B9NYqAvIUAhq+OkhO4VM1tENCt0cjlsNShw==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -602,13 +587,12 @@ } }, "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", - "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz", + "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -666,14 +650,14 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.28.0.tgz", - "integrity": "sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.29.0.tgz", + "integrity": "sha512-va0VdWro4zlBr2JsXC+ofCPB2iG12wPtVGTWFx2WLDOM3nYQZZIGP82qku2eW/JR83sD+k2k+CsNtyEbUqhU6w==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-plugin-utils": "^7.28.6", "@babel/helper-remap-async-to-generator": "^7.27.1", - "@babel/traverse": "^7.28.0" + "@babel/traverse": "^7.29.0" }, "engines": { "node": ">=6.9.0" @@ -683,14 +667,13 @@ } }, "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.27.1.tgz", - "integrity": "sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.28.6.tgz", + "integrity": "sha512-ilTRcmbuXjsMmcZ3HASTe4caH5Tpo93PkTxF9oG2VZsSWsahydmcEHhix9Ik122RcTnZnUzPbmux4wh1swfv7g==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", "@babel/helper-remap-async-to-generator": "^7.27.1" }, "engines": { @@ -717,12 +700,12 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.0.tgz", - "integrity": "sha512-gKKnwjpdx5sER/wl0WN0efUBFzF/56YZO0RJrSYP4CljXnP31ByY7fol89AzomdlLNzI36AvOTmYHsnZTCkq8Q==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.6.tgz", + "integrity": "sha512-tt/7wOtBmwHPNMPu7ax4pdPz6shjFrmHDghvNC+FG9Qvj7D6mJcoRQIF5dy4njmxR941l6rgtvfSB2zX3VlUIw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -732,14 +715,13 @@ } }, "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.27.1.tgz", - "integrity": "sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.28.6.tgz", + "integrity": "sha512-dY2wS3I2G7D697VHndN91TJr8/AAfXQNt5ynCTI/MpxMsSzHp+52uNivYT5wCPax3whc47DR8Ba7cmlQMg24bw==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -749,14 +731,13 @@ } }, "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.27.1.tgz", - "integrity": "sha512-s734HmYU78MVzZ++joYM+NkJusItbdRcbm+AGRgJCt3iA+yux0QpD9cBVdz3tKyrjVYWRl7j0mHSmv4lhV0aoA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.6.tgz", + "integrity": "sha512-rfQ++ghVwTWTqQ7w8qyDxL1XGihjBss4CmTgGRCTAC9RIbhVpyp4fOeZtta0Lbf+dTNIVJer6ych2ibHwkZqsQ==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -766,17 +747,17 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.0.tgz", - "integrity": "sha512-IjM1IoJNw72AZFlj33Cu8X0q2XK/6AaVC3jQu+cgQ5lThWD5ajnuUAml80dqRmOhmPkTH8uAwnpMu9Rvj0LTRA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.6.tgz", + "integrity": "sha512-EF5KONAqC5zAqT783iMGuM2ZtmEBy+mJMOKl2BCvPZ2lVrwvXnB6o+OBWCS+CoeCCpVRF2sA2RBKUxvT8tQT5Q==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-globals": "^7.28.0", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1", - "@babel/traverse": "^7.28.0" + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-replace-supers": "^7.28.6", + "@babel/traverse": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -786,14 +767,13 @@ } }, "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.27.1.tgz", - "integrity": "sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.28.6.tgz", + "integrity": "sha512-bcc3k0ijhHbc2lEfpFHgx7eYw9KNXqOerKWfzbxEHUGKnS3sz9C4CNL9OiFN1297bDNfUiSO7DaLzbvHQQQ1BQ==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/template": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/template": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -803,13 +783,13 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.0.tgz", - "integrity": "sha512-v1nrSMBiKcodhsyJ4Gf+Z0U/yawmJDBOTpEB3mcQY52r9RIyPneGyAS/yM6seP/8I+mWI3elOMtT5dB8GJVs+A==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.5.tgz", + "integrity": "sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.28.0" + "@babel/traverse": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -819,14 +799,13 @@ } }, "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.27.1.tgz", - "integrity": "sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.28.6.tgz", + "integrity": "sha512-SljjowuNKB7q5Oayv4FoPzeB74g3QgLt8IVJw9ADvWy3QnUb/01aw8I4AVv8wYnPvQz2GDDZ/g3GhcNyDBI4Bg==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -852,14 +831,13 @@ } }, "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.27.1.tgz", - "integrity": "sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.29.0.tgz", + "integrity": "sha512-zBPcW2lFGxdiD8PUnPwJjag2J9otbcLQzvbiOzDxpYXyCuYX9agOwMPGn1prVH0a4qzhCKu24rlH4c1f7yA8rw==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -885,13 +863,13 @@ } }, "node_modules/@babel/plugin-transform-explicit-resource-management": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.0.tgz", - "integrity": "sha512-K8nhUcn3f6iB+P3gwCv/no7OdzOZQcKchW6N389V6PD8NUWKZHzndOd9sPDVbMoBsbmjMqlB4L9fm+fEFNVlwQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.6.tgz", + "integrity": "sha512-Iao5Konzx2b6g7EPqTy40UZbcdXE126tTxVFr/nAIj+WItNxjKSYTEw3RC+A2/ZetmdJsgueL1KhaMCQHkLPIg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/plugin-transform-destructuring": "^7.28.0" + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/plugin-transform-destructuring": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -901,13 +879,12 @@ } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.27.1.tgz", - "integrity": "sha512-uspvXnhHvGKf2r4VVtBpeFnuDWsJLQ6MF6lGJLC89jBR1uoVeqM416AZtTuhTezOfgHicpJQmoD5YUakO/YmXQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.28.6.tgz", + "integrity": "sha512-WitabqiGjV/vJ0aPOLSFfNY1u9U3R7W36B03r5I2KoNix+a3sOhJ3pKFB3R5It9/UiK78NiO0KE9P21cMhlPkw==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -968,13 +945,12 @@ } }, "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.27.1.tgz", - "integrity": "sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.28.6.tgz", + "integrity": "sha512-Nr+hEN+0geQkzhbdgQVPoqr47lZbm+5fCUmO70722xJZd0Mvb59+33QLImGj6F+DkK3xgDi1YVysP8whD6FQAw==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1000,13 +976,12 @@ } }, "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.27.1.tgz", - "integrity": "sha512-SJvDs5dXxiae4FbSL1aBJlG4wvl594N6YEVVn9e3JGulwioy6z3oPjx/sQBO3Y4NwUu5HNix6KJ3wBZoewcdbw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.28.6.tgz", + "integrity": "sha512-+anKKair6gpi8VsM/95kmomGNMD0eLz1NQ8+Pfw5sAwWH9fGYXT50E55ZpV0pHUHWf6IUTWPM+f/7AAff+wr9A==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1049,14 +1024,13 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz", - "integrity": "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.28.6.tgz", + "integrity": "sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1101,14 +1075,13 @@ } }, "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.27.1.tgz", - "integrity": "sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.29.0.tgz", + "integrity": "sha512-1CZQA5KNAD6ZYQLPw7oi5ewtDNxH/2vuCh+6SmvgDfhumForvs8a1o9n0UrEoBD8HU4djO2yWngTQlXl1NDVEQ==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1134,13 +1107,12 @@ } }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.27.1.tgz", - "integrity": "sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.28.6.tgz", + "integrity": "sha512-3wKbRgmzYbw24mDJXT7N+ADXw8BC/imU9yo9c9X9NKaLF1fW+e5H1U5QjMUBe4Qo4Ox/o++IyUkl1sVCLgevKg==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1150,13 +1122,12 @@ } }, "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.27.1.tgz", - "integrity": "sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.28.6.tgz", + "integrity": "sha512-SJR8hPynj8outz+SlStQSwvziMN4+Bq99it4tMIf5/Caq+3iOc0JtKyse8puvyXkk3eFRIA5ID/XfunGgO5i6w==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1166,16 +1137,16 @@ } }, "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.0.tgz", - "integrity": "sha512-9VNGikXxzu5eCiQjdE4IZn8sb9q7Xsk5EXLDBKUYg1e/Tve8/05+KJEtcxGxAgCY5t/BpKQM+JEL/yT4tvgiUA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.6.tgz", + "integrity": "sha512-5rh+JR4JBC4pGkXLAcYdLHZjXudVxWMXbB6u6+E9lRL5TrGVbHt1TjxGbZ8CkmYw9zjkB7jutzOROArsqtncEA==", "dev": true, "dependencies": { - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/plugin-transform-destructuring": "^7.28.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/plugin-transform-destructuring": "^7.28.5", "@babel/plugin-transform-parameters": "^7.27.7", - "@babel/traverse": "^7.28.0" + "@babel/traverse": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1202,13 +1173,12 @@ } }, "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.27.1.tgz", - "integrity": "sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.28.6.tgz", + "integrity": "sha512-R8ja/Pyrv0OGAvAXQhSTmWyPJPml+0TMqXlO5w+AsMEiwb2fg3WkOvob7UxFSL3OIttFSGSRFKQsOhJ/X6HQdQ==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1218,13 +1188,12 @@ } }, "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.27.1.tgz", - "integrity": "sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.28.6.tgz", + "integrity": "sha512-A4zobikRGJTsX9uqVFdafzGkqD30t26ck2LmOzAuLL8b2x6k3TIqRiT2xVvA9fNmFeTX484VpsdgmKNA0bS23w==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-plugin-utils": "^7.28.6", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "engines": { @@ -1250,14 +1219,13 @@ } }, "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.27.1.tgz", - "integrity": "sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.28.6.tgz", + "integrity": "sha512-piiuapX9CRv7+0st8lmuUlRSmX6mBcVeNQ1b4AYzJxfCMuBfB0vBXDiGSmm03pKJw1v6cZ8KSeM+oUnM6yAExg==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1267,15 +1235,14 @@ } }, "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.27.1.tgz", - "integrity": "sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.28.6.tgz", + "integrity": "sha512-b97jvNSOb5+ehyQmBpmhOCiUC5oVK4PMnpRvO7+ymFBoqYjeDHIU9jnrNUuwHOiL9RpGDoKBpSViarV+BU+eVA==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-create-class-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1369,12 +1336,12 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.28.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.1.tgz", - "integrity": "sha512-P0QiV/taaa3kXpLY+sXla5zec4E+4t4Aqc9ggHlfZ7a2cp8/x/Gv08jfwEtn9gnnYIMvHx6aoOZ8XJL8eU71Dg==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.29.0.tgz", + "integrity": "sha512-FijqlqMA7DmRdg/aINBSs04y8XNTYw/lr1gJ2WsmBnnaNw1iS43EPkJW+zK7z65auG3AWRFXWj+NcTQwYptUog==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1384,14 +1351,13 @@ } }, "node_modules/@babel/plugin-transform-regexp-modifiers": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.27.1.tgz", - "integrity": "sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.28.6.tgz", + "integrity": "sha512-QGWAepm9qxpaIs7UM9FvUSnCGlb8Ua1RhyM4/veAxLwt3gMat/LSGrZixyuj4I6+Kn9iwvqCyPTtbdxanYoWYg==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1433,13 +1399,12 @@ } }, "node_modules/@babel/plugin-transform-spread": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.27.1.tgz", - "integrity": "sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.28.6.tgz", + "integrity": "sha512-9U4QObUC0FtJl05AsUcodau/RWDytrU6uKgkxu09mLR9HLDAtUMoPuuskm5huQsoktmsYpI+bGmq+iapDcriKA==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-plugin-utils": "^7.28.6", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "engines": { @@ -1514,14 +1479,13 @@ } }, "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.27.1.tgz", - "integrity": "sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.28.6.tgz", + "integrity": "sha512-4Wlbdl/sIZjzi/8St0evF0gEZrgOswVO6aOzqxh1kDZOl9WmLrHq2HtGhnOJZmHZYKP8WZ1MDLCt5DAWwRo57A==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1548,14 +1512,13 @@ } }, "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.27.1.tgz", - "integrity": "sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.28.6.tgz", + "integrity": "sha512-/wHc/paTUmsDYN7SZkpWxogTOBNnlx7nBQYfy6JJlCT7G3mVhltk3e++N7zV0XfgGsrqBxd4rJQt9H16I21Y1Q==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1565,80 +1528,81 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.28.0.tgz", - "integrity": "sha512-VmaxeGOwuDqzLl5JUkIRM1X2Qu2uKGxHEQWh+cvvbl7JuJRgKGJSfsEF/bUaxFhJl/XAyxBe7q7qSuTbKFuCyg==", + "version": "7.29.5", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.29.5.tgz", + "integrity": "sha512-/69t2aEzGKHD76DyLbHysF/QH2LJOB8iFnYO37unDTKBTubzcMRv0f3H5EiN1Q6ajOd/eB7dAInF0qdFVS06kA==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.28.0", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-plugin-utils": "^7.27.1", + "@babel/compat-data": "^7.29.3", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.27.1", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.28.5", "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1", + "@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": "^7.29.3", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.27.1", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.6", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-import-assertions": "^7.27.1", - "@babel/plugin-syntax-import-attributes": "^7.27.1", + "@babel/plugin-syntax-import-assertions": "^7.28.6", + "@babel/plugin-syntax-import-attributes": "^7.28.6", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", "@babel/plugin-transform-arrow-functions": "^7.27.1", - "@babel/plugin-transform-async-generator-functions": "^7.28.0", - "@babel/plugin-transform-async-to-generator": "^7.27.1", + "@babel/plugin-transform-async-generator-functions": "^7.29.0", + "@babel/plugin-transform-async-to-generator": "^7.28.6", "@babel/plugin-transform-block-scoped-functions": "^7.27.1", - "@babel/plugin-transform-block-scoping": "^7.28.0", - "@babel/plugin-transform-class-properties": "^7.27.1", - "@babel/plugin-transform-class-static-block": "^7.27.1", - "@babel/plugin-transform-classes": "^7.28.0", - "@babel/plugin-transform-computed-properties": "^7.27.1", - "@babel/plugin-transform-destructuring": "^7.28.0", - "@babel/plugin-transform-dotall-regex": "^7.27.1", + "@babel/plugin-transform-block-scoping": "^7.28.6", + "@babel/plugin-transform-class-properties": "^7.28.6", + "@babel/plugin-transform-class-static-block": "^7.28.6", + "@babel/plugin-transform-classes": "^7.28.6", + "@babel/plugin-transform-computed-properties": "^7.28.6", + "@babel/plugin-transform-destructuring": "^7.28.5", + "@babel/plugin-transform-dotall-regex": "^7.28.6", "@babel/plugin-transform-duplicate-keys": "^7.27.1", - "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.27.1", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.29.0", "@babel/plugin-transform-dynamic-import": "^7.27.1", - "@babel/plugin-transform-explicit-resource-management": "^7.28.0", - "@babel/plugin-transform-exponentiation-operator": "^7.27.1", + "@babel/plugin-transform-explicit-resource-management": "^7.28.6", + "@babel/plugin-transform-exponentiation-operator": "^7.28.6", "@babel/plugin-transform-export-namespace-from": "^7.27.1", "@babel/plugin-transform-for-of": "^7.27.1", "@babel/plugin-transform-function-name": "^7.27.1", - "@babel/plugin-transform-json-strings": "^7.27.1", + "@babel/plugin-transform-json-strings": "^7.28.6", "@babel/plugin-transform-literals": "^7.27.1", - "@babel/plugin-transform-logical-assignment-operators": "^7.27.1", + "@babel/plugin-transform-logical-assignment-operators": "^7.28.6", "@babel/plugin-transform-member-expression-literals": "^7.27.1", "@babel/plugin-transform-modules-amd": "^7.27.1", - "@babel/plugin-transform-modules-commonjs": "^7.27.1", - "@babel/plugin-transform-modules-systemjs": "^7.27.1", + "@babel/plugin-transform-modules-commonjs": "^7.28.6", + "@babel/plugin-transform-modules-systemjs": "^7.29.4", "@babel/plugin-transform-modules-umd": "^7.27.1", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.27.1", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.29.0", "@babel/plugin-transform-new-target": "^7.27.1", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.27.1", - "@babel/plugin-transform-numeric-separator": "^7.27.1", - "@babel/plugin-transform-object-rest-spread": "^7.28.0", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.28.6", + "@babel/plugin-transform-numeric-separator": "^7.28.6", + "@babel/plugin-transform-object-rest-spread": "^7.28.6", "@babel/plugin-transform-object-super": "^7.27.1", - "@babel/plugin-transform-optional-catch-binding": "^7.27.1", - "@babel/plugin-transform-optional-chaining": "^7.27.1", + "@babel/plugin-transform-optional-catch-binding": "^7.28.6", + "@babel/plugin-transform-optional-chaining": "^7.28.6", "@babel/plugin-transform-parameters": "^7.27.7", - "@babel/plugin-transform-private-methods": "^7.27.1", - "@babel/plugin-transform-private-property-in-object": "^7.27.1", + "@babel/plugin-transform-private-methods": "^7.28.6", + "@babel/plugin-transform-private-property-in-object": "^7.28.6", "@babel/plugin-transform-property-literals": "^7.27.1", - "@babel/plugin-transform-regenerator": "^7.28.0", - "@babel/plugin-transform-regexp-modifiers": "^7.27.1", + "@babel/plugin-transform-regenerator": "^7.29.0", + "@babel/plugin-transform-regexp-modifiers": "^7.28.6", "@babel/plugin-transform-reserved-words": "^7.27.1", "@babel/plugin-transform-shorthand-properties": "^7.27.1", - "@babel/plugin-transform-spread": "^7.27.1", + "@babel/plugin-transform-spread": "^7.28.6", "@babel/plugin-transform-sticky-regex": "^7.27.1", "@babel/plugin-transform-template-literals": "^7.27.1", "@babel/plugin-transform-typeof-symbol": "^7.27.1", "@babel/plugin-transform-unicode-escapes": "^7.27.1", - "@babel/plugin-transform-unicode-property-regex": "^7.27.1", + "@babel/plugin-transform-unicode-property-regex": "^7.28.6", "@babel/plugin-transform-unicode-regex": "^7.27.1", - "@babel/plugin-transform-unicode-sets-regex": "^7.27.1", + "@babel/plugin-transform-unicode-sets-regex": "^7.28.6", "@babel/preset-modules": "0.1.6-no-external-plugins", - "babel-plugin-polyfill-corejs2": "^0.4.14", - "babel-plugin-polyfill-corejs3": "^0.13.0", - "babel-plugin-polyfill-regenerator": "^0.6.5", - "core-js-compat": "^3.43.0", + "babel-plugin-polyfill-corejs2": "^0.4.15", + "babel-plugin-polyfill-corejs3": "^0.14.0", + "babel-plugin-polyfill-regenerator": "^0.6.6", + "core-js-compat": "^3.48.0", "semver": "^6.3.1" }, "engines": { @@ -1878,9 +1842,9 @@ } }, "node_modules/@discoveryjs/json-ext": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.6.3.tgz", - "integrity": "sha512-4B4OijXeVNOPZlYA2oEwWOTkzyltLao+xbotHQeqN++Rv27Y6s818+n2Qkp8q+Fxhn0t/5lA5X1Mxktud8eayQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-1.1.0.tgz", + "integrity": "sha512-Xc3VhU02wqZ1HvHRJUwL09HkZSTvidqY5Ya0NXBSYOxAp+Ln9dcJr9fySI+CkONzP3PekQo9WdzCv0PGER/mOA==", "dev": true, "engines": { "node": ">=14.17.0" @@ -2025,74 +1989,138 @@ "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==" }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", "dev": true, "dependencies": { - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.3" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, + "funding": { + "url": "https://opencollective.com/eslint" + }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@eslint-community/regexpp": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", - "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "node_modules/@eslint/config-array": { + "version": "0.23.5", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz", + "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", "dev": true, "dependencies": { - "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" + "@eslint/object-schema": "^3.0.5", + "debug": "^4.3.1", + "minimatch": "^10.2.4" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/config-array/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@eslint/config-array/node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dev": true, + "dependencies": { + "balanced-match": "^4.0.2" }, - "funding": { - "url": "https://opencollective.com/eslint" + "engines": { + "node": "18 || 20 || >=22" } }, - "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==", + "node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "dev": true, "dependencies": { - "type-fest": "^0.20.2" + "brace-expansion": "^5.0.5" }, "engines": { - "node": ">=8" + "node": "18 || 20 || >=22" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@eslint/js": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", - "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", + "node_modules/@eslint/config-helpers": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.5.tgz", + "integrity": "sha512-eIJYKTCECbP/nsKaaruF6LW967mtbQbsw4JTtSVkUQc9MneSkbrgPJAbKl9nWr0ZeowV8BfsarBmPpBzGelA2w==", "dev": true, + "dependencies": { + "@eslint/core": "^1.2.1" + }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", + "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/object-schema": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", + "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", + "dev": true, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.1.tgz", + "integrity": "sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ==", + "dev": true, + "dependencies": { + "@eslint/core": "^1.2.1", + "levn": "^0.4.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" } }, "node_modules/@floating-ui/core": { @@ -2145,18 +2173,39 @@ "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==" }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.13", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", - "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^2.0.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" + "@humanfs/types": "^0.15.0" }, "engines": { - "node": ">=10.10.0" + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "dev": true, + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "engines": { + "node": ">=18.18.0" } }, "node_modules/@humanwhocodes/module-importer": { @@ -2172,23 +2221,36 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", - "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", - "dev": true + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } }, "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==", + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", "devOptional": true, "dependencies": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" - }, - "engines": { - "node": ">=6.0.0" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" } }, "node_modules/@jridgewell/resolve-uri": { @@ -2199,15 +2261,6 @@ "node": ">=6.0.0" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "devOptional": true, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@jridgewell/source-map": { "version": "0.3.11", "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", @@ -2218,16 +2271,6 @@ "@jridgewell/trace-mapping": "^0.3.25" } }, - "node_modules/@jridgewell/source-map/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", @@ -2298,41 +2341,6 @@ "dev": true, "optional": true }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/@parcel/watcher": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.0.tgz", @@ -2676,6 +2684,12 @@ "@types/estree": "*" } }, + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -2793,12 +2807,6 @@ "react-dom": ">=17.0.0" } }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true - }, "node_modules/@webassemblyjs/ast": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", @@ -2945,50 +2953,6 @@ "@xtuc/long": "4.2.2" } }, - "node_modules/@webpack-cli/configtest": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-3.0.1.tgz", - "integrity": "sha512-u8d0pJ5YFgneF/GuvEiDA61Tf1VDomHHYMjv/wc9XzYj7nopltpG96nXN5dJRstxZhcNpV1g+nT6CydO7pHbjA==", - "dev": true, - "engines": { - "node": ">=18.12.0" - }, - "peerDependencies": { - "webpack": "^5.82.0", - "webpack-cli": "6.x.x" - } - }, - "node_modules/@webpack-cli/info": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-3.0.1.tgz", - "integrity": "sha512-coEmDzc2u/ffMvuW9aCjoRzNSPDl/XLuhPdlFRpT9tZHmJ/039az33CE7uH+8s0uL1j5ZNtfdv0HkfaKRBGJsQ==", - "dev": true, - "engines": { - "node": ">=18.12.0" - }, - "peerDependencies": { - "webpack": "^5.82.0", - "webpack-cli": "6.x.x" - } - }, - "node_modules/@webpack-cli/serve": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-3.0.1.tgz", - "integrity": "sha512-sbgw03xQaCLiT6gcY/6u3qBDn01CWw/nbaXl3gTdTFuJJ75Gffv3E3DBpgvY2fkkrdS1fpjaXNOmJlnbtKauKg==", - "dev": true, - "engines": { - "node": ">=18.12.0" - }, - "peerDependencies": { - "webpack": "^5.82.0", - "webpack-cli": "6.x.x" - }, - "peerDependenciesMeta": { - "webpack-dev-server": { - "optional": true - } - } - }, "node_modules/@xtuc/ieee754": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", @@ -3002,9 +2966,9 @@ "dev": true }, "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -3100,15 +3064,6 @@ "ajv": "^6.9.1" } }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/anymatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", @@ -3123,12 +3078,6 @@ "node": ">= 8" } }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, "node_modules/array-buffer-byte-length": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", @@ -3265,11 +3214,10 @@ } }, "node_modules/babel-loader": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-10.0.0.tgz", - "integrity": "sha512-z8jt+EdS61AMw22nSfoNJAZ0vrtmhPRVi6ghL3rCeRZI8cdNYFiV5xeV3HbE7rlZZNmGH8BVccwWt8/ED0QOHA==", + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-10.1.1.tgz", + "integrity": "sha512-JwKSzk2kjIe7mgPK+/lyZ2QAaJcpahNAdM+hgR2HI8D0OJVkdj8Rl6J3kaLYki9pwF7P2iWnD8qVv80Lq1ABtg==", "dev": true, - "license": "MIT", "dependencies": { "find-up": "^5.0.0" }, @@ -3277,8 +3225,17 @@ "node": "^18.20.0 || ^20.10.0 || >=22.0.0" }, "peerDependencies": { - "@babel/core": "^7.12.0", + "@babel/core": "^7.12.0 || ^8.0.0-beta.1", + "@rspack/core": "^1.0.0 || ^2.0.0-0", "webpack": ">=5.61.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } } }, "node_modules/babel-loader/node_modules/find-up": { @@ -3361,13 +3318,13 @@ } }, "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.14", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.14.tgz", - "integrity": "sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg==", + "version": "0.4.17", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.17.tgz", + "integrity": "sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.27.7", - "@babel/helper-define-polyfill-provider": "^0.6.5", + "@babel/compat-data": "^7.28.6", + "@babel/helper-define-polyfill-provider": "^0.6.8", "semver": "^6.3.1" }, "peerDependencies": { @@ -3375,25 +3332,25 @@ } }, "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.13.0.tgz", - "integrity": "sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==", + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.14.2.tgz", + "integrity": "sha512-coWpDLJ410R781Npmn/SIBZEsAetR4xVi0SxLMXPaMO4lSf1MwnkGYMtkFxew0Dn8B3/CpbpYxN0JCgg8mn67g==", "dev": true, "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.5", - "core-js-compat": "^3.43.0" + "@babel/helper-define-polyfill-provider": "^0.6.8", + "core-js-compat": "^3.48.0" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.5.tgz", - "integrity": "sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg==", + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.8.tgz", + "integrity": "sha512-M762rNHfSF1EV3SLtnCJXFoQbbIIz0OyRwnCmV0KPC7qosSfCO0QLTSuJX3ayAebubhE6oYBAYPrBA5ljowaZg==", "dev": true, "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.5" + "@babel/helper-define-polyfill-provider": "^0.6.8" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" @@ -3623,12 +3580,6 @@ "@codemirror/view": "^6.0.0" } }, - "node_modules/colorette": { - "version": "2.0.16", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", - "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==", - "dev": true - }, "node_modules/commander": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", @@ -3696,12 +3647,12 @@ "hasInstallScript": true }, "node_modules/core-js-compat": { - "version": "3.44.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.44.0.tgz", - "integrity": "sha512-JepmAj2zfl6ogy34qfWtcE7nHKAJnKsQFRn++scjVS2bZFllwptzw61BZcZFYBPpUznLfAvh0LGhxKppk04ClA==", + "version": "3.49.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.49.0.tgz", + "integrity": "sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==", "dev": true, "dependencies": { - "browserslist": "^4.25.1" + "browserslist": "^4.28.1" }, "funding": { "type": "opencollective", @@ -3871,9 +3822,9 @@ } }, "node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dependencies": { "ms": "^2.1.3" }, @@ -3970,18 +3921,6 @@ "redux": "^4.2.0" } }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/dom-helpers": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", @@ -4057,13 +3996,13 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.19.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.19.0.tgz", - "integrity": "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==", + "version": "5.21.5", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.21.5.tgz", + "integrity": "sha512-mLCNbrQli11K1ySUmuNt4ZUB3OpGIDq4q2vTBTf5cL2lpsRjI9QKqSD0ndjW8FyvcW/Jj46gMe9syyHAsvMa/A==", "dev": true, "dependencies": { "graceful-fs": "^4.2.4", - "tapable": "^2.3.0" + "tapable": "^2.3.3" }, "engines": { "node": ">=10.13.0" @@ -4176,7 +4115,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, "engines": { "node": ">= 0.4" } @@ -4274,58 +4212,58 @@ } }, "node_modules/eslint": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", - "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.56.0", - "@humanwhocodes/config-array": "^0.11.13", + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.3.0.tgz", + "integrity": "sha512-XbEXaRva5cF0ZQB8w6MluHA0kZZfV2DuCMJ3ozyEOHLwDpZX2Lmm/7Pp0xdJmI0GL1W05VH5VwIFHEm1Vcw2gw==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.5", + "@eslint/config-helpers": "^0.5.5", + "@eslint/core": "^1.2.1", + "@eslint/plugin-kit": "^0.7.1", + "@humanfs/node": "^0.16.6", "@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", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "cross-spawn": "^7.0.6", "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", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.2.0", + "esquery": "^1.7.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", + "file-entry-cache": "^8.0.0", "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", + "minimatch": "^10.2.4", "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" + "optionator": "^0.9.3" }, "bin": { "eslint": "bin/eslint.js" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } } }, "node_modules/eslint-plugin-react": { @@ -4412,66 +4350,38 @@ } }, "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==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", "dev": true, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/eslint/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/eslint/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": "18 || 20 || >=22" } }, - "node_modules/eslint/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/eslint/node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "balanced-match": "^4.0.2" }, "engines": { - "node": ">=7.0.0" + "node": "18 || 20 || >=22" } }, - "node_modules/eslint/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, "node_modules/eslint/node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -4485,16 +4395,18 @@ } }, "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", "dev": true, "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", "esrecurse": "^4.3.0", "estraverse": "^5.2.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://opencollective.com/eslint" @@ -4537,34 +4449,34 @@ "node": ">=10.13.0" } }, - "node_modules/eslint/node_modules/globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "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==", "dev": true, "dependencies": { - "type-fest": "^0.20.2" + "p-locate": "^5.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "node_modules/eslint/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "dev": true, "dependencies": { - "p-locate": "^5.0.0" + "brace-expansion": "^5.0.5" }, "engines": { - "node": ">=10" + "node": "18 || 20 || >=22" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/eslint/node_modules/p-limit": { @@ -4597,39 +4509,27 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "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==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", "dev": true, "dependencies": { - "acorn": "^8.9.0", + "acorn": "^8.16.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" + "eslint-visitor-keys": "^5.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", "dev": true, "dependencies": { "estraverse": "^5.1.0" @@ -4734,25 +4634,16 @@ "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==", "dev": true }, - "node_modules/fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", - "dev": true, - "dependencies": { - "reusify": "^1.0.4" - } - }, "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==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, "dependencies": { - "flat-cache": "^3.0.4" + "flat-cache": "^4.0.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=16.0.0" } }, "node_modules/file-loader": { @@ -4845,16 +4736,16 @@ } }, "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, "dependencies": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" + "flatted": "^3.2.9", + "keyv": "^4.5.4" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=16" } }, "node_modules/flatted": { @@ -5059,12 +4950,6 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true - }, "node_modules/has-bigints": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", @@ -5200,9 +5085,9 @@ } }, "node_modules/ignore": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", - "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "engines": { "node": ">= 4" @@ -5543,15 +5428,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", @@ -5761,22 +5637,10 @@ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, - "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "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==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "bin": { "jsesc": "bin/jsesc" }, @@ -5784,6 +5648,12 @@ "node": ">=6" } }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", @@ -5831,6 +5701,15 @@ "resolved": "https://registry.npmjs.org/keycode/-/keycode-2.2.1.tgz", "integrity": "sha512-Rdgz9Hl9Iv4QKi8b0OlCRQEzp4AgVxyCtz5S/+VIHezDmrDhkp2N2TqBWOLz0/gbeREXOOiI9/4b8BY9uw2vFg==" }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, "node_modules/kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -5916,12 +5795,6 @@ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "dev": true }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -5994,30 +5867,18 @@ } }, "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, - "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==", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", "dev": true, - "dependencies": { - "mime-db": "1.52.0" - }, "engines": { "node": ">= 0.6" } }, "node_modules/mini-css-extract-plugin": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.0.tgz", - "integrity": "sha512-Zs1YsZVfemekSZG+44vBsYTLQORkPMwnlv+aehcxK/NLKC+EGhDB39/YePYYqx/sTk6NnYpuqikhSn7+JIevTA==", + "version": "2.10.2", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.10.2.tgz", + "integrity": "sha512-AOSS0IdEB95ayVkxn5oGzNQwqAi2J0Jb/kKm43t7H73s8+f5873g0yuj0PNvK4dO75mu5DHg4nlgp4k6Kga8eg==", "dev": true, "dependencies": { "schema-utils": "^4.0.0", @@ -6551,26 +6412,6 @@ "node": ">=6" } }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "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/react": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", @@ -6948,15 +6789,13 @@ "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/regenerate-unicode-properties": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", - "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.2.tgz", + "integrity": "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==", "dev": true, - "license": "MIT", "dependencies": { "regenerate": "^1.4.2" }, @@ -6989,18 +6828,17 @@ } }, "node_modules/regexpu-core": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz", - "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.4.0.tgz", + "integrity": "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==", "dev": true, - "license": "MIT", "dependencies": { "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.2.0", + "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", - "regjsparser": "^0.12.0", + "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.1.0" + "unicode-match-property-value-ecmascript": "^2.2.1" }, "engines": { "node": ">=4" @@ -7010,17 +6848,15 @@ "version": "0.8.0", "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/regjsparser": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz", - "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.13.1.tgz", + "integrity": "sha512-dLsljMd9sqwRkby8zhO1gSg3PnJIBFid8f4CQj/sXx+7cKx+E7u0PKhZ+U4wmhx7EfmtvnA318oVaIkAB1lRJw==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { - "jsesc": "~3.0.2" + "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" @@ -7036,11 +6872,12 @@ } }, "node_modules/resolve": { - "version": "1.22.10", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", "dependencies": { - "is-core-module": "^2.16.0", + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -7075,54 +6912,6 @@ "node": ">=8" } }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, "node_modules/safe-array-concat": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", @@ -7164,13 +6953,13 @@ } }, "node_modules/sass": { - "version": "1.94.2", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.94.2.tgz", - "integrity": "sha512-N+7WK20/wOr7CzA2snJcUSSNTCzeCGUTFY3OgeQP3mZ1aj9NMQ0mSTXwlrnd89j33zzQJGqIN52GIOmYrfq46A==", + "version": "1.99.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.99.0.tgz", + "integrity": "sha512-kgW13M54DUB7IsIRM5LvJkNlpH+WhMpooUcaWGFARkF1Tc82v9mIWkCbCYf+MBvpIUBSeSOTilpZjEPr2VYE6Q==", "dev": true, "dependencies": { "chokidar": "^4.0.0", - "immutable": "^5.0.2", + "immutable": "^5.1.5", "source-map-js": ">=0.6.2 <2.0.0" }, "bin": { @@ -7548,30 +7337,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "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==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/style-mod": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz", @@ -7615,9 +7380,9 @@ "license": "MIT" }, "node_modules/tapable": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", - "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", "dev": true, "engines": { "node": ">=6" @@ -7685,12 +7450,6 @@ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, "node_modules/tinyglobby": { "version": "0.2.12", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.12.tgz", @@ -7765,18 +7524,6 @@ "node": ">= 0.8.0" } }, - "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, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/typed-array-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", @@ -7884,7 +7631,6 @@ "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", "dev": true, - "license": "MIT", "engines": { "node": ">=4" } @@ -7894,7 +7640,6 @@ "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", "dev": true, - "license": "MIT", "dependencies": { "unicode-canonical-property-names-ecmascript": "^2.0.0", "unicode-property-aliases-ecmascript": "^2.0.0" @@ -7904,21 +7649,19 @@ } }, "node_modules/unicode-match-property-value-ecmascript": { - "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==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.1.tgz", + "integrity": "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==", "dev": true, - "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/unicode-property-aliases-ecmascript": { - "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==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.2.0.tgz", + "integrity": "sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=4" } @@ -8027,9 +7770,9 @@ } }, "node_modules/webpack": { - "version": "5.105.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.0.tgz", - "integrity": "sha512-gX/dMkRQc7QOMzgTe6KsYFM7DxeIONQSui1s0n/0xht36HvrgbxtM1xBlgx596NbpHuQU8P7QpKwrZYwUX48nw==", + "version": "5.106.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.106.2.tgz", + "integrity": "sha512-wGN3qcrBQIFmQ/c0AiOAQBvrZ5lmY8vbbMv4Mxfgzqd/B6+9pXtLo73WuS1dSGXM5QYY3hZnIbvx+K1xxe6FyA==", "dev": true, "dependencies": { "@types/eslint-scope": "^3.7.7", @@ -8038,25 +7781,24 @@ "@webassemblyjs/ast": "^1.14.1", "@webassemblyjs/wasm-edit": "^1.14.1", "@webassemblyjs/wasm-parser": "^1.14.1", - "acorn": "^8.15.0", + "acorn": "^8.16.0", "acorn-import-phases": "^1.0.3", "browserslist": "^4.28.1", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.19.0", + "enhanced-resolve": "^5.20.0", "es-module-lexer": "^2.0.0", "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.3.1", - "mime-types": "^2.1.27", + "mime-db": "^1.54.0", "neo-async": "^2.6.2", "schema-utils": "^4.3.3", "tapable": "^2.3.0", - "terser-webpack-plugin": "^5.3.16", + "terser-webpack-plugin": "^5.3.17", "watchpack": "^2.5.1", - "webpack-sources": "^3.3.3" + "webpack-sources": "^3.3.4" }, "bin": { "webpack": "bin/webpack.js" @@ -8075,18 +7817,14 @@ } }, "node_modules/webpack-cli": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-6.0.1.tgz", - "integrity": "sha512-MfwFQ6SfwinsUVi0rNJm7rHZ31GyTcpVE5pgVA3hwFRb7COD4TzjUUwhGWKfO50+xdc2MQPuEBBJoqIMGt3JDw==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-7.0.2.tgz", + "integrity": "sha512-dB0R4T+C/8YuvM+fabdvil6QE44/ChDXikV5lOOkrUeCkW5hTJv2pGLE3keh+D5hjYw8icBaJkZzpFoaHV4T+g==", "dev": true, "dependencies": { - "@discoveryjs/json-ext": "^0.6.1", - "@webpack-cli/configtest": "^3.0.1", - "@webpack-cli/info": "^3.0.1", - "@webpack-cli/serve": "^3.0.1", - "colorette": "^2.0.14", - "commander": "^12.1.0", - "cross-spawn": "^7.0.3", + "@discoveryjs/json-ext": "^1.0.0", + "commander": "^14.0.3", + "cross-spawn": "^7.0.6", "envinfo": "^7.14.0", "fastest-levenshtein": "^1.0.12", "import-local": "^3.0.2", @@ -8098,14 +7836,16 @@ "webpack-cli": "bin/cli.js" }, "engines": { - "node": ">=18.12.0" + "node": ">=20.9.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/webpack" }, "peerDependencies": { - "webpack": "^5.82.0" + "webpack": "^5.101.0", + "webpack-bundle-analyzer": "^4.0.0 || ^5.0.0", + "webpack-dev-server": "^5.0.0" }, "peerDependenciesMeta": { "webpack-bundle-analyzer": { @@ -8117,12 +7857,12 @@ } }, "node_modules/webpack-cli/node_modules/commander": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", + "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", "dev": true, "engines": { - "node": ">=18" + "node": ">=20" } }, "node_modules/webpack-merge": { @@ -8140,9 +7880,9 @@ } }, "node_modules/webpack-sources": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz", - "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.4.1.tgz", + "integrity": "sha512-eACpxRN02yaawnt+uUNIF7Qje6A9zArxBbcAJjK1PK3S9Ycg5jIuJ8pW4q8EMnwNZCEGltcjkRx1QzOxOkKD8A==", "dev": true, "engines": { "node": ">=10.13.0" @@ -8288,16 +8028,6 @@ "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", "dev": true }, - "@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", - "devOptional": true, - "requires": { - "@jridgewell/gen-mapping": "^0.1.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, "@babel/cli": { "version": "7.28.0", "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.28.0.tgz", @@ -8334,27 +8064,27 @@ } }, "@babel/compat-data": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz", - "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.3.tgz", + "integrity": "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==", "devOptional": true }, "@babel/core": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz", - "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "devOptional": true, "requires": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.0", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-module-transforms": "^7.27.3", - "@babel/helpers": "^7.27.6", - "@babel/parser": "^7.28.0", - "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.0", - "@babel/types": "^7.28.0", + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -8380,17 +8110,6 @@ "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" - }, - "dependencies": { - "@jridgewell/gen-mapping": { - "version": "0.3.12", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", - "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", - "requires": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - } } }, "@babel/helper-annotate-as-pure": { @@ -8403,12 +8122,12 @@ } }, "@babel/helper-compilation-targets": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", - "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", "devOptional": true, "requires": { - "@babel/compat-data": "^7.27.2", + "@babel/compat-data": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", @@ -8433,42 +8152,42 @@ } }, "@babel/helper-create-class-features-plugin": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.27.1.tgz", - "integrity": "sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A==", + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.29.3.tgz", + "integrity": "sha512-RpLYy2sb51oNLjuu1iD3bwBqCBWUzjO0ocp+iaCP/lJtb2CPLcnC2Fftw+4sAzaMELGeWTgExSKADbdo0GFVzA==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1", + "@babel/helper-replace-supers": "^7.28.6", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/traverse": "^7.27.1", + "@babel/traverse": "^7.29.0", "semver": "^6.3.1" } }, "@babel/helper-create-regexp-features-plugin": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.27.1.tgz", - "integrity": "sha512-uVDC72XVf8UbrH5qQTc18Agb8emwjTiZrQE11Nv3CuBEZmVvTwwE9CBUEvHku06gQCAyYf8Nv6ja1IN+6LMbxQ==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.28.5.tgz", + "integrity": "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "regexpu-core": "^6.2.0", + "@babel/helper-annotate-as-pure": "^7.27.3", + "regexpu-core": "^6.3.1", "semver": "^6.3.1" } }, "@babel/helper-define-polyfill-provider": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.5.tgz", - "integrity": "sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg==", + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.8.tgz", + "integrity": "sha512-47UwBLPpQi1NoWzLuHNjRoHlYXMwIJoBf7MFou6viC/sIHWYygpvr0B6IAyh5sBdA2nr2LPIRww8lfaUVQINBA==", "dev": true, "requires": { - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-plugin-utils": "^7.27.1", - "debug": "^4.4.1", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "debug": "^4.4.3", "lodash.debounce": "^4.0.8", - "resolve": "^1.22.10" + "resolve": "^1.22.11" } }, "@babel/helper-globals": { @@ -8477,13 +8196,13 @@ "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==" }, "@babel/helper-member-expression-to-functions": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz", - "integrity": "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz", + "integrity": "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==", "dev": true, "requires": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5" } }, "@babel/helper-module-imports": { @@ -8533,14 +8252,14 @@ } }, "@babel/helper-replace-supers": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", - "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.28.6.tgz", + "integrity": "sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==", "dev": true, "requires": { - "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/traverse": "^7.27.1" + "@babel/traverse": "^7.28.6" } }, "@babel/helper-skip-transparent-expression-wrappers": { @@ -8570,24 +8289,24 @@ "devOptional": true }, "@babel/helper-wrap-function": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.27.1.tgz", - "integrity": "sha512-NFJK2sHUvrjo8wAU/nQTWU890/zB2jj0qBcCbZbbf+005cAsv6tMjXz31fBign6M5ov1o0Bllu+9nbqkfsjjJQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.28.6.tgz", + "integrity": "sha512-z+PwLziMNBeSQJonizz2AGnndLsP2DeGHIxDAn+wdHOGuo4Fo1x1HBPPXeE9TAOPHNNWQKCSlA2VZyYyyibDnQ==", "dev": true, "requires": { - "@babel/template": "^7.27.1", - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" } }, "@babel/helpers": { - "version": "7.28.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.2.tgz", - "integrity": "sha512-/V9771t+EgXz62aCcyofnQhGM8DQACbRhvzKFsXKC9QM+5MadF8ZmIm0crDMaz3+o0h0zXfJnd4EhbYbxsrcFw==", + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", "devOptional": true, "requires": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.2" + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" } }, "@babel/parser": { @@ -8599,13 +8318,13 @@ } }, "@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.27.1.tgz", - "integrity": "sha512-QPG3C9cCVRQLxAVwmefEmwdTanECuUBMQZ/ym5kiw3XKCGA7qkuQLcjWWHcrD/GKbn/WmJwaezfuuAOcyKlRPA==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.28.5.tgz", + "integrity": "sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.27.1" + "@babel/traverse": "^7.28.5" } }, "@babel/plugin-bugfix-safari-class-field-initializer-scope": { @@ -8626,6 +8345,16 @@ "@babel/helper-plugin-utils": "^7.27.1" } }, + "@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": { + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array/-/plugin-bugfix-safari-rest-destructuring-rhs-array-7.29.3.tgz", + "integrity": "sha512-SRS46DFR4HqzUzCVgi90/xMoL+zeBDBvWdKYXSEzh79kXswNFEglUpMKxR04//dPqwYXWUBJ3mpUd933ru9Kmg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + } + }, "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz", @@ -8638,13 +8367,13 @@ } }, "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.27.1.tgz", - "integrity": "sha512-6BpaYGDavZqkI6yT+KSPdpZFfpnd68UKXbcjI9pJ13pvHhPrCKWOOLp+ysvMeA+DxnhuPpgIaRpxRxo5A9t5jw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.6.tgz", + "integrity": "sha512-a0aBScVTlNaiUe35UtfxAN7A/tehvvG4/ByO6+46VPKTRSlfnAFsgKy0FUh+qAkQrDTmhDkT+IBOKlOoMUxQ0g==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/traverse": "^7.28.6" } }, "@babel/plugin-proposal-private-property-in-object": { @@ -8655,21 +8384,21 @@ "requires": {} }, "@babel/plugin-syntax-import-assertions": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.27.1.tgz", - "integrity": "sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.28.6.tgz", + "integrity": "sha512-pSJUpFHdx9z5nqTSirOCMtYVP2wFgoWhP0p3g8ONK/4IHhLIBd0B9NYqAvIUAhq+OkhO4VM1tENCt0cjlsNShw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" } }, "@babel/plugin-syntax-import-attributes": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", - "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz", + "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" } }, "@babel/plugin-syntax-jsx": { @@ -8701,24 +8430,24 @@ } }, "@babel/plugin-transform-async-generator-functions": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.28.0.tgz", - "integrity": "sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.29.0.tgz", + "integrity": "sha512-va0VdWro4zlBr2JsXC+ofCPB2iG12wPtVGTWFx2WLDOM3nYQZZIGP82qku2eW/JR83sD+k2k+CsNtyEbUqhU6w==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-plugin-utils": "^7.28.6", "@babel/helper-remap-async-to-generator": "^7.27.1", - "@babel/traverse": "^7.28.0" + "@babel/traverse": "^7.29.0" } }, "@babel/plugin-transform-async-to-generator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.27.1.tgz", - "integrity": "sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.28.6.tgz", + "integrity": "sha512-ilTRcmbuXjsMmcZ3HASTe4caH5Tpo93PkTxF9oG2VZsSWsahydmcEHhix9Ik122RcTnZnUzPbmux4wh1swfv7g==", "dev": true, "requires": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", "@babel/helper-remap-async-to-generator": "^7.27.1" } }, @@ -8732,76 +8461,76 @@ } }, "@babel/plugin-transform-block-scoping": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.0.tgz", - "integrity": "sha512-gKKnwjpdx5sER/wl0WN0efUBFzF/56YZO0RJrSYP4CljXnP31ByY7fol89AzomdlLNzI36AvOTmYHsnZTCkq8Q==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.6.tgz", + "integrity": "sha512-tt/7wOtBmwHPNMPu7ax4pdPz6shjFrmHDghvNC+FG9Qvj7D6mJcoRQIF5dy4njmxR941l6rgtvfSB2zX3VlUIw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" } }, "@babel/plugin-transform-class-properties": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.27.1.tgz", - "integrity": "sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.28.6.tgz", + "integrity": "sha512-dY2wS3I2G7D697VHndN91TJr8/AAfXQNt5ynCTI/MpxMsSzHp+52uNivYT5wCPax3whc47DR8Ba7cmlQMg24bw==", "dev": true, "requires": { - "@babel/helper-create-class-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" } }, "@babel/plugin-transform-class-static-block": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.27.1.tgz", - "integrity": "sha512-s734HmYU78MVzZ++joYM+NkJusItbdRcbm+AGRgJCt3iA+yux0QpD9cBVdz3tKyrjVYWRl7j0mHSmv4lhV0aoA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.6.tgz", + "integrity": "sha512-rfQ++ghVwTWTqQ7w8qyDxL1XGihjBss4CmTgGRCTAC9RIbhVpyp4fOeZtta0Lbf+dTNIVJer6ych2ibHwkZqsQ==", "dev": true, "requires": { - "@babel/helper-create-class-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" } }, "@babel/plugin-transform-classes": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.0.tgz", - "integrity": "sha512-IjM1IoJNw72AZFlj33Cu8X0q2XK/6AaVC3jQu+cgQ5lThWD5ajnuUAml80dqRmOhmPkTH8uAwnpMu9Rvj0LTRA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.6.tgz", + "integrity": "sha512-EF5KONAqC5zAqT783iMGuM2ZtmEBy+mJMOKl2BCvPZ2lVrwvXnB6o+OBWCS+CoeCCpVRF2sA2RBKUxvT8tQT5Q==", "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-globals": "^7.28.0", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1", - "@babel/traverse": "^7.28.0" + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-replace-supers": "^7.28.6", + "@babel/traverse": "^7.28.6" } }, "@babel/plugin-transform-computed-properties": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.27.1.tgz", - "integrity": "sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.28.6.tgz", + "integrity": "sha512-bcc3k0ijhHbc2lEfpFHgx7eYw9KNXqOerKWfzbxEHUGKnS3sz9C4CNL9OiFN1297bDNfUiSO7DaLzbvHQQQ1BQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/template": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/template": "^7.28.6" } }, "@babel/plugin-transform-destructuring": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.0.tgz", - "integrity": "sha512-v1nrSMBiKcodhsyJ4Gf+Z0U/yawmJDBOTpEB3mcQY52r9RIyPneGyAS/yM6seP/8I+mWI3elOMtT5dB8GJVs+A==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.5.tgz", + "integrity": "sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.28.0" + "@babel/traverse": "^7.28.5" } }, "@babel/plugin-transform-dotall-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.27.1.tgz", - "integrity": "sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.28.6.tgz", + "integrity": "sha512-SljjowuNKB7q5Oayv4FoPzeB74g3QgLt8IVJw9ADvWy3QnUb/01aw8I4AVv8wYnPvQz2GDDZ/g3GhcNyDBI4Bg==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" } }, "@babel/plugin-transform-duplicate-keys": { @@ -8814,13 +8543,13 @@ } }, "@babel/plugin-transform-duplicate-named-capturing-groups-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.27.1.tgz", - "integrity": "sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.29.0.tgz", + "integrity": "sha512-zBPcW2lFGxdiD8PUnPwJjag2J9otbcLQzvbiOzDxpYXyCuYX9agOwMPGn1prVH0a4qzhCKu24rlH4c1f7yA8rw==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" } }, "@babel/plugin-transform-dynamic-import": { @@ -8833,22 +8562,22 @@ } }, "@babel/plugin-transform-explicit-resource-management": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.0.tgz", - "integrity": "sha512-K8nhUcn3f6iB+P3gwCv/no7OdzOZQcKchW6N389V6PD8NUWKZHzndOd9sPDVbMoBsbmjMqlB4L9fm+fEFNVlwQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.6.tgz", + "integrity": "sha512-Iao5Konzx2b6g7EPqTy40UZbcdXE126tTxVFr/nAIj+WItNxjKSYTEw3RC+A2/ZetmdJsgueL1KhaMCQHkLPIg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/plugin-transform-destructuring": "^7.28.0" + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/plugin-transform-destructuring": "^7.28.5" } }, "@babel/plugin-transform-exponentiation-operator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.27.1.tgz", - "integrity": "sha512-uspvXnhHvGKf2r4VVtBpeFnuDWsJLQ6MF6lGJLC89jBR1uoVeqM416AZtTuhTezOfgHicpJQmoD5YUakO/YmXQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.28.6.tgz", + "integrity": "sha512-WitabqiGjV/vJ0aPOLSFfNY1u9U3R7W36B03r5I2KoNix+a3sOhJ3pKFB3R5It9/UiK78NiO0KE9P21cMhlPkw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" } }, "@babel/plugin-transform-export-namespace-from": { @@ -8882,12 +8611,12 @@ } }, "@babel/plugin-transform-json-strings": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.27.1.tgz", - "integrity": "sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.28.6.tgz", + "integrity": "sha512-Nr+hEN+0geQkzhbdgQVPoqr47lZbm+5fCUmO70722xJZd0Mvb59+33QLImGj6F+DkK3xgDi1YVysP8whD6FQAw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" } }, "@babel/plugin-transform-literals": { @@ -8900,12 +8629,12 @@ } }, "@babel/plugin-transform-logical-assignment-operators": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.27.1.tgz", - "integrity": "sha512-SJvDs5dXxiae4FbSL1aBJlG4wvl594N6YEVVn9e3JGulwioy6z3oPjx/sQBO3Y4NwUu5HNix6KJ3wBZoewcdbw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.28.6.tgz", + "integrity": "sha512-+anKKair6gpi8VsM/95kmomGNMD0eLz1NQ8+Pfw5sAwWH9fGYXT50E55ZpV0pHUHWf6IUTWPM+f/7AAff+wr9A==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" } }, "@babel/plugin-transform-member-expression-literals": { @@ -8928,13 +8657,13 @@ } }, "@babel/plugin-transform-modules-commonjs": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz", - "integrity": "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.28.6.tgz", + "integrity": "sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" } }, "@babel/plugin-transform-modules-systemjs": { @@ -8960,13 +8689,13 @@ } }, "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.27.1.tgz", - "integrity": "sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.29.0.tgz", + "integrity": "sha512-1CZQA5KNAD6ZYQLPw7oi5ewtDNxH/2vuCh+6SmvgDfhumForvs8a1o9n0UrEoBD8HU4djO2yWngTQlXl1NDVEQ==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" } }, "@babel/plugin-transform-new-target": { @@ -8979,34 +8708,34 @@ } }, "@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.27.1.tgz", - "integrity": "sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.28.6.tgz", + "integrity": "sha512-3wKbRgmzYbw24mDJXT7N+ADXw8BC/imU9yo9c9X9NKaLF1fW+e5H1U5QjMUBe4Qo4Ox/o++IyUkl1sVCLgevKg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" } }, "@babel/plugin-transform-numeric-separator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.27.1.tgz", - "integrity": "sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.28.6.tgz", + "integrity": "sha512-SJR8hPynj8outz+SlStQSwvziMN4+Bq99it4tMIf5/Caq+3iOc0JtKyse8puvyXkk3eFRIA5ID/XfunGgO5i6w==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" } }, "@babel/plugin-transform-object-rest-spread": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.0.tgz", - "integrity": "sha512-9VNGikXxzu5eCiQjdE4IZn8sb9q7Xsk5EXLDBKUYg1e/Tve8/05+KJEtcxGxAgCY5t/BpKQM+JEL/yT4tvgiUA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.6.tgz", + "integrity": "sha512-5rh+JR4JBC4pGkXLAcYdLHZjXudVxWMXbB6u6+E9lRL5TrGVbHt1TjxGbZ8CkmYw9zjkB7jutzOROArsqtncEA==", "dev": true, "requires": { - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/plugin-transform-destructuring": "^7.28.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/plugin-transform-destructuring": "^7.28.5", "@babel/plugin-transform-parameters": "^7.27.7", - "@babel/traverse": "^7.28.0" + "@babel/traverse": "^7.28.6" } }, "@babel/plugin-transform-object-super": { @@ -9020,21 +8749,21 @@ } }, "@babel/plugin-transform-optional-catch-binding": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.27.1.tgz", - "integrity": "sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.28.6.tgz", + "integrity": "sha512-R8ja/Pyrv0OGAvAXQhSTmWyPJPml+0TMqXlO5w+AsMEiwb2fg3WkOvob7UxFSL3OIttFSGSRFKQsOhJ/X6HQdQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" } }, "@babel/plugin-transform-optional-chaining": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.27.1.tgz", - "integrity": "sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.28.6.tgz", + "integrity": "sha512-A4zobikRGJTsX9uqVFdafzGkqD30t26ck2LmOzAuLL8b2x6k3TIqRiT2xVvA9fNmFeTX484VpsdgmKNA0bS23w==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-plugin-utils": "^7.28.6", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" } }, @@ -9048,24 +8777,24 @@ } }, "@babel/plugin-transform-private-methods": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.27.1.tgz", - "integrity": "sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.28.6.tgz", + "integrity": "sha512-piiuapX9CRv7+0st8lmuUlRSmX6mBcVeNQ1b4AYzJxfCMuBfB0vBXDiGSmm03pKJw1v6cZ8KSeM+oUnM6yAExg==", "dev": true, "requires": { - "@babel/helper-create-class-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" } }, "@babel/plugin-transform-private-property-in-object": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.27.1.tgz", - "integrity": "sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.28.6.tgz", + "integrity": "sha512-b97jvNSOb5+ehyQmBpmhOCiUC5oVK4PMnpRvO7+ymFBoqYjeDHIU9jnrNUuwHOiL9RpGDoKBpSViarV+BU+eVA==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-create-class-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" } }, "@babel/plugin-transform-property-literals": { @@ -9119,22 +8848,22 @@ } }, "@babel/plugin-transform-regenerator": { - "version": "7.28.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.1.tgz", - "integrity": "sha512-P0QiV/taaa3kXpLY+sXla5zec4E+4t4Aqc9ggHlfZ7a2cp8/x/Gv08jfwEtn9gnnYIMvHx6aoOZ8XJL8eU71Dg==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.29.0.tgz", + "integrity": "sha512-FijqlqMA7DmRdg/aINBSs04y8XNTYw/lr1gJ2WsmBnnaNw1iS43EPkJW+zK7z65auG3AWRFXWj+NcTQwYptUog==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" } }, "@babel/plugin-transform-regexp-modifiers": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.27.1.tgz", - "integrity": "sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.28.6.tgz", + "integrity": "sha512-QGWAepm9qxpaIs7UM9FvUSnCGlb8Ua1RhyM4/veAxLwt3gMat/LSGrZixyuj4I6+Kn9iwvqCyPTtbdxanYoWYg==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" } }, "@babel/plugin-transform-reserved-words": { @@ -9156,12 +8885,12 @@ } }, "@babel/plugin-transform-spread": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.27.1.tgz", - "integrity": "sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.28.6.tgz", + "integrity": "sha512-9U4QObUC0FtJl05AsUcodau/RWDytrU6uKgkxu09mLR9HLDAtUMoPuuskm5huQsoktmsYpI+bGmq+iapDcriKA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-plugin-utils": "^7.28.6", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" } }, @@ -9202,13 +8931,13 @@ } }, "@babel/plugin-transform-unicode-property-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.27.1.tgz", - "integrity": "sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.28.6.tgz", + "integrity": "sha512-4Wlbdl/sIZjzi/8St0evF0gEZrgOswVO6aOzqxh1kDZOl9WmLrHq2HtGhnOJZmHZYKP8WZ1MDLCt5DAWwRo57A==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" } }, "@babel/plugin-transform-unicode-regex": { @@ -9222,90 +8951,91 @@ } }, "@babel/plugin-transform-unicode-sets-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.27.1.tgz", - "integrity": "sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.28.6.tgz", + "integrity": "sha512-/wHc/paTUmsDYN7SZkpWxogTOBNnlx7nBQYfy6JJlCT7G3mVhltk3e++N7zV0XfgGsrqBxd4rJQt9H16I21Y1Q==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" } }, "@babel/preset-env": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.28.0.tgz", - "integrity": "sha512-VmaxeGOwuDqzLl5JUkIRM1X2Qu2uKGxHEQWh+cvvbl7JuJRgKGJSfsEF/bUaxFhJl/XAyxBe7q7qSuTbKFuCyg==", + "version": "7.29.5", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.29.5.tgz", + "integrity": "sha512-/69t2aEzGKHD76DyLbHysF/QH2LJOB8iFnYO37unDTKBTubzcMRv0f3H5EiN1Q6ajOd/eB7dAInF0qdFVS06kA==", "dev": true, "requires": { - "@babel/compat-data": "^7.28.0", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-plugin-utils": "^7.27.1", + "@babel/compat-data": "^7.29.3", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.27.1", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.28.5", "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1", + "@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": "^7.29.3", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.27.1", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.6", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-import-assertions": "^7.27.1", - "@babel/plugin-syntax-import-attributes": "^7.27.1", + "@babel/plugin-syntax-import-assertions": "^7.28.6", + "@babel/plugin-syntax-import-attributes": "^7.28.6", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", "@babel/plugin-transform-arrow-functions": "^7.27.1", - "@babel/plugin-transform-async-generator-functions": "^7.28.0", - "@babel/plugin-transform-async-to-generator": "^7.27.1", + "@babel/plugin-transform-async-generator-functions": "^7.29.0", + "@babel/plugin-transform-async-to-generator": "^7.28.6", "@babel/plugin-transform-block-scoped-functions": "^7.27.1", - "@babel/plugin-transform-block-scoping": "^7.28.0", - "@babel/plugin-transform-class-properties": "^7.27.1", - "@babel/plugin-transform-class-static-block": "^7.27.1", - "@babel/plugin-transform-classes": "^7.28.0", - "@babel/plugin-transform-computed-properties": "^7.27.1", - "@babel/plugin-transform-destructuring": "^7.28.0", - "@babel/plugin-transform-dotall-regex": "^7.27.1", + "@babel/plugin-transform-block-scoping": "^7.28.6", + "@babel/plugin-transform-class-properties": "^7.28.6", + "@babel/plugin-transform-class-static-block": "^7.28.6", + "@babel/plugin-transform-classes": "^7.28.6", + "@babel/plugin-transform-computed-properties": "^7.28.6", + "@babel/plugin-transform-destructuring": "^7.28.5", + "@babel/plugin-transform-dotall-regex": "^7.28.6", "@babel/plugin-transform-duplicate-keys": "^7.27.1", - "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.27.1", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.29.0", "@babel/plugin-transform-dynamic-import": "^7.27.1", - "@babel/plugin-transform-explicit-resource-management": "^7.28.0", - "@babel/plugin-transform-exponentiation-operator": "^7.27.1", + "@babel/plugin-transform-explicit-resource-management": "^7.28.6", + "@babel/plugin-transform-exponentiation-operator": "^7.28.6", "@babel/plugin-transform-export-namespace-from": "^7.27.1", "@babel/plugin-transform-for-of": "^7.27.1", "@babel/plugin-transform-function-name": "^7.27.1", - "@babel/plugin-transform-json-strings": "^7.27.1", + "@babel/plugin-transform-json-strings": "^7.28.6", "@babel/plugin-transform-literals": "^7.27.1", - "@babel/plugin-transform-logical-assignment-operators": "^7.27.1", + "@babel/plugin-transform-logical-assignment-operators": "^7.28.6", "@babel/plugin-transform-member-expression-literals": "^7.27.1", "@babel/plugin-transform-modules-amd": "^7.27.1", - "@babel/plugin-transform-modules-commonjs": "^7.27.1", - "@babel/plugin-transform-modules-systemjs": "^7.27.1", + "@babel/plugin-transform-modules-commonjs": "^7.28.6", + "@babel/plugin-transform-modules-systemjs": "^7.29.4", "@babel/plugin-transform-modules-umd": "^7.27.1", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.27.1", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.29.0", "@babel/plugin-transform-new-target": "^7.27.1", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.27.1", - "@babel/plugin-transform-numeric-separator": "^7.27.1", - "@babel/plugin-transform-object-rest-spread": "^7.28.0", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.28.6", + "@babel/plugin-transform-numeric-separator": "^7.28.6", + "@babel/plugin-transform-object-rest-spread": "^7.28.6", "@babel/plugin-transform-object-super": "^7.27.1", - "@babel/plugin-transform-optional-catch-binding": "^7.27.1", - "@babel/plugin-transform-optional-chaining": "^7.27.1", + "@babel/plugin-transform-optional-catch-binding": "^7.28.6", + "@babel/plugin-transform-optional-chaining": "^7.28.6", "@babel/plugin-transform-parameters": "^7.27.7", - "@babel/plugin-transform-private-methods": "^7.27.1", - "@babel/plugin-transform-private-property-in-object": "^7.27.1", + "@babel/plugin-transform-private-methods": "^7.28.6", + "@babel/plugin-transform-private-property-in-object": "^7.28.6", "@babel/plugin-transform-property-literals": "^7.27.1", - "@babel/plugin-transform-regenerator": "^7.28.0", - "@babel/plugin-transform-regexp-modifiers": "^7.27.1", + "@babel/plugin-transform-regenerator": "^7.29.0", + "@babel/plugin-transform-regexp-modifiers": "^7.28.6", "@babel/plugin-transform-reserved-words": "^7.27.1", "@babel/plugin-transform-shorthand-properties": "^7.27.1", - "@babel/plugin-transform-spread": "^7.27.1", + "@babel/plugin-transform-spread": "^7.28.6", "@babel/plugin-transform-sticky-regex": "^7.27.1", "@babel/plugin-transform-template-literals": "^7.27.1", "@babel/plugin-transform-typeof-symbol": "^7.27.1", "@babel/plugin-transform-unicode-escapes": "^7.27.1", - "@babel/plugin-transform-unicode-property-regex": "^7.27.1", + "@babel/plugin-transform-unicode-property-regex": "^7.28.6", "@babel/plugin-transform-unicode-regex": "^7.27.1", - "@babel/plugin-transform-unicode-sets-regex": "^7.27.1", + "@babel/plugin-transform-unicode-sets-regex": "^7.28.6", "@babel/preset-modules": "0.1.6-no-external-plugins", - "babel-plugin-polyfill-corejs2": "^0.4.14", - "babel-plugin-polyfill-corejs3": "^0.13.0", - "babel-plugin-polyfill-regenerator": "^0.6.5", - "core-js-compat": "^3.43.0", + "babel-plugin-polyfill-corejs2": "^0.4.15", + "babel-plugin-polyfill-corejs3": "^0.14.0", + "babel-plugin-polyfill-regenerator": "^0.6.6", + "core-js-compat": "^3.48.0", "semver": "^6.3.1" } }, @@ -9507,9 +9237,9 @@ } }, "@discoveryjs/json-ext": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.6.3.tgz", - "integrity": "sha512-4B4OijXeVNOPZlYA2oEwWOTkzyltLao+xbotHQeqN++Rv27Y6s818+n2Qkp8q+Fxhn0t/5lA5X1Mxktud8eayQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-1.1.0.tgz", + "integrity": "sha512-Xc3VhU02wqZ1HvHRJUwL09HkZSTvidqY5Ya0NXBSYOxAp+Ln9dcJr9fySI+CkONzP3PekQo9WdzCv0PGER/mOA==", "dev": true }, "@emotion/babel-plugin": { @@ -9630,54 +9360,99 @@ "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==" }, "@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", "dev": true, "requires": { - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.3" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true + } } }, "@eslint-community/regexpp": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", - "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", "dev": true }, - "@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "@eslint/config-array": { + "version": "0.23.5", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz", + "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", "dev": true, "requires": { - "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" + "@eslint/object-schema": "^3.0.5", + "debug": "^4.3.1", + "minimatch": "^10.2.4" }, "dependencies": { - "globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true + }, + "brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dev": true, + "requires": { + "balanced-match": "^4.0.2" + } + }, + "minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "dev": true, "requires": { - "type-fest": "^0.20.2" + "brace-expansion": "^5.0.5" } } } }, - "@eslint/js": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", - "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", + "@eslint/config-helpers": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.5.tgz", + "integrity": "sha512-eIJYKTCECbP/nsKaaruF6LW967mtbQbsw4JTtSVkUQc9MneSkbrgPJAbKl9nWr0ZeowV8BfsarBmPpBzGelA2w==", + "dev": true, + "requires": { + "@eslint/core": "^1.2.1" + } + }, + "@eslint/core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", + "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.15" + } + }, + "@eslint/object-schema": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", + "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", "dev": true }, + "@eslint/plugin-kit": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.1.tgz", + "integrity": "sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ==", + "dev": true, + "requires": { + "@eslint/core": "^1.2.1", + "levn": "^0.4.1" + } + }, "@floating-ui/core": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz", @@ -9718,37 +9493,61 @@ "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==" }, - "@humanwhocodes/config-array": { - "version": "0.11.13", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", - "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", + "@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", + "dev": true, + "requires": { + "@humanfs/types": "^0.15.0" + } + }, + "@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", "dev": true, "requires": { - "@humanwhocodes/object-schema": "^2.0.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" } }, + "@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true + }, "@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true }, - "@humanwhocodes/object-schema": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", - "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", + "@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "dev": true }, "@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==", + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "requires": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", "devOptional": true, "requires": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" } }, "@jridgewell/resolve-uri": { @@ -9756,12 +9555,6 @@ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==" }, - "@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==", - "devOptional": true - }, "@jridgewell/source-map": { "version": "0.3.11", "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", @@ -9770,18 +9563,6 @@ "requires": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25" - }, - "dependencies": { - "@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, - "requires": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - } } }, "@jridgewell/sourcemap-codec": { @@ -9854,32 +9635,6 @@ "dev": true, "optional": true }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true - }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } - }, "@parcel/watcher": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.0.tgz", @@ -10041,6 +9796,12 @@ "@types/estree": "*" } }, + "@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true + }, "@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -10133,12 +9894,6 @@ "codemirror": "^6.0.0" } }, - "@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true - }, "@webassemblyjs/ast": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", @@ -10285,27 +10040,6 @@ "@xtuc/long": "4.2.2" } }, - "@webpack-cli/configtest": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-3.0.1.tgz", - "integrity": "sha512-u8d0pJ5YFgneF/GuvEiDA61Tf1VDomHHYMjv/wc9XzYj7nopltpG96nXN5dJRstxZhcNpV1g+nT6CydO7pHbjA==", - "dev": true, - "requires": {} - }, - "@webpack-cli/info": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-3.0.1.tgz", - "integrity": "sha512-coEmDzc2u/ffMvuW9aCjoRzNSPDl/XLuhPdlFRpT9tZHmJ/039az33CE7uH+8s0uL1j5ZNtfdv0HkfaKRBGJsQ==", - "dev": true, - "requires": {} - }, - "@webpack-cli/serve": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-3.0.1.tgz", - "integrity": "sha512-sbgw03xQaCLiT6gcY/6u3qBDn01CWw/nbaXl3gTdTFuJJ75Gffv3E3DBpgvY2fkkrdS1fpjaXNOmJlnbtKauKg==", - "dev": true, - "requires": {} - }, "@xtuc/ieee754": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", @@ -10319,9 +10053,9 @@ "dev": true }, "acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true }, "acorn-import-phases": { @@ -10386,12 +10120,6 @@ "dev": true, "requires": {} }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, "anymatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", @@ -10403,12 +10131,6 @@ "picomatch": "^2.0.4" } }, - "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 - }, "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", @@ -10503,9 +10225,9 @@ } }, "babel-loader": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-10.0.0.tgz", - "integrity": "sha512-z8jt+EdS61AMw22nSfoNJAZ0vrtmhPRVi6ghL3rCeRZI8cdNYFiV5xeV3HbE7rlZZNmGH8BVccwWt8/ED0QOHA==", + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-10.1.1.tgz", + "integrity": "sha512-JwKSzk2kjIe7mgPK+/lyZ2QAaJcpahNAdM+hgR2HI8D0OJVkdj8Rl6J3kaLYki9pwF7P2iWnD8qVv80Lq1ABtg==", "dev": true, "requires": { "find-up": "^5.0.0" @@ -10561,33 +10283,33 @@ } }, "babel-plugin-polyfill-corejs2": { - "version": "0.4.14", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.14.tgz", - "integrity": "sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg==", + "version": "0.4.17", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.17.tgz", + "integrity": "sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==", "dev": true, "requires": { - "@babel/compat-data": "^7.27.7", - "@babel/helper-define-polyfill-provider": "^0.6.5", + "@babel/compat-data": "^7.28.6", + "@babel/helper-define-polyfill-provider": "^0.6.8", "semver": "^6.3.1" } }, "babel-plugin-polyfill-corejs3": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.13.0.tgz", - "integrity": "sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==", + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.14.2.tgz", + "integrity": "sha512-coWpDLJ410R781Npmn/SIBZEsAetR4xVi0SxLMXPaMO4lSf1MwnkGYMtkFxew0Dn8B3/CpbpYxN0JCgg8mn67g==", "dev": true, "requires": { - "@babel/helper-define-polyfill-provider": "^0.6.5", - "core-js-compat": "^3.43.0" + "@babel/helper-define-polyfill-provider": "^0.6.8", + "core-js-compat": "^3.48.0" } }, "babel-plugin-polyfill-regenerator": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.5.tgz", - "integrity": "sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg==", + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.8.tgz", + "integrity": "sha512-M762rNHfSF1EV3SLtnCJXFoQbbIIz0OyRwnCmV0KPC7qosSfCO0QLTSuJX3ayAebubhE6oYBAYPrBA5ljowaZg==", "dev": true, "requires": { - "@babel/helper-define-polyfill-provider": "^0.6.5" + "@babel/helper-define-polyfill-provider": "^0.6.8" } }, "balanced-match": { @@ -10741,12 +10463,6 @@ "@codemirror/view": "^6.0.0" } }, - "colorette": { - "version": "2.0.16", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", - "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==", - "dev": true - }, "commander": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", @@ -10797,12 +10513,12 @@ "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==" }, "core-js-compat": { - "version": "3.44.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.44.0.tgz", - "integrity": "sha512-JepmAj2zfl6ogy34qfWtcE7nHKAJnKsQFRn++scjVS2bZFllwptzw61BZcZFYBPpUznLfAvh0LGhxKppk04ClA==", + "version": "3.49.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.49.0.tgz", + "integrity": "sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==", "dev": true, "requires": { - "browserslist": "^4.25.1" + "browserslist": "^4.28.1" } }, "cosmiconfig": { @@ -10910,9 +10626,9 @@ "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==" }, "debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "requires": { "ms": "^2.1.3" } @@ -10977,15 +10693,6 @@ "redux": "^4.2.0" } }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, "dom-helpers": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", @@ -11040,13 +10747,13 @@ "dev": true }, "enhanced-resolve": { - "version": "5.19.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.19.0.tgz", - "integrity": "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==", + "version": "5.21.5", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.21.5.tgz", + "integrity": "sha512-mLCNbrQli11K1ySUmuNt4ZUB3OpGIDq4q2vTBTf5cL2lpsRjI9QKqSD0ndjW8FyvcW/Jj46gMe9syyHAsvMa/A==", "dev": true, "requires": { "graceful-fs": "^4.2.4", - "tapable": "^2.3.0" + "tapable": "^2.3.3" } }, "entities": { @@ -11134,8 +10841,7 @@ "es-errors": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==" }, "es-iterator-helpers": { "version": "1.1.0", @@ -11212,85 +10918,58 @@ "devOptional": true }, "eslint": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", - "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", - "dev": true, - "requires": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.56.0", - "@humanwhocodes/config-array": "^0.11.13", + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.3.0.tgz", + "integrity": "sha512-XbEXaRva5cF0ZQB8w6MluHA0kZZfV2DuCMJ3ozyEOHLwDpZX2Lmm/7Pp0xdJmI0GL1W05VH5VwIFHEm1Vcw2gw==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.5", + "@eslint/config-helpers": "^0.5.5", + "@eslint/core": "^1.2.1", + "@eslint/plugin-kit": "^0.7.1", + "@humanfs/node": "^0.16.6", "@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", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "cross-spawn": "^7.0.6", "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", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.2.0", + "esquery": "^1.7.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", + "file-entry-cache": "^8.0.0", "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", + "minimatch": "^10.2.4", "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" + "optionator": "^0.9.3" }, "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } + "balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true }, - "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==", + "brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", "dev": true, "requires": { - "color-name": "~1.1.4" + "balanced-match": "^4.0.2" } }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, "escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -11298,11 +10977,13 @@ "dev": true }, "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==", + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", "dev": true, "requires": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } @@ -11332,15 +11013,6 @@ "is-glob": "^4.0.3" } }, - "globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, "locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -11350,6 +11022,15 @@ "p-locate": "^5.0.0" } }, + "minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "requires": { + "brace-expansion": "^5.0.5" + } + }, "p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -11367,15 +11048,6 @@ "requires": { "p-limit": "^3.0.2" } - }, - "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, - "requires": { - "has-flag": "^4.0.0" - } } } }, @@ -11444,26 +11116,26 @@ } }, "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==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", "dev": true }, "espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", "dev": true, "requires": { - "acorn": "^8.9.0", + "acorn": "^8.16.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" + "eslint-visitor-keys": "^5.0.1" } }, "esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", "dev": true, "requires": { "estraverse": "^5.1.0" @@ -11541,22 +11213,13 @@ "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==", "dev": true }, - "fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", - "dev": true, - "requires": { - "reusify": "^1.0.4" - } - }, "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==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, "requires": { - "flat-cache": "^3.0.4" + "flat-cache": "^4.0.0" } }, "file-loader": { @@ -11622,13 +11285,13 @@ "dev": true }, "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" + "flatted": "^3.2.9", + "keyv": "^4.5.4" } }, "flatted": { @@ -11778,12 +11441,6 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, - "graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true - }, "has-bigints": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", @@ -11873,9 +11530,9 @@ "requires": {} }, "ignore": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", - "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true }, "immutable": { @@ -12103,12 +11760,6 @@ "has-tostringtag": "^1.0.0" } }, - "is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true - }, "is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", @@ -12252,19 +11903,16 @@ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, - "js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, "jsesc": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==" + }, + "json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true }, "json-parse-even-better-errors": { "version": "2.3.1", @@ -12304,6 +11952,15 @@ "resolved": "https://registry.npmjs.org/keycode/-/keycode-2.2.1.tgz", "integrity": "sha512-Rdgz9Hl9Iv4QKi8b0OlCRQEzp4AgVxyCtz5S/+VIHezDmrDhkp2N2TqBWOLz0/gbeREXOOiI9/4b8BY9uw2vFg==" }, + "keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "requires": { + "json-buffer": "3.0.1" + } + }, "kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -12367,12 +12024,6 @@ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "dev": true }, - "lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, "loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -12431,24 +12082,15 @@ } }, "mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", "dev": true }, - "mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, - "requires": { - "mime-db": "1.52.0" - } - }, "mini-css-extract-plugin": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.0.tgz", - "integrity": "sha512-Zs1YsZVfemekSZG+44vBsYTLQORkPMwnlv+aehcxK/NLKC+EGhDB39/YePYYqx/sTk6NnYpuqikhSn7+JIevTA==", + "version": "2.10.2", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.10.2.tgz", + "integrity": "sha512-AOSS0IdEB95ayVkxn5oGzNQwqAi2J0Jb/kKm43t7H73s8+f5873g0yuj0PNvK4dO75mu5DHg4nlgp4k6Kga8eg==", "dev": true, "requires": { "schema-utils": "^4.0.0", @@ -12809,12 +12451,6 @@ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true }, - "queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true - }, "react": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", @@ -13096,9 +12732,9 @@ "dev": true }, "regenerate-unicode-properties": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", - "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.2.tgz", + "integrity": "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==", "dev": true, "requires": { "regenerate": "^1.4.2" @@ -13122,17 +12758,17 @@ } }, "regexpu-core": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz", - "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.4.0.tgz", + "integrity": "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==", "dev": true, "requires": { "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.2.0", + "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", - "regjsparser": "^0.12.0", + "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.1.0" + "unicode-match-property-value-ecmascript": "^2.2.1" } }, "regjsgen": { @@ -13142,12 +12778,12 @@ "dev": true }, "regjsparser": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz", - "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.13.1.tgz", + "integrity": "sha512-dLsljMd9sqwRkby8zhO1gSg3PnJIBFid8f4CQj/sXx+7cKx+E7u0PKhZ+U4wmhx7EfmtvnA318oVaIkAB1lRJw==", "dev": true, "requires": { - "jsesc": "~3.0.2" + "jsesc": "~3.1.0" } }, "require-from-string": { @@ -13157,11 +12793,12 @@ "dev": true }, "resolve": { - "version": "1.22.10", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", "requires": { - "is-core-module": "^2.16.0", + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" } @@ -13181,30 +12818,6 @@ "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "requires": { - "queue-microtask": "^1.2.2" - } - }, "safe-array-concat": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", @@ -13234,14 +12847,14 @@ } }, "sass": { - "version": "1.94.2", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.94.2.tgz", - "integrity": "sha512-N+7WK20/wOr7CzA2snJcUSSNTCzeCGUTFY3OgeQP3mZ1aj9NMQ0mSTXwlrnd89j33zzQJGqIN52GIOmYrfq46A==", + "version": "1.99.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.99.0.tgz", + "integrity": "sha512-kgW13M54DUB7IsIRM5LvJkNlpH+WhMpooUcaWGFARkF1Tc82v9mIWkCbCYf+MBvpIUBSeSOTilpZjEPr2VYE6Q==", "dev": true, "requires": { "@parcel/watcher": "^2.4.1", "chokidar": "^4.0.0", - "immutable": "^5.0.2", + "immutable": "^5.1.5", "source-map-js": ">=0.6.2 <2.0.0" }, "dependencies": { @@ -13494,21 +13107,6 @@ "es-object-atoms": "^1.0.0" } }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, "style-mod": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz", @@ -13539,9 +13137,9 @@ "integrity": "sha512-EIHvdY5bPLuWForiR/AN2Bxngzpuwn1is4asboytXtpTgsArc+WmSJKVLlhdh71u7jFcryDqB2A8lQvj78MkyQ==" }, "tapable": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", - "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", "dev": true }, "terser": { @@ -13576,12 +13174,6 @@ "terser": "^5.31.1" } }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, "tinyglobby": { "version": "0.2.12", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.12.tgz", @@ -13631,12 +13223,6 @@ "prelude-ls": "^1.2.1" } }, - "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 - }, "typed-array-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", @@ -13729,15 +13315,15 @@ } }, "unicode-match-property-value-ecmascript": { - "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==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.1.tgz", + "integrity": "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==", "dev": true }, "unicode-property-aliases-ecmascript": { - "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==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.2.0.tgz", + "integrity": "sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==", "dev": true }, "update-browserslist-db": { @@ -13807,9 +13393,9 @@ } }, "webpack": { - "version": "5.105.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.0.tgz", - "integrity": "sha512-gX/dMkRQc7QOMzgTe6KsYFM7DxeIONQSui1s0n/0xht36HvrgbxtM1xBlgx596NbpHuQU8P7QpKwrZYwUX48nw==", + "version": "5.106.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.106.2.tgz", + "integrity": "sha512-wGN3qcrBQIFmQ/c0AiOAQBvrZ5lmY8vbbMv4Mxfgzqd/B6+9pXtLo73WuS1dSGXM5QYY3hZnIbvx+K1xxe6FyA==", "dev": true, "requires": { "@types/eslint-scope": "^3.7.7", @@ -13818,40 +13404,35 @@ "@webassemblyjs/ast": "^1.14.1", "@webassemblyjs/wasm-edit": "^1.14.1", "@webassemblyjs/wasm-parser": "^1.14.1", - "acorn": "^8.15.0", + "acorn": "^8.16.0", "acorn-import-phases": "^1.0.3", "browserslist": "^4.28.1", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.19.0", + "enhanced-resolve": "^5.20.0", "es-module-lexer": "^2.0.0", "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.3.1", - "mime-types": "^2.1.27", + "mime-db": "^1.54.0", "neo-async": "^2.6.2", "schema-utils": "^4.3.3", "tapable": "^2.3.0", - "terser-webpack-plugin": "^5.3.16", + "terser-webpack-plugin": "^5.3.17", "watchpack": "^2.5.1", - "webpack-sources": "^3.3.3" + "webpack-sources": "^3.3.4" } }, "webpack-cli": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-6.0.1.tgz", - "integrity": "sha512-MfwFQ6SfwinsUVi0rNJm7rHZ31GyTcpVE5pgVA3hwFRb7COD4TzjUUwhGWKfO50+xdc2MQPuEBBJoqIMGt3JDw==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-7.0.2.tgz", + "integrity": "sha512-dB0R4T+C/8YuvM+fabdvil6QE44/ChDXikV5lOOkrUeCkW5hTJv2pGLE3keh+D5hjYw8icBaJkZzpFoaHV4T+g==", "dev": true, "requires": { - "@discoveryjs/json-ext": "^0.6.1", - "@webpack-cli/configtest": "^3.0.1", - "@webpack-cli/info": "^3.0.1", - "@webpack-cli/serve": "^3.0.1", - "colorette": "^2.0.14", - "commander": "^12.1.0", - "cross-spawn": "^7.0.3", + "@discoveryjs/json-ext": "^1.0.0", + "commander": "^14.0.3", + "cross-spawn": "^7.0.6", "envinfo": "^7.14.0", "fastest-levenshtein": "^1.0.12", "import-local": "^3.0.2", @@ -13861,9 +13442,9 @@ }, "dependencies": { "commander": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", + "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", "dev": true } } @@ -13880,9 +13461,9 @@ } }, "webpack-sources": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz", - "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.4.1.tgz", + "integrity": "sha512-eACpxRN02yaawnt+uUNIF7Qje6A9zArxBbcAJjK1PK3S9Ycg5jIuJ8pW4q8EMnwNZCEGltcjkRx1QzOxOkKD8A==", "dev": true }, "which": { diff --git a/package.json b/package.json index 073d1dcd37..155b150ae3 100644 --- a/package.json +++ b/package.json @@ -48,20 +48,20 @@ }, "devDependencies": { "@babel/cli": "^7.28.0", - "@babel/core": "^7.28.0", - "@babel/preset-env": "^7.28.0", + "@babel/core": "^7.29.0", + "@babel/preset-env": "^7.29.5", "@babel/preset-react": "^7.28.5", - "babel-loader": "^10.0.0", + "babel-loader": "^10.1.1", "copy-webpack-plugin": "^14.0.0", "css-loader": "^7.1.1", - "eslint": "~8.56.0", + "eslint": "~10.3.0", "eslint-plugin-react": "^7.37.2", "file-loader": "^6.2.0", - "mini-css-extract-plugin": "^2.9.0", - "sass": "^1.94.2", + "mini-css-extract-plugin": "^2.10.2", + "sass": "^1.99.0", "sass-loader": "^16.0.0", - "webpack": "^5.105.0", - "webpack-cli": "^6.0.1", + "webpack": "^5.106.2", + "webpack-cli": "^7.0.2", "webpack-merge": "6.0.1" } } From ef2b74de8b24e7c0a310c1cb3083867daca5f6ba Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 May 2026 09:21:10 +0000 Subject: [PATCH 062/131] build(deps-dev): bump the optional group across 1 directory with 3 updates Updates the requirements on [pytest-github-actions-annotate-failures](https://github.com/pytest-dev/pytest-github-actions-annotate-failures), [django-allauth](https://github.com/sponsors/pennersr) and [gunicorn](https://github.com/benoitc/gunicorn) to permit the latest version. Updates `pytest-github-actions-annotate-failures` to 0.4.0 - [Release notes](https://github.com/pytest-dev/pytest-github-actions-annotate-failures/releases) - [Commits](https://github.com/pytest-dev/pytest-github-actions-annotate-failures/compare/v0.2.0...v0.4.0) Updates `django-allauth` to 65.16.1 - [Commits](https://github.com/sponsors/pennersr/commits) Updates `gunicorn` to 26.0.0 - [Release notes](https://github.com/benoitc/gunicorn/releases) - [Commits](https://github.com/benoitc/gunicorn/compare/23.0.0...26.0.0) --- updated-dependencies: - dependency-name: django-allauth dependency-version: 65.15.0 dependency-type: direct:development dependency-group: optional - dependency-name: gunicorn dependency-version: 25.2.0 dependency-type: direct:development dependency-group: optional - dependency-name: pytest-github-actions-annotate-failures dependency-version: 0.4.0 dependency-type: direct:development dependency-group: optional ... Signed-off-by: dependabot[bot] --- pyproject.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 1b84c86033..4c9389c418 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -71,10 +71,10 @@ recommended = [ ] ci = [ "rdmo[allauth,dev,openapi,pytest]", - "pytest-github-actions-annotate-failures>=0.2.0,<0.4.0", + "pytest-github-actions-annotate-failures>=0.2.0,<0.5.0", ] allauth = [ - "django-allauth[socialaccount,openid]>=64.1.0,<65.15.0", + "django-allauth[socialaccount,openid]>=64.1.0,<65.17.0", ] dev = [ "pipdeptree>=2.13,<3.0", @@ -82,7 +82,7 @@ dev = [ "setuptools>=73,<81", ] gunicorn = [ - "gunicorn>=23.0,<24.0", + "gunicorn>=23.0,<27.0", ] ldap = [ "django-auth-ldap>=4.5,<6.0", From 9852c0f86d6c96306a6fd69df6299203ec429ccd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 May 2026 09:21:11 +0000 Subject: [PATCH 063/131] build(deps): bump the github-actions group across 1 directory with 2 updates Bumps the github-actions group with 2 updates in the / directory: [hynek/build-and-inspect-python-package](https://github.com/hynek/build-and-inspect-python-package) and [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish). Updates `hynek/build-and-inspect-python-package` from 2.14.0 to 2.18.0 - [Release notes](https://github.com/hynek/build-and-inspect-python-package/releases) - [Changelog](https://github.com/hynek/build-and-inspect-python-package/blob/main/CHANGELOG.md) - [Commits](https://github.com/hynek/build-and-inspect-python-package/compare/efb823f52190ad02594531168b7a2d5790e66516...d44ca7d91762de7a7d5436ddae667c6da6d1c3df) Updates `pypa/gh-action-pypi-publish` from 6733eb7d741f0b11ec6a39b58540dab7590f9b7d to cef221092ed1bacb1cc03d23a2d87d1d172e277b - [Release notes](https://github.com/pypa/gh-action-pypi-publish/releases) - [Commits](https://github.com/pypa/gh-action-pypi-publish/compare/6733eb7d741f0b11ec6a39b58540dab7590f9b7d...cef221092ed1bacb1cc03d23a2d87d1d172e277b) --- updated-dependencies: - dependency-name: hynek/build-and-inspect-python-package dependency-version: 2.18.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: github-actions - dependency-name: pypa/gh-action-pypi-publish dependency-version: cef221092ed1bacb1cc03d23a2d87d1d172e277b dependency-type: direct:production dependency-group: github-actions ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b195f702c4..a795ba2d22 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,7 +48,7 @@ jobs: - name: build front end run: npm ci && npm run build:dist - name: Build and inspect package - uses: hynek/build-and-inspect-python-package@efb823f52190ad02594531168b7a2d5790e66516 # v2.14.0 + uses: hynek/build-and-inspect-python-package@d44ca7d91762de7a7d5436ddae667c6da6d1c3df # v2.18.0 test: name: "Test (Python: ${{ matrix.python-version }}, DB: ${{ matrix.db-backend }})" @@ -266,7 +266,7 @@ jobs: name: Packages path: dist - name: Publish to PyPI - uses: pypa/gh-action-pypi-publish@6733eb7d741f0b11ec6a39b58540dab7590f9b7d # v1.14.0 + uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0 required-checks-pass: if: always() From 37b3f43287b96ab41b7bec6ca7dd0a00270a64e8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Mar 2026 00:43:36 +0000 Subject: [PATCH 064/131] build(deps-dev): update setuptools requirement from <81,>=73 to >=73,<83 Updates the requirements on [setuptools](https://github.com/pypa/setuptools) to permit the latest version. - [Release notes](https://github.com/pypa/setuptools/releases) - [Changelog](https://github.com/pypa/setuptools/blob/main/NEWS.rst) - [Commits](https://github.com/pypa/setuptools/compare/v73.0.0...v82.0.0) --- updated-dependencies: - dependency-name: setuptools dependency-version: 82.0.0 dependency-type: direct:development ... Signed-off-by: dependabot[bot] --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 1b84c86033..a4786876b2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -79,7 +79,7 @@ allauth = [ dev = [ "pipdeptree>=2.13,<3.0", "pre-commit>=3.4,<5.0", - "setuptools>=73,<81", + "setuptools>=73,<83", ] gunicorn = [ "gunicorn>=23.0,<24.0", From 51e406a30df9e28d5f92d246b8550a72ad10cd36 Mon Sep 17 00:00:00 2001 From: Claudia Malzer Date: Wed, 20 May 2026 15:13:42 +0200 Subject: [PATCH 065/131] Refactor get_invite_email_project_path and write test --- rdmo/projects/tests/test_utils.py | 125 +++++++++++++++++++++++++++++- rdmo/projects/utils.py | 22 +++--- 2 files changed, 135 insertions(+), 12 deletions(-) diff --git a/rdmo/projects/tests/test_utils.py b/rdmo/projects/tests/test_utils.py index f0fce819bf..c39dc6bb40 100644 --- a/rdmo/projects/tests/test_utils.py +++ b/rdmo/projects/tests/test_utils.py @@ -3,12 +3,18 @@ from django.contrib.auth.models import User from django.contrib.sites.models import Site from django.http import QueryDict +from django.test.client import RequestFactory from rdmo.core.tests.utils import compute_checksum from ..filters import ProjectFilter -from ..models import Project, Value -from ..utils import compute_set_prefix_from_set_value, copy_project, set_context_querystring_with_filter_and_page +from ..models import Invite, Project, Value +from ..utils import ( + compute_set_prefix_from_set_value, + copy_project, + get_invite_email_project_path, + set_context_querystring_with_filter_and_page, +) GET_queries = [ 'page=2&title=project', @@ -145,3 +151,118 @@ def test_copy_project(db, files): @pytest.mark.parametrize('set_value, value, result', SET_VALUES) def test_compute_set_prefix_from_set_value(set_value, value, result): assert compute_set_prefix_from_set_value(Value(**set_value), Value(**value)) == result + +INVITE_EMAIL_PROJECT_PATHS = [ + ( + { + 'MULTISITE': False, + 'PROJECT_INVITE_USE_PROJECT_SITE': False, + 'user': 'current', + }, + 'http://testserver/projects/join/test-token/' + ), + ( + { + 'MULTISITE': False, + 'PROJECT_INVITE_USE_PROJECT_SITE': False, + 'user': None, + }, + 'http://testserver/projects/join/test-token/' + ), + ( + { + 'MULTISITE': False, + 'PROJECT_INVITE_USE_PROJECT_SITE': True, + 'user': 'current', + }, + 'http://example.com/projects/join/test-token/' + ), + ( + { + 'MULTISITE': False, + 'PROJECT_INVITE_USE_PROJECT_SITE': True, + 'user': None, + }, + 'http://example.com/projects/join/test-token/' + ), + ( + { + 'MULTISITE': True, + 'PROJECT_INVITE_USE_PROJECT_SITE': False, + 'user': 'current', + }, + 'http://testserver/projects/join/test-token/' + ), + ( + { + 'MULTISITE': True, + 'PROJECT_INVITE_USE_PROJECT_SITE': False, + 'user': 'other' + }, + 'http://foo.com/projects/join/test-token/' + ), + ( + { + 'MULTISITE': True, + 'PROJECT_INVITE_USE_PROJECT_SITE': False, + 'user': None, + }, + 'http://testserver/projects/join/test-token/' + ), + ( + { + 'MULTISITE': True, + 'PROJECT_INVITE_USE_PROJECT_SITE': True, + 'user': 'current' + }, + 'http://example.com/projects/join/test-token/' + ), + ( + { + 'MULTISITE': True, + 'PROJECT_INVITE_USE_PROJECT_SITE': True, + 'user': 'other' + }, + 'http://example.com/projects/join/test-token/' + ), + ( + { + 'MULTISITE': True, + 'PROJECT_INVITE_USE_PROJECT_SITE': True, + 'user': None, + }, + 'http://example.com/projects/join/test-token/' + ), +] + +@pytest.mark.parametrize('config, expected', INVITE_EMAIL_PROJECT_PATHS) +def test_get_invite_email_project_path(db, settings, config, expected): + settings.MULTISITE = config['MULTISITE'] + settings.PROJECT_INVITE_USE_PROJECT_SITE = config['PROJECT_INVITE_USE_PROJECT_SITE'] + + # this is where testserver comes from: + request = RequestFactory().get('/') + + project = Project.objects.get(pk=1) + project.site.domain = 'example.com' + + invite_kwargs = { + 'project': project, + 'role': 'author' + } + + if config['user'] is not None: + if config['user'] == 'current': + user = User.objects.get(username='user') + else: + user = User.objects.get(username='foo-user') + + invite_kwargs['user'] = user + + else: + invite_kwargs['email'] = 'invite@example.com' + + invite = Invite(**invite_kwargs) + invite.token = 'test-token' + + assert get_invite_email_project_path(request, invite) == expected diff --git a/rdmo/projects/utils.py b/rdmo/projects/utils.py index e3063752ed..5d1d33d1f7 100644 --- a/rdmo/projects/utils.py +++ b/rdmo/projects/utils.py @@ -237,23 +237,25 @@ def save_import_views(project, views): project.views.add(view) -def get_invite_email_project_path(invite, scheme='http') -> str: +def get_invite_email_project_path(request, invite): project_invite_path = reverse('project_join', args=[invite.token]) - # check if the invited user exists and the multisite environment is enabled - if invite.user is not None and settings.MULTISITE and not settings.PROJECT_INVITE_USE_PROJECT_SITE: - # do nothing if user is a member of the current site + + if settings.PROJECT_INVITE_USE_PROJECT_SITE: + return f'{request.scheme}://{invite.project.site.domain}{project_invite_path}' + + elif settings.MULTISITE and invite.user is not None: current_site = Site.objects.get_current() + if not invite.user.role.member.filter(id=current_site.id).exists(): - # else take first site - invited_user_member_domain = invite.user.role.member.first().domain - project_invite_path = f'{scheme}://' + invited_user_member_domain + project_invite_path - return project_invite_path + # take users first site + return f'{request.scheme}://{invite.user.role.member.first().domain}{project_invite_path}' + + return request.build_absolute_uri(project_invite_path) def send_invite_email(request, invite): - project_invite_path = get_invite_email_project_path(invite, request.scheme) context = { - 'invite_url': request.build_absolute_uri(project_invite_path), + 'invite_url': get_invite_email_project_path(request, invite), 'invite_user': invite.user, 'invite_email': invite.email, 'project': invite.project, From fb77d662059e829c01f0baabb26c62fe0033e493 Mon Sep 17 00:00:00 2001 From: Claudia Malzer Date: Wed, 20 May 2026 15:15:21 +0200 Subject: [PATCH 066/131] Remove test from wrong file --- .../tests/test_view_membership_multisite.py | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/rdmo/projects/tests/test_view_membership_multisite.py b/rdmo/projects/tests/test_view_membership_multisite.py index 183228ffc0..bef8e4d55b 100644 --- a/rdmo/projects/tests/test_view_membership_multisite.py +++ b/rdmo/projects/tests/test_view_membership_multisite.py @@ -60,21 +60,6 @@ def test_get_invite_email_project_path_function(db, client, settings, username, else: assert invite_email_project_path.startswith('http://' + site_domain + '/projects') - -def test_get_invite_email_project_path_project_site(db, settings): - settings.MULTISITE = True - settings.PROJECT_INVITE_USE_PROJECT_SITE = True - - foo_user = get_user_model().objects.get(username='foo-user') - - project = Project.objects.get(pk=1) # example.com - invite = Invite(project=project, user=foo_user, role='owner') - invite.make_token() - invite.save() - - assert get_invite_email_project_path(invite) == reverse('project_join', args=[invite.token]) - - @pytest.mark.parametrize('username,password', users) @pytest.mark.parametrize('project_id', projects) @pytest.mark.parametrize('membership_role', membership_roles) From 7f029f041981f9200d11a96fd0559e6239c7d23d Mon Sep 17 00:00:00 2001 From: Claudia Malzer Date: Wed, 20 May 2026 15:49:04 +0200 Subject: [PATCH 067/131] Fix other test due to function changes --- rdmo/projects/tests/test_view_membership_multisite.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/rdmo/projects/tests/test_view_membership_multisite.py b/rdmo/projects/tests/test_view_membership_multisite.py index bef8e4d55b..49b24248d9 100644 --- a/rdmo/projects/tests/test_view_membership_multisite.py +++ b/rdmo/projects/tests/test_view_membership_multisite.py @@ -3,6 +3,7 @@ from django.contrib.auth import get_user_model from django.contrib.sites.models import Site from django.core import mail +from django.test.client import RequestFactory from django.urls import reverse from ..models import Invite, Project @@ -54,9 +55,10 @@ def test_get_invite_email_project_path_function(db, client, settings, username, invite.make_token() invite.save() - invite_email_project_path = get_invite_email_project_path(invite) + request = RequestFactory().get('/') + invite_email_project_path = get_invite_email_project_path(request, invite) if current_site.domain == site_domain: - assert invite_email_project_path.startswith('/projects') + assert invite_email_project_path.startswith('http://testserver/projects') else: assert invite_email_project_path.startswith('http://' + site_domain + '/projects') From 90e68a2e74ade4eb97921f8956d94d8215ea55c0 Mon Sep 17 00:00:00 2001 From: David Wallace Date: Wed, 20 May 2026 16:31:40 +0200 Subject: [PATCH 068/131] pin ESlint to max v9 Signed-off-by: David Wallace --- package-lock.json | 590 ++++++++++++++++++++++++++++------------------ package.json | 2 +- 2 files changed, 365 insertions(+), 227 deletions(-) diff --git a/package-lock.json b/package-lock.json index 65e6f4a18b..512059892b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -44,7 +44,7 @@ "babel-loader": "^10.1.1", "copy-webpack-plugin": "^14.0.0", "css-loader": "^7.1.1", - "eslint": "~10.3.0", + "eslint": "^9.39.4", "eslint-plugin-react": "^7.37.2", "file-loader": "^6.2.0", "mini-css-extract-plugin": "^2.10.2", @@ -2028,99 +2028,105 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.23.5", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz", - "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^3.0.5", + "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", - "minimatch": "^10.2.4" + "minimatch": "^3.1.5" }, "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@eslint/config-array/node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, "engines": { - "node": "18 || 20 || >=22" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@eslint/config-array/node_modules/brace-expansion": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "balanced-match": "^4.0.2" + "@types/json-schema": "^7.0.15" }, "engines": { - "node": "18 || 20 || >=22" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@eslint/config-array/node_modules/minimatch": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "node_modules/@eslint/eslintrc": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", + "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", "dev": true, + "license": "MIT", "dependencies": { - "brace-expansion": "^5.0.5" + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.5", + "strip-json-comments": "^3.1.1" }, "engines": { - "node": "18 || 20 || >=22" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://opencollective.com/eslint" } }, - "node_modules/@eslint/config-helpers": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.5.tgz", - "integrity": "sha512-eIJYKTCECbP/nsKaaruF6LW967mtbQbsw4JTtSVkUQc9MneSkbrgPJAbKl9nWr0ZeowV8BfsarBmPpBzGelA2w==", + "node_modules/@eslint/js": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", + "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", "dev": true, - "dependencies": { - "@eslint/core": "^1.2.1" - }, + "license": "MIT", "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - } - }, - "node_modules/@eslint/core": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", - "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.15" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" + "funding": { + "url": "https://eslint.org/donate" } }, "node_modules/@eslint/object-schema": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", - "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", "dev": true, + "license": "Apache-2.0", "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/plugin-kit": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.1.tgz", - "integrity": "sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^1.2.1", + "@eslint/core": "^0.17.0", "levn": "^0.4.1" }, "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@floating-ui/core": { @@ -2684,12 +2690,6 @@ "@types/estree": "*" } }, - "node_modules/@types/esrecurse": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", - "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", - "dev": true - }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -2994,6 +2994,7 @@ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, + "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } @@ -3064,6 +3065,22 @@ "ajv": "^6.9.1" } }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/anymatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", @@ -3078,6 +3095,13 @@ "node": ">= 8" } }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, "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", @@ -3505,6 +3529,36 @@ } ] }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -3580,6 +3634,26 @@ "@codemirror/view": "^6.0.0" } }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, "node_modules/commander": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", @@ -4212,29 +4286,33 @@ } }, "node_modules/eslint": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.3.0.tgz", - "integrity": "sha512-XbEXaRva5cF0ZQB8w6MluHA0kZZfV2DuCMJ3ozyEOHLwDpZX2Lmm/7Pp0xdJmI0GL1W05VH5VwIFHEm1Vcw2gw==", + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", + "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", - "@eslint-community/regexpp": "^4.12.2", - "@eslint/config-array": "^0.23.5", - "@eslint/config-helpers": "^0.5.5", - "@eslint/core": "^1.2.1", - "@eslint/plugin-kit": "^0.7.1", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.2", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "9.39.4", + "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.14.0", + "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^9.1.2", - "eslint-visitor-keys": "^5.0.1", - "espree": "^11.2.0", - "esquery": "^1.7.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", @@ -4244,7 +4322,8 @@ "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", - "minimatch": "^10.2.4", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.5", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, @@ -4252,7 +4331,7 @@ "eslint": "bin/eslint.js" }, "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://eslint.org/donate" @@ -4350,38 +4429,18 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", - "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, + "license": "Apache-2.0", "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "dev": true, - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", - "dev": true, - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - } - }, "node_modules/eslint/node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -4395,18 +4454,17 @@ } }, "node_modules/eslint/node_modules/eslint-scope": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", - "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "@types/esrecurse": "^4.3.1", - "@types/estree": "^1.0.8", "esrecurse": "^4.3.0", "estraverse": "^5.2.0" }, "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -4417,6 +4475,7 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=4.0" } @@ -4464,21 +4523,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/minimatch": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", - "dev": true, - "dependencies": { - "brace-expansion": "^5.0.5" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/eslint/node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -4510,17 +4554,18 @@ } }, "node_modules/espree": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", - "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.16.0", + "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^5.0.1" + "eslint-visitor-keys": "^4.2.1" }, "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -4916,6 +4961,19 @@ "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", "dev": true }, + "node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/globalthis": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", @@ -5637,6 +5695,19 @@ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", @@ -5795,6 +5866,13 @@ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "dev": true }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -7337,6 +7415,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/style-mod": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz", @@ -9383,73 +9474,70 @@ "dev": true }, "@eslint/config-array": { - "version": "0.23.5", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz", - "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", "dev": true, "requires": { - "@eslint/object-schema": "^3.0.5", + "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", - "minimatch": "^10.2.4" - }, - "dependencies": { - "balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "dev": true - }, - "brace-expansion": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", - "dev": true, - "requires": { - "balanced-match": "^4.0.2" - } - }, - "minimatch": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", - "dev": true, - "requires": { - "brace-expansion": "^5.0.5" - } - } + "minimatch": "^3.1.5" } }, "@eslint/config-helpers": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.5.tgz", - "integrity": "sha512-eIJYKTCECbP/nsKaaruF6LW967mtbQbsw4JTtSVkUQc9MneSkbrgPJAbKl9nWr0ZeowV8BfsarBmPpBzGelA2w==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", "dev": true, "requires": { - "@eslint/core": "^1.2.1" + "@eslint/core": "^0.17.0" } }, "@eslint/core": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", - "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", "dev": true, "requires": { "@types/json-schema": "^7.0.15" } }, + "@eslint/eslintrc": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", + "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", + "dev": true, + "requires": { + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.5", + "strip-json-comments": "^3.1.1" + } + }, + "@eslint/js": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", + "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", + "dev": true + }, "@eslint/object-schema": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", - "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", "dev": true }, "@eslint/plugin-kit": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.1.tgz", - "integrity": "sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", "dev": true, "requires": { - "@eslint/core": "^1.2.1", + "@eslint/core": "^0.17.0", "levn": "^0.4.1" } }, @@ -9796,12 +9884,6 @@ "@types/estree": "*" } }, - "@types/esrecurse": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", - "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", - "dev": true - }, "@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -10120,6 +10202,15 @@ "dev": true, "requires": {} }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, "anymatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", @@ -10131,6 +10222,12 @@ "picomatch": "^2.0.4" } }, + "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 + }, "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", @@ -10405,6 +10502,27 @@ "integrity": "sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg==", "devOptional": true }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "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, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -10463,6 +10581,21 @@ "@codemirror/view": "^6.0.0" } }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, "commander": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", @@ -10918,29 +11051,32 @@ "devOptional": true }, "eslint": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.3.0.tgz", - "integrity": "sha512-XbEXaRva5cF0ZQB8w6MluHA0kZZfV2DuCMJ3ozyEOHLwDpZX2Lmm/7Pp0xdJmI0GL1W05VH5VwIFHEm1Vcw2gw==", + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", + "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.8.0", - "@eslint-community/regexpp": "^4.12.2", - "@eslint/config-array": "^0.23.5", - "@eslint/config-helpers": "^0.5.5", - "@eslint/core": "^1.2.1", - "@eslint/plugin-kit": "^0.7.1", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.2", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "9.39.4", + "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.14.0", + "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^9.1.2", - "eslint-visitor-keys": "^5.0.1", - "espree": "^11.2.0", - "esquery": "^1.7.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", @@ -10950,26 +11086,12 @@ "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", - "minimatch": "^10.2.4", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.5", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "dependencies": { - "balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "dev": true - }, - "brace-expansion": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", - "dev": true, - "requires": { - "balanced-match": "^4.0.2" - } - }, "escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -10977,13 +11099,11 @@ "dev": true }, "eslint-scope": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", - "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, "requires": { - "@types/esrecurse": "^4.3.1", - "@types/estree": "^1.0.8", "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } @@ -11022,15 +11142,6 @@ "p-locate": "^5.0.0" } }, - "minimatch": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", - "dev": true, - "requires": { - "brace-expansion": "^5.0.5" - } - }, "p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -11116,20 +11227,20 @@ } }, "eslint-visitor-keys": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", - "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true }, "espree": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", - "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, "requires": { - "acorn": "^8.16.0", + "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^5.0.1" + "eslint-visitor-keys": "^4.2.1" } }, "esquery": { @@ -11416,6 +11527,12 @@ "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", "dev": true }, + "globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true + }, "globalthis": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", @@ -11903,6 +12020,15 @@ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, + "js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, "jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", @@ -12024,6 +12150,12 @@ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "dev": true }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, "loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -13107,6 +13239,12 @@ "es-object-atoms": "^1.0.0" } }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, "style-mod": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz", diff --git a/package.json b/package.json index 155b150ae3..93ea52214b 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "babel-loader": "^10.1.1", "copy-webpack-plugin": "^14.0.0", "css-loader": "^7.1.1", - "eslint": "~10.3.0", + "eslint": "^9.39.4", "eslint-plugin-react": "^7.37.2", "file-loader": "^6.2.0", "mini-css-extract-plugin": "^2.10.2", From 2671d316bcaa98a41ac3ec5b54a93998355c6397 Mon Sep 17 00:00:00 2001 From: David Wallace Date: Wed, 20 May 2026 17:32:36 +0200 Subject: [PATCH 069/131] Add AnswerTree related endpoints to projects api perf test Signed-off-by: David Wallace --- .../tests/test_api_performance_project.py | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/rdmo/projects/tests/test_api_performance_project.py b/rdmo/projects/tests/test_api_performance_project.py index 494e9b5632..233e9dea55 100644 --- a/rdmo/projects/tests/test_api_performance_project.py +++ b/rdmo/projects/tests/test_api_performance_project.py @@ -8,6 +8,13 @@ ('project_answers_export', 34, [1, 'html']), ] +answer_tree_max_queries = [ + # method, urlname, max_queries, url_args + ('get', 'v1-projects:project-navigation', 46, [1]), + ('post', 'v1-projects:project-progress', 47, [1]), + ('get', 'v1-projects:project-page-detail', 60, [1, 1]), +] + @pytest.mark.performance @pytest.mark.parametrize('urlname,max_queries,url_args', max_queries) @@ -26,3 +33,23 @@ def test_project_answer_endpoints_query_counts( response = client.get(url) assert response.status_code == 200 + + +@pytest.mark.performance +@pytest.mark.parametrize('method,urlname,max_queries,url_args', answer_tree_max_queries) +def test_project_answer_tree_endpoints_query_counts( + db, + client, + django_assert_max_num_queries, + method, + urlname, + max_queries, + url_args, +): + client.login(username='owner', password='owner') + url = reverse(urlname, args=url_args) + + with django_assert_max_num_queries(max_queries): + response = getattr(client, method)(url) + + assert response.status_code == 200 From 725a2fa750b164df20e790bb0ce1394c851131a7 Mon Sep 17 00:00:00 2001 From: Claudia Malzer Date: Wed, 20 May 2026 17:33:45 +0200 Subject: [PATCH 070/131] Use rf pytest fixture in tests --- rdmo/projects/tests/test_utils.py | 6 +++--- rdmo/projects/tests/test_view_membership_multisite.py | 5 ++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/rdmo/projects/tests/test_utils.py b/rdmo/projects/tests/test_utils.py index c39dc6bb40..1f274ae14a 100644 --- a/rdmo/projects/tests/test_utils.py +++ b/rdmo/projects/tests/test_utils.py @@ -3,8 +3,8 @@ from django.contrib.auth.models import User from django.contrib.sites.models import Site from django.http import QueryDict -from django.test.client import RequestFactory +#from django.test.client import RequestFactory from rdmo.core.tests.utils import compute_checksum from ..filters import ProjectFilter @@ -236,12 +236,12 @@ def test_compute_set_prefix_from_set_value(set_value, value, result): ] @pytest.mark.parametrize('config, expected', INVITE_EMAIL_PROJECT_PATHS) -def test_get_invite_email_project_path(db, settings, config, expected): +def test_get_invite_email_project_path(rf, db, settings, config, expected): settings.MULTISITE = config['MULTISITE'] settings.PROJECT_INVITE_USE_PROJECT_SITE = config['PROJECT_INVITE_USE_PROJECT_SITE'] # this is where testserver comes from: - request = RequestFactory().get('/') + request = rf.get('/') project = Project.objects.get(pk=1) project.site.domain = 'example.com' diff --git a/rdmo/projects/tests/test_view_membership_multisite.py b/rdmo/projects/tests/test_view_membership_multisite.py index 49b24248d9..d6d844ede2 100644 --- a/rdmo/projects/tests/test_view_membership_multisite.py +++ b/rdmo/projects/tests/test_view_membership_multisite.py @@ -3,7 +3,6 @@ from django.contrib.auth import get_user_model from django.contrib.sites.models import Site from django.core import mail -from django.test.client import RequestFactory from django.urls import reverse from ..models import Invite, Project @@ -36,7 +35,7 @@ @pytest.mark.parametrize('project_id', projects) @pytest.mark.parametrize('membership_role', membership_roles) @pytest.mark.parametrize('site_domain', sites_domains) -def test_get_invite_email_project_path_function(db, client, settings, username, password, project_id, +def test_get_invite_email_project_path_function(rf, db, client, settings, username, password, project_id, membership_role, site_domain): settings.MULTISITE = True @@ -55,7 +54,7 @@ def test_get_invite_email_project_path_function(db, client, settings, username, invite.make_token() invite.save() - request = RequestFactory().get('/') + request = rf.get('/') invite_email_project_path = get_invite_email_project_path(request, invite) if current_site.domain == site_domain: assert invite_email_project_path.startswith('http://testserver/projects') From c7c6af113c7b566e6ea40c605fe5abec9acb12cd Mon Sep 17 00:00:00 2001 From: Claudia Malzer Date: Wed, 20 May 2026 17:34:54 +0200 Subject: [PATCH 071/131] Remove comment --- rdmo/projects/tests/test_utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/rdmo/projects/tests/test_utils.py b/rdmo/projects/tests/test_utils.py index 1f274ae14a..0868852295 100644 --- a/rdmo/projects/tests/test_utils.py +++ b/rdmo/projects/tests/test_utils.py @@ -4,7 +4,6 @@ from django.contrib.sites.models import Site from django.http import QueryDict -#from django.test.client import RequestFactory from rdmo.core.tests.utils import compute_checksum from ..filters import ProjectFilter From c4cc1b307c7cb2176c47cd2bee043c3e13d32f37 Mon Sep 17 00:00:00 2001 From: David Wallace Date: Wed, 20 May 2026 17:36:26 +0200 Subject: [PATCH 072/131] Remove default_option from select related in Question prefetch and loosen export tests Signed-off-by: David Wallace --- rdmo/questions/prefetch.py | 1 - rdmo/questions/tests/test_api_performance_catalog.py | 4 ++-- rdmo/questions/tests/test_api_performance_page.py | 2 +- rdmo/questions/tests/test_api_performance_questionset.py | 2 +- rdmo/questions/tests/test_api_performance_section.py | 4 ++-- 5 files changed, 6 insertions(+), 7 deletions(-) diff --git a/rdmo/questions/prefetch.py b/rdmo/questions/prefetch.py index 103a70246c..bd196bb657 100644 --- a/rdmo/questions/prefetch.py +++ b/rdmo/questions/prefetch.py @@ -26,7 +26,6 @@ def question_prefetch(path, include_options=False): path, queryset=Question.objects.select_related( 'attribute', - 'default_option', ).prefetch_related( condition_prefetch('conditions'), *optionset_lookups, diff --git a/rdmo/questions/tests/test_api_performance_catalog.py b/rdmo/questions/tests/test_api_performance_catalog.py index 0c9c52818f..0bf08d602c 100644 --- a/rdmo/questions/tests/test_api_performance_catalog.py +++ b/rdmo/questions/tests/test_api_performance_catalog.py @@ -8,9 +8,9 @@ # action, max_queries url_kwargs ('list', 8, {}), ('index', 3, {}), - ('export', 30, {'export_format': 'xml'}), + ('export', 32, {'export_format': 'xml'}), ('detail', 8, {'pk': 1}), - ('detail_export', 29, {'pk': 1, 'export_format': 'xml'}), + ('detail_export', 32, {'pk': 1, 'export_format': 'xml'}), ] diff --git a/rdmo/questions/tests/test_api_performance_page.py b/rdmo/questions/tests/test_api_performance_page.py index dd969441d5..ff7d3bdabd 100644 --- a/rdmo/questions/tests/test_api_performance_page.py +++ b/rdmo/questions/tests/test_api_performance_page.py @@ -8,7 +8,7 @@ # action, max_queries, url_kwargs ('list', 10, {}), ('index', 3, {}), - ('export', 25, {'export_format': 'xml'}), + ('export', 28, {'export_format': 'xml'}), ('detail', 10, {'pk': 1}), ('detail_export', 13, {'pk': 1, 'export_format': 'xml'}), ] diff --git a/rdmo/questions/tests/test_api_performance_questionset.py b/rdmo/questions/tests/test_api_performance_questionset.py index 90dd9b4c31..af70dbdcd2 100644 --- a/rdmo/questions/tests/test_api_performance_questionset.py +++ b/rdmo/questions/tests/test_api_performance_questionset.py @@ -8,7 +8,7 @@ # action, max_queries, url_kwargs ('list', 11, {}), ('index', 3, {}), - ('export', 17, {'export_format': 'xml'}), + ('export', 19, {'export_format': 'xml'}), ('detail', 10, {'pk': 89}), ('detail_export', 13, {'pk': 89, 'export_format': 'xml'}), ] diff --git a/rdmo/questions/tests/test_api_performance_section.py b/rdmo/questions/tests/test_api_performance_section.py index d9ea7ff07b..78e0967478 100644 --- a/rdmo/questions/tests/test_api_performance_section.py +++ b/rdmo/questions/tests/test_api_performance_section.py @@ -8,9 +8,9 @@ # action, max_queries, url_kwargs ('list', 8, {}), ('index', 3, {}), - ('export', 27, {'export_format': 'xml'}), + ('export', 30, {'export_format': 'xml'}), ('detail', 8, {'pk': 1}), - ('detail_export', 12, {'pk': 1, 'export_format': 'xml'}), + ('detail_export', 14, {'pk': 1, 'export_format': 'xml'}), ] From 219cfa01a0c014789a7d4e85e025c2ea215b5626 Mon Sep 17 00:00:00 2001 From: David Wallace Date: Thu, 21 May 2026 09:38:53 +0200 Subject: [PATCH 073/131] ci: pin dependabot ESlint to max major 8 Signed-off-by: David Wallace --- .github/dependabot.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 3075df29da..6df43b0044 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -64,6 +64,8 @@ updates: update-types: ["version-update:semver-patch"] # ignore patch versions for all JavaScript dependencies - dependency-name: react-redux update-types: ["version-update:semver-major"] # ignore major for react-redux + - dependency-name: eslint + update-types: ["version-update:semver-major"] # keep eslint 8.56.0 for now groups: react: patterns: From b85868b16408ad76bf125da9f9754a6987d34eb0 Mon Sep 17 00:00:00 2001 From: David Wallace Date: Thu, 21 May 2026 09:40:42 +0200 Subject: [PATCH 074/131] js: keep ESLint 8 Signed-off-by: David Wallace --- package-lock.json | 833 +++++++++++++++++++++++++++------------------- package.json | 2 +- 2 files changed, 499 insertions(+), 336 deletions(-) diff --git a/package-lock.json b/package-lock.json index 512059892b..dc55e8bae0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -44,7 +44,7 @@ "babel-loader": "^10.1.1", "copy-webpack-plugin": "^14.0.0", "css-loader": "^7.1.1", - "eslint": "^9.39.4", + "eslint": "^8.56.0", "eslint-plugin-react": "^7.37.2", "file-loader": "^6.2.0", "mini-css-extract-plugin": "^2.10.2", @@ -2006,18 +2006,6 @@ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/@eslint-community/regexpp": { "version": "4.12.2", "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", @@ -2027,106 +2015,38 @@ "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, - "node_modules/@eslint/config-array": { - "version": "0.21.2", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", - "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^2.1.7", - "debug": "^4.3.1", - "minimatch": "^3.1.5" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/config-helpers": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", - "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.17.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/core": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", - "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, "node_modules/@eslint/eslintrc": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", - "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, "license": "MIT", "dependencies": { - "ajv": "^6.14.0", + "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", + "espree": "^9.6.0", + "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^4.1.1", - "minimatch": "^3.1.5", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/@eslint/js": { - "version": "9.39.4", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", - "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true, "license": "MIT", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - } - }, - "node_modules/@eslint/object-schema": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", - "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/plugin-kit": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", - "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.17.0", - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@floating-ui/core": { @@ -2179,39 +2099,20 @@ "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==" }, - "node_modules/@humanfs/core": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", - "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", - "dev": true, - "dependencies": { - "@humanfs/types": "^0.15.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node": { - "version": "0.16.8", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", - "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@humanfs/core": "^0.19.2", - "@humanfs/types": "^0.15.0", - "@humanwhocodes/retry": "^0.4.0" + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" }, "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/types": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", - "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", - "dev": true, - "engines": { - "node": ">=18.18.0" + "node": ">=10.10.0" } }, "node_modules/@humanwhocodes/module-importer": { @@ -2227,18 +2128,13 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", - "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", "dev": true, - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } + "license": "BSD-3-Clause" }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", @@ -2347,6 +2243,44 @@ "dev": true, "optional": true }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/@parcel/watcher": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.0.tgz", @@ -2807,6 +2741,13 @@ "react-dom": ">=17.0.0" } }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.1.tgz", + "integrity": "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==", + "dev": true, + "license": "ISC" + }, "node_modules/@webassemblyjs/ast": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", @@ -3065,6 +3006,16 @@ "ajv": "^6.9.1" } }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -3995,6 +3946,19 @@ "redux": "^4.2.0" } }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/dom-helpers": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", @@ -4286,63 +4250,60 @@ } }, "node_modules/eslint": { - "version": "9.39.4", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", - "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.8.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.2", - "@eslint/config-helpers": "^0.4.2", - "@eslint/core": "^0.17.0", - "@eslint/eslintrc": "^3.3.5", - "@eslint/js": "9.39.4", - "@eslint/plugin-kit": "^0.4.1", - "@humanfs/node": "^0.16.6", + "@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", - "@humanwhocodes/retry": "^0.4.2", - "@types/estree": "^1.0.6", - "ajv": "^6.14.0", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", - "cross-spawn": "^7.0.6", + "cross-spawn": "^7.0.2", "debug": "^4.3.2", + "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.4.0", - "eslint-visitor-keys": "^4.2.1", - "espree": "^10.4.0", - "esquery": "^1.5.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": "^8.0.0", + "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.5", + "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.3" + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" }, "bin": { "eslint": "bin/eslint.js" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint-plugin-react": { @@ -4429,13 +4390,13 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, "license": "Apache-2.0", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -4454,9 +4415,9 @@ } }, "node_modules/eslint/node_modules/eslint-scope": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", - "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -4464,7 +4425,7 @@ "estraverse": "^5.2.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -4554,18 +4515,18 @@ } }, "node_modules/espree": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", - "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.15.0", + "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.1" + "eslint-visitor-keys": "^3.4.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -4679,16 +4640,27 @@ "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==", "dev": true }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, "node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, + "license": "MIT", "dependencies": { - "flat-cache": "^4.0.0" + "flat-cache": "^3.0.4" }, "engines": { - "node": ">=16.0.0" + "node": "^10.12.0 || >=12.0.0" } }, "node_modules/file-loader": { @@ -4781,23 +4753,26 @@ } }, "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "dev": true, + "license": "MIT", "dependencies": { "flatted": "^3.2.9", - "keyv": "^4.5.4" + "keyv": "^4.5.3", + "rimraf": "^3.0.2" }, "engines": { - "node": ">=16" + "node": "^10.12.0 || >=12.0.0" } }, "node_modules/flatted": { "version": "3.4.2", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/font-awesome": { "version": "4.7.0", @@ -4962,13 +4937,16 @@ "dev": true }, "node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, "engines": { - "node": ">=18" + "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -5008,6 +4986,13 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, "node_modules/has-bigints": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", @@ -5147,6 +5132,7 @@ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, + "license": "MIT", "engines": { "node": ">= 4" } @@ -5486,6 +5472,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", @@ -5723,7 +5719,8 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", @@ -5777,6 +5774,7 @@ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, + "license": "MIT", "dependencies": { "json-buffer": "3.0.1" } @@ -6490,6 +6488,27 @@ "node": ">=6" } }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "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/react": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", @@ -6990,6 +7009,58 @@ "node": ">=8" } }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "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", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, "node_modules/safe-array-concat": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", @@ -7415,6 +7486,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "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", @@ -7541,6 +7625,13 @@ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT" + }, "node_modules/tinyglobby": { "version": "0.2.12", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.12.tgz", @@ -7615,6 +7706,19 @@ "node": ">= 0.8.0" } }, + "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)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/typed-array-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", @@ -9457,14 +9561,6 @@ "dev": true, "requires": { "eslint-visitor-keys": "^3.4.3" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true - } } }, "@eslint-community/regexpp": { @@ -9473,74 +9569,29 @@ "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", "dev": true }, - "@eslint/config-array": { - "version": "0.21.2", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", - "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", - "dev": true, - "requires": { - "@eslint/object-schema": "^2.1.7", - "debug": "^4.3.1", - "minimatch": "^3.1.5" - } - }, - "@eslint/config-helpers": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", - "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", - "dev": true, - "requires": { - "@eslint/core": "^0.17.0" - } - }, - "@eslint/core": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", - "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.15" - } - }, "@eslint/eslintrc": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", - "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, "requires": { - "ajv": "^6.14.0", + "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", + "espree": "^9.6.0", + "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^4.1.1", - "minimatch": "^3.1.5", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "@eslint/js": { - "version": "9.39.4", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", - "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", - "dev": true - }, - "@eslint/object-schema": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", - "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true }, - "@eslint/plugin-kit": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", - "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", - "dev": true, - "requires": { - "@eslint/core": "^0.17.0", - "levn": "^0.4.1" - } - }, "@floating-ui/core": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz", @@ -9581,42 +9632,27 @@ "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==" }, - "@humanfs/core": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", - "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", - "dev": true, - "requires": { - "@humanfs/types": "^0.15.0" - } - }, - "@humanfs/node": { - "version": "0.16.8", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", - "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "@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==", "dev": true, "requires": { - "@humanfs/core": "^0.19.2", - "@humanfs/types": "^0.15.0", - "@humanwhocodes/retry": "^0.4.0" + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" } }, - "@humanfs/types": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", - "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", - "dev": true - }, "@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true }, - "@humanwhocodes/retry": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", - "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "@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==", "dev": true }, "@jridgewell/gen-mapping": { @@ -9723,6 +9759,32 @@ "dev": true, "optional": true }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, "@parcel/watcher": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.0.tgz", @@ -9976,6 +10038,12 @@ "codemirror": "^6.0.0" } }, + "@ungap/structured-clone": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.1.tgz", + "integrity": "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==", + "dev": true + }, "@webassemblyjs/ast": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", @@ -10202,6 +10270,12 @@ "dev": true, "requires": {} }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, "ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -10826,6 +10900,15 @@ "redux": "^4.2.0" } }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, "dom-helpers": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", @@ -11051,45 +11134,49 @@ "devOptional": true }, "eslint": { - "version": "9.39.4", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", - "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", - "dev": true, - "requires": { - "@eslint-community/eslint-utils": "^4.8.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.2", - "@eslint/config-helpers": "^0.4.2", - "@eslint/core": "^0.17.0", - "@eslint/eslintrc": "^3.3.5", - "@eslint/js": "9.39.4", - "@eslint/plugin-kit": "^0.4.1", - "@humanfs/node": "^0.16.6", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "dev": true, + "requires": { + "@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", - "@humanwhocodes/retry": "^0.4.2", - "@types/estree": "^1.0.6", - "ajv": "^6.14.0", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", - "cross-spawn": "^7.0.6", + "cross-spawn": "^7.0.2", "debug": "^4.3.2", + "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.4.0", - "eslint-visitor-keys": "^4.2.1", - "espree": "^10.4.0", - "esquery": "^1.5.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": "^8.0.0", + "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.5", + "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.3" + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" }, "dependencies": { "escape-string-regexp": { @@ -11099,9 +11186,9 @@ "dev": true }, "eslint-scope": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", - "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "requires": { "esrecurse": "^4.3.0", @@ -11227,20 +11314,20 @@ } }, "eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true }, "espree": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", - "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "requires": { - "acorn": "^8.15.0", + "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.1" + "eslint-visitor-keys": "^3.4.1" } }, "esquery": { @@ -11324,13 +11411,22 @@ "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==", "dev": true }, + "fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, "file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, "requires": { - "flat-cache": "^4.0.0" + "flat-cache": "^3.0.4" } }, "file-loader": { @@ -11396,13 +11492,14 @@ "dev": true }, "flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "dev": true, "requires": { "flatted": "^3.2.9", - "keyv": "^4.5.4" + "keyv": "^4.5.3", + "rimraf": "^3.0.2" } }, "flatted": { @@ -11528,10 +11625,13 @@ "dev": true }, "globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } }, "globalthis": { "version": "1.0.4", @@ -11558,6 +11658,12 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, + "graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, "has-bigints": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", @@ -11877,6 +11983,12 @@ "has-tostringtag": "^1.0.0" } }, + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true + }, "is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", @@ -12583,6 +12695,12 @@ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, "react": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", @@ -12950,6 +13068,30 @@ "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true }, + "reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, "safe-array-concat": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", @@ -13239,6 +13381,15 @@ "es-object-atoms": "^1.0.0" } }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, "strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -13312,6 +13463,12 @@ "terser": "^5.31.1" } }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, "tinyglobby": { "version": "0.2.12", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.12.tgz", @@ -13361,6 +13518,12 @@ "prelude-ls": "^1.2.1" } }, + "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 + }, "typed-array-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", diff --git a/package.json b/package.json index 93ea52214b..5a98542ebc 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "babel-loader": "^10.1.1", "copy-webpack-plugin": "^14.0.0", "css-loader": "^7.1.1", - "eslint": "^9.39.4", + "eslint": "^8.56.0", "eslint-plugin-react": "^7.37.2", "file-loader": "^6.2.0", "mini-css-extract-plugin": "^2.10.2", From f2b5e25a9e8043d8b4f47f57660db13b2c1a4595 Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Thu, 21 May 2026 10:20:37 +0200 Subject: [PATCH 075/131] Add check for users with out site to get_invite_email_project_path --- rdmo/projects/utils.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rdmo/projects/utils.py b/rdmo/projects/utils.py index 5d1d33d1f7..d407e48806 100644 --- a/rdmo/projects/utils.py +++ b/rdmo/projects/utils.py @@ -248,7 +248,9 @@ def get_invite_email_project_path(request, invite): if not invite.user.role.member.filter(id=current_site.id).exists(): # take users first site - return f'{request.scheme}://{invite.user.role.member.first().domain}{project_invite_path}' + user_first_site = invite.user.role.member.first() + if user_first_site: + return f'{request.scheme}://{user_first_site.domain}{project_invite_path}' return request.build_absolute_uri(project_invite_path) From 1f91a0159e0d36fb8731e8a802ac31b18144c163 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 May 2026 09:21:46 +0000 Subject: [PATCH 076/131] build(deps): bump the prod-dependencies group across 1 directory with 6 updates Bumps the prod-dependencies group with 6 updates in the / directory: | Package | From | To | | --- | --- | --- | | [html-to-text](https://github.com/html-to-text/node-html-to-text) | `9.0.5` | `10.0.0` | | [jquery](https://github.com/jquery/jquery) | `3.7.1` | `3.7.1` | | [react-datepicker](https://github.com/Hacker0x01/react-datepicker) | `8.10.0` | `9.1.0` | | [react-diff-viewer-continued](https://github.com/Aeolun/react-diff-viewer-continued) | `3.4.0` | `4.2.2` | | [react-dropzone](https://github.com/react-dropzone/react-dropzone) | `14.3.8` | `15.0.0` | | [use-debounce](https://github.com/xnimorz/use-debounce) | `10.0.3` | `10.1.1` | Updates `html-to-text` from 9.0.5 to 10.0.0 - [Commits](https://github.com/html-to-text/node-html-to-text/compare/9.0.5...10.0.0) Updates `jquery` from 3.7.1 to 4.0.0 - [Release notes](https://github.com/jquery/jquery/releases) - [Changelog](https://github.com/jquery/jquery/blob/main/changelog.md) - [Commits](https://github.com/jquery/jquery/compare/3.7.1...4.0.0) Updates `react-datepicker` from 8.10.0 to 9.1.0 - [Release notes](https://github.com/Hacker0x01/react-datepicker/releases) - [Commits](https://github.com/Hacker0x01/react-datepicker/compare/v8.10.0...v9.1.0) Updates `react-diff-viewer-continued` from 3.4.0 to 4.2.2 - [Release notes](https://github.com/Aeolun/react-diff-viewer-continued/releases) - [Changelog](https://github.com/Aeolun/react-diff-viewer-continued/blob/main/CHANGELOG.md) - [Commits](https://github.com/Aeolun/react-diff-viewer-continued/compare/v3.4.0...v4.2.2) Updates `react-dropzone` from 14.3.8 to 15.0.0 - [Release notes](https://github.com/react-dropzone/react-dropzone/releases) - [Commits](https://github.com/react-dropzone/react-dropzone/compare/v14.3.8...v15.0.0) Updates `use-debounce` from 10.0.3 to 10.1.1 - [Release notes](https://github.com/xnimorz/use-debounce/releases) - [Changelog](https://github.com/xnimorz/use-debounce/blob/master/CHANGELOG.md) - [Commits](https://github.com/xnimorz/use-debounce/commits) --- updated-dependencies: - dependency-name: html-to-text dependency-version: 10.0.0 dependency-type: direct:production update-type: version-update:semver-major dependency-group: prod-dependencies - dependency-name: jquery dependency-version: 4.0.0 dependency-type: direct:production update-type: version-update:semver-major dependency-group: prod-dependencies - dependency-name: react-datepicker dependency-version: 9.1.0 dependency-type: direct:production update-type: version-update:semver-major dependency-group: prod-dependencies - dependency-name: react-diff-viewer-continued dependency-version: 4.2.2 dependency-type: direct:production update-type: version-update:semver-major dependency-group: prod-dependencies - dependency-name: react-dropzone dependency-version: 15.0.0 dependency-type: direct:production update-type: version-update:semver-major dependency-group: prod-dependencies - dependency-name: use-debounce dependency-version: 10.1.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: prod-dependencies ... Signed-off-by: dependabot[bot] --- package-lock.json | 820 +++++++++++++++++++++++++++------------------- package.json | 12 +- 2 files changed, 481 insertions(+), 351 deletions(-) diff --git a/package-lock.json b/package-lock.json index dc55e8bae0..6068940825 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,26 +15,26 @@ "classnames": "^2.5.1", "date-fns": "^4.1.0", "font-awesome": "4.7.0", - "html-to-text": "^9.0.5", - "jquery": "^3.7.1", + "html-to-text": "^10.0.0", + "jquery": "^4.0.0", "js-cookie": "^3.0.5", "lodash": "^4.18.1", "popper.js": "^1.16.1", "prop-types": "^15.7.2", "react": "^18.3.1", "react-bootstrap": "0.33.1", - "react-datepicker": "8.10.0", - "react-diff-viewer-continued": "^3.4.0", + "react-datepicker": "9.1.0", + "react-diff-viewer-continued": "^4.2.2", "react-dnd": "^16.0.1", "react-dnd-html5-backend": "^16.0.1", "react-dom": "^18.3.1", - "react-dropzone": "^14.3.8", + "react-dropzone": "^15.0.0", "react-redux": "^8.1.3", "react-select": "^5.10.2", "redux": "^4.1.1", "redux-logger": "^3.0.6", "redux-thunk": "^2.3.0", - "use-debounce": "^10.0.0" + "use-debounce": "^10.1.1" }, "devDependencies": { "@babel/cli": "^7.28.0", @@ -68,6 +68,19 @@ "node": ">=0.10.0" } }, + "node_modules/@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@babel/cli": { "version": "7.28.0", "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.28.0.tgz", @@ -159,7 +172,7 @@ "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==", - "devOptional": true + "dev": true }, "node_modules/@babel/generator": { "version": "7.29.1", @@ -208,7 +221,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "devOptional": true, + "dev": true, "dependencies": { "yallist": "^3.0.2" } @@ -217,7 +230,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "devOptional": true + "dev": true }, "node_modules/@babel/helper-create-class-features-plugin": { "version": "7.29.3", @@ -310,7 +323,7 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", - "devOptional": true, + "dev": true, "dependencies": { "@babel/helper-module-imports": "^7.28.6", "@babel/helper-validator-identifier": "^7.28.5", @@ -414,7 +427,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -1851,15 +1864,15 @@ } }, "node_modules/@emotion/babel-plugin": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz", - "integrity": "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==", + "version": "11.13.5", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", + "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==", "dependencies": { "@babel/helper-module-imports": "^7.16.7", "@babel/runtime": "^7.18.3", - "@emotion/hash": "^0.9.1", - "@emotion/memoize": "^0.8.1", - "@emotion/serialize": "^1.1.2", + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/serialize": "^1.3.3", "babel-plugin-macros": "^3.1.0", "convert-source-map": "^1.5.0", "escape-string-regexp": "^4.0.0", @@ -1868,17 +1881,6 @@ "stylis": "4.2.0" } }, - "node_modules/@emotion/babel-plugin/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==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@emotion/babel-plugin/node_modules/source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -1888,105 +1890,101 @@ } }, "node_modules/@emotion/cache": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz", - "integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==", - "dependencies": { - "@emotion/memoize": "^0.8.1", - "@emotion/sheet": "^1.2.2", - "@emotion/utils": "^1.2.1", - "@emotion/weak-memoize": "^0.3.1", + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", + "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==", + "dependencies": { + "@emotion/memoize": "^0.9.0", + "@emotion/sheet": "^1.4.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", "stylis": "4.2.0" } }, "node_modules/@emotion/css": { - "version": "11.11.2", - "resolved": "https://registry.npmjs.org/@emotion/css/-/css-11.11.2.tgz", - "integrity": "sha512-VJxe1ucoMYMS7DkiMdC2T7PWNbrEI0a39YRiyDvK2qq4lXwjRbVP/z4lpG+odCsRzadlR+1ywwrTzhdm5HNdew==", + "version": "11.13.5", + "resolved": "https://registry.npmjs.org/@emotion/css/-/css-11.13.5.tgz", + "integrity": "sha512-wQdD0Xhkn3Qy2VNcIzbLP9MR8TafI0MJb7BEAXKp+w4+XqErksWR4OXomuDzPsN4InLdGhVe6EYcn2ZIUCpB8w==", "dependencies": { - "@emotion/babel-plugin": "^11.11.0", - "@emotion/cache": "^11.11.0", - "@emotion/serialize": "^1.1.2", - "@emotion/sheet": "^1.2.2", - "@emotion/utils": "^1.2.1" + "@emotion/babel-plugin": "^11.13.5", + "@emotion/cache": "^11.13.5", + "@emotion/serialize": "^1.3.3", + "@emotion/sheet": "^1.4.0", + "@emotion/utils": "^1.4.2" } }, "node_modules/@emotion/hash": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz", - "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==" + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==" }, "node_modules/@emotion/memoize": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", - "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==" }, "node_modules/@emotion/react": { - "version": "11.10.5", - "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.10.5.tgz", - "integrity": "sha512-TZs6235tCJ/7iF6/rvTaOH4oxQg2gMAcdHemjwLKIjKz4rRuYe1HJ2TQJKnAcRAfOUDdU8XoDadCe1rl72iv8A==", + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", + "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", "dependencies": { "@babel/runtime": "^7.18.3", - "@emotion/babel-plugin": "^11.10.5", - "@emotion/cache": "^11.10.5", - "@emotion/serialize": "^1.1.1", - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", - "@emotion/utils": "^1.2.0", - "@emotion/weak-memoize": "^0.3.0", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/cache": "^11.14.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", "hoist-non-react-statics": "^3.3.1" }, "peerDependencies": { - "@babel/core": "^7.0.0", "react": ">=16.8.0" }, "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, "@types/react": { "optional": true } } }, "node_modules/@emotion/serialize": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.2.tgz", - "integrity": "sha512-zR6a/fkFP4EAcCMQtLOhIgpprZOwNmCldtpaISpvz348+DP4Mz8ZoKaGGCQpbzepNIUWbq4w6hNZkwDyKoS+HA==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", + "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==", "dependencies": { - "@emotion/hash": "^0.9.1", - "@emotion/memoize": "^0.8.1", - "@emotion/unitless": "^0.8.1", - "@emotion/utils": "^1.2.1", + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/unitless": "^0.10.0", + "@emotion/utils": "^1.4.2", "csstype": "^3.0.2" } }, "node_modules/@emotion/sheet": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz", - "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==" + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", + "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==" }, "node_modules/@emotion/unitless": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", - "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==" + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", + "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==" }, "node_modules/@emotion/use-insertion-effect-with-fallbacks": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.0.tgz", - "integrity": "sha512-1eEgUGmkaljiBnRMTdksDV1W4kUnmwgp7X9G8B++9GYwl1lUdqSndSriIrTJ0N7LQaoauY9JJ2yhiOYK5+NI4A==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz", + "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==", "peerDependencies": { "react": ">=16.8.0" } }, "node_modules/@emotion/utils": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz", - "integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==" + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", + "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==" }, "node_modules/@emotion/weak-memoize": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz", - "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==" + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", + "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==" }, "node_modules/@eslint-community/eslint-utils": { "version": "4.9.1", @@ -2163,6 +2161,15 @@ "node": ">=6.0.0" } }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@jridgewell/source-map": { "version": "0.3.11", "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", @@ -2593,15 +2600,18 @@ "integrity": "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==" }, "node_modules/@selderee/plugin-htmlparser2": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.11.0.tgz", - "integrity": "sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.12.0.tgz", + "integrity": "sha512-oELmoyA6ML9jDRMV3kgcMQFKxUfBU0yFVn6yTctVaLT5ygXnxH52I3TZEgV9EhXJC68/uFvE5Daj1/25c0Xa/A==", "dependencies": { - "domhandler": "^5.0.3", - "selderee": "^0.11.0" + "domelementtype": "~2.3.0", + "domhandler": "~5.0.3" }, "funding": { - "url": "https://ko-fi.com/killymxi" + "url": "https://github.com/sponsors/KillyMXI" + }, + "peerDependencies": { + "selderee": "~0.12.0" } }, "node_modules/@types/eslint": { @@ -2653,9 +2663,9 @@ "devOptional": true }, "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==" + "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==" }, "node_modules/@types/prop-types": { "version": "15.7.5", @@ -3341,7 +3351,7 @@ "version": "2.9.19", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz", "integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==", - "devOptional": true, + "dev": true, "bin": { "baseline-browser-mapping": "dist/cli.js" } @@ -3398,7 +3408,7 @@ "version": "4.28.1", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", - "devOptional": true, + "dev": true, "funding": [ { "type": "opencollective", @@ -3464,7 +3474,7 @@ "version": "1.0.30001769", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001769.tgz", "integrity": "sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg==", - "devOptional": true, + "dev": true, "funding": [ { "type": "opencollective", @@ -3621,12 +3631,9 @@ "dev": true }, "node_modules/convert-source-map": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", - "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", - "dependencies": { - "safe-buffer": "~5.1.1" - } + "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/copy-webpack-plugin": { "version": "14.0.0", @@ -3873,12 +3880,12 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "node_modules/deepmerge-ts": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz", + "integrity": "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==", "engines": { - "node": ">=0.10.0" + "node": ">=16.0.0" } }, "node_modules/define-data-property": { @@ -3929,9 +3936,9 @@ } }, "node_modules/diff": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.2.tgz", - "integrity": "sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-9.0.0.tgz", + "integrity": "sha512-svtcdpS8CgJyqAjEQIXdb3OjhFVVYjzGAPO8WGCmRbrml64SPw/jJD4GoE98aR7r25A0XcgrK3F02yw9R/vhQw==", "engines": { "node": ">=0.3.1" } @@ -4006,9 +4013,9 @@ } }, "node_modules/domutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", - "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", @@ -4022,7 +4029,7 @@ "version": "1.5.286", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz", "integrity": "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==", - "devOptional": true + "dev": true }, "node_modules/emojis-list": { "version": "3.0.0", @@ -4070,9 +4077,9 @@ } }, "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==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", "dependencies": { "is-arrayish": "^0.2.1" } @@ -4244,11 +4251,22 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "devOptional": true, + "dev": true, "engines": { "node": ">=6" } }, + "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==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/eslint": { "version": "8.57.1", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", @@ -4414,6 +4432,55 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "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/eslint/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/eslint/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, "node_modules/eslint/node_modules/eslint-scope": { "version": "7.2.2", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", @@ -4856,7 +4923,7 @@ "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==", - "devOptional": true, + "dev": true, "engines": { "node": ">=6.9.0" } @@ -5083,24 +5150,27 @@ } }, "node_modules/html-to-text": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-9.0.5.tgz", - "integrity": "sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-10.0.0.tgz", + "integrity": "sha512-2OH59Gtprdczel+7Rxgpz9hGVJREaf8Lt1H4kZwWHpEn70VQKRuMNGsb2eDbwaTzrYzb0hheiOG1P7Dim0B4dQ==", "dependencies": { - "@selderee/plugin-htmlparser2": "^0.11.0", - "deepmerge": "^4.3.1", + "@selderee/plugin-htmlparser2": "~0.12.0", + "deepmerge-ts": "^7.1.5", "dom-serializer": "^2.0.0", - "htmlparser2": "^8.0.2", - "selderee": "^0.11.0" + "htmlparser2": "^10.1.0", + "selderee": "~0.12.0" }, "engines": { - "node": ">=14" + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/KillyMXI" } }, "node_modules/htmlparser2": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", - "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz", + "integrity": "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==", "funding": [ "https://github.com/fb55/htmlparser2?sponsor=1", { @@ -5111,8 +5181,19 @@ "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "entities": "^4.4.0" + "domutils": "^3.2.2", + "entities": "^7.0.1" + } + }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" } }, "node_modules/icss-utils": { @@ -5674,9 +5755,9 @@ } }, "node_modules/jquery": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz", - "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-4.0.0.tgz", + "integrity": "sha512-TXCHVR3Lb6TZdtw1l3RTLf8RBWVGexdxL6AC8/e0xZKEpBflBsjh9/8LXw+dkNFuOyW9B7iB3O1sP7hS0Kiacg==" }, "node_modules/js-cookie": { "version": "3.0.5", @@ -5743,7 +5824,7 @@ "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "devOptional": true, + "dev": true, "bin": { "json5": "lib/cli.js" }, @@ -5789,11 +5870,11 @@ } }, "node_modules/leac": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/leac/-/leac-0.6.0.tgz", - "integrity": "sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/leac/-/leac-0.7.0.tgz", + "integrity": "sha512-qMrZeyEekgdRQ9o6a4NAB2EQZrv827GJdn1vnapwSJ90hWRB4TzUSunvacPkxQ2TnNqHNI1/zSt0hlo0crG8Jw==", "funding": { - "url": "https://ko-fi.com/killymxi" + "url": "https://github.com/sponsors/KillyMXI" } }, "node_modules/levn": { @@ -6030,7 +6111,7 @@ "version": "2.0.27", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", - "devOptional": true + "dev": true }, "node_modules/normalize-path": { "version": "3.0.0", @@ -6225,15 +6306,15 @@ } }, "node_modules/parseley": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/parseley/-/parseley-0.12.1.tgz", - "integrity": "sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw==", + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/parseley/-/parseley-0.13.1.tgz", + "integrity": "sha512-uNBJZzmb60l6p6VWLTmevizNAGnE0xoSf1n0B4q3ntegDNzcS68NRCcBDZTcyXHxt2XhBChsCuqj4M+nChvE/A==", "dependencies": { - "leac": "^0.6.0", - "peberminta": "^0.9.0" + "leac": "^0.7.0", + "peberminta": "^0.10.0" }, "funding": { - "url": "https://ko-fi.com/killymxi" + "url": "https://github.com/sponsors/KillyMXI" } }, "node_modules/path-exists": { @@ -6277,11 +6358,11 @@ } }, "node_modules/peberminta": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/peberminta/-/peberminta-0.9.0.tgz", - "integrity": "sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==", + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/peberminta/-/peberminta-0.10.0.tgz", + "integrity": "sha512-80B2AsU+I4Qdb0ZAPSfe9UwvGzwkM37IKIFEvdS3D/3Ndgv2bsuJ0bfG1+iEYO+l7Gfd4EUJmuRyq7efLgRMzQ==", "funding": { - "url": "https://ko-fi.com/killymxi" + "url": "https://github.com/sponsors/KillyMXI" } }, "node_modules/picocolors": { @@ -6553,36 +6634,43 @@ } }, "node_modules/react-datepicker": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-8.10.0.tgz", - "integrity": "sha512-JIXuA+g+qP3c4MVJpx24o7n1gnv3WV/8A/D6964HucY1FlSEc30+ITPNUfbKZXYHl5rruCtxYCwi2lzn7gaz7g==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-9.1.0.tgz", + "integrity": "sha512-lOp+m5bc+ttgtB5MHEjwiVu4nlp4CvJLS/PG1OiOe5pmg9kV73pEqO8H0Geqvg2E8gjqTaL9eRhSe+ZpeKP3nA==", "dependencies": { "@floating-ui/react": "^0.27.15", "clsx": "^2.1.1", "date-fns": "^4.1.0" }, "peerDependencies": { + "date-fns-tz": "^3.0.0", "react": "^16.9.0 || ^17 || ^18 || ^19 || ^19.0.0-rc", "react-dom": "^16.9.0 || ^17 || ^18 || ^19 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "date-fns-tz": { + "optional": true + } } }, "node_modules/react-diff-viewer-continued": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/react-diff-viewer-continued/-/react-diff-viewer-continued-3.4.0.tgz", - "integrity": "sha512-kMZmUyb3Pv5L9vUtCfIGYsdOHs8mUojblGy1U1Sm0D7FhAOEsH9QhnngEIRo5hXWIPNGupNRJls1TJ6Eqx84eg==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/react-diff-viewer-continued/-/react-diff-viewer-continued-4.2.2.tgz", + "integrity": "sha512-8wT0/smXGox70oXJ7SZkTYF1p1Uh7jNGXhN7ykqrqPven0sZ+wM2c62SG3ZqORx5aW75te+WhmUWo0E4NyoSvg==", "dependencies": { - "@emotion/css": "^11.11.2", - "classnames": "^2.3.2", - "diff": "^5.1.0", - "memoize-one": "^6.0.0", - "prop-types": "^15.8.1" + "@emotion/css": "^11.13.5", + "@emotion/react": "^11.14.0", + "classnames": "^2.5.1", + "diff": "^9.0.0", + "js-yaml": "^4.1.1", + "memoize-one": "^6.0.0" }, "engines": { - "node": ">= 8" + "node": ">= 16" }, "peerDependencies": { - "react": "^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" + "react": "^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "node_modules/react-dnd": { @@ -6636,9 +6724,9 @@ } }, "node_modules/react-dropzone": { - "version": "14.3.8", - "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-14.3.8.tgz", - "integrity": "sha512-sBgODnq+lcA4P296DY4wacOZz3JFpD99fp+hb//iBO2HHnyeZU3FwWyXJ6salNpqQdsZrgMrotuko/BdJMV8Ug==", + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-15.0.0.tgz", + "integrity": "sha512-lGjYV/EoqEjEWPnmiSvH4v5IoIAwQM2W4Z1C0Q/Pw2xD0eVzKPS359BQTUMum+1fa0kH2nrKjuavmTPOGhpLPg==", "dependencies": { "attr-accept": "^2.2.4", "file-selector": "^2.1.0", @@ -7079,11 +7167,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "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/safe-regex-test": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", @@ -7253,21 +7336,21 @@ "dev": true }, "node_modules/selderee": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/selderee/-/selderee-0.11.0.tgz", - "integrity": "sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/selderee/-/selderee-0.12.0.tgz", + "integrity": "sha512-b1YMh3+DHZp59DLna3qVwQ5iOla/nrI6mLBNW02XxU77M3046Df6VLkoaJyFz20VsGIG5kkp+FK0kg4K4HnUFw==", "dependencies": { - "parseley": "^0.12.0" + "parseley": "~0.13.1" }, "funding": { - "url": "https://ko-fi.com/killymxi" + "url": "https://github.com/sponsors/KillyMXI" } }, "node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "devOptional": true, + "dev": true, "bin": { "semver": "bin/semver.js" } @@ -7865,7 +7948,7 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", - "devOptional": true, + "dev": true, "funding": [ { "type": "opencollective", @@ -7901,9 +7984,9 @@ } }, "node_modules/use-debounce": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/use-debounce/-/use-debounce-10.0.3.tgz", - "integrity": "sha512-DxQSI9ZKso689WM1mjgGU3ozcxU1TJElBJ3X6S4SMzMNcm2lVH0AHmyXB+K7ewjz2BSUKJTDqTcwtSMRfB89dg==", + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/use-debounce/-/use-debounce-10.1.1.tgz", + "integrity": "sha512-kvds8BHR2k28cFsxW8k3nc/tRga2rs1RHYCqmmGqb90MEeE++oALwzh2COiuBLO1/QXiOuShXoSN2ZpWnMmvuQ==", "engines": { "node": ">= 16.0.0" }, @@ -8223,6 +8306,16 @@ "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", "dev": true }, + "@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "dev": true, + "requires": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, "@babel/cli": { "version": "7.28.0", "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.28.0.tgz", @@ -8291,7 +8384,7 @@ "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==", - "devOptional": true + "dev": true } } }, @@ -8333,7 +8426,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "devOptional": true, + "dev": true, "requires": { "yallist": "^3.0.2" } @@ -8342,7 +8435,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "devOptional": true + "dev": true } } }, @@ -8413,7 +8506,7 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", - "devOptional": true, + "dev": true, "requires": { "@babel/helper-module-imports": "^7.28.6", "@babel/helper-validator-identifier": "^7.28.5", @@ -8481,7 +8574,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", - "devOptional": true + "dev": true }, "@babel/helper-wrap-function": { "version": "7.28.6", @@ -9438,15 +9531,15 @@ "dev": true }, "@emotion/babel-plugin": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz", - "integrity": "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==", + "version": "11.13.5", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", + "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==", "requires": { "@babel/helper-module-imports": "^7.16.7", "@babel/runtime": "^7.18.3", - "@emotion/hash": "^0.9.1", - "@emotion/memoize": "^0.8.1", - "@emotion/serialize": "^1.1.2", + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/serialize": "^1.3.3", "babel-plugin-macros": "^3.1.0", "convert-source-map": "^1.5.0", "escape-string-regexp": "^4.0.0", @@ -9455,11 +9548,6 @@ "stylis": "4.2.0" }, "dependencies": { - "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==" - }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -9468,91 +9556,91 @@ } }, "@emotion/cache": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz", - "integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==", - "requires": { - "@emotion/memoize": "^0.8.1", - "@emotion/sheet": "^1.2.2", - "@emotion/utils": "^1.2.1", - "@emotion/weak-memoize": "^0.3.1", + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", + "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==", + "requires": { + "@emotion/memoize": "^0.9.0", + "@emotion/sheet": "^1.4.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", "stylis": "4.2.0" } }, "@emotion/css": { - "version": "11.11.2", - "resolved": "https://registry.npmjs.org/@emotion/css/-/css-11.11.2.tgz", - "integrity": "sha512-VJxe1ucoMYMS7DkiMdC2T7PWNbrEI0a39YRiyDvK2qq4lXwjRbVP/z4lpG+odCsRzadlR+1ywwrTzhdm5HNdew==", + "version": "11.13.5", + "resolved": "https://registry.npmjs.org/@emotion/css/-/css-11.13.5.tgz", + "integrity": "sha512-wQdD0Xhkn3Qy2VNcIzbLP9MR8TafI0MJb7BEAXKp+w4+XqErksWR4OXomuDzPsN4InLdGhVe6EYcn2ZIUCpB8w==", "requires": { - "@emotion/babel-plugin": "^11.11.0", - "@emotion/cache": "^11.11.0", - "@emotion/serialize": "^1.1.2", - "@emotion/sheet": "^1.2.2", - "@emotion/utils": "^1.2.1" + "@emotion/babel-plugin": "^11.13.5", + "@emotion/cache": "^11.13.5", + "@emotion/serialize": "^1.3.3", + "@emotion/sheet": "^1.4.0", + "@emotion/utils": "^1.4.2" } }, "@emotion/hash": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz", - "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==" + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==" }, "@emotion/memoize": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", - "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==" }, "@emotion/react": { - "version": "11.10.5", - "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.10.5.tgz", - "integrity": "sha512-TZs6235tCJ/7iF6/rvTaOH4oxQg2gMAcdHemjwLKIjKz4rRuYe1HJ2TQJKnAcRAfOUDdU8XoDadCe1rl72iv8A==", + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", + "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", "requires": { "@babel/runtime": "^7.18.3", - "@emotion/babel-plugin": "^11.10.5", - "@emotion/cache": "^11.10.5", - "@emotion/serialize": "^1.1.1", - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", - "@emotion/utils": "^1.2.0", - "@emotion/weak-memoize": "^0.3.0", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/cache": "^11.14.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", "hoist-non-react-statics": "^3.3.1" } }, "@emotion/serialize": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.2.tgz", - "integrity": "sha512-zR6a/fkFP4EAcCMQtLOhIgpprZOwNmCldtpaISpvz348+DP4Mz8ZoKaGGCQpbzepNIUWbq4w6hNZkwDyKoS+HA==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", + "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==", "requires": { - "@emotion/hash": "^0.9.1", - "@emotion/memoize": "^0.8.1", - "@emotion/unitless": "^0.8.1", - "@emotion/utils": "^1.2.1", + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/unitless": "^0.10.0", + "@emotion/utils": "^1.4.2", "csstype": "^3.0.2" } }, "@emotion/sheet": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz", - "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==" + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", + "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==" }, "@emotion/unitless": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", - "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==" + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", + "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==" }, "@emotion/use-insertion-effect-with-fallbacks": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.0.tgz", - "integrity": "sha512-1eEgUGmkaljiBnRMTdksDV1W4kUnmwgp7X9G8B++9GYwl1lUdqSndSriIrTJ0N7LQaoauY9JJ2yhiOYK5+NI4A==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz", + "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==", "requires": {} }, "@emotion/utils": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz", - "integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==" + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", + "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==" }, "@emotion/weak-memoize": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz", - "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==" + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", + "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==" }, "@eslint-community/eslint-utils": { "version": "4.9.1", @@ -9679,6 +9767,12 @@ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==" }, + "@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true + }, "@jridgewell/source-map": { "version": "0.3.11", "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", @@ -9918,12 +10012,12 @@ "integrity": "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==" }, "@selderee/plugin-htmlparser2": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.11.0.tgz", - "integrity": "sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.12.0.tgz", + "integrity": "sha512-oELmoyA6ML9jDRMV3kgcMQFKxUfBU0yFVn6yTctVaLT5ygXnxH52I3TZEgV9EhXJC68/uFvE5Daj1/25c0Xa/A==", "requires": { - "domhandler": "^5.0.3", - "selderee": "^0.11.0" + "domelementtype": "~2.3.0", + "domhandler": "~5.0.3" } }, "@types/eslint": { @@ -9974,9 +10068,9 @@ "devOptional": true }, "@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==" + "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==" }, "@types/prop-types": { "version": "15.7.5", @@ -10299,8 +10393,7 @@ "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 + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, "array-buffer-byte-length": { "version": "1.0.1", @@ -10493,7 +10586,7 @@ "version": "2.9.19", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz", "integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==", - "devOptional": true + "dev": true }, "big.js": { "version": "5.2.2", @@ -10537,7 +10630,7 @@ "version": "4.28.1", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", - "devOptional": true, + "dev": true, "requires": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -10574,7 +10667,7 @@ "version": "1.0.30001769", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001769.tgz", "integrity": "sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg==", - "devOptional": true + "dev": true }, "chalk": { "version": "4.1.2", @@ -10683,12 +10776,9 @@ "dev": true }, "convert-source-map": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", - "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", - "requires": { - "safe-buffer": "~5.1.1" - } + "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==" }, "copy-webpack-plugin": { "version": "14.0.0", @@ -10851,10 +10941,10 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, - "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==" + "deepmerge-ts": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz", + "integrity": "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==" }, "define-data-property": { "version": "1.1.4", @@ -10886,9 +10976,9 @@ "optional": true }, "diff": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.2.tgz", - "integrity": "sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A==" + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-9.0.0.tgz", + "integrity": "sha512-svtcdpS8CgJyqAjEQIXdb3OjhFVVYjzGAPO8WGCmRbrml64SPw/jJD4GoE98aR7r25A0XcgrK3F02yw9R/vhQw==" }, "dnd-core": { "version": "16.0.1", @@ -10941,9 +11031,9 @@ } }, "domutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", - "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", "requires": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", @@ -10954,7 +11044,7 @@ "version": "1.5.286", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz", "integrity": "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==", - "devOptional": true + "dev": true }, "emojis-list": { "version": "3.0.0", @@ -10984,9 +11074,9 @@ "dev": true }, "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==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", "requires": { "is-arrayish": "^0.2.1" } @@ -11131,7 +11221,12 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "devOptional": true + "dev": true + }, + "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==" }, "eslint": { "version": "8.57.1", @@ -11184,6 +11279,39 @@ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "eslint-scope": { "version": "7.2.2", @@ -11568,7 +11696,7 @@ "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==", - "devOptional": true + "dev": true }, "get-intrinsic": { "version": "1.2.4", @@ -11723,26 +11851,33 @@ } }, "html-to-text": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-9.0.5.tgz", - "integrity": "sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-10.0.0.tgz", + "integrity": "sha512-2OH59Gtprdczel+7Rxgpz9hGVJREaf8Lt1H4kZwWHpEn70VQKRuMNGsb2eDbwaTzrYzb0hheiOG1P7Dim0B4dQ==", "requires": { - "@selderee/plugin-htmlparser2": "^0.11.0", - "deepmerge": "^4.3.1", + "@selderee/plugin-htmlparser2": "~0.12.0", + "deepmerge-ts": "^7.1.5", "dom-serializer": "^2.0.0", - "htmlparser2": "^8.0.2", - "selderee": "^0.11.0" + "htmlparser2": "^10.1.0", + "selderee": "~0.12.0" } }, "htmlparser2": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", - "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz", + "integrity": "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==", "requires": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "entities": "^4.4.0" + "domutils": "^3.2.2", + "entities": "^7.0.1" + }, + "dependencies": { + "entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==" + } } }, "icss-utils": { @@ -12118,9 +12253,9 @@ } }, "jquery": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz", - "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-4.0.0.tgz", + "integrity": "sha512-TXCHVR3Lb6TZdtw1l3RTLf8RBWVGexdxL6AC8/e0xZKEpBflBsjh9/8LXw+dkNFuOyW9B7iB3O1sP7hS0Kiacg==" }, "js-cookie": { "version": "3.0.5", @@ -12136,7 +12271,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", - "dev": true, "requires": { "argparse": "^2.0.1" } @@ -12173,7 +12307,7 @@ "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "devOptional": true + "dev": true }, "jsx-ast-utils": { "version": "3.3.3", @@ -12206,9 +12340,9 @@ "dev": true }, "leac": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/leac/-/leac-0.6.0.tgz", - "integrity": "sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==" + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/leac/-/leac-0.7.0.tgz", + "integrity": "sha512-qMrZeyEekgdRQ9o6a4NAB2EQZrv827GJdn1vnapwSJ90hWRB4TzUSunvacPkxQ2TnNqHNI1/zSt0hlo0crG8Jw==" }, "levn": { "version": "0.4.1", @@ -12384,7 +12518,7 @@ "version": "2.0.27", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", - "devOptional": true + "dev": true }, "normalize-path": { "version": "3.0.0", @@ -12522,12 +12656,12 @@ } }, "parseley": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/parseley/-/parseley-0.12.1.tgz", - "integrity": "sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw==", + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/parseley/-/parseley-0.13.1.tgz", + "integrity": "sha512-uNBJZzmb60l6p6VWLTmevizNAGnE0xoSf1n0B4q3ntegDNzcS68NRCcBDZTcyXHxt2XhBChsCuqj4M+nChvE/A==", "requires": { - "leac": "^0.6.0", - "peberminta": "^0.9.0" + "leac": "^0.7.0", + "peberminta": "^0.10.0" } }, "path-exists": { @@ -12559,9 +12693,9 @@ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" }, "peberminta": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/peberminta/-/peberminta-0.9.0.tgz", - "integrity": "sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==" + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/peberminta/-/peberminta-0.10.0.tgz", + "integrity": "sha512-80B2AsU+I4Qdb0ZAPSfe9UwvGzwkM37IKIFEvdS3D/3Ndgv2bsuJ0bfG1+iEYO+l7Gfd4EUJmuRyq7efLgRMzQ==" }, "picocolors": { "version": "1.1.1", @@ -12739,9 +12873,9 @@ } }, "react-datepicker": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-8.10.0.tgz", - "integrity": "sha512-JIXuA+g+qP3c4MVJpx24o7n1gnv3WV/8A/D6964HucY1FlSEc30+ITPNUfbKZXYHl5rruCtxYCwi2lzn7gaz7g==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-9.1.0.tgz", + "integrity": "sha512-lOp+m5bc+ttgtB5MHEjwiVu4nlp4CvJLS/PG1OiOe5pmg9kV73pEqO8H0Geqvg2E8gjqTaL9eRhSe+ZpeKP3nA==", "requires": { "@floating-ui/react": "^0.27.15", "clsx": "^2.1.1", @@ -12749,15 +12883,16 @@ } }, "react-diff-viewer-continued": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/react-diff-viewer-continued/-/react-diff-viewer-continued-3.4.0.tgz", - "integrity": "sha512-kMZmUyb3Pv5L9vUtCfIGYsdOHs8mUojblGy1U1Sm0D7FhAOEsH9QhnngEIRo5hXWIPNGupNRJls1TJ6Eqx84eg==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/react-diff-viewer-continued/-/react-diff-viewer-continued-4.2.2.tgz", + "integrity": "sha512-8wT0/smXGox70oXJ7SZkTYF1p1Uh7jNGXhN7ykqrqPven0sZ+wM2c62SG3ZqORx5aW75te+WhmUWo0E4NyoSvg==", "requires": { - "@emotion/css": "^11.11.2", - "classnames": "^2.3.2", - "diff": "^5.1.0", - "memoize-one": "^6.0.0", - "prop-types": "^15.8.1" + "@emotion/css": "^11.13.5", + "@emotion/react": "^11.14.0", + "classnames": "^2.5.1", + "diff": "^9.0.0", + "js-yaml": "^4.1.1", + "memoize-one": "^6.0.0" } }, "react-dnd": { @@ -12790,9 +12925,9 @@ } }, "react-dropzone": { - "version": "14.3.8", - "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-14.3.8.tgz", - "integrity": "sha512-sBgODnq+lcA4P296DY4wacOZz3JFpD99fp+hb//iBO2HHnyeZU3FwWyXJ6salNpqQdsZrgMrotuko/BdJMV8Ug==", + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-15.0.0.tgz", + "integrity": "sha512-lGjYV/EoqEjEWPnmiSvH4v5IoIAwQM2W4Z1C0Q/Pw2xD0eVzKPS359BQTUMum+1fa0kH2nrKjuavmTPOGhpLPg==", "requires": { "attr-accept": "^2.2.4", "file-selector": "^2.1.0", @@ -13104,11 +13239,6 @@ "isarray": "^2.0.5" } }, - "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==" - }, "safe-regex-test": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", @@ -13208,18 +13338,18 @@ } }, "selderee": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/selderee/-/selderee-0.11.0.tgz", - "integrity": "sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/selderee/-/selderee-0.12.0.tgz", + "integrity": "sha512-b1YMh3+DHZp59DLna3qVwQ5iOla/nrI6mLBNW02XxU77M3046Df6VLkoaJyFz20VsGIG5kkp+FK0kg4K4HnUFw==", "requires": { - "parseley": "^0.12.0" + "parseley": "~0.13.1" } }, "semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "devOptional": true + "dev": true }, "serialize-javascript": { "version": "7.0.5", @@ -13631,7 +13761,7 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", - "devOptional": true, + "dev": true, "requires": { "escalade": "^3.2.0", "picocolors": "^1.1.1" @@ -13647,9 +13777,9 @@ } }, "use-debounce": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/use-debounce/-/use-debounce-10.0.3.tgz", - "integrity": "sha512-DxQSI9ZKso689WM1mjgGU3ozcxU1TJElBJ3X6S4SMzMNcm2lVH0AHmyXB+K7ewjz2BSUKJTDqTcwtSMRfB89dg==", + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/use-debounce/-/use-debounce-10.1.1.tgz", + "integrity": "sha512-kvds8BHR2k28cFsxW8k3nc/tRga2rs1RHYCqmmGqb90MEeE++oALwzh2COiuBLO1/QXiOuShXoSN2ZpWnMmvuQ==", "requires": {} }, "use-isomorphic-layout-effect": { diff --git a/package.json b/package.json index 5a98542ebc..a6a4045d99 100644 --- a/package.json +++ b/package.json @@ -25,26 +25,26 @@ "classnames": "^2.5.1", "date-fns": "^4.1.0", "font-awesome": "4.7.0", - "html-to-text": "^9.0.5", - "jquery": "^3.7.1", + "html-to-text": "^10.0.0", + "jquery": "^4.0.0", "js-cookie": "^3.0.5", "lodash": "^4.18.1", "popper.js": "^1.16.1", "prop-types": "^15.7.2", "react": "^18.3.1", "react-bootstrap": "0.33.1", - "react-datepicker": "8.10.0", - "react-diff-viewer-continued": "^3.4.0", + "react-datepicker": "9.1.0", + "react-diff-viewer-continued": "^4.2.2", "react-dnd": "^16.0.1", "react-dnd-html5-backend": "^16.0.1", "react-dom": "^18.3.1", - "react-dropzone": "^14.3.8", + "react-dropzone": "^15.0.0", "react-redux": "^8.1.3", "react-select": "^5.10.2", "redux": "^4.1.1", "redux-logger": "^3.0.6", "redux-thunk": "^2.3.0", - "use-debounce": "^10.0.0" + "use-debounce": "^10.1.1" }, "devDependencies": { "@babel/cli": "^7.28.0", From 7159131effaefd7fbe754a37513202d08c8e2177 Mon Sep 17 00:00:00 2001 From: David Wallace Date: Wed, 20 May 2026 16:08:47 +0200 Subject: [PATCH 077/131] Add ignoreKnownDependencyWarnings to build:dist for react-datepicker Signed-off-by: David Wallace --- webpack.config.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/webpack.config.js b/webpack.config.js index cbd1d6d81e..7294baaf58 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -164,6 +164,17 @@ const ignorePerformanceWarnings = { }, } +const ignoreKnownDependencyWarnings = { + ignoreWarnings: [ + { + // react-datepicker >=9.x triggers a webpack dynamic require warning + // upstream: https://github.com/Hacker0x01/react-datepicker/issues/6181 + module: /react-datepicker/, + message: /Critical dependency: the request of a dependency is an expression/ + } + ] +} + // combine config depending on the provided --mode command line option module.exports = (env, argv) => { return configList.map(config => { @@ -176,7 +187,7 @@ module.exports = (env, argv) => { case 'production': if (env && env['ignore-perf']) { // build:dist will ignore performance warnings but fails on other warnings - return merge(config, baseConfig, productionConfig, ignorePerformanceWarnings) + return merge(config, baseConfig, productionConfig, ignorePerformanceWarnings, ignoreKnownDependencyWarnings) } // build:prod return merge(config, baseConfig, productionConfig) From a8544863ab59bdeb4fe7472ed2327061e0008ab4 Mon Sep 17 00:00:00 2001 From: David Wallace Date: Thu, 21 May 2026 09:26:58 +0200 Subject: [PATCH 078/131] ci: pin dependabot jquery to max major 3 Signed-off-by: David Wallace --- .github/dependabot.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 6df43b0044..cb3b69f3bb 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -66,6 +66,8 @@ updates: update-types: ["version-update:semver-major"] # ignore major for react-redux - dependency-name: eslint update-types: ["version-update:semver-major"] # keep eslint 8.56.0 for now + - dependency-name: jquery + update-types: [ "version-update:semver-major" ] # keep jQuery 3.x until Bootstrap is upgraded groups: react: patterns: From d79b244192589fc035143ba5922314039e5a266b Mon Sep 17 00:00:00 2001 From: David Wallace Date: Thu, 21 May 2026 09:30:53 +0200 Subject: [PATCH 079/131] js: keep jquery 3.7.1 Signed-off-by: David Wallace --- package-lock.json | 15 ++++++++------- package.json | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6068940825..c08b64aafc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,7 @@ "date-fns": "^4.1.0", "font-awesome": "4.7.0", "html-to-text": "^10.0.0", - "jquery": "^4.0.0", + "jquery": "^3.7.1", "js-cookie": "^3.0.5", "lodash": "^4.18.1", "popper.js": "^1.16.1", @@ -5755,9 +5755,10 @@ } }, "node_modules/jquery": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jquery/-/jquery-4.0.0.tgz", - "integrity": "sha512-TXCHVR3Lb6TZdtw1l3RTLf8RBWVGexdxL6AC8/e0xZKEpBflBsjh9/8LXw+dkNFuOyW9B7iB3O1sP7hS0Kiacg==" + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz", + "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==", + "license": "MIT" }, "node_modules/js-cookie": { "version": "3.0.5", @@ -12253,9 +12254,9 @@ } }, "jquery": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jquery/-/jquery-4.0.0.tgz", - "integrity": "sha512-TXCHVR3Lb6TZdtw1l3RTLf8RBWVGexdxL6AC8/e0xZKEpBflBsjh9/8LXw+dkNFuOyW9B7iB3O1sP7hS0Kiacg==" + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz", + "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==" }, "js-cookie": { "version": "3.0.5", diff --git a/package.json b/package.json index a6a4045d99..af6aec0972 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "date-fns": "^4.1.0", "font-awesome": "4.7.0", "html-to-text": "^10.0.0", - "jquery": "^4.0.0", + "jquery": "^3.7.1", "js-cookie": "^3.0.5", "lodash": "^4.18.1", "popper.js": "^1.16.1", From ca011e069602eab862462e115e1c0b82191c0e9f Mon Sep 17 00:00:00 2001 From: David Wallace Date: Thu, 21 May 2026 11:01:46 +0200 Subject: [PATCH 080/131] Fix styling for react-diff rows at import Signed-off-by: David Wallace --- .../components/import/common/FieldRowDiffs.js | 22 ++++++++++ rdmo/management/assets/scss/management.scss | 42 +++++++++---------- 2 files changed, 43 insertions(+), 21 deletions(-) diff --git a/rdmo/management/assets/js/components/import/common/FieldRowDiffs.js b/rdmo/management/assets/js/components/import/common/FieldRowDiffs.js index 8bfe470552..10a726d2bf 100644 --- a/rdmo/management/assets/js/components/import/common/FieldRowDiffs.js +++ b/rdmo/management/assets/js/components/import/common/FieldRowDiffs.js @@ -27,8 +27,30 @@ const FieldRowDiffs = ({ element, field }) => { gutterBackground: '#fff', }, }, + diffContainer: { + minWidth: '100%', + maxWidth: '100%', + width: '100%', + }, + marker: { + width: '28px', + minWidth: '28px', + paddingLeft: 8, + paddingRight: 4, + textAlign: 'left', + overflow: 'visible', + pre: { + display: 'inline', + lineHeight: '1.6em', + overflow: 'visible', + }, + }, contentText: { backgroundColor: '#fff !important', + display: 'block', + lineHeight: '1.6em', + overflowWrap: 'anywhere', + wordBreak: 'break-word', }, } diff --git a/rdmo/management/assets/scss/management.scss b/rdmo/management/assets/scss/management.scss index 787c2b4b0b..d4bb573ec9 100644 --- a/rdmo/management/assets/scss/management.scss +++ b/rdmo/management/assets/scss/management.scss @@ -69,6 +69,10 @@ margin-left: auto; } } + + .field-diff { + overflow-x: auto; + } } &.panel-import-warnings, @@ -238,18 +242,10 @@ border-collapse: separate; // needed for border radius since this is a table tr { - td[class*="marker"] { - border-left: 1px solid #ccc; - } td[class*="content"] { border-right: 1px solid #ccc; } &:first-child { - td[class*="marker"] { - border-top: 1px solid #ccc; - border-left: 1px solid #ccc; - border-top-left-radius: 4px; - } td[class*="content"] { border-top: 1px solid #ccc; border-right: 1px solid #ccc; @@ -257,11 +253,6 @@ } } &:last-child { - td[class*="marker"] { - border-bottom: 1px solid #ccc; - border-left: 1px solid #ccc; - border-bottom-left-radius: 4px; - } td[class*="content"] { border-bottom: 1px solid #ccc; border-right: 1px solid #ccc; @@ -270,12 +261,25 @@ } } - // Target elements with classes containing "react-diff-", and "marker" - // Target elements with classes containing "react-diff-", and "content" - [class*="marker"], - [class*="content"] { + td[class*="marker"], + td[class*="content"] { padding: 8px 12px; vertical-align: top; + } + + td[class*="marker"] { + padding: 8px 0 8px 8px; + + pre { + padding: 0; + border-radius: 0; + font-size: 13px; + line-height: 18px; + background-color: transparent; + } + } + + td[class*="content"] { pre { padding: 5px 10px; border-radius: 4px; @@ -284,9 +288,5 @@ background-color: #fff; } } - - [class*="marker"] { - padding-right: 0; - } } } From 00b507e1495ad175a0bf035cb299f2d9cd3564f1 Mon Sep 17 00:00:00 2001 From: David Wallace Date: Thu, 21 May 2026 13:59:13 +0200 Subject: [PATCH 081/131] Fix styling of borders of react-diff pre at import Signed-off-by: David Wallace --- .../components/import/common/FieldRowDiffs.js | 3 +++ rdmo/management/assets/scss/management.scss | 21 +++++++------------ 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/rdmo/management/assets/js/components/import/common/FieldRowDiffs.js b/rdmo/management/assets/js/components/import/common/FieldRowDiffs.js index 10a726d2bf..93f166a0a3 100644 --- a/rdmo/management/assets/js/components/import/common/FieldRowDiffs.js +++ b/rdmo/management/assets/js/components/import/common/FieldRowDiffs.js @@ -41,6 +41,8 @@ const FieldRowDiffs = ({ element, field }) => { overflow: 'visible', pre: { display: 'inline', + border: 'none', + boxShadow: 'none', lineHeight: '1.6em', overflow: 'visible', }, @@ -61,6 +63,7 @@ const FieldRowDiffs = ({ element, field }) => { oldValue={oldVal} newValue={newVal} splitView={splitView} + hideSummary={true} hideLineNumbers={hideLineNumbers} // leftTitle={leftTitle} // rightTitle={rightTitle} diff --git a/rdmo/management/assets/scss/management.scss b/rdmo/management/assets/scss/management.scss index d4bb573ec9..b3d0d3a8a8 100644 --- a/rdmo/management/assets/scss/management.scss +++ b/rdmo/management/assets/scss/management.scss @@ -240,23 +240,14 @@ table[class*="react-diff-"][class*="diff-container"] { margin: 0; border-collapse: separate; // needed for border radius since this is a table + border: 1px solid #ccc; + border-radius: 4px; tr { - td[class*="content"] { - border-right: 1px solid #ccc; - } - &:first-child { - td[class*="content"] { - border-top: 1px solid #ccc; - border-right: 1px solid #ccc; - border-top-right-radius: 4px; - } - } - &:last-child { + &:not(:last-child) { + td[class*="marker"], td[class*="content"] { - border-bottom: 1px solid #ccc; - border-right: 1px solid #ccc; - border-bottom-right-radius: 4px; + border-bottom: 1px solid #ddd; } } } @@ -272,7 +263,9 @@ pre { padding: 0; + border: none; border-radius: 0; + box-shadow: none; font-size: 13px; line-height: 18px; background-color: transparent; From 18c7625be569833f3af0bd3f5224bc052b311305 Mon Sep 17 00:00:00 2001 From: David Wallace Date: Thu, 21 May 2026 15:07:00 +0200 Subject: [PATCH 082/131] js: re-make package-lock.json with npm install" Signed-off-by: David Wallace --- package-lock.json | 9139 ++++++++++----------------------------------- 1 file changed, 2048 insertions(+), 7091 deletions(-) diff --git a/package-lock.json b/package-lock.json index c08b64aafc..ddb843eb76 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,7 +1,6 @@ { "name": "rdmo", - "version": "1.8.0", - "lockfileVersion": 2, + "lockfileVersion": 3, "requires": true, "packages": { "": { @@ -59,33 +58,12 @@ "npm": "10.x" } }, - "node_modules/@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", - "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.1.0", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@babel/cli": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.28.0.tgz", - "integrity": "sha512-CYrZG7FagtE8ReKDBfItxnrEBf2khq2eTMnPuqO8UVN0wzhp1eMX1wfda8b1a32l2aqYLwRRIOGNovm8FVzmMw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.28.6.tgz", + "integrity": "sha512-6EUNcuBbNkj08Oj4gAZ+BUU8yLCgKzgVX4gaTh09Ya2C8ICM4P+G30g4m3akRxSYAp3A/gnWchrNst7px4/nUQ==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "^0.3.28", "commander": "^6.2.0", @@ -110,16 +88,11 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/cli/node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true - }, "node_modules/@babel/code-frame": { "version": "7.29.0", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", @@ -133,7 +106,8 @@ "version": "7.29.3", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.3.tgz", "integrity": "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==", - "devOptional": true, + "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -142,7 +116,8 @@ "version": "7.29.0", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", - "devOptional": true, + "dev": true, + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -168,16 +143,11 @@ "url": "https://opencollective.com/babel" } }, - "node_modules/@babel/core/node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true - }, "node_modules/@babel/generator": { "version": "7.29.1", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "license": "MIT", "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", @@ -194,6 +164,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/types": "^7.27.3" }, @@ -205,7 +176,8 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", - "devOptional": true, + "dev": true, + "license": "MIT", "dependencies": { "@babel/compat-data": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", @@ -217,26 +189,12 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, "node_modules/@babel/helper-create-class-features-plugin": { "version": "7.29.3", "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.29.3.tgz", "integrity": "sha512-RpLYy2sb51oNLjuu1iD3bwBqCBWUzjO0ocp+iaCP/lJtb2CPLcnC2Fftw+4sAzaMELGeWTgExSKADbdo0GFVzA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-member-expression-to-functions": "^7.28.5", @@ -258,6 +216,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.28.5.tgz", "integrity": "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", @@ -275,6 +234,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.8.tgz", "integrity": "sha512-47UwBLPpQi1NoWzLuHNjRoHlYXMwIJoBf7MFou6viC/sIHWYygpvr0B6IAyh5sBdA2nr2LPIRww8lfaUVQINBA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-plugin-utils": "^7.28.6", @@ -290,6 +250,7 @@ "version": "7.28.0", "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -299,6 +260,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz", "integrity": "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" @@ -311,6 +273,7 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "license": "MIT", "dependencies": { "@babel/traverse": "^7.28.6", "@babel/types": "^7.28.6" @@ -324,6 +287,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.28.6", "@babel/helper-validator-identifier": "^7.28.5", @@ -354,6 +318,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -363,6 +328,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz", "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-wrap-function": "^7.27.1", @@ -380,6 +346,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.28.6.tgz", "integrity": "sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", @@ -419,6 +386,7 @@ "version": "7.28.5", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -438,6 +406,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.28.6.tgz", "integrity": "sha512-z+PwLziMNBeSQJonizz2AGnndLsP2DeGHIxDAn+wdHOGuo4Fo1x1HBPPXeE9TAOPHNNWQKCSlA2VZyYyyibDnQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/template": "^7.28.6", "@babel/traverse": "^7.28.6", @@ -451,7 +420,8 @@ "version": "7.29.2", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", - "devOptional": true, + "dev": true, + "license": "MIT", "dependencies": { "@babel/template": "^7.28.6", "@babel/types": "^7.29.0" @@ -464,6 +434,7 @@ "version": "7.29.3", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz", "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==", + "license": "MIT", "dependencies": { "@babel/types": "^7.29.0" }, @@ -479,6 +450,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.28.5.tgz", "integrity": "sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/traverse": "^7.28.5" @@ -527,6 +499,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array/-/plugin-bugfix-safari-rest-destructuring-rhs-array-7.29.3.tgz", "integrity": "sha512-SRS46DFR4HqzUzCVgi90/xMoL+zeBDBvWdKYXSEzh79kXswNFEglUpMKxR04//dPqwYXWUBJ3mpUd933ru9Kmg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.28.6", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" @@ -561,6 +534,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.6.tgz", "integrity": "sha512-a0aBScVTlNaiUe35UtfxAN7A/tehvvG4/ByO6+46VPKTRSlfnAFsgKy0FUh+qAkQrDTmhDkT+IBOKlOoMUxQ0g==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.28.6", "@babel/traverse": "^7.28.6" @@ -577,6 +551,7 @@ "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==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" }, @@ -589,6 +564,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.28.6.tgz", "integrity": "sha512-pSJUpFHdx9z5nqTSirOCMtYVP2wFgoWhP0p3g8ONK/4IHhLIBd0B9NYqAvIUAhq+OkhO4VM1tENCt0cjlsNShw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" }, @@ -604,6 +580,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz", "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" }, @@ -615,13 +592,13 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", - "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", + "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -635,6 +612,7 @@ "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==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.18.6", "@babel/helper-plugin-utils": "^7.18.6" @@ -667,6 +645,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.29.0.tgz", "integrity": "sha512-va0VdWro4zlBr2JsXC+ofCPB2iG12wPtVGTWFx2WLDOM3nYQZZIGP82qku2eW/JR83sD+k2k+CsNtyEbUqhU6w==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.28.6", "@babel/helper-remap-async-to-generator": "^7.27.1", @@ -684,6 +663,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.28.6.tgz", "integrity": "sha512-ilTRcmbuXjsMmcZ3HASTe4caH5Tpo93PkTxF9oG2VZsSWsahydmcEHhix9Ik122RcTnZnUzPbmux4wh1swfv7g==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.28.6", "@babel/helper-plugin-utils": "^7.28.6", @@ -717,6 +697,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.6.tgz", "integrity": "sha512-tt/7wOtBmwHPNMPu7ax4pdPz6shjFrmHDghvNC+FG9Qvj7D6mJcoRQIF5dy4njmxR941l6rgtvfSB2zX3VlUIw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" }, @@ -732,6 +713,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.28.6.tgz", "integrity": "sha512-dY2wS3I2G7D697VHndN91TJr8/AAfXQNt5ynCTI/MpxMsSzHp+52uNivYT5wCPax3whc47DR8Ba7cmlQMg24bw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-create-class-features-plugin": "^7.28.6", "@babel/helper-plugin-utils": "^7.28.6" @@ -748,6 +730,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.6.tgz", "integrity": "sha512-rfQ++ghVwTWTqQ7w8qyDxL1XGihjBss4CmTgGRCTAC9RIbhVpyp4fOeZtta0Lbf+dTNIVJer6ych2ibHwkZqsQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-create-class-features-plugin": "^7.28.6", "@babel/helper-plugin-utils": "^7.28.6" @@ -764,6 +747,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.6.tgz", "integrity": "sha512-EF5KONAqC5zAqT783iMGuM2ZtmEBy+mJMOKl2BCvPZ2lVrwvXnB6o+OBWCS+CoeCCpVRF2sA2RBKUxvT8tQT5Q==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-compilation-targets": "^7.28.6", @@ -784,6 +768,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.28.6.tgz", "integrity": "sha512-bcc3k0ijhHbc2lEfpFHgx7eYw9KNXqOerKWfzbxEHUGKnS3sz9C4CNL9OiFN1297bDNfUiSO7DaLzbvHQQQ1BQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.28.6", "@babel/template": "^7.28.6" @@ -800,6 +785,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.5.tgz", "integrity": "sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/traverse": "^7.28.5" @@ -816,6 +802,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.28.6.tgz", "integrity": "sha512-SljjowuNKB7q5Oayv4FoPzeB74g3QgLt8IVJw9ADvWy3QnUb/01aw8I4AVv8wYnPvQz2GDDZ/g3GhcNyDBI4Bg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.28.5", "@babel/helper-plugin-utils": "^7.28.6" @@ -848,6 +835,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.29.0.tgz", "integrity": "sha512-zBPcW2lFGxdiD8PUnPwJjag2J9otbcLQzvbiOzDxpYXyCuYX9agOwMPGn1prVH0a4qzhCKu24rlH4c1f7yA8rw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.28.5", "@babel/helper-plugin-utils": "^7.28.6" @@ -880,6 +868,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.6.tgz", "integrity": "sha512-Iao5Konzx2b6g7EPqTy40UZbcdXE126tTxVFr/nAIj+WItNxjKSYTEw3RC+A2/ZetmdJsgueL1KhaMCQHkLPIg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.28.6", "@babel/plugin-transform-destructuring": "^7.28.5" @@ -896,6 +885,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.28.6.tgz", "integrity": "sha512-WitabqiGjV/vJ0aPOLSFfNY1u9U3R7W36B03r5I2KoNix+a3sOhJ3pKFB3R5It9/UiK78NiO0KE9P21cMhlPkw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" }, @@ -962,6 +952,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.28.6.tgz", "integrity": "sha512-Nr+hEN+0geQkzhbdgQVPoqr47lZbm+5fCUmO70722xJZd0Mvb59+33QLImGj6F+DkK3xgDi1YVysP8whD6FQAw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" }, @@ -993,6 +984,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.28.6.tgz", "integrity": "sha512-+anKKair6gpi8VsM/95kmomGNMD0eLz1NQ8+Pfw5sAwWH9fGYXT50E55ZpV0pHUHWf6IUTWPM+f/7AAff+wr9A==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" }, @@ -1041,6 +1033,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.28.6.tgz", "integrity": "sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-module-transforms": "^7.28.6", "@babel/helper-plugin-utils": "^7.28.6" @@ -1057,6 +1050,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.4.tgz", "integrity": "sha512-N7QmZ0xRZfjHOfZeQLJjwgX2zS9pdGHSVl/cjSGlo4dXMqvurfxXDMKY4RqEKzPozV78VMcd0lxyG13mlbKc4w==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-module-transforms": "^7.28.6", "@babel/helper-plugin-utils": "^7.28.6", @@ -1092,6 +1086,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.29.0.tgz", "integrity": "sha512-1CZQA5KNAD6ZYQLPw7oi5ewtDNxH/2vuCh+6SmvgDfhumForvs8a1o9n0UrEoBD8HU4djO2yWngTQlXl1NDVEQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.28.5", "@babel/helper-plugin-utils": "^7.28.6" @@ -1124,6 +1119,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.28.6.tgz", "integrity": "sha512-3wKbRgmzYbw24mDJXT7N+ADXw8BC/imU9yo9c9X9NKaLF1fW+e5H1U5QjMUBe4Qo4Ox/o++IyUkl1sVCLgevKg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" }, @@ -1139,6 +1135,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.28.6.tgz", "integrity": "sha512-SJR8hPynj8outz+SlStQSwvziMN4+Bq99it4tMIf5/Caq+3iOc0JtKyse8puvyXkk3eFRIA5ID/XfunGgO5i6w==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" }, @@ -1154,6 +1151,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.6.tgz", "integrity": "sha512-5rh+JR4JBC4pGkXLAcYdLHZjXudVxWMXbB6u6+E9lRL5TrGVbHt1TjxGbZ8CkmYw9zjkB7jutzOROArsqtncEA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-plugin-utils": "^7.28.6", @@ -1190,6 +1188,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.28.6.tgz", "integrity": "sha512-R8ja/Pyrv0OGAvAXQhSTmWyPJPml+0TMqXlO5w+AsMEiwb2fg3WkOvob7UxFSL3OIttFSGSRFKQsOhJ/X6HQdQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" }, @@ -1205,6 +1204,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.28.6.tgz", "integrity": "sha512-A4zobikRGJTsX9uqVFdafzGkqD30t26ck2LmOzAuLL8b2x6k3TIqRiT2xVvA9fNmFeTX484VpsdgmKNA0bS23w==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.28.6", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" @@ -1221,6 +1221,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz", "integrity": "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -1236,6 +1237,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.28.6.tgz", "integrity": "sha512-piiuapX9CRv7+0st8lmuUlRSmX6mBcVeNQ1b4AYzJxfCMuBfB0vBXDiGSmm03pKJw1v6cZ8KSeM+oUnM6yAExg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-create-class-features-plugin": "^7.28.6", "@babel/helper-plugin-utils": "^7.28.6" @@ -1252,6 +1254,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.28.6.tgz", "integrity": "sha512-b97jvNSOb5+ehyQmBpmhOCiUC5oVK4PMnpRvO7+ymFBoqYjeDHIU9jnrNUuwHOiL9RpGDoKBpSViarV+BU+eVA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-create-class-features-plugin": "^7.28.6", @@ -1285,6 +1288,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.28.0.tgz", "integrity": "sha512-D6Eujc2zMxKjfa4Zxl4GHMsmhKKZ9VpcqIchJLvwTxad9zWIYulwYItBovpDOoNLISpcZSXoDJ5gaGbQUDqViA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -1296,17 +1300,17 @@ } }, "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.27.1.tgz", - "integrity": "sha512-2KH4LWGSrJIkVf5tSiBFYuXDAoWRq2MMwgivCf+93dd0GQi8RXLjKA/0EvRnVV5G0hrHczsquXuD01L8s6dmBw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.28.6.tgz", + "integrity": "sha512-61bxqhiRfAACulXSLd/GxqmAedUSrRZIu/cbaT18T1CetkTmtDN15it7i80ru4DVqRK1WMxQhXs+Lf9kajm5Ow==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/plugin-syntax-jsx": "^7.27.1", - "@babel/types": "^7.27.1" + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/plugin-syntax-jsx": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1353,6 +1357,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.29.0.tgz", "integrity": "sha512-FijqlqMA7DmRdg/aINBSs04y8XNTYw/lr1gJ2WsmBnnaNw1iS43EPkJW+zK7z65auG3AWRFXWj+NcTQwYptUog==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" }, @@ -1368,6 +1373,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.28.6.tgz", "integrity": "sha512-QGWAepm9qxpaIs7UM9FvUSnCGlb8Ua1RhyM4/veAxLwt3gMat/LSGrZixyuj4I6+Kn9iwvqCyPTtbdxanYoWYg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.28.5", "@babel/helper-plugin-utils": "^7.28.6" @@ -1416,6 +1422,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.28.6.tgz", "integrity": "sha512-9U4QObUC0FtJl05AsUcodau/RWDytrU6uKgkxu09mLR9HLDAtUMoPuuskm5huQsoktmsYpI+bGmq+iapDcriKA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.28.6", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" @@ -1496,6 +1503,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.28.6.tgz", "integrity": "sha512-4Wlbdl/sIZjzi/8St0evF0gEZrgOswVO6aOzqxh1kDZOl9WmLrHq2HtGhnOJZmHZYKP8WZ1MDLCt5DAWwRo57A==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.28.5", "@babel/helper-plugin-utils": "^7.28.6" @@ -1529,6 +1537,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.28.6.tgz", "integrity": "sha512-/wHc/paTUmsDYN7SZkpWxogTOBNnlx7nBQYfy6JJlCT7G3mVhltk3e++N7zV0XfgGsrqBxd4rJQt9H16I21Y1Q==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.28.5", "@babel/helper-plugin-utils": "^7.28.6" @@ -1545,6 +1554,7 @@ "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.29.5.tgz", "integrity": "sha512-/69t2aEzGKHD76DyLbHysF/QH2LJOB8iFnYO37unDTKBTubzcMRv0f3H5EiN1Q6ajOd/eB7dAInF0qdFVS06kA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/compat-data": "^7.29.3", "@babel/helper-compilation-targets": "^7.28.6", @@ -1630,6 +1640,7 @@ "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==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@babel/types": "^7.4.4", @@ -1644,6 +1655,7 @@ "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.28.5.tgz", "integrity": "sha512-Z3J8vhRq7CeLjdC58jLv4lnZ5RKFUJWqH5emvxmv9Hv3BD1T9R/Im713R4MTKwvFaV74ejZ3sM01LyEKk4ugNQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", @@ -1660,25 +1672,21 @@ } }, "node_modules/@babel/runtime": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz", - "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==", + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/runtime-corejs2": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.26.10.tgz", - "integrity": "sha512-JfoPiD7f/vvd/PaOfu5cr9CyzwDMPg4T0nX3MQr6IgTq49DhjvUcmjmjA7j6+xih1Evq+QKZnge1SoIlYozv/Q==", + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.29.2.tgz", + "integrity": "sha512-+FqVkbqWaDleqS9fgzFypApKoPvmGFgk5X2lGXbL9wgz6tf88qt2HEUuEn9E3yBeLt7p8pIgODbJ5icVRALKhQ==", "license": "MIT", "dependencies": { - "core-js": "^2.6.12", - "regenerator-runtime": "^0.14.0" + "core-js": "^2.6.12" }, "engines": { "node": ">=6.9.0" @@ -1688,6 +1696,7 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/parser": "^7.28.6", @@ -1701,6 +1710,7 @@ "version": "7.29.0", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -1718,6 +1728,7 @@ "version": "7.29.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" @@ -1727,64 +1738,64 @@ } }, "node_modules/@codemirror/autocomplete": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.4.2.tgz", - "integrity": "sha512-8WE2xp+D0MpWEv5lZ6zPW1/tf4AGb358T5GWYiKEuCP8MvFfT3tH2mIF9Y2yr2e3KbHuSvsVhosiEyqCpiJhZQ==", + "version": "6.20.2", + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.20.2.tgz", + "integrity": "sha512-G5FPkgIiLjOgZMjqVjvuKQ1rGPtHogLldJr33eFJdVLtmwY+giGrlv/ewljLz6b9BSQLkjxuwBc6g6omDM+YxQ==", + "license": "MIT", "dependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.6.0", - "@lezer/common": "^1.0.0" - }, - "peerDependencies": { - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", + "@codemirror/view": "^6.17.0", "@lezer/common": "^1.0.0" } }, "node_modules/@codemirror/commands": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.2.1.tgz", - "integrity": "sha512-FFiNKGuHA5O8uC6IJE5apI5rT9gyjlw4whqy4vlcX0wE/myxL6P1s0upwDhY4HtMWLOwzwsp0ap3bjdQhvfDOA==", + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.10.3.tgz", + "integrity": "sha512-JFRiqhKu+bvSkDLI+rUhJwSxQxYb759W5GBezE8Uc8mHLqC9aV/9aTC7yJSqCtB3F00pylrLCwnyS91Ap5ej4Q==", + "license": "MIT", "dependencies": { "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.2.0", - "@codemirror/view": "^6.0.0", - "@lezer/common": "^1.0.0" + "@codemirror/state": "^6.6.0", + "@codemirror/view": "^6.27.0", + "@lezer/common": "^1.1.0" } }, "node_modules/@codemirror/lang-css": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@codemirror/lang-css/-/lang-css-6.0.2.tgz", - "integrity": "sha512-4V4zmUOl2Glx0GWw0HiO1oGD4zvMlIQ3zx5hXOE6ipCjhohig2bhWRAasrZylH9pRNTcl1VMa59Lsl8lZWlTzw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/@codemirror/lang-css/-/lang-css-6.3.1.tgz", + "integrity": "sha512-kr5fwBGiGtmz6l0LSJIbno9QrifNMUusivHbnA1H6Dmqy4HZFte3UAICix1VuKo0lMPKQr2rqB+0BkKi/S3Ejg==", + "license": "MIT", "dependencies": { "@codemirror/autocomplete": "^6.0.0", "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.0.0", - "@lezer/css": "^1.0.0" + "@lezer/common": "^1.0.2", + "@lezer/css": "^1.1.7" } }, "node_modules/@codemirror/lang-html": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.2.tgz", - "integrity": "sha512-bqCBASkteKySwtIbiV/WCtGnn/khLRbbiV5TE+d9S9eQJD7BA4c5dTRm2b3bVmSpilff5EYxvB4PQaZzM/7cNw==", + "version": "6.4.11", + "resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.11.tgz", + "integrity": "sha512-9NsXp7Nwp891pQchI7gPdTwBuSuT3K65NGTHWHNJ55HjYcHLllr0rbIZNdOzas9ztc1EUVBlHou85FFZS4BNnw==", + "license": "MIT", "dependencies": { "@codemirror/autocomplete": "^6.0.0", "@codemirror/lang-css": "^6.0.0", "@codemirror/lang-javascript": "^6.0.0", "@codemirror/language": "^6.4.0", "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.2.2", + "@codemirror/view": "^6.17.0", "@lezer/common": "^1.0.0", "@lezer/css": "^1.1.0", - "@lezer/html": "^1.3.0" + "@lezer/html": "^1.3.12" } }, "node_modules/@codemirror/lang-javascript": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.2.tgz", - "integrity": "sha512-VGQfY+FCc285AhWuwjYxQyUQcYurWlxdKYT4bqwr3Twnd5wP5WSeu52t4tvvuWmljT4EmgEgZCqSieokhtY8hg==", + "version": "6.2.5", + "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.5.tgz", + "integrity": "sha512-zD4e5mS+50htS7F+TYjBPsiIFGanfVqg4HyUz6WNFikgOPf2BgKlx+TQedI1w6n/IqRBVBbBWmGFdLB/7uxO4A==", + "license": "MIT", "dependencies": { "@codemirror/autocomplete": "^6.0.0", "@codemirror/language": "^6.6.0", @@ -1796,47 +1807,55 @@ } }, "node_modules/@codemirror/language": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.6.0.tgz", - "integrity": "sha512-cwUd6lzt3MfNYOobdjf14ZkLbJcnv4WtndYaoBkbor/vF+rCNguMPK0IRtvZJG4dsWiaWPcK8x1VijhvSxnstg==", + "version": "6.12.3", + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.12.3.tgz", + "integrity": "sha512-QwCZW6Tt1siP37Jet9Tb02Zs81TQt6qQrZR2H+eGMcFsL1zMrk2/b9CLC7/9ieP1fjIUMgviLWMmgiHoJrj+ZA==", + "license": "MIT", "dependencies": { "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", - "@lezer/common": "^1.0.0", + "@codemirror/view": "^6.23.0", + "@lezer/common": "^1.5.0", "@lezer/highlight": "^1.0.0", "@lezer/lr": "^1.0.0", "style-mod": "^4.0.0" } }, "node_modules/@codemirror/lint": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.1.1.tgz", - "integrity": "sha512-e+M543x0NVHGayNHQzLP4XByJsvbu/ojY6+0VF2Y4Uu66Rt1nADuxNflZwECLf7gS009smIsptSUa6bUj/U/rw==", + "version": "6.9.6", + "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.9.6.tgz", + "integrity": "sha512-6Kp7r6XfCi/D/5sdXieMfg9pJU1bUEx96WITuLU6ESaKizCz0QHFMjY/TaFSbigDdEAIgi93itLBIUETP4oK+A==", + "license": "MIT", "dependencies": { "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", + "@codemirror/view": "^6.42.0", "crelt": "^1.0.5" } }, "node_modules/@codemirror/search": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.2.3.tgz", - "integrity": "sha512-V9n9233lopQhB1dyjsBK2Wc1i+8hcCqxl1wQ46c5HWWLePoe4FluV3TGHoZ04rBRlGjNyz9DTmpJErig8UE4jw==", + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.7.0.tgz", + "integrity": "sha512-ZvGm99wc/s2cITtMT15LFdn8aH/aS+V+DqyGq/N5ZlV5vWtH+nILvC2nw0zX7ByNoHHDZ2IxxdW38O0tc5nVHg==", + "license": "MIT", "dependencies": { "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", + "@codemirror/view": "^6.37.0", "crelt": "^1.0.5" } }, "node_modules/@codemirror/state": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.4.1.tgz", - "integrity": "sha512-QkEyUiLhsJoZkbumGZlswmAhA7CBU02Wrz7zvH4SrcifbsqwlXShVXg65f3v/ts57W3dqyamEriMhij1Z3Zz4A==" + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.6.0.tgz", + "integrity": "sha512-4nbvra5R5EtiCzr9BTHiTLc+MLXK2QGiAVYMyi8PkQd3SR+6ixar/Q/01Fa21TBIDOZXgeWV4WppsQolSreAPQ==", + "license": "MIT", + "dependencies": { + "@marijn/find-cluster-break": "^1.0.0" + } }, "node_modules/@codemirror/theme-one-dark": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.1.tgz", - "integrity": "sha512-+CfzmScfJuD6uDF5bHJkAjWTQ2QAAHxODCPxUEgcImDYcJLT+4l5vLnBHmDVv46kCC5uUJGMrBJct2Z6JbvqyQ==", + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.3.tgz", + "integrity": "sha512-NzBdIvEJmx6fjeremiGp3t/okrLPYT0d9orIc7AFun8oZcRk58aejkqhv6spnz4MLAevrKNPMQYXEWMg4s+sKA==", + "license": "MIT", "dependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.0.0", @@ -1845,11 +1864,13 @@ } }, "node_modules/@codemirror/view": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.24.1.tgz", - "integrity": "sha512-sBfP4rniPBRQzNakwuQEqjEuiJDWJyF2kqLLqij4WXRoVwPPJfjx966Eq3F7+OPQxDtMt/Q9MWLoZLWjeveBlg==", + "version": "6.43.0", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.43.0.tgz", + "integrity": "sha512-V7ZCLQO3Jus9hzh2jVCCPW3mO4IBMr43O37PqSUYautJSnnJF41YlgLw21x0fLJTYvJ+Vkm6Gp+qKGH9pltgXA==", + "license": "MIT", "dependencies": { - "@codemirror/state": "^6.4.0", + "@codemirror/state": "^6.6.0", + "crelt": "^1.0.6", "style-mod": "^4.1.0", "w3c-keyname": "^2.2.4" } @@ -1859,6 +1880,7 @@ "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-1.1.0.tgz", "integrity": "sha512-Xc3VhU02wqZ1HvHRJUwL09HkZSTvidqY5Ya0NXBSYOxAp+Ln9dcJr9fySI+CkONzP3PekQo9WdzCv0PGER/mOA==", "dev": true, + "license": "MIT", "engines": { "node": ">=14.17.0" } @@ -1867,6 +1889,7 @@ "version": "11.13.5", "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==", + "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.16.7", "@babel/runtime": "^7.18.3", @@ -1881,18 +1904,17 @@ "stylis": "4.2.0" } }, - "node_modules/@emotion/babel-plugin/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==", - "engines": { - "node": ">=0.10.0" - } + "node_modules/@emotion/babel-plugin/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/@emotion/cache": { "version": "11.14.0", "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==", + "license": "MIT", "dependencies": { "@emotion/memoize": "^0.9.0", "@emotion/sheet": "^1.4.0", @@ -1905,6 +1927,7 @@ "version": "11.13.5", "resolved": "https://registry.npmjs.org/@emotion/css/-/css-11.13.5.tgz", "integrity": "sha512-wQdD0Xhkn3Qy2VNcIzbLP9MR8TafI0MJb7BEAXKp+w4+XqErksWR4OXomuDzPsN4InLdGhVe6EYcn2ZIUCpB8w==", + "license": "MIT", "dependencies": { "@emotion/babel-plugin": "^11.13.5", "@emotion/cache": "^11.13.5", @@ -1916,17 +1939,20 @@ "node_modules/@emotion/hash": { "version": "0.9.2", "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", - "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==" + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", + "license": "MIT" }, "node_modules/@emotion/memoize": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", - "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==" + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", + "license": "MIT" }, "node_modules/@emotion/react": { "version": "11.14.0", "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", @@ -1950,6 +1976,7 @@ "version": "1.3.3", "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==", + "license": "MIT", "dependencies": { "@emotion/hash": "^0.9.2", "@emotion/memoize": "^0.9.0", @@ -1961,17 +1988,20 @@ "node_modules/@emotion/sheet": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", - "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==" + "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==", + "license": "MIT" }, "node_modules/@emotion/unitless": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", - "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==" + "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==", + "license": "MIT" }, "node_modules/@emotion/use-insertion-effect-with-fallbacks": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz", "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==", + "license": "MIT", "peerDependencies": { "react": ">=16.8.0" } @@ -1979,18 +2009,21 @@ "node_modules/@emotion/utils": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", - "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==" + "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==", + "license": "MIT" }, "node_modules/@emotion/weak-memoize": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", - "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==" + "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", + "license": "MIT" }, "node_modules/@eslint-community/eslint-utils": { "version": "4.9.1", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", "dev": true, + "license": "MIT", "dependencies": { "eslint-visitor-keys": "^3.4.3" }, @@ -2009,6 +2042,7 @@ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", "dev": true, + "license": "MIT", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } @@ -2048,30 +2082,32 @@ } }, "node_modules/@floating-ui/core": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz", - "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==", + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.5.tgz", + "integrity": "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==", + "license": "MIT", "dependencies": { - "@floating-ui/utils": "^0.2.10" + "@floating-ui/utils": "^0.2.11" } }, "node_modules/@floating-ui/dom": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz", - "integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==", + "version": "1.7.6", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.6.tgz", + "integrity": "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==", + "license": "MIT", "dependencies": { - "@floating-ui/core": "^1.7.3", - "@floating-ui/utils": "^0.2.10" + "@floating-ui/core": "^1.7.5", + "@floating-ui/utils": "^0.2.11" } }, "node_modules/@floating-ui/react": { - "version": "0.27.16", - "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.27.16.tgz", - "integrity": "sha512-9O8N4SeG2z++TSM8QA/KTeKFBVCNEz/AGS7gWPJf6KFRzmRWixFRnCnkPHRDwSVZW6QPDO6uT0P2SpWNKCc9/g==", + "version": "0.27.19", + "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.27.19.tgz", + "integrity": "sha512-31B8h5mm8YxotlE7/AU/PhNAl8eWxAmjL/v2QOxroDNkTFLk3Uu82u63N3b6TXa4EGJeeZLVcd/9AlNlVqzeog==", "license": "MIT", "dependencies": { - "@floating-ui/react-dom": "^2.1.6", - "@floating-ui/utils": "^0.2.10", + "@floating-ui/react-dom": "^2.1.8", + "@floating-ui/utils": "^0.2.11", "tabbable": "^6.0.0" }, "peerDependencies": { @@ -2080,12 +2116,12 @@ } }, "node_modules/@floating-ui/react-dom": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.6.tgz", - "integrity": "sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.8.tgz", + "integrity": "sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==", "license": "MIT", "dependencies": { - "@floating-ui/dom": "^1.7.4" + "@floating-ui/dom": "^1.7.6" }, "peerDependencies": { "react": ">=16.8.0", @@ -2093,9 +2129,10 @@ } }, "node_modules/@floating-ui/utils": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", - "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==" + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.11.tgz", + "integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==", + "license": "MIT" }, "node_modules/@humanwhocodes/config-array": { "version": "0.13.0", @@ -2118,6 +2155,7 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=12.22" }, @@ -2138,6 +2176,7 @@ "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" @@ -2147,25 +2186,18 @@ "version": "2.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "devOptional": true, + "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, + "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": ">=6.0.0" } @@ -2175,79 +2207,97 @@ "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25" } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", - "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==" + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.29", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", - "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "node_modules/@lezer/common": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.0.2.tgz", - "integrity": "sha512-SVgiGtMnMnW3ActR8SXgsDhw7a0w0ChHSYAyAUxxrOiJ1OqYWEKk/xJd84tTSPo1mo6DXLObAJALNnd0Hrv7Ng==" + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.5.2.tgz", + "integrity": "sha512-sxQE460fPZyU3sdc8lafxiPwJHBzZRy/udNFynGQky1SePYBdhkBl1kOagA9uT3pxR8K09bOrmTUqA9wb/PjSQ==", + "license": "MIT" }, "node_modules/@lezer/css": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.1.1.tgz", - "integrity": "sha512-mSjx+unLLapEqdOYDejnGBokB5+AiJKZVclmud0MKQOKx3DLJ5b5VTCstgDDknR6iIV4gVrN6euzsCnj0A2gQA==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.3.3.tgz", + "integrity": "sha512-RzBo8r+/6QJeow7aPHIpGVIH59xTcJXp399820gZoMo9noQDRVpJLheIBUicYwKcsbOYoBRoLZlf2720dG/4Tg==", + "license": "MIT", "dependencies": { + "@lezer/common": "^1.2.0", "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0" + "@lezer/lr": "^1.3.0" } }, "node_modules/@lezer/highlight": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.1.3.tgz", - "integrity": "sha512-3vLKLPThO4td43lYRBygmMY18JN3CPh9w+XS2j8WC30vR4yZeFG4z1iFe4jXE43NtGqe//zHW5q8ENLlHvz9gw==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.3.tgz", + "integrity": "sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==", + "license": "MIT", "dependencies": { - "@lezer/common": "^1.0.0" + "@lezer/common": "^1.3.0" } }, "node_modules/@lezer/html": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.3.tgz", - "integrity": "sha512-04Fyvu66DjV2EjhDIG1kfDdktn5Pfw56SXPrzKNQH5B2m7BDfc6bDsz+ZJG8dLS3kIPEKbyyq1Sm2/kjeG0+AA==", + "version": "1.3.13", + "resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.13.tgz", + "integrity": "sha512-oI7n6NJml729m7pjm9lvLvmXbdoMoi2f+1pwSDJkl9d68zGr7a9Btz8NdHTGQZtW2DA25ybeuv/SyDb9D5tseg==", + "license": "MIT", "dependencies": { - "@lezer/common": "^1.0.0", + "@lezer/common": "^1.2.0", "@lezer/highlight": "^1.0.0", "@lezer/lr": "^1.0.0" } }, "node_modules/@lezer/javascript": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.4.1.tgz", - "integrity": "sha512-Hqx36DJeYhKtdpc7wBYPR0XF56ZzIp0IkMO/zNNj80xcaFOV4Oj/P7TQc/8k2TxNhzl7tV5tXS8ZOCPbT4L3nA==", + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.5.4.tgz", + "integrity": "sha512-vvYx3MhWqeZtGPwDStM2dwgljd5smolYD2lR2UyFcHfxbBQebqx8yjmFmxtJ/E6nN6u1D9srOiVWm3Rb4tmcUA==", + "license": "MIT", "dependencies": { + "@lezer/common": "^1.2.0", "@lezer/highlight": "^1.1.3", "@lezer/lr": "^1.3.0" } }, "node_modules/@lezer/lr": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.3.3.tgz", - "integrity": "sha512-JPQe3mwJlzEVqy67iQiiGozhcngbO8QBgpqZM6oL1Wj/dXckrEexpBLeFkq0edtW5IqnPRFxA24BHJni8Js69w==", + "version": "1.4.10", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.10.tgz", + "integrity": "sha512-rnCpTIBafOx4mRp43xOxDJbFipJm/c0cia/V5TiGlhmMa+wsSdoGmUN3w5Bqrks/09Q/D4tNAmWaT8p6NRi77A==", + "license": "MIT", "dependencies": { "@lezer/common": "^1.0.0" } }, + "node_modules/@marijn/find-cluster-break": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz", + "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==", + "license": "MIT" + }, "node_modules/@nicolo-ribaudo/chokidar-2": { "version": "2.1.8-no-fsevents.3", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz", "integrity": "sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ==", "dev": true, + "license": "MIT", "optional": true }, "node_modules/@nodelib/fs.scandir": { @@ -2289,17 +2339,18 @@ } }, "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==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.6.tgz", + "integrity": "sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==", "dev": true, "hasInstallScript": true, + "license": "MIT", "optional": true, "dependencies": { - "detect-libc": "^1.0.3", + "detect-libc": "^2.0.3", "is-glob": "^4.0.3", - "micromatch": "^4.0.5", - "node-addon-api": "^7.0.0" + "node-addon-api": "^7.0.0", + "picomatch": "^4.0.3" }, "engines": { "node": ">= 10.0.0" @@ -2309,29 +2360,30 @@ "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" + "@parcel/watcher-android-arm64": "2.5.6", + "@parcel/watcher-darwin-arm64": "2.5.6", + "@parcel/watcher-darwin-x64": "2.5.6", + "@parcel/watcher-freebsd-x64": "2.5.6", + "@parcel/watcher-linux-arm-glibc": "2.5.6", + "@parcel/watcher-linux-arm-musl": "2.5.6", + "@parcel/watcher-linux-arm64-glibc": "2.5.6", + "@parcel/watcher-linux-arm64-musl": "2.5.6", + "@parcel/watcher-linux-x64-glibc": "2.5.6", + "@parcel/watcher-linux-x64-musl": "2.5.6", + "@parcel/watcher-win32-arm64": "2.5.6", + "@parcel/watcher-win32-ia32": "2.5.6", + "@parcel/watcher-win32-x64": "2.5.6" } }, "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==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.6.tgz", + "integrity": "sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" @@ -2345,13 +2397,14 @@ } }, "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==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.6.tgz", + "integrity": "sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -2365,13 +2418,14 @@ } }, "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==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.6.tgz", + "integrity": "sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -2385,13 +2439,14 @@ } }, "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==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.6.tgz", + "integrity": "sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -2405,13 +2460,14 @@ } }, "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==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.6.tgz", + "integrity": "sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -2425,13 +2481,14 @@ } }, "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==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.6.tgz", + "integrity": "sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -2445,13 +2502,14 @@ } }, "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==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.6.tgz", + "integrity": "sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -2465,13 +2523,14 @@ } }, "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==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.6.tgz", + "integrity": "sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -2485,13 +2544,14 @@ } }, "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==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.6.tgz", + "integrity": "sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -2505,13 +2565,14 @@ } }, "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==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.6.tgz", + "integrity": "sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -2525,13 +2586,14 @@ } }, "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==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.6.tgz", + "integrity": "sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -2545,13 +2607,14 @@ } }, "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==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.6.tgz", + "integrity": "sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -2565,13 +2628,14 @@ } }, "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==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.6.tgz", + "integrity": "sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -2584,25 +2648,43 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/@parcel/watcher/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/@react-dnd/asap": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-5.0.2.tgz", - "integrity": "sha512-WLyfoHvxhs0V9U+GTsGilGgf2QsPl6ZZ44fnv0/b8T3nQyvzxidxsg/ZltbWssbsRDlYW8UKSQMTGotuTotZ6A==" + "integrity": "sha512-WLyfoHvxhs0V9U+GTsGilGgf2QsPl6ZZ44fnv0/b8T3nQyvzxidxsg/ZltbWssbsRDlYW8UKSQMTGotuTotZ6A==", + "license": "MIT" }, "node_modules/@react-dnd/invariant": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@react-dnd/invariant/-/invariant-4.0.2.tgz", - "integrity": "sha512-xKCTqAK/FFauOM9Ta2pswIyT3D8AQlfrYdOi/toTPEhqCuAs1v5tcJ3Y08Izh1cJ5Jchwy9SeAXmMg6zrKs2iw==" + "integrity": "sha512-xKCTqAK/FFauOM9Ta2pswIyT3D8AQlfrYdOi/toTPEhqCuAs1v5tcJ3Y08Izh1cJ5Jchwy9SeAXmMg6zrKs2iw==", + "license": "MIT" }, "node_modules/@react-dnd/shallowequal": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-4.0.2.tgz", - "integrity": "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==" + "integrity": "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==", + "license": "MIT" }, "node_modules/@selderee/plugin-htmlparser2": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.12.0.tgz", "integrity": "sha512-oELmoyA6ML9jDRMV3kgcMQFKxUfBU0yFVn6yTctVaLT5ygXnxH52I3TZEgV9EhXJC68/uFvE5Daj1/25c0Xa/A==", + "license": "MIT", "dependencies": { "domelementtype": "~2.3.0", "domhandler": "~5.0.3" @@ -2614,39 +2696,23 @@ "selderee": "~0.12.0" } }, - "node_modules/@types/eslint": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", - "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", - "dev": true, - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "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==", - "dev": true, - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" }, "node_modules/@types/hoist-non-react-statics": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", - "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.7.tgz", + "integrity": "sha512-PQTyIulDkIDro8P+IHbKCsw7U2xxBYflVzW/FgWdCAePD9xGSidgA76/GeJ6lBKoblyhf9pBY763gbrN+1dI8g==", + "license": "MIT", "dependencies": { - "@types/react": "*", "hoist-non-react-statics": "^3.3.0" + }, + "peerDependencies": { + "@types/react": "*" } }, "node_modules/@types/json-schema": { @@ -2657,53 +2723,57 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "17.0.31", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.31.tgz", - "integrity": "sha512-AR0x5HbXGqkEx9CadRH3EBYx/VkiUgZIhP4wvPn/+5KIsgpNoyFaRlVe0Zlx9gRtg8fA06a9tskE2MSN7TcG4Q==", - "devOptional": true + "version": "25.9.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.1.tgz", + "integrity": "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "undici-types": ">=7.24.0 <7.24.7" + } }, "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==" + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", + "license": "MIT" }, "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==" + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "license": "MIT" }, "node_modules/@types/react": { - "version": "18.0.8", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.8.tgz", - "integrity": "sha512-+j2hk9BzCOrrOSJASi5XiOyBbERk9jG5O73Ya4M0env5Ixi6vUNli4qy994AINcEF+1IEHISYFfIT4zwr++LKw==", + "version": "18.3.29", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.29.tgz", + "integrity": "sha512-ch0qJdr2JY0r04NXSprbK6TXOgnaJ1Tz23fm5W+z0/CBah6BSBc3n96h7K9GOtwh0HrilNWHIBzE1Ko4Dcw/Wg==", + "license": "MIT", "dependencies": { "@types/prop-types": "*", - "@types/scheduler": "*", - "csstype": "^3.0.2" + "csstype": "^3.2.2" } }, "node_modules/@types/react-transition-group": { - "version": "4.4.4", - "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.4.tgz", - "integrity": "sha512-7gAPz7anVK5xzbeQW9wFBDg7G++aPLAFY0QaSMOou9rJZpbuI58WAuJrgu+qR92l61grlnCUe7AFX8KGahAgug==", - "dependencies": { + "version": "4.4.12", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz", + "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==", + "license": "MIT", + "peerDependencies": { "@types/react": "*" } }, - "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/use-sync-external-store": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", - "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==" + "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==", + "license": "MIT" }, "node_modules/@uiw/codemirror-extensions-basic-setup": { - "version": "4.25.1", - "resolved": "https://registry.npmjs.org/@uiw/codemirror-extensions-basic-setup/-/codemirror-extensions-basic-setup-4.25.1.tgz", - "integrity": "sha512-zxgA2QkvP3ZDKxTBc9UltNFTrSeFezGXcZtZj6qcsBxiMzowoEMP5mVwXcKjpzldpZVRuY+JCC+RsekEgid4vg==", + "version": "4.25.9", + "resolved": "https://registry.npmjs.org/@uiw/codemirror-extensions-basic-setup/-/codemirror-extensions-basic-setup-4.25.9.tgz", + "integrity": "sha512-QFAqr+pu6lDmNpAlecODcF49TlsrZ0bj15zPzfhiqSDl+Um3EsDLFLppixC7kFLn+rdDM2LTvVjn5CPvefpRgw==", + "license": "MIT", "dependencies": { "@codemirror/autocomplete": "^6.0.0", "@codemirror/commands": "^6.0.0", @@ -2727,15 +2797,16 @@ } }, "node_modules/@uiw/react-codemirror": { - "version": "4.25.1", - "resolved": "https://registry.npmjs.org/@uiw/react-codemirror/-/react-codemirror-4.25.1.tgz", - "integrity": "sha512-eESBKHndoYkaEGlKCwRO4KrnTw1HkWBxVpEeqntoWTpoFEUYxdLWUYmkPBVk4/u8YzVy9g91nFfIRpqe5LjApg==", + "version": "4.25.9", + "resolved": "https://registry.npmjs.org/@uiw/react-codemirror/-/react-codemirror-4.25.9.tgz", + "integrity": "sha512-HftqCBUYShAOH0pGi1CHP8vfm5L8fQ3+0j0VI6lQD6QpK+UBu3J7nxfEN5O/BXMilMNf9ZyFJRvRcuMMOLHMng==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.18.6", "@codemirror/commands": "^6.1.0", "@codemirror/state": "^6.1.1", "@codemirror/theme-one-dark": "^6.0.0", - "@uiw/codemirror-extensions-basic-setup": "4.25.1", + "@uiw/codemirror-extensions-basic-setup": "4.25.9", "codemirror": "^6.0.0" }, "funding": { @@ -2763,6 +2834,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", "dev": true, + "license": "MIT", "dependencies": { "@webassemblyjs/helper-numbers": "1.13.2", "@webassemblyjs/helper-wasm-bytecode": "1.13.2" @@ -2772,25 +2844,29 @@ "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==", - "dev": true + "dev": true, + "license": "MIT" }, "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==", - "dev": true + "dev": true, + "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==", - "dev": true + "dev": true, + "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==", "dev": true, + "license": "MIT", "dependencies": { "@webassemblyjs/floating-point-hex-parser": "1.13.2", "@webassemblyjs/helper-api-error": "1.13.2", @@ -2801,13 +2877,15 @@ "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==", - "dev": true + "dev": true, + "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==", "dev": true, + "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -2820,6 +2898,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", "dev": true, + "license": "MIT", "dependencies": { "@xtuc/ieee754": "^1.2.0" } @@ -2829,6 +2908,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@xtuc/long": "4.2.2" } @@ -2837,13 +2917,15 @@ "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", - "dev": true + "dev": true, + "license": "MIT" }, "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==", "dev": true, + "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -2860,6 +2942,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", "dev": true, + "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-wasm-bytecode": "1.13.2", @@ -2873,6 +2956,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", "dev": true, + "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -2885,6 +2969,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", "dev": true, + "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-api-error": "1.13.2", @@ -2899,6 +2984,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", "dev": true, + "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.14.1", "@xtuc/long": "4.2.2" @@ -2908,19 +2994,22 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true + "dev": true, + "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==", - "dev": true + "dev": true, + "license": "Apache-2.0" }, "node_modules/acorn": { "version": "8.16.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, + "license": "MIT", "bin": { "acorn": "bin/acorn" }, @@ -2933,6 +3022,7 @@ "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=10.13.0" }, @@ -2951,9 +3041,9 @@ } }, "node_modules/ajv": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", - "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", "dev": true, "license": "MIT", "dependencies": { @@ -2972,6 +3062,7 @@ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", "dev": true, + "license": "MIT", "dependencies": { "ajv": "^8.0.0" }, @@ -2985,9 +3076,9 @@ } }, "node_modules/ajv-formats/node_modules/ajv": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", - "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "dev": true, "license": "MIT", "dependencies": { @@ -3005,13 +3096,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true + "dev": true, + "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==", "dev": true, + "license": "MIT", "peerDependencies": { "ajv": "^6.9.1" } @@ -3043,10 +3136,11 @@ } }, "node_modules/anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, + "license": "ISC", "optional": true, "dependencies": { "normalize-path": "^3.0.0", @@ -3060,17 +3154,17 @@ "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/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==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.5", - "is-array-buffer": "^3.0.4" + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" }, "engines": { "node": ">= 0.4" @@ -3080,17 +3174,20 @@ } }, "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==", + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", + "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", "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" + "es-abstract": "^1.24.0", + "es-object-atoms": "^1.1.1", + "get-intrinsic": "^1.3.0", + "is-string": "^1.1.1", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -3104,6 +3201,7 @@ "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -3119,16 +3217,36 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/array.prototype.flat": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/array.prototype.flatmap": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", - "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -3142,6 +3260,7 @@ "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -3154,19 +3273,19 @@ } }, "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==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", "dev": true, + "license": "MIT", "dependencies": { "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.5", + "call-bind": "^1.0.8", "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" + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" }, "engines": { "node": ">= 0.4" @@ -3175,10 +3294,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/attr-accept": { "version": "2.2.5", "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.5.tgz", "integrity": "sha512-0bDNnY/u6pPwHDMoF0FieU354oBi0a8rD9FcsLwzcGWbc8KS8KPIi7y+s13OlVY+gMWc/9xEMUgNE6Qm8ZllYQ==", + "license": "MIT", "engines": { "node": ">=4" } @@ -3188,6 +3318,7 @@ "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", "dev": true, + "license": "MIT", "dependencies": { "possible-typed-array-names": "^1.0.0" }, @@ -3203,6 +3334,7 @@ "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-10.1.1.tgz", "integrity": "sha512-JwKSzk2kjIe7mgPK+/lyZ2QAaJcpahNAdM+hgR2HI8D0OJVkdj8Rl6J3kaLYki9pwF7P2iWnD8qVv80Lq1ABtg==", "dev": true, + "license": "MIT", "dependencies": { "find-up": "^5.0.0" }, @@ -3223,75 +3355,11 @@ } } }, - "node_modules/babel-loader/node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/babel-loader/node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/babel-loader/node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/babel-loader/node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "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": { "@babel/runtime": "^7.12.5", "cosmiconfig": "^7.0.0", @@ -3307,6 +3375,7 @@ "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.17.tgz", "integrity": "sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==", "dev": true, + "license": "MIT", "dependencies": { "@babel/compat-data": "^7.28.6", "@babel/helper-define-polyfill-provider": "^0.6.8", @@ -3321,6 +3390,7 @@ "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.14.2.tgz", "integrity": "sha512-coWpDLJ410R781Npmn/SIBZEsAetR4xVi0SxLMXPaMO4lSf1MwnkGYMtkFxew0Dn8B3/CpbpYxN0JCgg8mn67g==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.8", "core-js-compat": "^3.48.0" @@ -3334,6 +3404,7 @@ "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.8.tgz", "integrity": "sha512-M762rNHfSF1EV3SLtnCJXFoQbbIIz0OyRwnCmV0KPC7qosSfCO0QLTSuJX3ayAebubhE6oYBAYPrBA5ljowaZg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.8" }, @@ -3345,15 +3416,20 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.9.19", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz", - "integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==", + "version": "2.10.31", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.31.tgz", + "integrity": "sha512-MujYO3eP72uvmSE0i4wltsodRfIpZATP3jvzRNRGGxgzId7aVocVJJV3nf01qnzzKFGxQVC9bpWxl5cjxTr/7Q==", "dev": true, + "license": "Apache-2.0", "bin": { - "baseline-browser-mapping": "dist/cli.js" + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" } }, "node_modules/big.js": { @@ -3361,29 +3437,35 @@ "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", "dev": true, + "license": "MIT", "engines": { "node": "*" } }, "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==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "dev": true, + "license": "MIT", "optional": true, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/bootstrap-sass": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/bootstrap-sass/-/bootstrap-sass-3.4.3.tgz", - "integrity": "sha512-vPgFnGMp1jWZZupOND65WS6mkR8rxhJxndT/AcMbqcq1hHMdkcH4sMPhznLzzoHOHkSCrd6J9F8pWBriPCKP2Q==" + "integrity": "sha512-vPgFnGMp1jWZZupOND65WS6mkR8rxhJxndT/AcMbqcq1hHMdkcH4sMPhznLzzoHOHkSCrd6J9F8pWBriPCKP2Q==", + "license": "MIT" }, "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", "dev": true, "license": "MIT", "dependencies": { @@ -3396,6 +3478,7 @@ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, + "license": "MIT", "optional": true, "dependencies": { "fill-range": "^7.1.1" @@ -3405,9 +3488,9 @@ } }, "node_modules/browserslist": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", - "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", "dev": true, "funding": [ { @@ -3423,12 +3506,13 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.9.0", - "caniuse-lite": "^1.0.30001759", - "electron-to-chromium": "^1.5.263", - "node-releases": "^2.0.27", - "update-browserslist-db": "^1.2.0" + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" @@ -3441,19 +3525,51 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true + "dev": true, + "license": "MIT" }, "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==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", + "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==", "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "get-intrinsic": "^1.3.0", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", "dependencies": { - "es-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" + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" }, "engines": { "node": ">= 0.4" @@ -3466,14 +3582,15 @@ "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/caniuse-lite": { - "version": "1.0.30001769", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001769.tgz", - "integrity": "sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg==", + "version": "1.0.30001793", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001793.tgz", + "integrity": "sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==", "dev": true, "funding": [ { @@ -3488,7 +3605,8 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ] + ], + "license": "CC-BY-4.0" }, "node_modules/chalk": { "version": "4.1.2", @@ -3507,24 +3625,12 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, + "license": "MIT", "optional": true, "dependencies": { "anymatch": "~3.1.2", @@ -3546,10 +3652,11 @@ } }, "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==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.0" } @@ -3557,13 +3664,15 @@ "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==" + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", + "license": "MIT" }, "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": { "is-plain-object": "^2.0.4", "kind-of": "^6.0.2", @@ -3577,14 +3686,16 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/codemirror": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.1.tgz", - "integrity": "sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.2.tgz", + "integrity": "sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==", + "license": "MIT", "dependencies": { "@codemirror/autocomplete": "^6.0.0", "@codemirror/commands": "^6.0.0", @@ -3620,6 +3731,7 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 6" } @@ -3627,13 +3739,16 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" }, "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==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" }, "node_modules/copy-webpack-plugin": { "version": "14.0.0", @@ -3664,6 +3779,7 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, + "license": "ISC", "dependencies": { "is-glob": "^4.0.3" }, @@ -3676,13 +3792,15 @@ "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", - "hasInstallScript": true + "hasInstallScript": true, + "license": "MIT" }, "node_modules/core-js-compat": { "version": "3.49.0", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.49.0.tgz", "integrity": "sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==", "dev": true, + "license": "MIT", "dependencies": { "browserslist": "^4.28.1" }, @@ -3695,6 +3813,7 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "license": "MIT", "dependencies": { "@types/parse-json": "^4.0.0", "import-fresh": "^3.2.1", @@ -3707,9 +3826,10 @@ } }, "node_modules/crelt": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.5.tgz", - "integrity": "sha512-+BO9wPPi+DWTDcNYhr/W90myha8ptzftZT+LwcmUbbok0rcP/fequmFYCw8NMoH7pkAZQzU78b3kYrlua5a9eA==" + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==", + "license": "MIT" }, "node_modules/cross-spawn": { "version": "7.0.6", @@ -3727,19 +3847,20 @@ } }, "node_modules/css-loader": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.1.tgz", - "integrity": "sha512-OxIR5P2mjO1PSXk44bWuQ8XtMK4dpEqpIyERCx3ewOo3I8EmbcxMPUc5ScLtQfgXtOojoMv57So4V/C02HQLsw==", + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.4.tgz", + "integrity": "sha512-vv3J9tlOl04WjiMvHQI/9tmIrCxVrj6PFbHemBB1iihpeRbi/I4h033eoFIhwxBBqLhI0KYFS7yvynBFhIZfTw==", "dev": true, + "license": "MIT", "dependencies": { "icss-utils": "^5.1.0", - "postcss": "^8.4.33", + "postcss": "^8.4.40", "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" + "semver": "^7.6.3" }, "engines": { "node": ">= 18.12.0" @@ -3749,7 +3870,7 @@ "url": "https://opencollective.com/webpack" }, "peerDependencies": { - "@rspack/core": "0.x || 1.x", + "@rspack/core": "0.x || ^1.0.0 || ^2.0.0-0", "webpack": "^5.27.0" }, "peerDependenciesMeta": { @@ -3762,13 +3883,11 @@ } }, "node_modules/css-loader/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==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", + "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -3781,6 +3900,7 @@ "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", "dev": true, + "license": "MIT", "bin": { "cssesc": "bin/cssesc" }, @@ -3789,19 +3909,21 @@ } }, "node_modules/csstype": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz", - "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==" + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" }, "node_modules/data-view-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", - "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.6", + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" + "is-data-view": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -3811,29 +3933,31 @@ } }, "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==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" + "is-data-view": "^1.0.2" }, "engines": { "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/inspect-js" } }, "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==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.6", + "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-data-view": "^1.0.1" }, @@ -3845,9 +3969,10 @@ } }, "node_modules/date-fns": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", - "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.2.1.tgz", + "integrity": "sha512-37RhSdxaG1suen6VDCza6rNrQfooyQh57HFVPwQGEq2QWliVLzPQZ8Oa017weOu+HZCnzI7N3Pf/wyoBKfEqrA==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/kossnocorp" @@ -3857,6 +3982,7 @@ "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", "dependencies": { "ms": "^2.1.3" }, @@ -3872,18 +3998,22 @@ "node_modules/deep-diff": { "version": "0.3.8", "resolved": "https://registry.npmjs.org/deep-diff/-/deep-diff-0.3.8.tgz", - "integrity": "sha512-yVn6RZmHiGnxRKR9sJb3iVV2XTF1Ghh2DiWRZ3dMnGc43yUdWWF/kX6lQyk3+P84iprfWKU/8zFTrlkvtFm1ug==" + "integrity": "sha512-yVn6RZmHiGnxRKR9sJb3iVV2XTF1Ghh2DiWRZ3dMnGc43yUdWWF/kX6lQyk3+P84iprfWKU/8zFTrlkvtFm1ug==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "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==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/deepmerge-ts": { "version": "7.1.5", "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz", "integrity": "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==", + "license": "BSD-3-Clause", "engines": { "node": ">=16.0.0" } @@ -3893,6 +4023,7 @@ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dev": true, + "license": "MIT", "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -3910,6 +4041,7 @@ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dev": true, + "license": "MIT", "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", @@ -3923,22 +4055,21 @@ } }, "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==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", "dev": true, + "license": "Apache-2.0", "optional": true, - "bin": { - "detect-libc": "bin/detect-libc.js" - }, "engines": { - "node": ">=0.10" + "node": ">=8" } }, "node_modules/diff": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/diff/-/diff-9.0.0.tgz", "integrity": "sha512-svtcdpS8CgJyqAjEQIXdb3OjhFVVYjzGAPO8WGCmRbrml64SPw/jJD4GoE98aR7r25A0XcgrK3F02yw9R/vhQw==", + "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" } @@ -3947,6 +4078,7 @@ "version": "16.0.1", "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-16.0.1.tgz", "integrity": "sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng==", + "license": "MIT", "dependencies": { "@react-dnd/asap": "^5.0.1", "@react-dnd/invariant": "^4.0.1", @@ -3970,6 +4102,7 @@ "version": "3.4.0", "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.1.2" } @@ -3978,6 +4111,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "license": "MIT", "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", @@ -3996,12 +4130,14 @@ "type": "github", "url": "https://github.com/sponsors/fb55" } - ] + ], + "license": "BSD-2-Clause" }, "node_modules/domhandler": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "license": "BSD-2-Clause", "dependencies": { "domelementtype": "^2.3.0" }, @@ -4016,6 +4152,7 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "license": "BSD-2-Clause", "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", @@ -4025,26 +4162,44 @@ "url": "https://github.com/fb55/domutils?sponsor=1" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/electron-to-chromium": { - "version": "1.5.286", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz", - "integrity": "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==", - "dev": true + "version": "1.5.360", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.360.tgz", + "integrity": "sha512-GkcBt6YYAw9SxFWn+xVar4cLVGlXVuswwtRLBozi2zp0GjXs4ZnOrqV4zbXzg35n7w81hCkyJNYicgXlVHAmBA==", + "dev": true, + "license": "ISC" }, "node_modules/emojis-list": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", "dev": true, + "license": "MIT", "engines": { "node": ">= 4" } }, "node_modules/enhanced-resolve": { - "version": "5.21.5", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.21.5.tgz", - "integrity": "sha512-mLCNbrQli11K1ySUmuNt4ZUB3OpGIDq4q2vTBTf5cL2lpsRjI9QKqSD0ndjW8FyvcW/Jj46gMe9syyHAsvMa/A==", + "version": "5.21.6", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.21.6.tgz", + "integrity": "sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ==", "dev": true, + "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" @@ -4057,6 +4212,7 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", "engines": { "node": ">=0.12" }, @@ -4065,10 +4221,11 @@ } }, "node_modules/envinfo": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.14.0.tgz", - "integrity": "sha512-CO40UI41xDQzhLB1hWyqUKgFhs250pNcGbyGKe1l/e4FSaI/+YE4IMG76GDt0In67WLPACIITC+sOi08x4wIvg==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.21.0.tgz", + "integrity": "sha512-Lw7I8Zp5YKHFCXL7+Dz95g4CcbMEpgvqZNNq3AmlT5XAV6CgAAk6gyAMqn2zjw08K9BHfcNuKrMiCPLByGafow==", "dev": true, + "license": "MIT", "bin": { "envinfo": "dist/cli.js" }, @@ -4080,62 +4237,72 @@ "version": "1.3.4", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "license": "MIT", "dependencies": { "is-arrayish": "^0.2.1" } }, "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==", + "version": "1.24.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.2.tgz", + "integrity": "sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==", "dev": true, + "license": "MIT", "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "arraybuffer.prototype.slice": "^1.0.3", + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", "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", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-set-tostringtag": "^2.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", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.3", - "has-symbols": "^1.0.3", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", "hasown": "^2.0.2", - "internal-slot": "^1.0.7", - "is-array-buffer": "^3.0.4", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", - "is-data-view": "^1.0.1", + "is-data-view": "^1.0.2", "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", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", "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", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", "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" + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" }, "engines": { "node": ">= 0.4" @@ -4145,13 +4312,11 @@ } }, "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==", "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.4" - }, + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -4160,46 +4325,52 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", "engines": { "node": ">= 0.4" } }, "node_modules/es-iterator-helpers": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.1.0.tgz", - "integrity": "sha512-/SurEfycdyssORP/E+bj4sEu1CWw4EmLDsHynHwSXQ7utgbrMRWW195pTrCjFgFCddf/UkYm3oqKPRq5i8bJbw==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.3.2.tgz", + "integrity": "sha512-HVLACW1TppGYjJ8H6/jqH/pqOtKRw6wMlrB23xfExmFWxFquAIWCmwoLsOyN96K4a5KbmOf5At9ZUO3GZbetAw==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", "define-properties": "^1.2.1", - "es-abstract": "^1.23.3", + "es-abstract": "^1.24.2", "es-errors": "^1.3.0", - "es-set-tostringtag": "^2.0.3", + "es-set-tostringtag": "^2.1.0", "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", + "get-intrinsic": "^1.3.0", "globalthis": "^1.0.4", + "gopd": "^1.2.0", "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" + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "iterator.prototype": "^1.1.5", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" } }, "node_modules/es-module-lexer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", - "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==", - "dev": true + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", + "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", + "dev": true, + "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==", "dev": true, + "license": "MIT", "dependencies": { "es-errors": "^1.3.0" }, @@ -4208,37 +4379,44 @@ } }, "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==", "dev": true, + "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" } }, "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==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", "dev": true, + "license": "MIT", "dependencies": { - "hasown": "^2.0.0" + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" } }, "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==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", "dev": true, + "license": "MIT", "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" }, "engines": { "node": ">= 0.4" @@ -4252,6 +4430,7 @@ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -4260,6 +4439,7 @@ "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" }, @@ -4325,28 +4505,29 @@ } }, "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==", + "version": "7.37.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", + "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", "dev": true, + "license": "MIT", "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", - "array.prototype.flatmap": "^1.3.2", + "array.prototype.flatmap": "^1.3.3", "array.prototype.tosorted": "^1.1.4", "doctrine": "^2.1.0", - "es-iterator-helpers": "^1.1.0", + "es-iterator-helpers": "^1.2.1", "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.entries": "^1.1.9", "object.fromentries": "^2.0.8", - "object.values": "^1.2.0", + "object.values": "^1.2.1", "prop-types": "^15.8.1", "resolve": "^2.0.0-next.5", "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.11", + "string.prototype.matchall": "^4.0.12", "string.prototype.repeat": "^1.0.0" }, "engines": { @@ -4361,6 +4542,7 @@ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "dev": true, + "license": "Apache-2.0", "dependencies": { "esutils": "^2.0.2" }, @@ -4368,43 +4550,45 @@ "node": ">=0.10.0" } }, - "node_modules/eslint-plugin-react/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, "node_modules/eslint-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==", + "version": "2.0.0-next.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.7.tgz", + "integrity": "sha512-tqt+NBWwyaMgw3zDsnygx4CByWjQEJHOPMdslYhppaQSJUtL/D4JO9CcBBlhPoI8lz9oJIDXkwXfhF4aWqP8xQ==", "dev": true, + "license": "MIT", "dependencies": { - "is-core-module": "^2.13.0", + "es-errors": "^1.3.0", + "is-core-module": "^2.16.2", + "node-exports-info": "^1.6.0", + "object-keys": "^1.1.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "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==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" + "estraverse": "^5.2.0" }, "engines": { - "node": ">=8.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint-visitor-keys": { @@ -4420,85 +4604,64 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/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/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, + "license": "ISC", "dependencies": { - "color-convert": "^2.0.1" + "is-glob": "^4.0.3" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=10.13.0" } }, - "node_modules/eslint/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.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/eslint/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "color-name": "~1.1.4" + "estraverse": "^5.1.0" }, "engines": { - "node": ">=7.0.0" + "node": ">=0.10" } }, - "node_modules/eslint/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "esrecurse": "^4.3.0", "estraverse": "^5.2.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=4.0" } }, - "node_modules/eslint/node_modules/estraverse": { + "node_modules/estraverse": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", @@ -4508,182 +4671,45 @@ "node": ">=4.0" } }, - "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==", + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, + "license": "BSD-2-Clause", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, - "node_modules/eslint/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/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "dev": true, - "dependencies": { - "is-glob": "^4.0.3" - }, + "license": "MIT", "engines": { - "node": ">=10.13.0" - } - }, - "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==", - "dev": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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==", - "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esquery": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", - "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", - "dev": true, - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esquery/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true, - "engines": { - "node": ">=0.8.x" + "node": ">=0.8.x" } }, "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==" + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" }, "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==", - "dev": true + "dev": true, + "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==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fast-uri": { "version": "3.1.2", @@ -4699,13 +4725,18 @@ "type": "opencollective", "url": "https://opencollective.com/fastify" } - ] + ], + "license": "BSD-3-Clause" }, "node_modules/fastest-levenshtein": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz", - "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==", - "dev": true + "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": ">= 4.9.1" + } }, "node_modules/fastq": { "version": "1.20.1", @@ -4735,6 +4766,7 @@ "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", "dev": true, + "license": "MIT", "dependencies": { "loader-utils": "^2.0.0", "schema-utils": "^3.0.0" @@ -4751,10 +4783,11 @@ } }, "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==", + "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==", "dev": true, + "license": "MIT", "dependencies": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", @@ -4772,6 +4805,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-2.1.2.tgz", "integrity": "sha512-QgXo+mXTe8ljeqUFaX3QVHc5osSItJ/Km+xpocx0aSqWGMSCf6qYs/VnzZgS864Pjn5iceMRFigeAV7AfTlaig==", + "license": "MIT", "dependencies": { "tslib": "^2.7.0" }, @@ -4784,6 +4818,7 @@ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, + "license": "MIT", "optional": true, "dependencies": { "to-regex-range": "^5.0.1" @@ -4795,19 +4830,24 @@ "node_modules/find-root": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "license": "MIT" }, "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==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, + "license": "MIT", "dependencies": { - "locate-path": "^5.0.0", + "locate-path": "^6.0.0", "path-exists": "^4.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/flat": { @@ -4815,6 +4855,7 @@ "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" } @@ -4845,37 +4886,48 @@ "version": "4.7.0", "resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz", "integrity": "sha512-U6kGnykA/6bFmg1M/oT9EkFeIYv7JlX3bozwQJWiiLz6L0w3F5vBVPxHlwyX/vtNq1ckcpRKOB9f2Qal/VtFpg==", + "license": "(OFL-1.1 AND MIT)", "engines": { "node": ">=0.10.3" } }, "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==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", "dev": true, + "license": "MIT", "dependencies": { - "is-callable": "^1.1.3" + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/fs-readdir-recursive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" }, "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "hasInstallScript": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -4888,20 +4940,24 @@ "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==", + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" }, "engines": { "node": ">= 0.4" @@ -4915,30 +4971,48 @@ "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/generator-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", + "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "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==", "dev": true, + "license": "MIT", "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", - "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" @@ -4947,15 +5021,30 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "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==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.5", + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4" + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -4965,15 +5054,17 @@ } }, "node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, + "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" }, @@ -4989,6 +5080,7 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, + "license": "ISC", "optional": true, "dependencies": { "is-glob": "^4.0.1" @@ -5001,7 +5093,8 @@ "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==", - "dev": true + "dev": true, + "license": "BSD-2-Clause" }, "node_modules/globals": { "version": "13.24.0", @@ -5024,6 +5117,7 @@ "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", "dev": true, + "license": "MIT", "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" @@ -5036,12 +5130,13 @@ } }, "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==", "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.3" + "license": "MIT", + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -5051,7 +5146,8 @@ "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/graphemer": { "version": "1.4.0", @@ -5061,10 +5157,14 @@ "license": "MIT" }, "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -5084,6 +5184,7 @@ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dev": true, + "license": "MIT", "dependencies": { "es-define-property": "^1.0.0" }, @@ -5092,10 +5193,14 @@ } }, "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==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -5104,10 +5209,11 @@ } }, "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==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -5120,6 +5226,7 @@ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, + "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" }, @@ -5131,9 +5238,10 @@ } }, "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", + "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", + "license": "MIT", "dependencies": { "function-bind": "^1.1.2" }, @@ -5145,6 +5253,7 @@ "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "license": "BSD-3-Clause", "dependencies": { "react-is": "^16.7.0" } @@ -5153,6 +5262,7 @@ "version": "10.0.0", "resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-10.0.0.tgz", "integrity": "sha512-2OH59Gtprdczel+7Rxgpz9hGVJREaf8Lt1H4kZwWHpEn70VQKRuMNGsb2eDbwaTzrYzb0hheiOG1P7Dim0B4dQ==", + "license": "MIT", "dependencies": { "@selderee/plugin-htmlparser2": "~0.12.0", "deepmerge-ts": "^7.1.5", @@ -5178,6 +5288,7 @@ "url": "https://github.com/sponsors/fb55" } ], + "license": "MIT", "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", @@ -5189,6 +5300,7 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "license": "BSD-2-Clause", "engines": { "node": ">=0.12" }, @@ -5201,6 +5313,7 @@ "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", "dev": true, + "license": "ISC", "engines": { "node": "^10 || ^12 || >= 14" }, @@ -5226,9 +5339,10 @@ "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==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "license": "MIT", "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -5240,19 +5354,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", - "engines": { - "node": ">=4" - } - }, "node_modules/import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", "dev": true, + "license": "MIT", "dependencies": { "pkg-dir": "^4.2.0", "resolve-cwd": "^3.0.0" @@ -5272,6 +5379,7 @@ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8.19" } @@ -5279,8 +5387,10 @@ "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "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.", "dev": true, + "license": "ISC", "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -5290,17 +5400,19 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "dev": true, + "license": "ISC" }, "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==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", "dev": true, + "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" + "hasown": "^2.0.2", + "side-channel": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -5311,6 +5423,7 @@ "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=10.13.0" } @@ -5319,18 +5432,21 @@ "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/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==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -5342,15 +5458,21 @@ "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==" + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "license": "MIT" }, "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==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", "dev": true, + "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -5360,12 +5482,16 @@ } }, "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", "dev": true, + "license": "MIT", "dependencies": { - "has-bigints": "^1.0.1" + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -5376,6 +5502,7 @@ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, + "license": "MIT", "optional": true, "dependencies": { "binary-extensions": "^2.0.0" @@ -5385,13 +5512,14 @@ } }, "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==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -5405,6 +5533,7 @@ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -5413,11 +5542,12 @@ } }, "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", + "license": "MIT", "dependencies": { - "hasown": "^2.0.2" + "hasown": "^2.0.3" }, "engines": { "node": ">= 0.4" @@ -5427,11 +5557,14 @@ } }, "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==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", "dev": true, + "license": "MIT", "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", "is-typed-array": "^1.1.13" }, "engines": { @@ -5442,12 +5575,14 @@ } }, "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==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", "dev": true, + "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -5459,31 +5594,41 @@ "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "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==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2" + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "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==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", + "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", "dev": true, + "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.4", + "generator-function": "^2.0.0", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -5497,6 +5642,7 @@ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, + "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, @@ -5509,6 +5655,7 @@ "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -5521,6 +5668,7 @@ "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -5533,18 +5681,21 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, + "license": "MIT", "optional": true, "engines": { "node": ">=0.12.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==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", "dev": true, + "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -5568,6 +5719,7 @@ "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": { "isobject": "^3.0.1" }, @@ -5576,13 +5728,16 @@ } }, "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==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -5596,6 +5751,7 @@ "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -5604,12 +5760,13 @@ } }, "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==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7" + "call-bound": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -5619,12 +5776,14 @@ } }, "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", "dev": true, + "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -5634,12 +5793,15 @@ } }, "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==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", "dev": true, + "license": "MIT", "dependencies": { - "has-symbols": "^1.0.2" + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -5649,12 +5811,13 @@ } }, "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==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", "dev": true, + "license": "MIT", "dependencies": { - "which-typed-array": "^1.1.14" + "which-typed-array": "^1.1.16" }, "engines": { "node": ">= 0.4" @@ -5668,6 +5831,7 @@ "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -5676,25 +5840,30 @@ } }, "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==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2" + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "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==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4" + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -5707,34 +5876,39 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" }, "node_modules/isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "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==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", + "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", "dev": true, + "license": "MIT", "dependencies": { - "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" + "define-data-property": "^1.1.4", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "get-proto": "^1.0.0", + "has-symbols": "^1.1.0", + "set-function-name": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -5745,6 +5919,7 @@ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*", "merge-stream": "^2.0.0", @@ -5754,6 +5929,22 @@ "node": ">= 10.13.0" } }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/jquery": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz", @@ -5761,23 +5952,24 @@ "license": "MIT" }, "node_modules/js-cookie": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", - "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.7.tgz", + "integrity": "sha512-z/wZZgDrkNV1eA0ULjM/F9/50Ya8fbzgKneSpoPsXSGd0KnpdtHfOZWK+GcwLk+EZbS4F9RBhU+K2RgzuDaItw==", + "license": "MIT", "engines": { - "node": ">=14" + "node": ">=20" } }, "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==" + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" }, "node_modules/js-yaml": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", - "dev": true, "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -5790,6 +5982,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "license": "MIT", "bin": { "jsesc": "bin/jsesc" }, @@ -5807,25 +6000,29 @@ "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==" + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "license": "MIT" }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true + "dev": true, + "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==", "dev": true, + "license": "MIT", "bin": { "json5": "lib/cli.js" }, @@ -5834,13 +6031,16 @@ } }, "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==", + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", "dev": true, + "license": "MIT", "dependencies": { - "array-includes": "^3.1.5", - "object.assign": "^4.1.3" + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" }, "engines": { "node": ">=4.0" @@ -5849,7 +6049,8 @@ "node_modules/keycode": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/keycode/-/keycode-2.2.1.tgz", - "integrity": "sha512-Rdgz9Hl9Iv4QKi8b0OlCRQEzp4AgVxyCtz5S/+VIHezDmrDhkp2N2TqBWOLz0/gbeREXOOiI9/4b8BY9uw2vFg==" + "integrity": "sha512-Rdgz9Hl9Iv4QKi8b0OlCRQEzp4AgVxyCtz5S/+VIHezDmrDhkp2N2TqBWOLz0/gbeREXOOiI9/4b8BY9uw2vFg==", + "license": "MIT" }, "node_modules/keyv": { "version": "4.5.4", @@ -5866,6 +6067,7 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -5874,6 +6076,7 @@ "version": "0.7.0", "resolved": "https://registry.npmjs.org/leac/-/leac-0.7.0.tgz", "integrity": "sha512-qMrZeyEekgdRQ9o6a4NAB2EQZrv827GJdn1vnapwSJ90hWRB4TzUSunvacPkxQ2TnNqHNI1/zSt0hlo0crG8Jw==", + "license": "MIT", "funding": { "url": "https://github.com/sponsors/KillyMXI" } @@ -5883,6 +6086,7 @@ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, + "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -5894,13 +6098,15 @@ "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==" + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" }, "node_modules/loader-runner": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz", - "integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.2.tgz", + "integrity": "sha512-DFEqQ3ihfS9blba08cLfYf1NRAIEm+dDjic073DRDc3/JspI/8wYmtDsHwd3+4hwvdxSK7PGaElfTmm0awWJ4w==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.11.5" }, @@ -5914,6 +6120,7 @@ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", "dev": true, + "license": "MIT", "dependencies": { "big.js": "^5.2.2", "emojis-list": "^3.0.0", @@ -5924,27 +6131,33 @@ } }, "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==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, + "license": "MIT", "dependencies": { - "p-locate": "^4.1.0" + "p-locate": "^5.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/lodash": { "version": "4.18.1", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", - "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==" + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "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==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.merge": { "version": "4.6.2", @@ -5957,6 +6170,7 @@ "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": { "js-tokens": "^3.0.0 || ^4.0.0" }, @@ -5965,15 +6179,13 @@ } }, "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==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, + "license": "ISC", "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" + "yallist": "^3.0.2" } }, "node_modules/make-dir": { @@ -5981,6 +6193,7 @@ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", "dev": true, + "license": "MIT", "dependencies": { "pify": "^4.0.1", "semver": "^5.6.0" @@ -5994,41 +6207,40 @@ "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/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/memoize-one": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", - "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==" + "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==", + "license": "MIT" }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } + "license": "MIT" }, "node_modules/mime-db": { "version": "1.54.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -6038,6 +6250,7 @@ "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.10.2.tgz", "integrity": "sha512-AOSS0IdEB95ayVkxn5oGzNQwqAi2J0Jb/kKm43t7H73s8+f5873g0yuj0PNvK4dO75mu5DHg4nlgp4k6Kga8eg==", "dev": true, + "license": "MIT", "dependencies": { "schema-utils": "^4.0.0", "tapable": "^2.2.1" @@ -6058,6 +6271,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -6068,12 +6282,13 @@ "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-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", "dev": true, "funding": [ { @@ -6093,32 +6308,59 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true + "dev": true, + "license": "MIT" }, "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==", - "dev": true + "dev": true, + "license": "MIT" }, "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-exports-info": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/node-exports-info/-/node-exports-info-1.6.0.tgz", + "integrity": "sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "array.prototype.flatmap": "^1.3.3", + "es-errors": "^1.3.0", + "object.entries": "^1.1.9", + "semver": "^6.3.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/node-releases": { - "version": "2.0.27", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", - "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", - "dev": true + "version": "2.0.45", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.45.tgz", + "integrity": "sha512-iIbHXV9eBB2nB0wa7oTsrrXq+qQt+9SIlx9AX3T96YgobtEQfis5n6TJ6vV+3QP8DwdriEAcGhARaFCu37peBg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -6126,16 +6368,21 @@ "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/object-inspect": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", - "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -6145,19 +6392,23 @@ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" } }, "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==", + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.5", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "define-properties": "^1.2.1", - "has-symbols": "^1.0.3", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", "object-keys": "^1.1.1" }, "engines": { @@ -6168,14 +6419,16 @@ } }, "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==", + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", + "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" + "es-object-atoms": "^1.1.1" }, "engines": { "node": ">= 0.4" @@ -6186,6 +6439,7 @@ "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -6200,12 +6454,14 @@ } }, "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==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" }, @@ -6219,62 +6475,88 @@ "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, + "license": "ISC", "dependencies": { "wrappy": "1" } }, "node_modules/optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, + "license": "MIT", "dependencies": { - "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" }, "engines": { "node": ">= 0.8.0" } }, - "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==", + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", "dev": true, + "license": "MIT", "dependencies": { - "p-try": "^2.0.0" + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" }, "engines": { - "node": ">=6" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "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==", + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, + "license": "MIT", "dependencies": { - "p-limit": "^2.2.0" + "yocto-queue": "^0.1.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, - "engines": { + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { "node": ">=6" } }, @@ -6282,6 +6564,7 @@ "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" }, @@ -6293,6 +6576,7 @@ "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": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", @@ -6310,6 +6594,7 @@ "version": "0.13.1", "resolved": "https://registry.npmjs.org/parseley/-/parseley-0.13.1.tgz", "integrity": "sha512-uNBJZzmb60l6p6VWLTmevizNAGnE0xoSf1n0B4q3ntegDNzcS68NRCcBDZTcyXHxt2XhBChsCuqj4M+nChvE/A==", + "license": "MIT", "dependencies": { "leac": "^0.7.0", "peberminta": "^0.10.0" @@ -6323,6 +6608,7 @@ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -6330,8 +6616,9 @@ "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": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -6341,6 +6628,7 @@ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -6348,12 +6636,14 @@ "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==" + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" }, "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==", + "license": "MIT", "engines": { "node": ">=8" } @@ -6362,6 +6652,7 @@ "version": "0.10.0", "resolved": "https://registry.npmjs.org/peberminta/-/peberminta-0.10.0.tgz", "integrity": "sha512-80B2AsU+I4Qdb0ZAPSfe9UwvGzwkM37IKIFEvdS3D/3Ndgv2bsuJ0bfG1+iEYO+l7Gfd4EUJmuRyq7efLgRMzQ==", + "license": "MIT", "funding": { "url": "https://github.com/sponsors/KillyMXI" } @@ -6369,13 +6660,15 @@ "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, + "license": "MIT", "optional": true, "engines": { "node": ">=8.6" @@ -6389,6 +6682,7 @@ "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -6398,6 +6692,7 @@ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, + "license": "MIT", "dependencies": { "find-up": "^4.0.0" }, @@ -6405,29 +6700,87 @@ "node": ">=8" } }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/popper.js": { "version": "1.16.1", "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz", "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==", "deprecated": "You can find the new Popper v2 at @popperjs/core, this package is dedicated to the legacy v1", + "license": "MIT", "funding": { "type": "opencollective", "url": "https://opencollective.com/popperjs" } }, "node_modules/possible-typed-array-names": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", - "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" } }, "node_modules/postcss": { - "version": "8.4.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", - "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==", + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", "dev": true, "funding": [ { @@ -6443,10 +6796,11 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" + "nanoid": "^3.3.12", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" @@ -6457,6 +6811,7 @@ "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", "dev": true, + "license": "ISC", "engines": { "node": "^10 || ^12 || >= 14" }, @@ -6465,13 +6820,14 @@ } }, "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==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.2.0.tgz", + "integrity": "sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==", "dev": true, + "license": "MIT", "dependencies": { "icss-utils": "^5.0.0", - "postcss-selector-parser": "^6.0.2", + "postcss-selector-parser": "^7.0.0", "postcss-value-parser": "^4.1.0" }, "engines": { @@ -6482,12 +6838,13 @@ } }, "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==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz", + "integrity": "sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==", "dev": true, + "license": "ISC", "dependencies": { - "postcss-selector-parser": "^6.0.4" + "postcss-selector-parser": "^7.0.0" }, "engines": { "node": "^10 || ^12 || >= 14" @@ -6501,6 +6858,7 @@ "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", "dev": true, + "license": "ISC", "dependencies": { "icss-utils": "^5.0.0" }, @@ -6512,10 +6870,11 @@ } }, "node_modules/postcss-selector-parser": { - "version": "6.0.16", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz", - "integrity": "sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "dev": true, + "license": "MIT", "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -6528,13 +6887,15 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true + "dev": true, + "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==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8.0" } @@ -6543,6 +6904,7 @@ "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", @@ -6553,6 +6915,7 @@ "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" @@ -6561,11 +6924,21 @@ "react": ">=0.14.0" } }, + "node_modules/prop-types-extra/node_modules/warning": { + "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/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -6607,6 +6980,7 @@ "version": "0.33.1", "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-0.33.1.tgz", "integrity": "sha512-qWTRravSds87P8WC82tETy2yIso8qDqlIm0czsrduCaYAFtHuyLu0XDbUlfLXeRzqgwm5sRk2wRaTNoiVkk/YQ==", + "license": "MIT", "dependencies": { "@babel/runtime-corejs2": "^7.0.0", "classnames": "^2.2.5", @@ -6626,18 +7000,11 @@ "react-dom": ">=16.3.0" } }, - "node_modules/react-bootstrap/node_modules/warning": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", - "integrity": "sha512-jMBt6pUrKn5I+OGgtQ4YZLdhIeJmObddh6CsibPxyQ5yPZm1XExSyzC1LCNX7BzhxWgiHmizBWJTHJIjMjTQYQ==", - "dependencies": { - "loose-envify": "^1.0.0" - } - }, "node_modules/react-datepicker": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-9.1.0.tgz", "integrity": "sha512-lOp+m5bc+ttgtB5MHEjwiVu4nlp4CvJLS/PG1OiOe5pmg9kV73pEqO8H0Geqvg2E8gjqTaL9eRhSe+ZpeKP3nA==", + "license": "MIT", "dependencies": { "@floating-ui/react": "^0.27.15", "clsx": "^2.1.1", @@ -6658,6 +7025,7 @@ "version": "4.2.2", "resolved": "https://registry.npmjs.org/react-diff-viewer-continued/-/react-diff-viewer-continued-4.2.2.tgz", "integrity": "sha512-8wT0/smXGox70oXJ7SZkTYF1p1Uh7jNGXhN7ykqrqPven0sZ+wM2c62SG3ZqORx5aW75te+WhmUWo0E4NyoSvg==", + "license": "MIT", "dependencies": { "@emotion/css": "^11.13.5", "@emotion/react": "^11.14.0", @@ -6678,6 +7046,7 @@ "version": "16.0.1", "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-16.0.1.tgz", "integrity": "sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q==", + "license": "MIT", "dependencies": { "@react-dnd/invariant": "^4.0.1", "@react-dnd/shallowequal": "^4.0.1", @@ -6707,6 +7076,7 @@ "version": "16.0.1", "resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-16.0.1.tgz", "integrity": "sha512-Wu3dw5aDJmOGw8WjH1I1/yTH+vlXEL4vmjk5p+MHxP8HuHJS1lAGeIdG/hze1AvNeXWo/JgULV87LyQOr+r5jw==", + "license": "MIT", "dependencies": { "dnd-core": "^16.0.1" } @@ -6728,6 +7098,7 @@ "version": "15.0.0", "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-15.0.0.tgz", "integrity": "sha512-lGjYV/EoqEjEWPnmiSvH4v5IoIAwQM2W4Z1C0Q/Pw2xD0eVzKPS359BQTUMum+1fa0kH2nrKjuavmTPOGhpLPg==", + "license": "MIT", "dependencies": { "attr-accept": "^2.2.4", "file-selector": "^2.1.0", @@ -6743,12 +7114,14 @@ "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==" + "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==" + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==", + "license": "MIT" }, "node_modules/react-overlays": { "version": "0.9.3", @@ -6768,19 +7141,11 @@ "react-dom": ">=16.3.0" } }, - "node_modules/react-overlays/node_modules/warning": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", - "integrity": "sha512-jMBt6pUrKn5I+OGgtQ4YZLdhIeJmObddh6CsibPxyQ5yPZm1XExSyzC1LCNX7BzhxWgiHmizBWJTHJIjMjTQYQ==", - "license": "BSD-3-Clause", - "dependencies": { - "loose-envify": "^1.0.0" - } - }, "node_modules/react-prop-types": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/react-prop-types/-/react-prop-types-0.4.0.tgz", "integrity": "sha512-IyjsJhDX9JkoOV9wlmLaS7z+oxYoIWhfzDcFy7inwoAKTu+VcVNrVpPmLeioJ94y6GeDRsnwarG1py5qofFQMg==", + "license": "MIT", "dependencies": { "warning": "^3.0.0" }, @@ -6788,18 +7153,11 @@ "react": ">=0.14.0" } }, - "node_modules/react-prop-types/node_modules/warning": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", - "integrity": "sha512-jMBt6pUrKn5I+OGgtQ4YZLdhIeJmObddh6CsibPxyQ5yPZm1XExSyzC1LCNX7BzhxWgiHmizBWJTHJIjMjTQYQ==", - "dependencies": { - "loose-envify": "^1.0.0" - } - }, "node_modules/react-redux": { "version": "8.1.3", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.3.tgz", "integrity": "sha512-n0ZrutD7DaX/j9VscF+uTALI3oUPa/pO4Z3soOBIjuRn/FzVu6aehhysxZCLi6y7duMf52WNZGMl7CtuK5EnRw==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.12.1", "@types/hoist-non-react-statics": "^3.3.1", @@ -6835,14 +7193,16 @@ } }, "node_modules/react-redux/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==" + "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/react-select": { "version": "5.10.2", "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.10.2.tgz", "integrity": "sha512-Z33nHdEFWq9tfnfVXaiM12rbJmk+QjFEztWLtmXqQhz6Al4UZZ9xc0wiatmGtUOCCnHN0WizL3tCMYRENX4rVQ==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.12.0", "@emotion/cache": "^11.4.0", @@ -6906,6 +7266,7 @@ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, + "license": "MIT", "optional": true, "dependencies": { "picomatch": "^2.2.1" @@ -6919,6 +7280,7 @@ "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", "dev": true, + "license": "MIT", "dependencies": { "resolve": "^1.20.0" }, @@ -6927,9 +7289,10 @@ } }, "node_modules/redux": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.0.tgz", - "integrity": "sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", + "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.9.2" } @@ -6938,6 +7301,7 @@ "version": "3.0.6", "resolved": "https://registry.npmjs.org/redux-logger/-/redux-logger-3.0.6.tgz", "integrity": "sha512-JoCIok7bg/XpqA1JqCqXFypuqBbQzGQySrhFzewB7ThcnysTO30l4VCst86AuB9T9tuT03MAA56Jw2PNhRSNCg==", + "license": "MIT", "dependencies": { "deep-diff": "^0.3.5" } @@ -6946,23 +7310,26 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz", "integrity": "sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==", + "license": "MIT", "peerDependencies": { "redux": "^4" } }, "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==", + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", "define-properties": "^1.2.1", - "es-abstract": "^1.23.1", + "es-abstract": "^1.23.9", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "globalthis": "^1.0.3", - "which-builtin-type": "^1.1.3" + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" }, "engines": { "node": ">= 0.4" @@ -6975,13 +7342,15 @@ "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/regenerate-unicode-properties": { "version": "10.2.2", "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.2.tgz", "integrity": "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==", "dev": true, + "license": "MIT", "dependencies": { "regenerate": "^1.4.2" }, @@ -6989,22 +7358,19 @@ "node": ">=4" } }, - "node_modules/regenerator-runtime": { - "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/regexp.prototype.flags": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", - "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.6", + "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-errors": "^1.3.0", - "set-function-name": "^2.0.1" + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -7018,6 +7384,7 @@ "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.4.0.tgz", "integrity": "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==", "dev": true, + "license": "MIT", "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", @@ -7034,13 +7401,15 @@ "version": "0.8.0", "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/regjsparser": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.13.1.tgz", "integrity": "sha512-dLsljMd9sqwRkby8zhO1gSg3PnJIBFid8f4CQj/sXx+7cKx+E7u0PKhZ+U4wmhx7EfmtvnA318oVaIkAB1lRJw==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "jsesc": "~3.1.0" }, @@ -7053,6 +7422,7 @@ "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==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -7061,6 +7431,7 @@ "version": "1.22.12", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", + "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "is-core-module": "^2.16.1", @@ -7082,6 +7453,7 @@ "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", "dev": true, + "license": "MIT", "dependencies": { "resolve-from": "^5.0.0" }, @@ -7089,15 +7461,25 @@ "node": ">=8" } }, - "node_modules/resolve-from": { + "node_modules/resolve-cwd/node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, + "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/reusify": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", @@ -7151,14 +7533,16 @@ } }, "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==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.4.tgz", + "integrity": "sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4", - "has-symbols": "^1.0.3", + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "get-intrinsic": "^1.3.0", + "has-symbols": "^1.1.0", "isarray": "^2.0.5" }, "engines": { @@ -7168,15 +7552,33 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/safe-regex-test": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", - "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.6", + "call-bound": "^1.0.2", "es-errors": "^1.3.0", - "is-regex": "^1.1.4" + "is-regex": "^1.2.1" }, "engines": { "node": ">= 0.4" @@ -7190,6 +7592,7 @@ "resolved": "https://registry.npmjs.org/sass/-/sass-1.99.0.tgz", "integrity": "sha512-kgW13M54DUB7IsIRM5LvJkNlpH+WhMpooUcaWGFARkF1Tc82v9mIWkCbCYf+MBvpIUBSeSOTilpZjEPr2VYE6Q==", "dev": true, + "license": "MIT", "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.1.5", @@ -7206,10 +7609,11 @@ } }, "node_modules/sass-loader": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-16.0.0.tgz", - "integrity": "sha512-n13Z+3rU9A177dk4888czcVFiC8CL9dii4qpXWUg3YIIgZEvi9TCFKjOQcbK0kJM7DJu9VucrZFddvNfYCPwtw==", + "version": "16.0.8", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-16.0.8.tgz", + "integrity": "sha512-hcov4ZwZJIGbEuyNr9EmiTmZueyrxSToE6GOzoZnq5JM7ecRO7ttyvilPn+VmRsqiP16+VYZzVnGZj/hzZgKBA==", "dev": true, + "license": "MIT", "dependencies": { "neo-async": "^2.6.2" }, @@ -7221,7 +7625,7 @@ "url": "https://opencollective.com/webpack" }, "peerDependencies": { - "@rspack/core": "0.x || 1.x", + "@rspack/core": "0.x || ^1.0.0 || ^2.0.0-0", "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0", "sass": "^1.3.0", "sass-embedded": "*", @@ -7246,10 +7650,11 @@ } }, "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==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", "dev": true, + "license": "MIT", "dependencies": { "readdirp": "^4.0.1" }, @@ -7261,12 +7666,13 @@ } }, "node_modules/sass/node_modules/readdirp": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.1.tgz", - "integrity": "sha512-GkMg9uOTpIWWKbSsgwb5fA4EavTR+SG/PMPoAY8hkhHfEEY0/vqljY+XHqtDf2cr2IJtoNRDbrrEpZUiZCkYRw==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", "dev": true, + "license": "MIT", "engines": { - "node": ">= 14.16.0" + "node": ">= 14.18.0" }, "funding": { "type": "individual", @@ -7287,6 +7693,7 @@ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", "dev": true, + "license": "MIT", "dependencies": { "@types/json-schema": "^7.0.9", "ajv": "^8.9.0", @@ -7302,9 +7709,9 @@ } }, "node_modules/schema-utils/node_modules/ajv": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", - "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "dev": true, "license": "MIT", "dependencies": { @@ -7323,6 +7730,7 @@ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3" }, @@ -7334,12 +7742,14 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/selderee": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/selderee/-/selderee-0.12.0.tgz", "integrity": "sha512-b1YMh3+DHZp59DLna3qVwQ5iOla/nrI6mLBNW02XxU77M3046Df6VLkoaJyFz20VsGIG5kkp+FK0kg4K4HnUFw==", + "license": "MIT", "dependencies": { "parseley": "~0.13.1" }, @@ -7352,6 +7762,7 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" } @@ -7361,22 +7772,24 @@ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-7.0.5.tgz", "integrity": "sha512-F4LcB0UqUl1zErq+1nYEEzSHJnIwb3AF2XWB94b+afhrekOUijwooAYqFyRbjYkm2PAKBabx6oYv/xDxNi8IBw==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=20.0.0" } }, "node_modules/set-function-length": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz", - "integrity": "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "dev": true, + "license": "MIT", "dependencies": { - "define-data-property": "^1.1.2", + "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.3", + "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.1" + "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -7387,6 +7800,7 @@ "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", "dev": true, + "license": "MIT", "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -7397,11 +7811,27 @@ "node": ">= 0.4" } }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "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": { "kind-of": "^6.0.2" }, @@ -7414,6 +7844,7 @@ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, + "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" }, @@ -7426,20 +7857,23 @@ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "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==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -7448,29 +7882,87 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", "dev": true, - "engines": { - "node": ">=6" - } + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "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==", + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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==", + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, "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==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -7480,29 +7972,56 @@ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, + "license": "MIT", "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, + "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==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/string.prototype.matchall": { - "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==", + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", + "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", + "es-abstract": "^1.23.6", "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.7", - "regexp.prototype.flags": "^1.5.2", + "get-intrinsic": "^1.2.6", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "regexp.prototype.flags": "^1.5.3", "set-function-name": "^2.0.2", - "side-channel": "^1.0.6" + "side-channel": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -7516,21 +8035,26 @@ "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", "dev": true, + "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==", + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", "define-properties": "^1.2.1", - "es-abstract": "^1.23.0", - "es-object-atoms": "^1.0.0" + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -7540,15 +8064,20 @@ } }, "node_modules/string.prototype.trimend": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", - "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -7558,6 +8087,7 @@ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -7597,34 +8127,35 @@ } }, "node_modules/style-mod": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz", - "integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==" + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.3.tgz", + "integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==", + "license": "MIT" }, "node_modules/stylis": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", - "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", + "license": "MIT" }, "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==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "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" }, @@ -7633,9 +8164,9 @@ } }, "node_modules/tabbable": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.3.0.tgz", - "integrity": "sha512-EIHvdY5bPLuWForiR/AN2Bxngzpuwn1is4asboytXtpTgsArc+WmSJKVLlhdh71u7jFcryDqB2A8lQvj78MkyQ==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.4.0.tgz", + "integrity": "sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==", "license": "MIT" }, "node_modules/tapable": { @@ -7643,6 +8174,7 @@ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" }, @@ -7652,10 +8184,11 @@ } }, "node_modules/terser": { - "version": "5.46.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.0.tgz", - "integrity": "sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==", + "version": "5.47.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.47.1.tgz", + "integrity": "sha512-tPbLXTI6ohPASb/1YViL428oEHu6/qv1OxqYnfaonVCFHqx4+wCd95pHrQWsL5X4pl90CTyW9piSAsS2L0VoMw==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.15.0", @@ -7670,9 +8203,9 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.3.17", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.17.tgz", - "integrity": "sha512-YR7PtUp6GMU91BgSJmlaX/rS2lGDbAF7D+Wtq7hRO+MiljNmodYvqslzCFiYVAgW+Qoaaia/QUIP4lGXufjdZw==", + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.6.0.tgz", + "integrity": "sha512-Eum+5ajkaOhf5KbM26osvv21kLD7BaGqQ1UA4Ami4arYwylmGUQTgHFpHDdmJod1q4QXa66p0to/FBKID+J1vA==", "dev": true, "license": "MIT", "dependencies": { @@ -7692,12 +8225,39 @@ "webpack": "^5.1.0" }, "peerDependenciesMeta": { + "@minify-html/node": { + "optional": true + }, "@swc/core": { "optional": true }, + "@swc/css": { + "optional": true + }, + "@swc/html": { + "optional": true + }, + "clean-css": { + "optional": true + }, + "cssnano": { + "optional": true + }, + "csso": { + "optional": true + }, "esbuild": { "optional": true }, + "html-minifier-terser": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "postcss": { + "optional": true + }, "uglify-js": { "optional": true } @@ -7707,7 +8267,8 @@ "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==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/text-table": { "version": "0.2.0", @@ -7717,14 +8278,14 @@ "license": "MIT" }, "node_modules/tinyglobby": { - "version": "0.2.12", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.12.tgz", - "integrity": "sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==", + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", "dev": true, "license": "MIT", "dependencies": { - "fdir": "^6.4.3", - "picomatch": "^4.0.2" + "fdir": "^6.5.0", + "picomatch": "^4.0.4" }, "engines": { "node": ">=12.0.0" @@ -7734,11 +8295,14 @@ } }, "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.4.3", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz", - "integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, "peerDependencies": { "picomatch": "^3 || ^4" }, @@ -7753,6 +8317,7 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -7765,6 +8330,7 @@ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, + "license": "MIT", "optional": true, "dependencies": { "is-number": "^7.0.0" @@ -7776,13 +8342,15 @@ "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==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, + "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1" }, @@ -7804,30 +8372,32 @@ } }, "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==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "is-typed-array": "^1.1.13" + "is-typed-array": "^1.1.14" }, "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==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" }, "engines": { "node": ">= 0.4" @@ -7837,17 +8407,19 @@ } }, "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==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", "dev": true, + "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" }, "engines": { "node": ">= 0.4" @@ -7857,17 +8429,18 @@ } }, "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==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "for-each": "^0.3.3", "gopd": "^1.0.1", - "has-proto": "^1.0.3", "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0" + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" }, "engines": { "node": ">= 0.4" @@ -7877,15 +8450,19 @@ } }, "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", + "call-bound": "^1.0.3", "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -7895,6 +8472,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", @@ -7905,11 +8483,19 @@ "react": ">=15.0.0" } }, + "node_modules/undici-types": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", + "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", + "devOptional": true, + "license": "MIT" + }, "node_modules/unicode-canonical-property-names-ecmascript": { "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==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } @@ -7919,6 +8505,7 @@ "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", "dev": true, + "license": "MIT", "dependencies": { "unicode-canonical-property-names-ecmascript": "^2.0.0", "unicode-property-aliases-ecmascript": "^2.0.0" @@ -7932,6 +8519,7 @@ "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.1.tgz", "integrity": "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } @@ -7941,6 +8529,7 @@ "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.2.0.tgz", "integrity": "sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } @@ -7964,6 +8553,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" @@ -7980,6 +8570,7 @@ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" } @@ -7988,6 +8579,7 @@ "version": "10.1.1", "resolved": "https://registry.npmjs.org/use-debounce/-/use-debounce-10.1.1.tgz", "integrity": "sha512-kvds8BHR2k28cFsxW8k3nc/tRga2rs1RHYCqmmGqb90MEeE++oALwzh2COiuBLO1/QXiOuShXoSN2ZpWnMmvuQ==", + "license": "MIT", "engines": { "node": ">= 16.0.0" }, @@ -7999,6 +8591,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.2.1.tgz", "integrity": "sha512-tpZZ+EX0gaghDAiFR37hj5MgY6ZN55kLiPkJsKxBMZ6GZdOSPJXiOzPM984oPYZ5AnehYx5WQp1+ME8I/P/pRA==", + "license": "MIT", "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, @@ -8012,6 +8605,7 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "license": "MIT", "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } @@ -8020,17 +8614,20 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/w3c-keyname": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.6.tgz", - "integrity": "sha512-f+fciywl1SJEniZHD6H+kUO8gOnwIr7f4ijKA6+ZvJFjeGi1r4PDLl53Ayud9O/rk64RqgoQine0feoeOU0kXg==" + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", + "license": "MIT" }, "node_modules/warning": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", - "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", + "integrity": "sha512-jMBt6pUrKn5I+OGgtQ4YZLdhIeJmObddh6CsibPxyQ5yPZm1XExSyzC1LCNX7BzhxWgiHmizBWJTHJIjMjTQYQ==", + "license": "BSD-3-Clause", "dependencies": { "loose-envify": "^1.0.0" } @@ -8040,6 +8637,7 @@ "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz", "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==", "dev": true, + "license": "MIT", "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" @@ -8049,12 +8647,12 @@ } }, "node_modules/webpack": { - "version": "5.106.2", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.106.2.tgz", - "integrity": "sha512-wGN3qcrBQIFmQ/c0AiOAQBvrZ5lmY8vbbMv4Mxfgzqd/B6+9pXtLo73WuS1dSGXM5QYY3hZnIbvx+K1xxe6FyA==", + "version": "5.107.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.107.0.tgz", + "integrity": "sha512-PSxeHk/dmLYZlnTU+vL1Gej6Evg5RNtl3flhxBresfznFnzxinHMzHKloHnywM/3ouQv7/AlZCswWDIkNSggUA==", "dev": true, + "license": "MIT", "dependencies": { - "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", "@types/json-schema": "^7.0.15", "@webassemblyjs/ast": "^1.14.1", @@ -8064,20 +8662,20 @@ "acorn-import-phases": "^1.0.3", "browserslist": "^4.28.1", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.20.0", - "es-module-lexer": "^2.0.0", + "enhanced-resolve": "^5.21.4", + "es-module-lexer": "^2.1.0", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.2.11", - "loader-runner": "^4.3.1", + "loader-runner": "^4.3.2", "mime-db": "^1.54.0", "neo-async": "^2.6.2", "schema-utils": "^4.3.3", "tapable": "^2.3.0", - "terser-webpack-plugin": "^5.3.17", + "terser-webpack-plugin": "^5.5.0", "watchpack": "^2.5.1", - "webpack-sources": "^3.3.4" + "webpack-sources": "^3.4.1" }, "bin": { "webpack": "bin/webpack.js" @@ -8100,6 +8698,7 @@ "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-7.0.2.tgz", "integrity": "sha512-dB0R4T+C/8YuvM+fabdvil6QE44/ChDXikV5lOOkrUeCkW5hTJv2pGLE3keh+D5hjYw8icBaJkZzpFoaHV4T+g==", "dev": true, + "license": "MIT", "dependencies": { "@discoveryjs/json-ext": "^1.0.0", "commander": "^14.0.3", @@ -8140,6 +8739,7 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", "dev": true, + "license": "MIT", "engines": { "node": ">=20" } @@ -8149,6 +8749,7 @@ "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-6.0.1.tgz", "integrity": "sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==", "dev": true, + "license": "MIT", "dependencies": { "clone-deep": "^4.0.1", "flat": "^5.0.2", @@ -8163,15 +8764,41 @@ "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.4.1.tgz", "integrity": "sha512-eACpxRN02yaawnt+uUNIF7Qje6A9zArxBbcAJjK1PK3S9Ycg5jIuJ8pW4q8EMnwNZCEGltcjkRx1QzOxOkKD8A==", "dev": true, + "license": "MIT", "engines": { "node": ">=10.13.0" } }, + "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==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "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==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, + "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, @@ -8183,39 +8810,45 @@ } }, "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==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", "dev": true, + "license": "MIT", "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/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==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", "dev": true, + "license": "MIT", "dependencies": { + "call-bound": "^1.0.2", "function.prototype.name": "^1.1.6", "has-tostringtag": "^1.0.2", "is-async-function": "^2.0.0", - "is-date-object": "^1.0.5", - "is-finalizationregistry": "^1.0.2", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", "is-generator-function": "^1.0.10", - "is-regex": "^1.1.4", + "is-regex": "^1.2.1", "is-weakref": "^1.0.2", "isarray": "^2.0.5", - "which-boxed-primitive": "^1.0.2", + "which-boxed-primitive": "^1.1.0", "which-collection": "^1.0.2", - "which-typed-array": "^1.1.15" + "which-typed-array": "^1.1.16" }, "engines": { "node": ">= 0.4" @@ -8229,6 +8862,7 @@ "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", "dev": true, + "license": "MIT", "dependencies": { "is-map": "^2.0.3", "is-set": "^2.0.3", @@ -8243,15 +8877,18 @@ } }, "node_modules/which-typed-array": { - "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==", + "version": "1.1.20", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz", + "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", "dev": true, + "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" }, "engines": { @@ -8265,24 +8902,38 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", - "dev": true + "dev": true, + "license": "MIT" + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" }, "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" }, "node_modules/yaml": { "version": "1.10.3", "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz", "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==", + "license": "ISC", "engines": { "node": ">= 6" } @@ -8292,6 +8943,7 @@ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -8299,5700 +8951,5 @@ "url": "https://github.com/sponsors/sindresorhus" } } - }, - "dependencies": { - "@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true - }, - "@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", - "dev": true, - "requires": { - "@jridgewell/gen-mapping": "^0.1.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@babel/cli": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.28.0.tgz", - "integrity": "sha512-CYrZG7FagtE8ReKDBfItxnrEBf2khq2eTMnPuqO8UVN0wzhp1eMX1wfda8b1a32l2aqYLwRRIOGNovm8FVzmMw==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.28", - "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents.3", - "chokidar": "^3.6.0", - "commander": "^6.2.0", - "convert-source-map": "^2.0.0", - "fs-readdir-recursive": "^1.1.0", - "glob": "^7.2.0", - "make-dir": "^2.1.0", - "slash": "^2.0.0" - }, - "dependencies": { - "convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true - } - } - }, - "@babel/code-frame": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", - "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", - "requires": { - "@babel/helper-validator-identifier": "^7.28.5", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - } - }, - "@babel/compat-data": { - "version": "7.29.3", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.3.tgz", - "integrity": "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==", - "devOptional": true - }, - "@babel/core": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", - "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", - "devOptional": true, - "requires": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helpers": "^7.28.6", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/traverse": "^7.29.0", - "@babel/types": "^7.29.0", - "@jridgewell/remapping": "^2.3.5", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "dependencies": { - "convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true - } - } - }, - "@babel/generator": { - "version": "7.29.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", - "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", - "requires": { - "@babel/parser": "^7.29.0", - "@babel/types": "^7.29.0", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" - } - }, - "@babel/helper-annotate-as-pure": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", - "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", - "dev": true, - "requires": { - "@babel/types": "^7.27.3" - } - }, - "@babel/helper-compilation-targets": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", - "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", - "devOptional": true, - "requires": { - "@babel/compat-data": "^7.28.6", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "dependencies": { - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - } - } - }, - "@babel/helper-create-class-features-plugin": { - "version": "7.29.3", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.29.3.tgz", - "integrity": "sha512-RpLYy2sb51oNLjuu1iD3bwBqCBWUzjO0ocp+iaCP/lJtb2CPLcnC2Fftw+4sAzaMELGeWTgExSKADbdo0GFVzA==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-member-expression-to-functions": "^7.28.5", - "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/helper-replace-supers": "^7.28.6", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/traverse": "^7.29.0", - "semver": "^6.3.1" - } - }, - "@babel/helper-create-regexp-features-plugin": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.28.5.tgz", - "integrity": "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "regexpu-core": "^6.3.1", - "semver": "^6.3.1" - } - }, - "@babel/helper-define-polyfill-provider": { - "version": "0.6.8", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.8.tgz", - "integrity": "sha512-47UwBLPpQi1NoWzLuHNjRoHlYXMwIJoBf7MFou6viC/sIHWYygpvr0B6IAyh5sBdA2nr2LPIRww8lfaUVQINBA==", - "dev": true, - "requires": { - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "debug": "^4.4.3", - "lodash.debounce": "^4.0.8", - "resolve": "^1.22.11" - } - }, - "@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==" - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz", - "integrity": "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==", - "dev": true, - "requires": { - "@babel/traverse": "^7.28.5", - "@babel/types": "^7.28.5" - } - }, - "@babel/helper-module-imports": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", - "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", - "requires": { - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6" - } - }, - "@babel/helper-module-transforms": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", - "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-validator-identifier": "^7.28.5", - "@babel/traverse": "^7.28.6" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", - "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", - "dev": true, - "requires": { - "@babel/types": "^7.27.1" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", - "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", - "dev": true - }, - "@babel/helper-remap-async-to-generator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz", - "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-wrap-function": "^7.27.1", - "@babel/traverse": "^7.27.1" - } - }, - "@babel/helper-replace-supers": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.28.6.tgz", - "integrity": "sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==", - "dev": true, - "requires": { - "@babel/helper-member-expression-to-functions": "^7.28.5", - "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/traverse": "^7.28.6" - } - }, - "@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", - "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", - "dev": true, - "requires": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - } - }, - "@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==" - }, - "@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==" - }, - "@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", - "dev": true - }, - "@babel/helper-wrap-function": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.28.6.tgz", - "integrity": "sha512-z+PwLziMNBeSQJonizz2AGnndLsP2DeGHIxDAn+wdHOGuo4Fo1x1HBPPXeE9TAOPHNNWQKCSlA2VZyYyyibDnQ==", - "dev": true, - "requires": { - "@babel/template": "^7.28.6", - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6" - } - }, - "@babel/helpers": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", - "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", - "devOptional": true, - "requires": { - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0" - } - }, - "@babel/parser": { - "version": "7.29.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz", - "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==", - "requires": { - "@babel/types": "^7.29.0" - } - }, - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.28.5.tgz", - "integrity": "sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.28.5" - } - }, - "@babel/plugin-bugfix-safari-class-field-initializer-scope": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.27.1.tgz", - "integrity": "sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.27.1", - "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.27.1.tgz", - "integrity": "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": { - "version": "7.29.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array/-/plugin-bugfix-safari-rest-destructuring-rhs-array-7.29.3.tgz", - "integrity": "sha512-SRS46DFR4HqzUzCVgi90/xMoL+zeBDBvWdKYXSEzh79kXswNFEglUpMKxR04//dPqwYXWUBJ3mpUd933ru9Kmg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - } - }, - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz", - "integrity": "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/plugin-transform-optional-chaining": "^7.27.1" - } - }, - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.6.tgz", - "integrity": "sha512-a0aBScVTlNaiUe35UtfxAN7A/tehvvG4/ByO6+46VPKTRSlfnAFsgKy0FUh+qAkQrDTmhDkT+IBOKlOoMUxQ0g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/traverse": "^7.28.6" - } - }, - "@babel/plugin-proposal-private-property-in-object": { - "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==", - "dev": true, - "requires": {} - }, - "@babel/plugin-syntax-import-assertions": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.28.6.tgz", - "integrity": "sha512-pSJUpFHdx9z5nqTSirOCMtYVP2wFgoWhP0p3g8ONK/4IHhLIBd0B9NYqAvIUAhq+OkhO4VM1tENCt0cjlsNShw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.28.6" - } - }, - "@babel/plugin-syntax-import-attributes": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz", - "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.28.6" - } - }, - "@babel/plugin-syntax-jsx": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", - "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@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==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-transform-arrow-functions": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz", - "integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-async-generator-functions": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.29.0.tgz", - "integrity": "sha512-va0VdWro4zlBr2JsXC+ofCPB2iG12wPtVGTWFx2WLDOM3nYQZZIGP82qku2eW/JR83sD+k2k+CsNtyEbUqhU6w==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-remap-async-to-generator": "^7.27.1", - "@babel/traverse": "^7.29.0" - } - }, - "@babel/plugin-transform-async-to-generator": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.28.6.tgz", - "integrity": "sha512-ilTRcmbuXjsMmcZ3HASTe4caH5Tpo93PkTxF9oG2VZsSWsahydmcEHhix9Ik122RcTnZnUzPbmux4wh1swfv7g==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-remap-async-to-generator": "^7.27.1" - } - }, - "@babel/plugin-transform-block-scoped-functions": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz", - "integrity": "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-block-scoping": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.6.tgz", - "integrity": "sha512-tt/7wOtBmwHPNMPu7ax4pdPz6shjFrmHDghvNC+FG9Qvj7D6mJcoRQIF5dy4njmxR941l6rgtvfSB2zX3VlUIw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.28.6" - } - }, - "@babel/plugin-transform-class-properties": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.28.6.tgz", - "integrity": "sha512-dY2wS3I2G7D697VHndN91TJr8/AAfXQNt5ynCTI/MpxMsSzHp+52uNivYT5wCPax3whc47DR8Ba7cmlQMg24bw==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6" - } - }, - "@babel/plugin-transform-class-static-block": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.6.tgz", - "integrity": "sha512-rfQ++ghVwTWTqQ7w8qyDxL1XGihjBss4CmTgGRCTAC9RIbhVpyp4fOeZtta0Lbf+dTNIVJer6ych2ibHwkZqsQ==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6" - } - }, - "@babel/plugin-transform-classes": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.6.tgz", - "integrity": "sha512-EF5KONAqC5zAqT783iMGuM2ZtmEBy+mJMOKl2BCvPZ2lVrwvXnB6o+OBWCS+CoeCCpVRF2sA2RBKUxvT8tQT5Q==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-globals": "^7.28.0", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-replace-supers": "^7.28.6", - "@babel/traverse": "^7.28.6" - } - }, - "@babel/plugin-transform-computed-properties": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.28.6.tgz", - "integrity": "sha512-bcc3k0ijhHbc2lEfpFHgx7eYw9KNXqOerKWfzbxEHUGKnS3sz9C4CNL9OiFN1297bDNfUiSO7DaLzbvHQQQ1BQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/template": "^7.28.6" - } - }, - "@babel/plugin-transform-destructuring": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.5.tgz", - "integrity": "sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.28.5" - } - }, - "@babel/plugin-transform-dotall-regex": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.28.6.tgz", - "integrity": "sha512-SljjowuNKB7q5Oayv4FoPzeB74g3QgLt8IVJw9ADvWy3QnUb/01aw8I4AVv8wYnPvQz2GDDZ/g3GhcNyDBI4Bg==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.28.5", - "@babel/helper-plugin-utils": "^7.28.6" - } - }, - "@babel/plugin-transform-duplicate-keys": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz", - "integrity": "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-duplicate-named-capturing-groups-regex": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.29.0.tgz", - "integrity": "sha512-zBPcW2lFGxdiD8PUnPwJjag2J9otbcLQzvbiOzDxpYXyCuYX9agOwMPGn1prVH0a4qzhCKu24rlH4c1f7yA8rw==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.28.5", - "@babel/helper-plugin-utils": "^7.28.6" - } - }, - "@babel/plugin-transform-dynamic-import": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.27.1.tgz", - "integrity": "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-explicit-resource-management": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.6.tgz", - "integrity": "sha512-Iao5Konzx2b6g7EPqTy40UZbcdXE126tTxVFr/nAIj+WItNxjKSYTEw3RC+A2/ZetmdJsgueL1KhaMCQHkLPIg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/plugin-transform-destructuring": "^7.28.5" - } - }, - "@babel/plugin-transform-exponentiation-operator": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.28.6.tgz", - "integrity": "sha512-WitabqiGjV/vJ0aPOLSFfNY1u9U3R7W36B03r5I2KoNix+a3sOhJ3pKFB3R5It9/UiK78NiO0KE9P21cMhlPkw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.28.6" - } - }, - "@babel/plugin-transform-export-namespace-from": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.27.1.tgz", - "integrity": "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-for-of": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz", - "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - } - }, - "@babel/plugin-transform-function-name": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz", - "integrity": "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==", - "dev": true, - "requires": { - "@babel/helper-compilation-targets": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.27.1" - } - }, - "@babel/plugin-transform-json-strings": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.28.6.tgz", - "integrity": "sha512-Nr+hEN+0geQkzhbdgQVPoqr47lZbm+5fCUmO70722xJZd0Mvb59+33QLImGj6F+DkK3xgDi1YVysP8whD6FQAw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.28.6" - } - }, - "@babel/plugin-transform-literals": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz", - "integrity": "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-logical-assignment-operators": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.28.6.tgz", - "integrity": "sha512-+anKKair6gpi8VsM/95kmomGNMD0eLz1NQ8+Pfw5sAwWH9fGYXT50E55ZpV0pHUHWf6IUTWPM+f/7AAff+wr9A==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.28.6" - } - }, - "@babel/plugin-transform-member-expression-literals": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz", - "integrity": "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-modules-amd": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz", - "integrity": "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-modules-commonjs": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.28.6.tgz", - "integrity": "sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6" - } - }, - "@babel/plugin-transform-modules-systemjs": { - "version": "7.29.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.4.tgz", - "integrity": "sha512-N7QmZ0xRZfjHOfZeQLJjwgX2zS9pdGHSVl/cjSGlo4dXMqvurfxXDMKY4RqEKzPozV78VMcd0lxyG13mlbKc4w==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-validator-identifier": "^7.28.5", - "@babel/traverse": "^7.29.0" - } - }, - "@babel/plugin-transform-modules-umd": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz", - "integrity": "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.29.0.tgz", - "integrity": "sha512-1CZQA5KNAD6ZYQLPw7oi5ewtDNxH/2vuCh+6SmvgDfhumForvs8a1o9n0UrEoBD8HU4djO2yWngTQlXl1NDVEQ==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.28.5", - "@babel/helper-plugin-utils": "^7.28.6" - } - }, - "@babel/plugin-transform-new-target": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz", - "integrity": "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.28.6.tgz", - "integrity": "sha512-3wKbRgmzYbw24mDJXT7N+ADXw8BC/imU9yo9c9X9NKaLF1fW+e5H1U5QjMUBe4Qo4Ox/o++IyUkl1sVCLgevKg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.28.6" - } - }, - "@babel/plugin-transform-numeric-separator": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.28.6.tgz", - "integrity": "sha512-SJR8hPynj8outz+SlStQSwvziMN4+Bq99it4tMIf5/Caq+3iOc0JtKyse8puvyXkk3eFRIA5ID/XfunGgO5i6w==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.28.6" - } - }, - "@babel/plugin-transform-object-rest-spread": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.6.tgz", - "integrity": "sha512-5rh+JR4JBC4pGkXLAcYdLHZjXudVxWMXbB6u6+E9lRL5TrGVbHt1TjxGbZ8CkmYw9zjkB7jutzOROArsqtncEA==", - "dev": true, - "requires": { - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/plugin-transform-destructuring": "^7.28.5", - "@babel/plugin-transform-parameters": "^7.27.7", - "@babel/traverse": "^7.28.6" - } - }, - "@babel/plugin-transform-object-super": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz", - "integrity": "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1" - } - }, - "@babel/plugin-transform-optional-catch-binding": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.28.6.tgz", - "integrity": "sha512-R8ja/Pyrv0OGAvAXQhSTmWyPJPml+0TMqXlO5w+AsMEiwb2fg3WkOvob7UxFSL3OIttFSGSRFKQsOhJ/X6HQdQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.28.6" - } - }, - "@babel/plugin-transform-optional-chaining": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.28.6.tgz", - "integrity": "sha512-A4zobikRGJTsX9uqVFdafzGkqD30t26ck2LmOzAuLL8b2x6k3TIqRiT2xVvA9fNmFeTX484VpsdgmKNA0bS23w==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - } - }, - "@babel/plugin-transform-parameters": { - "version": "7.27.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz", - "integrity": "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-private-methods": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.28.6.tgz", - "integrity": "sha512-piiuapX9CRv7+0st8lmuUlRSmX6mBcVeNQ1b4AYzJxfCMuBfB0vBXDiGSmm03pKJw1v6cZ8KSeM+oUnM6yAExg==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6" - } - }, - "@babel/plugin-transform-private-property-in-object": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.28.6.tgz", - "integrity": "sha512-b97jvNSOb5+ehyQmBpmhOCiUC5oVK4PMnpRvO7+ymFBoqYjeDHIU9jnrNUuwHOiL9RpGDoKBpSViarV+BU+eVA==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-create-class-features-plugin": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6" - } - }, - "@babel/plugin-transform-property-literals": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz", - "integrity": "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-react-display-name": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.28.0.tgz", - "integrity": "sha512-D6Eujc2zMxKjfa4Zxl4GHMsmhKKZ9VpcqIchJLvwTxad9zWIYulwYItBovpDOoNLISpcZSXoDJ5gaGbQUDqViA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-react-jsx": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.27.1.tgz", - "integrity": "sha512-2KH4LWGSrJIkVf5tSiBFYuXDAoWRq2MMwgivCf+93dd0GQi8RXLjKA/0EvRnVV5G0hrHczsquXuD01L8s6dmBw==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/plugin-syntax-jsx": "^7.27.1", - "@babel/types": "^7.27.1" - } - }, - "@babel/plugin-transform-react-jsx-development": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.27.1.tgz", - "integrity": "sha512-ykDdF5yI4f1WrAolLqeF3hmYU12j9ntLQl/AOG1HAS21jxyg1Q0/J/tpREuYLfatGdGmXp/3yS0ZA76kOlVq9Q==", - "dev": true, - "requires": { - "@babel/plugin-transform-react-jsx": "^7.27.1" - } - }, - "@babel/plugin-transform-react-pure-annotations": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.27.1.tgz", - "integrity": "sha512-JfuinvDOsD9FVMTHpzA/pBLisxpv1aSf+OIV8lgH3MuWrks19R27e6a6DipIg4aX1Zm9Wpb04p8wljfKrVSnPA==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-regenerator": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.29.0.tgz", - "integrity": "sha512-FijqlqMA7DmRdg/aINBSs04y8XNTYw/lr1gJ2WsmBnnaNw1iS43EPkJW+zK7z65auG3AWRFXWj+NcTQwYptUog==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.28.6" - } - }, - "@babel/plugin-transform-regexp-modifiers": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.28.6.tgz", - "integrity": "sha512-QGWAepm9qxpaIs7UM9FvUSnCGlb8Ua1RhyM4/veAxLwt3gMat/LSGrZixyuj4I6+Kn9iwvqCyPTtbdxanYoWYg==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.28.5", - "@babel/helper-plugin-utils": "^7.28.6" - } - }, - "@babel/plugin-transform-reserved-words": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz", - "integrity": "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-shorthand-properties": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz", - "integrity": "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-spread": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.28.6.tgz", - "integrity": "sha512-9U4QObUC0FtJl05AsUcodau/RWDytrU6uKgkxu09mLR9HLDAtUMoPuuskm5huQsoktmsYpI+bGmq+iapDcriKA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - } - }, - "@babel/plugin-transform-sticky-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz", - "integrity": "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-template-literals": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz", - "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-typeof-symbol": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz", - "integrity": "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-unicode-escapes": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz", - "integrity": "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-unicode-property-regex": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.28.6.tgz", - "integrity": "sha512-4Wlbdl/sIZjzi/8St0evF0gEZrgOswVO6aOzqxh1kDZOl9WmLrHq2HtGhnOJZmHZYKP8WZ1MDLCt5DAWwRo57A==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.28.5", - "@babel/helper-plugin-utils": "^7.28.6" - } - }, - "@babel/plugin-transform-unicode-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz", - "integrity": "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-unicode-sets-regex": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.28.6.tgz", - "integrity": "sha512-/wHc/paTUmsDYN7SZkpWxogTOBNnlx7nBQYfy6JJlCT7G3mVhltk3e++N7zV0XfgGsrqBxd4rJQt9H16I21Y1Q==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.28.5", - "@babel/helper-plugin-utils": "^7.28.6" - } - }, - "@babel/preset-env": { - "version": "7.29.5", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.29.5.tgz", - "integrity": "sha512-/69t2aEzGKHD76DyLbHysF/QH2LJOB8iFnYO37unDTKBTubzcMRv0f3H5EiN1Q6ajOd/eB7dAInF0qdFVS06kA==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.29.3", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-validator-option": "^7.27.1", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.28.5", - "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1", - "@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": "^7.29.3", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.6", - "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-import-assertions": "^7.28.6", - "@babel/plugin-syntax-import-attributes": "^7.28.6", - "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.27.1", - "@babel/plugin-transform-async-generator-functions": "^7.29.0", - "@babel/plugin-transform-async-to-generator": "^7.28.6", - "@babel/plugin-transform-block-scoped-functions": "^7.27.1", - "@babel/plugin-transform-block-scoping": "^7.28.6", - "@babel/plugin-transform-class-properties": "^7.28.6", - "@babel/plugin-transform-class-static-block": "^7.28.6", - "@babel/plugin-transform-classes": "^7.28.6", - "@babel/plugin-transform-computed-properties": "^7.28.6", - "@babel/plugin-transform-destructuring": "^7.28.5", - "@babel/plugin-transform-dotall-regex": "^7.28.6", - "@babel/plugin-transform-duplicate-keys": "^7.27.1", - "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.29.0", - "@babel/plugin-transform-dynamic-import": "^7.27.1", - "@babel/plugin-transform-explicit-resource-management": "^7.28.6", - "@babel/plugin-transform-exponentiation-operator": "^7.28.6", - "@babel/plugin-transform-export-namespace-from": "^7.27.1", - "@babel/plugin-transform-for-of": "^7.27.1", - "@babel/plugin-transform-function-name": "^7.27.1", - "@babel/plugin-transform-json-strings": "^7.28.6", - "@babel/plugin-transform-literals": "^7.27.1", - "@babel/plugin-transform-logical-assignment-operators": "^7.28.6", - "@babel/plugin-transform-member-expression-literals": "^7.27.1", - "@babel/plugin-transform-modules-amd": "^7.27.1", - "@babel/plugin-transform-modules-commonjs": "^7.28.6", - "@babel/plugin-transform-modules-systemjs": "^7.29.4", - "@babel/plugin-transform-modules-umd": "^7.27.1", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.29.0", - "@babel/plugin-transform-new-target": "^7.27.1", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.28.6", - "@babel/plugin-transform-numeric-separator": "^7.28.6", - "@babel/plugin-transform-object-rest-spread": "^7.28.6", - "@babel/plugin-transform-object-super": "^7.27.1", - "@babel/plugin-transform-optional-catch-binding": "^7.28.6", - "@babel/plugin-transform-optional-chaining": "^7.28.6", - "@babel/plugin-transform-parameters": "^7.27.7", - "@babel/plugin-transform-private-methods": "^7.28.6", - "@babel/plugin-transform-private-property-in-object": "^7.28.6", - "@babel/plugin-transform-property-literals": "^7.27.1", - "@babel/plugin-transform-regenerator": "^7.29.0", - "@babel/plugin-transform-regexp-modifiers": "^7.28.6", - "@babel/plugin-transform-reserved-words": "^7.27.1", - "@babel/plugin-transform-shorthand-properties": "^7.27.1", - "@babel/plugin-transform-spread": "^7.28.6", - "@babel/plugin-transform-sticky-regex": "^7.27.1", - "@babel/plugin-transform-template-literals": "^7.27.1", - "@babel/plugin-transform-typeof-symbol": "^7.27.1", - "@babel/plugin-transform-unicode-escapes": "^7.27.1", - "@babel/plugin-transform-unicode-property-regex": "^7.28.6", - "@babel/plugin-transform-unicode-regex": "^7.27.1", - "@babel/plugin-transform-unicode-sets-regex": "^7.28.6", - "@babel/preset-modules": "0.1.6-no-external-plugins", - "babel-plugin-polyfill-corejs2": "^0.4.15", - "babel-plugin-polyfill-corejs3": "^0.14.0", - "babel-plugin-polyfill-regenerator": "^0.6.6", - "core-js-compat": "^3.48.0", - "semver": "^6.3.1" - } - }, - "@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==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" - } - }, - "@babel/preset-react": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.28.5.tgz", - "integrity": "sha512-Z3J8vhRq7CeLjdC58jLv4lnZ5RKFUJWqH5emvxmv9Hv3BD1T9R/Im713R4MTKwvFaV74ejZ3sM01LyEKk4ugNQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-validator-option": "^7.27.1", - "@babel/plugin-transform-react-display-name": "^7.28.0", - "@babel/plugin-transform-react-jsx": "^7.27.1", - "@babel/plugin-transform-react-jsx-development": "^7.27.1", - "@babel/plugin-transform-react-pure-annotations": "^7.27.1" - } - }, - "@babel/runtime": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz", - "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==", - "requires": { - "regenerator-runtime": "^0.14.0" - } - }, - "@babel/runtime-corejs2": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.26.10.tgz", - "integrity": "sha512-JfoPiD7f/vvd/PaOfu5cr9CyzwDMPg4T0nX3MQr6IgTq49DhjvUcmjmjA7j6+xih1Evq+QKZnge1SoIlYozv/Q==", - "requires": { - "core-js": "^2.6.12", - "regenerator-runtime": "^0.14.0" - } - }, - "@babel/template": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", - "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", - "requires": { - "@babel/code-frame": "^7.28.6", - "@babel/parser": "^7.28.6", - "@babel/types": "^7.28.6" - } - }, - "@babel/traverse": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", - "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", - "requires": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0", - "debug": "^4.3.1" - } - }, - "@babel/types": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", - "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", - "requires": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" - } - }, - "@codemirror/autocomplete": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.4.2.tgz", - "integrity": "sha512-8WE2xp+D0MpWEv5lZ6zPW1/tf4AGb358T5GWYiKEuCP8MvFfT3tH2mIF9Y2yr2e3KbHuSvsVhosiEyqCpiJhZQ==", - "requires": { - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.6.0", - "@lezer/common": "^1.0.0" - } - }, - "@codemirror/commands": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.2.1.tgz", - "integrity": "sha512-FFiNKGuHA5O8uC6IJE5apI5rT9gyjlw4whqy4vlcX0wE/myxL6P1s0upwDhY4HtMWLOwzwsp0ap3bjdQhvfDOA==", - "requires": { - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.2.0", - "@codemirror/view": "^6.0.0", - "@lezer/common": "^1.0.0" - } - }, - "@codemirror/lang-css": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@codemirror/lang-css/-/lang-css-6.0.2.tgz", - "integrity": "sha512-4V4zmUOl2Glx0GWw0HiO1oGD4zvMlIQ3zx5hXOE6ipCjhohig2bhWRAasrZylH9pRNTcl1VMa59Lsl8lZWlTzw==", - "requires": { - "@codemirror/autocomplete": "^6.0.0", - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@lezer/css": "^1.0.0" - } - }, - "@codemirror/lang-html": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.2.tgz", - "integrity": "sha512-bqCBASkteKySwtIbiV/WCtGnn/khLRbbiV5TE+d9S9eQJD7BA4c5dTRm2b3bVmSpilff5EYxvB4PQaZzM/7cNw==", - "requires": { - "@codemirror/autocomplete": "^6.0.0", - "@codemirror/lang-css": "^6.0.0", - "@codemirror/lang-javascript": "^6.0.0", - "@codemirror/language": "^6.4.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.2.2", - "@lezer/common": "^1.0.0", - "@lezer/css": "^1.1.0", - "@lezer/html": "^1.3.0" - } - }, - "@codemirror/lang-javascript": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.2.tgz", - "integrity": "sha512-VGQfY+FCc285AhWuwjYxQyUQcYurWlxdKYT4bqwr3Twnd5wP5WSeu52t4tvvuWmljT4EmgEgZCqSieokhtY8hg==", - "requires": { - "@codemirror/autocomplete": "^6.0.0", - "@codemirror/language": "^6.6.0", - "@codemirror/lint": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.17.0", - "@lezer/common": "^1.0.0", - "@lezer/javascript": "^1.0.0" - } - }, - "@codemirror/language": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.6.0.tgz", - "integrity": "sha512-cwUd6lzt3MfNYOobdjf14ZkLbJcnv4WtndYaoBkbor/vF+rCNguMPK0IRtvZJG4dsWiaWPcK8x1VijhvSxnstg==", - "requires": { - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", - "@lezer/common": "^1.0.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0", - "style-mod": "^4.0.0" - } - }, - "@codemirror/lint": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.1.1.tgz", - "integrity": "sha512-e+M543x0NVHGayNHQzLP4XByJsvbu/ojY6+0VF2Y4Uu66Rt1nADuxNflZwECLf7gS009smIsptSUa6bUj/U/rw==", - "requires": { - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", - "crelt": "^1.0.5" - } - }, - "@codemirror/search": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.2.3.tgz", - "integrity": "sha512-V9n9233lopQhB1dyjsBK2Wc1i+8hcCqxl1wQ46c5HWWLePoe4FluV3TGHoZ04rBRlGjNyz9DTmpJErig8UE4jw==", - "requires": { - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", - "crelt": "^1.0.5" - } - }, - "@codemirror/state": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.4.1.tgz", - "integrity": "sha512-QkEyUiLhsJoZkbumGZlswmAhA7CBU02Wrz7zvH4SrcifbsqwlXShVXg65f3v/ts57W3dqyamEriMhij1Z3Zz4A==" - }, - "@codemirror/theme-one-dark": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.1.tgz", - "integrity": "sha512-+CfzmScfJuD6uDF5bHJkAjWTQ2QAAHxODCPxUEgcImDYcJLT+4l5vLnBHmDVv46kCC5uUJGMrBJct2Z6JbvqyQ==", - "requires": { - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", - "@lezer/highlight": "^1.0.0" - } - }, - "@codemirror/view": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.24.1.tgz", - "integrity": "sha512-sBfP4rniPBRQzNakwuQEqjEuiJDWJyF2kqLLqij4WXRoVwPPJfjx966Eq3F7+OPQxDtMt/Q9MWLoZLWjeveBlg==", - "requires": { - "@codemirror/state": "^6.4.0", - "style-mod": "^4.1.0", - "w3c-keyname": "^2.2.4" - } - }, - "@discoveryjs/json-ext": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-1.1.0.tgz", - "integrity": "sha512-Xc3VhU02wqZ1HvHRJUwL09HkZSTvidqY5Ya0NXBSYOxAp+Ln9dcJr9fySI+CkONzP3PekQo9WdzCv0PGER/mOA==", - "dev": true - }, - "@emotion/babel-plugin": { - "version": "11.13.5", - "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", - "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==", - "requires": { - "@babel/helper-module-imports": "^7.16.7", - "@babel/runtime": "^7.18.3", - "@emotion/hash": "^0.9.2", - "@emotion/memoize": "^0.9.0", - "@emotion/serialize": "^1.3.3", - "babel-plugin-macros": "^3.1.0", - "convert-source-map": "^1.5.0", - "escape-string-regexp": "^4.0.0", - "find-root": "^1.1.0", - "source-map": "^0.5.7", - "stylis": "4.2.0" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==" - } - } - }, - "@emotion/cache": { - "version": "11.14.0", - "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", - "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==", - "requires": { - "@emotion/memoize": "^0.9.0", - "@emotion/sheet": "^1.4.0", - "@emotion/utils": "^1.4.2", - "@emotion/weak-memoize": "^0.4.0", - "stylis": "4.2.0" - } - }, - "@emotion/css": { - "version": "11.13.5", - "resolved": "https://registry.npmjs.org/@emotion/css/-/css-11.13.5.tgz", - "integrity": "sha512-wQdD0Xhkn3Qy2VNcIzbLP9MR8TafI0MJb7BEAXKp+w4+XqErksWR4OXomuDzPsN4InLdGhVe6EYcn2ZIUCpB8w==", - "requires": { - "@emotion/babel-plugin": "^11.13.5", - "@emotion/cache": "^11.13.5", - "@emotion/serialize": "^1.3.3", - "@emotion/sheet": "^1.4.0", - "@emotion/utils": "^1.4.2" - } - }, - "@emotion/hash": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", - "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==" - }, - "@emotion/memoize": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", - "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==" - }, - "@emotion/react": { - "version": "11.14.0", - "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", - "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", - "requires": { - "@babel/runtime": "^7.18.3", - "@emotion/babel-plugin": "^11.13.5", - "@emotion/cache": "^11.14.0", - "@emotion/serialize": "^1.3.3", - "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", - "@emotion/utils": "^1.4.2", - "@emotion/weak-memoize": "^0.4.0", - "hoist-non-react-statics": "^3.3.1" - } - }, - "@emotion/serialize": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", - "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==", - "requires": { - "@emotion/hash": "^0.9.2", - "@emotion/memoize": "^0.9.0", - "@emotion/unitless": "^0.10.0", - "@emotion/utils": "^1.4.2", - "csstype": "^3.0.2" - } - }, - "@emotion/sheet": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", - "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==" - }, - "@emotion/unitless": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", - "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==" - }, - "@emotion/use-insertion-effect-with-fallbacks": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz", - "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==", - "requires": {} - }, - "@emotion/utils": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", - "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==" - }, - "@emotion/weak-memoize": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", - "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==" - }, - "@eslint-community/eslint-utils": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", - "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^3.4.3" - } - }, - "@eslint-community/regexpp": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", - "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", - "dev": true - }, - "@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", - "dev": true, - "requires": { - "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" - } - }, - "@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==", - "dev": true - }, - "@floating-ui/core": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz", - "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==", - "requires": { - "@floating-ui/utils": "^0.2.10" - } - }, - "@floating-ui/dom": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz", - "integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==", - "requires": { - "@floating-ui/core": "^1.7.3", - "@floating-ui/utils": "^0.2.10" - } - }, - "@floating-ui/react": { - "version": "0.27.16", - "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.27.16.tgz", - "integrity": "sha512-9O8N4SeG2z++TSM8QA/KTeKFBVCNEz/AGS7gWPJf6KFRzmRWixFRnCnkPHRDwSVZW6QPDO6uT0P2SpWNKCc9/g==", - "requires": { - "@floating-ui/react-dom": "^2.1.6", - "@floating-ui/utils": "^0.2.10", - "tabbable": "^6.0.0" - } - }, - "@floating-ui/react-dom": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.6.tgz", - "integrity": "sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==", - "requires": { - "@floating-ui/dom": "^1.7.4" - } - }, - "@floating-ui/utils": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", - "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==" - }, - "@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==", - "dev": true, - "requires": { - "@humanwhocodes/object-schema": "^2.0.3", - "debug": "^4.3.1", - "minimatch": "^3.0.5" - } - }, - "@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true - }, - "@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==", - "dev": true - }, - "@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "requires": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "devOptional": true, - "requires": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==" - }, - "@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true - }, - "@jridgewell/source-map": { - "version": "0.3.11", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", - "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", - "dev": true, - "requires": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25" - } - }, - "@jridgewell/sourcemap-codec": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", - "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==" - }, - "@jridgewell/trace-mapping": { - "version": "0.3.29", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", - "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", - "requires": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "@lezer/common": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.0.2.tgz", - "integrity": "sha512-SVgiGtMnMnW3ActR8SXgsDhw7a0w0ChHSYAyAUxxrOiJ1OqYWEKk/xJd84tTSPo1mo6DXLObAJALNnd0Hrv7Ng==" - }, - "@lezer/css": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.1.1.tgz", - "integrity": "sha512-mSjx+unLLapEqdOYDejnGBokB5+AiJKZVclmud0MKQOKx3DLJ5b5VTCstgDDknR6iIV4gVrN6euzsCnj0A2gQA==", - "requires": { - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0" - } - }, - "@lezer/highlight": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.1.3.tgz", - "integrity": "sha512-3vLKLPThO4td43lYRBygmMY18JN3CPh9w+XS2j8WC30vR4yZeFG4z1iFe4jXE43NtGqe//zHW5q8ENLlHvz9gw==", - "requires": { - "@lezer/common": "^1.0.0" - } - }, - "@lezer/html": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.3.tgz", - "integrity": "sha512-04Fyvu66DjV2EjhDIG1kfDdktn5Pfw56SXPrzKNQH5B2m7BDfc6bDsz+ZJG8dLS3kIPEKbyyq1Sm2/kjeG0+AA==", - "requires": { - "@lezer/common": "^1.0.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0" - } - }, - "@lezer/javascript": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.4.1.tgz", - "integrity": "sha512-Hqx36DJeYhKtdpc7wBYPR0XF56ZzIp0IkMO/zNNj80xcaFOV4Oj/P7TQc/8k2TxNhzl7tV5tXS8ZOCPbT4L3nA==", - "requires": { - "@lezer/highlight": "^1.1.3", - "@lezer/lr": "^1.3.0" - } - }, - "@lezer/lr": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.3.3.tgz", - "integrity": "sha512-JPQe3mwJlzEVqy67iQiiGozhcngbO8QBgpqZM6oL1Wj/dXckrEexpBLeFkq0edtW5IqnPRFxA24BHJni8Js69w==", - "requires": { - "@lezer/common": "^1.0.0" - } - }, - "@nicolo-ribaudo/chokidar-2": { - "version": "2.1.8-no-fsevents.3", - "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz", - "integrity": "sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ==", - "dev": true, - "optional": true - }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true - }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } - }, - "@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, - "optional": true, - "requires": { - "@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", - "detect-libc": "^1.0.3", - "is-glob": "^4.0.3", - "micromatch": "^4.0.5", - "node-addon-api": "^7.0.0" - } - }, - "@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==", - "dev": true, - "optional": true - }, - "@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==", - "dev": true, - "optional": true - }, - "@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==", - "dev": true, - "optional": true - }, - "@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==", - "dev": true, - "optional": true - }, - "@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==", - "dev": true, - "optional": true - }, - "@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==", - "dev": true, - "optional": true - }, - "@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==", - "dev": true, - "optional": true - }, - "@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==", - "dev": true, - "optional": true - }, - "@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==", - "dev": true, - "optional": true - }, - "@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==", - "dev": true, - "optional": true - }, - "@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==", - "dev": true, - "optional": true - }, - "@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==", - "dev": true, - "optional": true - }, - "@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==", - "dev": true, - "optional": true - }, - "@react-dnd/asap": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-5.0.2.tgz", - "integrity": "sha512-WLyfoHvxhs0V9U+GTsGilGgf2QsPl6ZZ44fnv0/b8T3nQyvzxidxsg/ZltbWssbsRDlYW8UKSQMTGotuTotZ6A==" - }, - "@react-dnd/invariant": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@react-dnd/invariant/-/invariant-4.0.2.tgz", - "integrity": "sha512-xKCTqAK/FFauOM9Ta2pswIyT3D8AQlfrYdOi/toTPEhqCuAs1v5tcJ3Y08Izh1cJ5Jchwy9SeAXmMg6zrKs2iw==" - }, - "@react-dnd/shallowequal": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-4.0.2.tgz", - "integrity": "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==" - }, - "@selderee/plugin-htmlparser2": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.12.0.tgz", - "integrity": "sha512-oELmoyA6ML9jDRMV3kgcMQFKxUfBU0yFVn6yTctVaLT5ygXnxH52I3TZEgV9EhXJC68/uFvE5Daj1/25c0Xa/A==", - "requires": { - "domelementtype": "~2.3.0", - "domhandler": "~5.0.3" - } - }, - "@types/eslint": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", - "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", - "dev": true, - "requires": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "@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==", - "dev": true, - "requires": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, - "@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true - }, - "@types/hoist-non-react-statics": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", - "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", - "requires": { - "@types/react": "*", - "hoist-non-react-statics": "^3.3.0" - } - }, - "@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true - }, - "@types/node": { - "version": "17.0.31", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.31.tgz", - "integrity": "sha512-AR0x5HbXGqkEx9CadRH3EBYx/VkiUgZIhP4wvPn/+5KIsgpNoyFaRlVe0Zlx9gRtg8fA06a9tskE2MSN7TcG4Q==", - "devOptional": true - }, - "@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==" - }, - "@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==" - }, - "@types/react": { - "version": "18.0.8", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.8.tgz", - "integrity": "sha512-+j2hk9BzCOrrOSJASi5XiOyBbERk9jG5O73Ya4M0env5Ixi6vUNli4qy994AINcEF+1IEHISYFfIT4zwr++LKw==", - "requires": { - "@types/prop-types": "*", - "@types/scheduler": "*", - "csstype": "^3.0.2" - } - }, - "@types/react-transition-group": { - "version": "4.4.4", - "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.4.tgz", - "integrity": "sha512-7gAPz7anVK5xzbeQW9wFBDg7G++aPLAFY0QaSMOou9rJZpbuI58WAuJrgu+qR92l61grlnCUe7AFX8KGahAgug==", - "requires": { - "@types/react": "*" - } - }, - "@types/scheduler": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", - "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" - }, - "@types/use-sync-external-store": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", - "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==" - }, - "@uiw/codemirror-extensions-basic-setup": { - "version": "4.25.1", - "resolved": "https://registry.npmjs.org/@uiw/codemirror-extensions-basic-setup/-/codemirror-extensions-basic-setup-4.25.1.tgz", - "integrity": "sha512-zxgA2QkvP3ZDKxTBc9UltNFTrSeFezGXcZtZj6qcsBxiMzowoEMP5mVwXcKjpzldpZVRuY+JCC+RsekEgid4vg==", - "requires": { - "@codemirror/autocomplete": "^6.0.0", - "@codemirror/commands": "^6.0.0", - "@codemirror/language": "^6.0.0", - "@codemirror/lint": "^6.0.0", - "@codemirror/search": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0" - } - }, - "@uiw/react-codemirror": { - "version": "4.25.1", - "resolved": "https://registry.npmjs.org/@uiw/react-codemirror/-/react-codemirror-4.25.1.tgz", - "integrity": "sha512-eESBKHndoYkaEGlKCwRO4KrnTw1HkWBxVpEeqntoWTpoFEUYxdLWUYmkPBVk4/u8YzVy9g91nFfIRpqe5LjApg==", - "requires": { - "@babel/runtime": "^7.18.6", - "@codemirror/commands": "^6.1.0", - "@codemirror/state": "^6.1.1", - "@codemirror/theme-one-dark": "^6.0.0", - "@uiw/codemirror-extensions-basic-setup": "4.25.1", - "codemirror": "^6.0.0" - } - }, - "@ungap/structured-clone": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.1.tgz", - "integrity": "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==", - "dev": true - }, - "@webassemblyjs/ast": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", - "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", - "dev": true, - "requires": { - "@webassemblyjs/helper-numbers": "1.13.2", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2" - } - }, - "@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==", - "dev": true - }, - "@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==", - "dev": true - }, - "@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==", - "dev": true - }, - "@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==", - "dev": true, - "requires": { - "@webassemblyjs/floating-point-hex-parser": "1.13.2", - "@webassemblyjs/helper-api-error": "1.13.2", - "@xtuc/long": "4.2.2" - } - }, - "@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==", - "dev": true - }, - "@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==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/wasm-gen": "1.14.1" - } - }, - "@webassemblyjs/ieee754": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", - "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", - "dev": true, - "requires": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "@webassemblyjs/leb128": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", - "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", - "dev": true, - "requires": { - "@xtuc/long": "4.2.2" - } - }, - "@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==", - "dev": true - }, - "@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==", - "dev": true, - "requires": { - "@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" - } - }, - "@webassemblyjs/wasm-gen": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", - "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", - "dev": true, - "requires": { - "@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" - } - }, - "@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==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/wasm-gen": "1.14.1", - "@webassemblyjs/wasm-parser": "1.14.1" - } - }, - "@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==", - "dev": true, - "requires": { - "@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" - } - }, - "@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==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.14.1", - "@xtuc/long": "4.2.2" - } - }, - "@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true - }, - "@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==", - "dev": true - }, - "acorn": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", - "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", - "dev": true - }, - "acorn-import-phases": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", - "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", - "dev": true, - "requires": {} - }, - "acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "requires": {} - }, - "ajv": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", - "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "dev": true, - "requires": { - "ajv": "^8.0.0" - }, - "dependencies": { - "ajv": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", - "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - } - }, - "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==", - "dev": true - } - } - }, - "ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "requires": {} - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, - "optional": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, - "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==", - "dev": true, - "requires": { - "call-bind": "^1.0.5", - "is-array-buffer": "^3.0.4" - } - }, - "array-includes": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", - "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", - "dev": true, - "requires": { - "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" - } - }, - "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==", - "dev": true, - "requires": { - "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" - } - }, - "array.prototype.flatmap": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", - "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - } - }, - "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==", - "dev": true, - "requires": { - "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" - } - }, - "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==", - "dev": true, - "requires": { - "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" - } - }, - "attr-accept": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.5.tgz", - "integrity": "sha512-0bDNnY/u6pPwHDMoF0FieU354oBi0a8rD9FcsLwzcGWbc8KS8KPIi7y+s13OlVY+gMWc/9xEMUgNE6Qm8ZllYQ==" - }, - "available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", - "dev": true, - "requires": { - "possible-typed-array-names": "^1.0.0" - } - }, - "babel-loader": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-10.1.1.tgz", - "integrity": "sha512-JwKSzk2kjIe7mgPK+/lyZ2QAaJcpahNAdM+hgR2HI8D0OJVkdj8Rl6J3kaLYki9pwF7P2iWnD8qVv80Lq1ABtg==", - "dev": true, - "requires": { - "find-up": "^5.0.0" - }, - "dependencies": { - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "requires": { - "p-locate": "^5.0.0" - } - }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "requires": { - "p-limit": "^3.0.2" - } - } - } - }, - "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==", - "requires": { - "@babel/runtime": "^7.12.5", - "cosmiconfig": "^7.0.0", - "resolve": "^1.19.0" - } - }, - "babel-plugin-polyfill-corejs2": { - "version": "0.4.17", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.17.tgz", - "integrity": "sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.28.6", - "@babel/helper-define-polyfill-provider": "^0.6.8", - "semver": "^6.3.1" - } - }, - "babel-plugin-polyfill-corejs3": { - "version": "0.14.2", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.14.2.tgz", - "integrity": "sha512-coWpDLJ410R781Npmn/SIBZEsAetR4xVi0SxLMXPaMO4lSf1MwnkGYMtkFxew0Dn8B3/CpbpYxN0JCgg8mn67g==", - "dev": true, - "requires": { - "@babel/helper-define-polyfill-provider": "^0.6.8", - "core-js-compat": "^3.48.0" - } - }, - "babel-plugin-polyfill-regenerator": { - "version": "0.6.8", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.8.tgz", - "integrity": "sha512-M762rNHfSF1EV3SLtnCJXFoQbbIIz0OyRwnCmV0KPC7qosSfCO0QLTSuJX3ayAebubhE6oYBAYPrBA5ljowaZg==", - "dev": true, - "requires": { - "@babel/helper-define-polyfill-provider": "^0.6.8" - } - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "baseline-browser-mapping": { - "version": "2.9.19", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz", - "integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==", - "dev": true - }, - "big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "dev": true - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, - "optional": true - }, - "bootstrap-sass": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/bootstrap-sass/-/bootstrap-sass-3.4.3.tgz", - "integrity": "sha512-vPgFnGMp1jWZZupOND65WS6mkR8rxhJxndT/AcMbqcq1hHMdkcH4sMPhznLzzoHOHkSCrd6J9F8pWBriPCKP2Q==" - }, - "brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "optional": true, - "requires": { - "fill-range": "^7.1.1" - } - }, - "browserslist": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", - "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", - "dev": true, - "requires": { - "baseline-browser-mapping": "^2.9.0", - "caniuse-lite": "^1.0.30001759", - "electron-to-chromium": "^1.5.263", - "node-releases": "^2.0.27", - "update-browserslist-db": "^1.2.0" - } - }, - "buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", - "dev": true, - "requires": { - "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" - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" - }, - "caniuse-lite": { - "version": "1.0.30001769", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001769.tgz", - "integrity": "sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg==", - "dev": true - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "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, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "optional": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.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" - } - }, - "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==", - "dev": true - }, - "classnames": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", - "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==" - }, - "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, - "requires": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - } - }, - "clsx": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==" - }, - "codemirror": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.1.tgz", - "integrity": "sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==", - "requires": { - "@codemirror/autocomplete": "^6.0.0", - "@codemirror/commands": "^6.0.0", - "@codemirror/language": "^6.0.0", - "@codemirror/lint": "^6.0.0", - "@codemirror/search": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "commander": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", - "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "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==" - }, - "copy-webpack-plugin": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-14.0.0.tgz", - "integrity": "sha512-3JLW90aBGeaTLpM7mYQKpnVdgsUZRExY55giiZgLuX/xTQRUs1dOCwbBnWnvY6Q6rfZoXMNwzOQJCSZPppfqXA==", - "dev": true, - "requires": { - "glob-parent": "^6.0.1", - "normalize-path": "^3.0.0", - "schema-utils": "^4.2.0", - "serialize-javascript": "^7.0.3", - "tinyglobby": "^0.2.12" - }, - "dependencies": { - "glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "requires": { - "is-glob": "^4.0.3" - } - } - } - }, - "core-js": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", - "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==" - }, - "core-js-compat": { - "version": "3.49.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.49.0.tgz", - "integrity": "sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==", - "dev": true, - "requires": { - "browserslist": "^4.28.1" - } - }, - "cosmiconfig": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", - "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", - "requires": { - "@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" - } - }, - "crelt": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.5.tgz", - "integrity": "sha512-+BO9wPPi+DWTDcNYhr/W90myha8ptzftZT+LwcmUbbok0rcP/fequmFYCw8NMoH7pkAZQzU78b3kYrlua5a9eA==" - }, - "cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "css-loader": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.1.tgz", - "integrity": "sha512-OxIR5P2mjO1PSXk44bWuQ8XtMK4dpEqpIyERCx3ewOo3I8EmbcxMPUc5ScLtQfgXtOojoMv57So4V/C02HQLsw==", - "dev": true, - "requires": { - "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" - }, - "dependencies": { - "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==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true - }, - "csstype": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz", - "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==" - }, - "data-view-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", - "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", - "dev": true, - "requires": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - } - }, - "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==", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - } - }, - "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==", - "dev": true, - "requires": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - } - }, - "date-fns": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", - "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==" - }, - "debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "requires": { - "ms": "^2.1.3" - } - }, - "deep-diff": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/deep-diff/-/deep-diff-0.3.8.tgz", - "integrity": "sha512-yVn6RZmHiGnxRKR9sJb3iVV2XTF1Ghh2DiWRZ3dMnGc43yUdWWF/kX6lQyk3+P84iprfWKU/8zFTrlkvtFm1ug==" - }, - "deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "deepmerge-ts": { - "version": "7.1.5", - "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz", - "integrity": "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==" - }, - "define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dev": true, - "requires": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - } - }, - "define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "dev": true, - "requires": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - } - }, - "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, - "optional": true - }, - "diff": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-9.0.0.tgz", - "integrity": "sha512-svtcdpS8CgJyqAjEQIXdb3OjhFVVYjzGAPO8WGCmRbrml64SPw/jJD4GoE98aR7r25A0XcgrK3F02yw9R/vhQw==" - }, - "dnd-core": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-16.0.1.tgz", - "integrity": "sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng==", - "requires": { - "@react-dnd/asap": "^5.0.1", - "@react-dnd/invariant": "^4.0.1", - "redux": "^4.2.0" - } - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "dom-helpers": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", - "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==", - "requires": { - "@babel/runtime": "^7.1.2" - } - }, - "dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "requires": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - } - }, - "domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==" - }, - "domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "requires": { - "domelementtype": "^2.3.0" - } - }, - "domutils": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", - "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", - "requires": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - } - }, - "electron-to-chromium": { - "version": "1.5.286", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz", - "integrity": "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==", - "dev": true - }, - "emojis-list": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", - "dev": true - }, - "enhanced-resolve": { - "version": "5.21.5", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.21.5.tgz", - "integrity": "sha512-mLCNbrQli11K1ySUmuNt4ZUB3OpGIDq4q2vTBTf5cL2lpsRjI9QKqSD0ndjW8FyvcW/Jj46gMe9syyHAsvMa/A==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.4", - "tapable": "^2.3.3" - } - }, - "entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==" - }, - "envinfo": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.14.0.tgz", - "integrity": "sha512-CO40UI41xDQzhLB1hWyqUKgFhs250pNcGbyGKe1l/e4FSaI/+YE4IMG76GDt0In67WLPACIITC+sOi08x4wIvg==", - "dev": true - }, - "error-ex": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", - "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "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==", - "dev": true, - "requires": { - "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" - } - }, - "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==", - "dev": true, - "requires": { - "get-intrinsic": "^1.2.4" - } - }, - "es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==" - }, - "es-iterator-helpers": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.1.0.tgz", - "integrity": "sha512-/SurEfycdyssORP/E+bj4sEu1CWw4EmLDsHynHwSXQ7utgbrMRWW195pTrCjFgFCddf/UkYm3oqKPRq5i8bJbw==", - "dev": true, - "requires": { - "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", - "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" - } - }, - "es-module-lexer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", - "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==", - "dev": true - }, - "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==", - "dev": true, - "requires": { - "es-errors": "^1.3.0" - } - }, - "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==", - "dev": true, - "requires": { - "get-intrinsic": "^1.2.4", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.1" - } - }, - "es-shim-unscopables": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", - "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", - "dev": true, - "requires": { - "hasown": "^2.0.0" - } - }, - "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==", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true - }, - "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==" - }, - "eslint": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", - "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", - "dev": true, - "requires": { - "@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" - }, - "dependencies": { - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - } - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - }, - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "requires": { - "is-glob": "^4.0.3" - } - }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "requires": { - "p-locate": "^5.0.0" - } - }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "requires": { - "p-limit": "^3.0.2" - } - } - } - }, - "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==", - "dev": true, - "requires": { - "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" - }, - "dependencies": { - "doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - }, - "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==", - "dev": true, - "requires": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - } - } - }, - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, - "eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true - }, - "espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dev": true, - "requires": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - } - }, - "esquery": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", - "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } - } - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "requires": { - "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true - }, - "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==" - }, - "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==", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "fast-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", - "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", - "dev": true - }, - "fastest-levenshtein": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz", - "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==", - "dev": true - }, - "fastq": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", - "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", - "dev": true, - "requires": { - "reusify": "^1.0.4" - } - }, - "file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "requires": { - "flat-cache": "^3.0.4" - } - }, - "file-loader": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", - "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", - "dev": true, - "requires": { - "loader-utils": "^2.0.0", - "schema-utils": "^3.0.0" - }, - "dependencies": { - "schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - } - } - } - }, - "file-selector": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-2.1.2.tgz", - "integrity": "sha512-QgXo+mXTe8ljeqUFaX3QVHc5osSItJ/Km+xpocx0aSqWGMSCf6qYs/VnzZgS864Pjn5iceMRFigeAV7AfTlaig==", - "requires": { - "tslib": "^2.7.0" - } - }, - "fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "optional": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "find-root": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true - }, - "flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", - "dev": true, - "requires": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" - } - }, - "flatted": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", - "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", - "dev": true - }, - "font-awesome": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz", - "integrity": "sha512-U6kGnykA/6bFmg1M/oT9EkFeIYv7JlX3bozwQJWiiLz6L0w3F5vBVPxHlwyX/vtNq1ckcpRKOB9f2Qal/VtFpg==" - }, - "for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dev": true, - "requires": { - "is-callable": "^1.1.3" - } - }, - "fs-readdir-recursive": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", - "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", - "dev": true - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, - "function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" - }, - "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==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" - } - }, - "functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true - }, - "gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true - }, - "get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", - "dev": true, - "requires": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - } - }, - "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==", - "dev": true, - "requires": { - "call-bind": "^1.0.5", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4" - } - }, - "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, - "requires": { - "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" - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "optional": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "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==", - "dev": true - }, - "globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "globalthis": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", - "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", - "dev": true, - "requires": { - "define-properties": "^1.2.1", - "gopd": "^1.0.1" - } - }, - "gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dev": true, - "requires": { - "get-intrinsic": "^1.1.3" - } - }, - "graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true - }, - "graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true - }, - "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==", - "dev": true - }, - "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 - }, - "has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dev": true, - "requires": { - "es-define-property": "^1.0.0" - } - }, - "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==", - "dev": true - }, - "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true - }, - "has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, - "requires": { - "has-symbols": "^1.0.3" - } - }, - "hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "requires": { - "function-bind": "^1.1.2" - } - }, - "hoist-non-react-statics": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", - "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", - "requires": { - "react-is": "^16.7.0" - } - }, - "html-to-text": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-10.0.0.tgz", - "integrity": "sha512-2OH59Gtprdczel+7Rxgpz9hGVJREaf8Lt1H4kZwWHpEn70VQKRuMNGsb2eDbwaTzrYzb0hheiOG1P7Dim0B4dQ==", - "requires": { - "@selderee/plugin-htmlparser2": "~0.12.0", - "deepmerge-ts": "^7.1.5", - "dom-serializer": "^2.0.0", - "htmlparser2": "^10.1.0", - "selderee": "~0.12.0" - } - }, - "htmlparser2": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz", - "integrity": "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==", - "requires": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.2.2", - "entities": "^7.0.1" - }, - "dependencies": { - "entities": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", - "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==" - } - } - }, - "icss-utils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", - "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "dev": true, - "requires": {} - }, - "ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true - }, - "immutable": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.5.tgz", - "integrity": "sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==", - "dev": true - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "dependencies": { - "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==" - } - } - }, - "import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "dev": true, - "requires": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "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==", - "dev": true, - "requires": { - "es-errors": "^1.3.0", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" - } - }, - "interpret": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", - "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", - "dev": true - }, - "invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "requires": { - "loose-envify": "^1.0.0" - } - }, - "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==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1" - } - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" - }, - "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==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, - "requires": { - "has-bigints": "^1.0.1" - } - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "optional": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "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==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true - }, - "is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "requires": { - "hasown": "^2.0.2" - } - }, - "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==", - "dev": true, - "requires": { - "is-typed-array": "^1.1.13" - } - }, - "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==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "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==", - "dev": true, - "requires": { - "call-bind": "^1.0.2" - } - }, - "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==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-map": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", - "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", - "dev": true - }, - "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==", - "dev": true - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "optional": true - }, - "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==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true - }, - "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, - "requires": { - "isobject": "^3.0.1" - } - }, - "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==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-set": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", - "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", - "dev": true - }, - "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==", - "dev": true, - "requires": { - "call-bind": "^1.0.7" - } - }, - "is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dev": true, - "requires": { - "has-symbols": "^1.0.2" - } - }, - "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==", - "dev": true, - "requires": { - "which-typed-array": "^1.1.14" - } - }, - "is-weakmap": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", - "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", - "dev": true - }, - "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==", - "dev": true, - "requires": { - "call-bind": "^1.0.2" - } - }, - "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==", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4" - } - }, - "isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "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==", - "dev": true, - "requires": { - "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" - } - }, - "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==", - "dev": true, - "requires": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - } - }, - "jquery": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz", - "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==" - }, - "js-cookie": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", - "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==" - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", - "requires": { - "argparse": "^2.0.1" - } - }, - "jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==" - }, - "json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true - }, - "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==" - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true - }, - "json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true - }, - "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==", - "dev": true, - "requires": { - "array-includes": "^3.1.5", - "object.assign": "^4.1.3" - } - }, - "keycode": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/keycode/-/keycode-2.2.1.tgz", - "integrity": "sha512-Rdgz9Hl9Iv4QKi8b0OlCRQEzp4AgVxyCtz5S/+VIHezDmrDhkp2N2TqBWOLz0/gbeREXOOiI9/4b8BY9uw2vFg==" - }, - "keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "requires": { - "json-buffer": "3.0.1" - } - }, - "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==", - "dev": true - }, - "leac": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/leac/-/leac-0.7.0.tgz", - "integrity": "sha512-qMrZeyEekgdRQ9o6a4NAB2EQZrv827GJdn1vnapwSJ90hWRB4TzUSunvacPkxQ2TnNqHNI1/zSt0hlo0crG8Jw==" - }, - "levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, - "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==" - }, - "loader-runner": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz", - "integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==", - "dev": true - }, - "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==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "lodash": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", - "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==" - }, - "lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", - "dev": true - }, - "lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "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==", - "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" - } - }, - "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==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "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==", - "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - }, - "dependencies": { - "semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true - } - } - }, - "memoize-one": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", - "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==" - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "optional": true, - "requires": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - } - }, - "mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", - "dev": true - }, - "mini-css-extract-plugin": { - "version": "2.10.2", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.10.2.tgz", - "integrity": "sha512-AOSS0IdEB95ayVkxn5oGzNQwqAi2J0Jb/kKm43t7H73s8+f5873g0yuj0PNvK4dO75mu5DHg4nlgp4k6Kga8eg==", - "dev": true, - "requires": { - "schema-utils": "^4.0.0", - "tapable": "^2.2.1" - } - }, - "minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "dev": true - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "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==", - "dev": true - }, - "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, - "optional": true - }, - "node-releases": { - "version": "2.0.27", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", - "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", - "dev": true - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "object-inspect": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", - "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", - "dev": true - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - }, - "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==", - "dev": true, - "requires": { - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - } - }, - "object.entries": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz", - "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - } - }, - "object.fromentries": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", - "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" - } - }, - "object.values": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", - "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", - "dev": true, - "requires": { - "@aashutoshrathi/word-wrap": "^1.2.3", - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "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==", - "requires": { - "callsites": "^3.0.0" - } - }, - "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==", - "requires": { - "@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" - } - }, - "parseley": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/parseley/-/parseley-0.13.1.tgz", - "integrity": "sha512-uNBJZzmb60l6p6VWLTmevizNAGnE0xoSf1n0B4q3ntegDNzcS68NRCcBDZTcyXHxt2XhBChsCuqj4M+nChvE/A==", - "requires": { - "leac": "^0.7.0", - "peberminta": "^0.10.0" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "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==" - }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" - }, - "peberminta": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/peberminta/-/peberminta-0.10.0.tgz", - "integrity": "sha512-80B2AsU+I4Qdb0ZAPSfe9UwvGzwkM37IKIFEvdS3D/3Ndgv2bsuJ0bfG1+iEYO+l7Gfd4EUJmuRyq7efLgRMzQ==" - }, - "picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" - }, - "picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "dev": true, - "optional": true - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "requires": { - "find-up": "^4.0.0" - } - }, - "popper.js": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz", - "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==" - }, - "possible-typed-array-names": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", - "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", - "dev": true - }, - "postcss": { - "version": "8.4.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", - "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==", - "dev": true, - "requires": { - "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - } - }, - "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==", - "dev": true, - "requires": {} - }, - "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==", - "dev": true, - "requires": { - "icss-utils": "^5.0.0", - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.1.0" - } - }, - "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==", - "dev": true, - "requires": { - "postcss-selector-parser": "^6.0.4" - } - }, - "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==", - "dev": true, - "requires": { - "icss-utils": "^5.0.0" - } - }, - "postcss-selector-parser": { - "version": "6.0.16", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz", - "integrity": "sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==", - "dev": true, - "requires": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - } - }, - "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==", - "dev": true - }, - "prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true - }, - "prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "requires": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "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==", - "requires": { - "react-is": "^16.3.2", - "warning": "^4.0.0" - } - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true - }, - "queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true - }, - "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==", - "requires": { - "loose-envify": "^1.1.0" - } - }, - "react-bootstrap": { - "version": "0.33.1", - "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-0.33.1.tgz", - "integrity": "sha512-qWTRravSds87P8WC82tETy2yIso8qDqlIm0czsrduCaYAFtHuyLu0XDbUlfLXeRzqgwm5sRk2wRaTNoiVkk/YQ==", - "requires": { - "@babel/runtime-corejs2": "^7.0.0", - "classnames": "^2.2.5", - "dom-helpers": "^3.2.0", - "invariant": "^2.2.4", - "keycode": "^2.2.0", - "prop-types": "^15.6.1", - "prop-types-extra": "^1.0.1", - "react-overlays": "^0.9.0", - "react-prop-types": "^0.4.0", - "react-transition-group": "^2.0.0", - "uncontrollable": "^7.0.2", - "warning": "^3.0.0" - }, - "dependencies": { - "warning": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", - "integrity": "sha512-jMBt6pUrKn5I+OGgtQ4YZLdhIeJmObddh6CsibPxyQ5yPZm1XExSyzC1LCNX7BzhxWgiHmizBWJTHJIjMjTQYQ==", - "requires": { - "loose-envify": "^1.0.0" - } - } - } - }, - "react-datepicker": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-9.1.0.tgz", - "integrity": "sha512-lOp+m5bc+ttgtB5MHEjwiVu4nlp4CvJLS/PG1OiOe5pmg9kV73pEqO8H0Geqvg2E8gjqTaL9eRhSe+ZpeKP3nA==", - "requires": { - "@floating-ui/react": "^0.27.15", - "clsx": "^2.1.1", - "date-fns": "^4.1.0" - } - }, - "react-diff-viewer-continued": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/react-diff-viewer-continued/-/react-diff-viewer-continued-4.2.2.tgz", - "integrity": "sha512-8wT0/smXGox70oXJ7SZkTYF1p1Uh7jNGXhN7ykqrqPven0sZ+wM2c62SG3ZqORx5aW75te+WhmUWo0E4NyoSvg==", - "requires": { - "@emotion/css": "^11.13.5", - "@emotion/react": "^11.14.0", - "classnames": "^2.5.1", - "diff": "^9.0.0", - "js-yaml": "^4.1.1", - "memoize-one": "^6.0.0" - } - }, - "react-dnd": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-16.0.1.tgz", - "integrity": "sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q==", - "requires": { - "@react-dnd/invariant": "^4.0.1", - "@react-dnd/shallowequal": "^4.0.1", - "dnd-core": "^16.0.1", - "fast-deep-equal": "^3.1.3", - "hoist-non-react-statics": "^3.3.2" - } - }, - "react-dnd-html5-backend": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-16.0.1.tgz", - "integrity": "sha512-Wu3dw5aDJmOGw8WjH1I1/yTH+vlXEL4vmjk5p+MHxP8HuHJS1lAGeIdG/hze1AvNeXWo/JgULV87LyQOr+r5jw==", - "requires": { - "dnd-core": "^16.0.1" - } - }, - "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==", - "requires": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" - } - }, - "react-dropzone": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-15.0.0.tgz", - "integrity": "sha512-lGjYV/EoqEjEWPnmiSvH4v5IoIAwQM2W4Z1C0Q/Pw2xD0eVzKPS359BQTUMum+1fa0kH2nrKjuavmTPOGhpLPg==", - "requires": { - "attr-accept": "^2.2.4", - "file-selector": "^2.1.0", - "prop-types": "^15.8.1" - } - }, - "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==" - }, - "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==" - }, - "react-overlays": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/react-overlays/-/react-overlays-0.9.3.tgz", - "integrity": "sha512-u2T7nOLnK+Hrntho4p0Nxh+BsJl0bl4Xuwj/Y0a56xywLMetgAfyjnDVrudLXsNcKGaspoC+t3C1V80W9QQTdQ==", - "requires": { - "classnames": "^2.2.5", - "dom-helpers": "^3.2.1", - "prop-types": "^15.5.10", - "prop-types-extra": "^1.0.1", - "react-transition-group": "^2.2.1", - "warning": "^3.0.0" - }, - "dependencies": { - "warning": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", - "integrity": "sha512-jMBt6pUrKn5I+OGgtQ4YZLdhIeJmObddh6CsibPxyQ5yPZm1XExSyzC1LCNX7BzhxWgiHmizBWJTHJIjMjTQYQ==", - "requires": { - "loose-envify": "^1.0.0" - } - } - } - }, - "react-prop-types": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/react-prop-types/-/react-prop-types-0.4.0.tgz", - "integrity": "sha512-IyjsJhDX9JkoOV9wlmLaS7z+oxYoIWhfzDcFy7inwoAKTu+VcVNrVpPmLeioJ94y6GeDRsnwarG1py5qofFQMg==", - "requires": { - "warning": "^3.0.0" - }, - "dependencies": { - "warning": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", - "integrity": "sha512-jMBt6pUrKn5I+OGgtQ4YZLdhIeJmObddh6CsibPxyQ5yPZm1XExSyzC1LCNX7BzhxWgiHmizBWJTHJIjMjTQYQ==", - "requires": { - "loose-envify": "^1.0.0" - } - } - } - }, - "react-redux": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.3.tgz", - "integrity": "sha512-n0ZrutD7DaX/j9VscF+uTALI3oUPa/pO4Z3soOBIjuRn/FzVu6aehhysxZCLi6y7duMf52WNZGMl7CtuK5EnRw==", - "requires": { - "@babel/runtime": "^7.12.1", - "@types/hoist-non-react-statics": "^3.3.1", - "@types/use-sync-external-store": "^0.0.3", - "hoist-non-react-statics": "^3.3.2", - "react-is": "^18.0.0", - "use-sync-external-store": "^1.0.0" - }, - "dependencies": { - "react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" - } - } - }, - "react-select": { - "version": "5.10.2", - "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.10.2.tgz", - "integrity": "sha512-Z33nHdEFWq9tfnfVXaiM12rbJmk+QjFEztWLtmXqQhz6Al4UZZ9xc0wiatmGtUOCCnHN0WizL3tCMYRENX4rVQ==", - "requires": { - "@babel/runtime": "^7.12.0", - "@emotion/cache": "^11.4.0", - "@emotion/react": "^11.8.1", - "@floating-ui/dom": "^1.0.1", - "@types/react-transition-group": "^4.4.0", - "memoize-one": "^6.0.0", - "prop-types": "^15.6.0", - "react-transition-group": "^4.3.0", - "use-isomorphic-layout-effect": "^1.2.0" - }, - "dependencies": { - "dom-helpers": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", - "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", - "requires": { - "@babel/runtime": "^7.8.7", - "csstype": "^3.0.2" - } - }, - "react-transition-group": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", - "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", - "requires": { - "@babel/runtime": "^7.5.5", - "dom-helpers": "^5.0.1", - "loose-envify": "^1.4.0", - "prop-types": "^15.6.2" - } - } - } - }, - "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==", - "requires": { - "dom-helpers": "^3.4.0", - "loose-envify": "^1.4.0", - "prop-types": "^15.6.2", - "react-lifecycles-compat": "^3.0.4" - } - }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "optional": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "rechoir": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", - "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", - "dev": true, - "requires": { - "resolve": "^1.20.0" - } - }, - "redux": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.0.tgz", - "integrity": "sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==", - "requires": { - "@babel/runtime": "^7.9.2" - } - }, - "redux-logger": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/redux-logger/-/redux-logger-3.0.6.tgz", - "integrity": "sha512-JoCIok7bg/XpqA1JqCqXFypuqBbQzGQySrhFzewB7ThcnysTO30l4VCst86AuB9T9tuT03MAA56Jw2PNhRSNCg==", - "requires": { - "deep-diff": "^0.3.5" - } - }, - "redux-thunk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz", - "integrity": "sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==", - "requires": {} - }, - "reflect.getprototypeof": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz", - "integrity": "sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==", - "dev": true, - "requires": { - "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" - } - }, - "regenerate": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", - "dev": true - }, - "regenerate-unicode-properties": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.2.tgz", - "integrity": "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==", - "dev": true, - "requires": { - "regenerate": "^1.4.2" - } - }, - "regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" - }, - "regexp.prototype.flags": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", - "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", - "dev": true, - "requires": { - "call-bind": "^1.0.6", - "define-properties": "^1.2.1", - "es-errors": "^1.3.0", - "set-function-name": "^2.0.1" - } - }, - "regexpu-core": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.4.0.tgz", - "integrity": "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==", - "dev": true, - "requires": { - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.2.2", - "regjsgen": "^0.8.0", - "regjsparser": "^0.13.0", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.2.1" - } - }, - "regjsgen": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", - "dev": true - }, - "regjsparser": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.13.1.tgz", - "integrity": "sha512-dLsljMd9sqwRkby8zhO1gSg3PnJIBFid8f4CQj/sXx+7cKx+E7u0PKhZ+U4wmhx7EfmtvnA318oVaIkAB1lRJw==", - "dev": true, - "requires": { - "jsesc": "~3.1.0" - } - }, - "require-from-string": { - "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==", - "dev": true - }, - "resolve": { - "version": "1.22.12", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", - "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", - "requires": { - "es-errors": "^1.3.0", - "is-core-module": "^2.16.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "requires": { - "resolve-from": "^5.0.0" - } - }, - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - }, - "reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "dev": true - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "requires": { - "queue-microtask": "^1.2.2" - } - }, - "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==", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4", - "has-symbols": "^1.0.3", - "isarray": "^2.0.5" - } - }, - "safe-regex-test": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", - "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", - "dev": true, - "requires": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-regex": "^1.1.4" - } - }, - "sass": { - "version": "1.99.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.99.0.tgz", - "integrity": "sha512-kgW13M54DUB7IsIRM5LvJkNlpH+WhMpooUcaWGFARkF1Tc82v9mIWkCbCYf+MBvpIUBSeSOTilpZjEPr2VYE6Q==", - "dev": true, - "requires": { - "@parcel/watcher": "^2.4.1", - "chokidar": "^4.0.0", - "immutable": "^5.1.5", - "source-map-js": ">=0.6.2 <2.0.0" - }, - "dependencies": { - "chokidar": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz", - "integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==", - "dev": true, - "requires": { - "readdirp": "^4.0.1" - } - }, - "readdirp": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.1.tgz", - "integrity": "sha512-GkMg9uOTpIWWKbSsgwb5fA4EavTR+SG/PMPoAY8hkhHfEEY0/vqljY+XHqtDf2cr2IJtoNRDbrrEpZUiZCkYRw==", - "dev": true - } - } - }, - "sass-loader": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-16.0.0.tgz", - "integrity": "sha512-n13Z+3rU9A177dk4888czcVFiC8CL9dii4qpXWUg3YIIgZEvi9TCFKjOQcbK0kJM7DJu9VucrZFddvNfYCPwtw==", - "dev": true, - "requires": { - "neo-async": "^2.6.2" - } - }, - "scheduler": { - "version": "0.23.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", - "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", - "requires": { - "loose-envify": "^1.1.0" - } - }, - "schema-utils": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", - "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "dependencies": { - "ajv": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", - "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - } - }, - "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==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.3" - } - }, - "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==", - "dev": true - } - } - }, - "selderee": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/selderee/-/selderee-0.12.0.tgz", - "integrity": "sha512-b1YMh3+DHZp59DLna3qVwQ5iOla/nrI6mLBNW02XxU77M3046Df6VLkoaJyFz20VsGIG5kkp+FK0kg4K4HnUFw==", - "requires": { - "parseley": "~0.13.1" - } - }, - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - }, - "serialize-javascript": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-7.0.5.tgz", - "integrity": "sha512-F4LcB0UqUl1zErq+1nYEEzSHJnIwb3AF2XWB94b+afhrekOUijwooAYqFyRbjYkm2PAKBabx6oYv/xDxNi8IBw==", - "dev": true - }, - "set-function-length": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz", - "integrity": "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==", - "dev": true, - "requires": { - "define-data-property": "^1.1.2", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.3", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.1" - } - }, - "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==", - "dev": true, - "requires": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.2" - } - }, - "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, - "requires": { - "kind-of": "^6.0.2" - } - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "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==", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" - } - }, - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "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==", - "dev": true - }, - "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==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "string.prototype.matchall": { - "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==", - "dev": true, - "requires": { - "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.7", - "regexp.prototype.flags": "^1.5.2", - "set-function-name": "^2.0.2", - "side-channel": "^1.0.6" - } - }, - "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==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - } - }, - "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==", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.0", - "es-object-atoms": "^1.0.0" - } - }, - "string.prototype.trimend": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", - "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - } - }, - "string.prototype.trimstart": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", - "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, - "style-mod": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz", - "integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==" - }, - "stylis": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", - "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" - }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "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==" - }, - "tabbable": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.3.0.tgz", - "integrity": "sha512-EIHvdY5bPLuWForiR/AN2Bxngzpuwn1is4asboytXtpTgsArc+WmSJKVLlhdh71u7jFcryDqB2A8lQvj78MkyQ==" - }, - "tapable": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", - "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", - "dev": true - }, - "terser": { - "version": "5.46.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.0.tgz", - "integrity": "sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==", - "dev": true, - "requires": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.15.0", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "dependencies": { - "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==", - "dev": true - } - } - }, - "terser-webpack-plugin": { - "version": "5.3.17", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.17.tgz", - "integrity": "sha512-YR7PtUp6GMU91BgSJmlaX/rS2lGDbAF7D+Wtq7hRO+MiljNmodYvqslzCFiYVAgW+Qoaaia/QUIP4lGXufjdZw==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.25", - "jest-worker": "^27.4.5", - "schema-utils": "^4.3.0", - "terser": "^5.31.1" - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, - "tinyglobby": { - "version": "0.2.12", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.12.tgz", - "integrity": "sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==", - "dev": true, - "requires": { - "fdir": "^6.4.3", - "picomatch": "^4.0.2" - }, - "dependencies": { - "fdir": { - "version": "6.4.3", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz", - "integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==", - "dev": true, - "requires": {} - }, - "picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true - } - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "optional": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" - }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1" - } - }, - "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 - }, - "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==", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "is-typed-array": "^1.1.13" - } - }, - "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==", - "dev": true, - "requires": { - "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" - } - }, - "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==", - "dev": true, - "requires": { - "available-typed-arrays": "^1.0.7", - "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" - } - }, - "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==", - "dev": true, - "requires": { - "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" - } - }, - "unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - } - }, - "uncontrollable": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.2.1.tgz", - "integrity": "sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ==", - "requires": { - "@babel/runtime": "^7.6.3", - "@types/react": ">=16.9.11", - "invariant": "^2.2.4", - "react-lifecycles-compat": "^3.0.4" - } - }, - "unicode-canonical-property-names-ecmascript": { - "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==", - "dev": true - }, - "unicode-match-property-ecmascript": { - "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==", - "dev": true, - "requires": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" - } - }, - "unicode-match-property-value-ecmascript": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.1.tgz", - "integrity": "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==", - "dev": true - }, - "unicode-property-aliases-ecmascript": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.2.0.tgz", - "integrity": "sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==", - "dev": true - }, - "update-browserslist-db": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", - "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", - "dev": true, - "requires": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - } - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "use-debounce": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/use-debounce/-/use-debounce-10.1.1.tgz", - "integrity": "sha512-kvds8BHR2k28cFsxW8k3nc/tRga2rs1RHYCqmmGqb90MEeE++oALwzh2COiuBLO1/QXiOuShXoSN2ZpWnMmvuQ==", - "requires": {} - }, - "use-isomorphic-layout-effect": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.2.1.tgz", - "integrity": "sha512-tpZZ+EX0gaghDAiFR37hj5MgY6ZN55kLiPkJsKxBMZ6GZdOSPJXiOzPM984oPYZ5AnehYx5WQp1+ME8I/P/pRA==", - "requires": {} - }, - "use-sync-external-store": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", - "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", - "requires": {} - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true - }, - "w3c-keyname": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.6.tgz", - "integrity": "sha512-f+fciywl1SJEniZHD6H+kUO8gOnwIr7f4ijKA6+ZvJFjeGi1r4PDLl53Ayud9O/rk64RqgoQine0feoeOU0kXg==" - }, - "warning": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", - "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", - "requires": { - "loose-envify": "^1.0.0" - } - }, - "watchpack": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz", - "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==", - "dev": true, - "requires": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - } - }, - "webpack": { - "version": "5.106.2", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.106.2.tgz", - "integrity": "sha512-wGN3qcrBQIFmQ/c0AiOAQBvrZ5lmY8vbbMv4Mxfgzqd/B6+9pXtLo73WuS1dSGXM5QYY3hZnIbvx+K1xxe6FyA==", - "dev": true, - "requires": { - "@types/eslint-scope": "^3.7.7", - "@types/estree": "^1.0.8", - "@types/json-schema": "^7.0.15", - "@webassemblyjs/ast": "^1.14.1", - "@webassemblyjs/wasm-edit": "^1.14.1", - "@webassemblyjs/wasm-parser": "^1.14.1", - "acorn": "^8.16.0", - "acorn-import-phases": "^1.0.3", - "browserslist": "^4.28.1", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.20.0", - "es-module-lexer": "^2.0.0", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.11", - "loader-runner": "^4.3.1", - "mime-db": "^1.54.0", - "neo-async": "^2.6.2", - "schema-utils": "^4.3.3", - "tapable": "^2.3.0", - "terser-webpack-plugin": "^5.3.17", - "watchpack": "^2.5.1", - "webpack-sources": "^3.3.4" - } - }, - "webpack-cli": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-7.0.2.tgz", - "integrity": "sha512-dB0R4T+C/8YuvM+fabdvil6QE44/ChDXikV5lOOkrUeCkW5hTJv2pGLE3keh+D5hjYw8icBaJkZzpFoaHV4T+g==", - "dev": true, - "requires": { - "@discoveryjs/json-ext": "^1.0.0", - "commander": "^14.0.3", - "cross-spawn": "^7.0.6", - "envinfo": "^7.14.0", - "fastest-levenshtein": "^1.0.12", - "import-local": "^3.0.2", - "interpret": "^3.1.1", - "rechoir": "^0.8.0", - "webpack-merge": "^6.0.1" - }, - "dependencies": { - "commander": { - "version": "14.0.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", - "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", - "dev": true - } - } - }, - "webpack-merge": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-6.0.1.tgz", - "integrity": "sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==", - "dev": true, - "requires": { - "clone-deep": "^4.0.1", - "flat": "^5.0.2", - "wildcard": "^2.0.1" - } - }, - "webpack-sources": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.4.1.tgz", - "integrity": "sha512-eACpxRN02yaawnt+uUNIF7Qje6A9zArxBbcAJjK1PK3S9Ycg5jIuJ8pW4q8EMnwNZCEGltcjkRx1QzOxOkKD8A==", - "dev": true - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dev": true, - "requires": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - } - }, - "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==", - "dev": true, - "requires": { - "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" - } - }, - "which-collection": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", - "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", - "dev": true, - "requires": { - "is-map": "^2.0.3", - "is-set": "^2.0.3", - "is-weakmap": "^2.0.2", - "is-weakset": "^2.0.3" - } - }, - "which-typed-array": { - "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==", - "dev": true, - "requires": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.2" - } - }, - "wildcard": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", - "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", - "dev": true - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "yaml": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz", - "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==" - }, - "yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true - } } } From c7ff02f1486ee5d98c877337544a0dacefb64e17 Mon Sep 17 00:00:00 2001 From: David Wallace Date: Thu, 21 May 2026 15:09:13 +0200 Subject: [PATCH 083/131] ci: do not use parallel xdist for test-e2e Signed-off-by: David Wallace --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b195f702c4..565fd09a2c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -152,7 +152,7 @@ jobs: - name: Collect static files into static root (only required if rdmo is installed from wheel) run: python testing/manage.py collectstatic --noinput - name: Run end-to-end tests - run: pytest -p randomly -p no:cacheprovider --reuse-db --numprocesses=auto --dist=loadscope -m e2e --nomigrations + run: pytest -p randomly -p no:cacheprovider -m e2e --nomigrations env: DJANGO_DEBUG: True GITHUB_DB_BACKEND: ${{ matrix.db-backend }} From 9cccacc530c3baf9bd7cdef9819b7a8d64ad04e8 Mon Sep 17 00:00:00 2001 From: David Wallace Date: Thu, 21 May 2026 16:10:20 +0200 Subject: [PATCH 084/131] Select a specific catalog for e2e copy test Signed-off-by: David Wallace --- .../test_frontend_management_catalog_copy.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/rdmo/management/tests/e2e/test_frontend_management_catalog_copy.py b/rdmo/management/tests/e2e/test_frontend_management_catalog_copy.py index 537ff1b193..ecb2dc9b79 100644 --- a/rdmo/management/tests/e2e/test_frontend_management_catalog_copy.py +++ b/rdmo/management/tests/e2e/test_frontend_management_catalog_copy.py @@ -10,19 +10,25 @@ @pytest.mark.parametrize("page", ["page_single", "page_multisite"], indirect=True) def test_management_catalog_copy(page: Page) -> None: """Test that each content type is available through the navigation.""" + source_uri = "http://example.com/terms/questions/catalog" + copied_uri = "http://example.com/terms/questions/catalog-copy" + copied_prefix = "http://example.com/terms" expect(page.get_by_role("heading", name="Management")).to_be_visible() expect(page.locator("strong").filter(has_text="Catalogs")).to_be_visible() - # open the copy form: click on the "Copy catalog" button - # page.goto(f"/management/catalogs/1/copy") # goto does not work with auto return after copy - # page.locator('a[title="Copy catalog"]').first.click() - page.get_by_title("Copy catalog").first.click() + # Open the copy form from the example.com catalog row. + source_row = page.locator(".list-group-item").filter( + has=page.get_by_role("link", name=source_uri, exact=True) + ) + expect(source_row).to_be_visible() + source_row.get_by_title("Copy catalog").click() # expect the form is there with a URI Prefix field expect(page.get_by_role("textbox", name="URI Prefix")).to_be_visible() # create a copy of the catalog + page.get_by_role("textbox", name="URI Prefix").fill(copied_prefix) page.get_by_role("textbox", name="URI Path").click() page.get_by_role("textbox", name="URI Path").fill("catalog-copy") page.get_by_role("textbox", name="Title (English)").click() @@ -33,6 +39,6 @@ def test_management_catalog_copy(page: Page) -> None: # assert copy was successful # page.wait_for_url("/management/catalogs/") expect(page.locator("strong").filter(has_text="Catalogs")).to_be_visible() - expect(page.get_by_role("link", name="http://example.com/terms/questions/catalog-copy")).to_be_visible() + expect(page.get_by_role("link", name=copied_uri)).to_be_visible() - assert Catalog.objects.filter(uri="http://example.com/terms/questions/catalog-copy").exists() + assert Catalog.objects.filter(uri=copied_uri).exists() From 5368b91604a736ea51a3f014daee7469595f23c3 Mon Sep 17 00:00:00 2001 From: David Wallace Date: Thu, 21 May 2026 17:24:29 +0200 Subject: [PATCH 085/131] tests(e2e): refactor fixtures and make teardowns more robust Signed-off-by: David Wallace --- rdmo/core/tests/e2e/conftest.py | 94 ++----------------- rdmo/core/tests/e2e/fixtures.py | 92 ++++++++++++++++++ rdmo/management/tests/e2e/conftest.py | 32 +++---- .../tests/e2e/test_frontend_import_options.py | 2 +- .../e2e/test_frontend_import_questions.py | 2 +- .../test_frontend_management_catalog_copy.py | 2 +- .../e2e/test_frontend_management_elements.py | 3 +- rdmo/projects/tests/e2e/conftest.py | 8 +- .../tests/e2e/test_frontend_project_detail.py | 2 +- .../tests/e2e/test_frontend_projects.py | 2 +- 10 files changed, 124 insertions(+), 115 deletions(-) create mode 100644 rdmo/core/tests/e2e/fixtures.py diff --git a/rdmo/core/tests/e2e/conftest.py b/rdmo/core/tests/e2e/conftest.py index fe4139c20a..0ae8257f48 100644 --- a/rdmo/core/tests/e2e/conftest.py +++ b/rdmo/core/tests/e2e/conftest.py @@ -1,86 +1,8 @@ -import os - -import pytest - -from django.conf import settings -from django.core.management import call_command - -from playwright.sync_api import Browser, BrowserContext, Page - -from rdmo.accounts.utils import set_group_permissions - -PLAYWRIGHT_TIMEOUT = 10_000 # timeout in ms - -@pytest.fixture(scope="session", autouse=True) -def _set_django_allow_async_unsafe(): - """pytest-playwright needs this setting to be enabled.""" - os.environ.setdefault("DJANGO_ALLOW_ASYNC_UNSAFE", "true") - - -@pytest.fixture -def django_db_setup(django_db_setup, django_db_blocker, fixtures): - """Set up database and populate with fixtures, that get restored for every test case. - - This fixture overrides the django_db_setup in the main conftest.py, this only applies to the e2e tests - in this directory. - """ - with django_db_blocker.unblock(): - call_command("loaddata", *fixtures, verbosity=0) - set_group_permissions() - - -@pytest.fixture -def authenticated_context(client, login, live_server, browser: Browser, e2e_username) -> BrowserContext: - """Creates an authenticated Playwright browser context with session cookies.""" - # retrieve the session cookie from the authenticated client - login(e2e_username) - session_cookie = client.cookies[settings.SESSION_COOKIE_NAME] - cookie = { - "name": session_cookie.key, - "value": session_cookie.value, - "url": live_server.url, - } - - context = browser.new_context(base_url=live_server.url) - # the browser context is now "authenticated" with the session cookie - context.add_cookies([cookie]) - yield context - context.close() - - -@pytest.fixture -def authenticated_page(authenticated_context: BrowserContext) -> Page: - """Provides an authenticated Playwright page without forcing navigation. - The page is authenticated with session cookies from authenticated_context. - """ - page = authenticated_context.new_page() - page.set_default_timeout(PLAYWRIGHT_TIMEOUT) - page.set_default_navigation_timeout(PLAYWRIGHT_TIMEOUT) - yield page - page.close() - - -@pytest.fixture(autouse=True) -def fail_on_js_error(page: Page): - """Fail the test immediately when a JavaScript error occurs.""" - - # List of ignored warning substrings - ignored_warnings = [ - "legacy childContextTypes API", - "legacy contextTypes API", - "Use React.createContext()", - ] - - def log_console_msg(msg): - if msg.type == "error": - # Check if the message contains any ignored warning - if any(ignored in msg.text for ignored in ignored_warnings): - print(f"Ignoring warning: {msg.text}") # Log it for visibility but don't fail - return - pytest.fail(f"JavaScript error detected: {msg.text}") - - def log_page_error(exception): - pytest.exit(f"Uncaught page error detected: {exception}") - - page.on("console", log_console_msg) - page.on("pageerror", log_page_error) +from rdmo.core.tests.e2e.fixtures import ( # noqa: F401 + PLAYWRIGHT_TIMEOUT, + _set_django_allow_async_unsafe, + allow_live_server_host, + authenticated_context, + authenticated_page, + django_db_setup, +) diff --git a/rdmo/core/tests/e2e/fixtures.py b/rdmo/core/tests/e2e/fixtures.py new file mode 100644 index 0000000000..d81afc65ab --- /dev/null +++ b/rdmo/core/tests/e2e/fixtures.py @@ -0,0 +1,92 @@ +import os +from urllib.parse import urlparse + +import pytest + +from django.conf import settings +from django.core.management import call_command + +from playwright.sync_api import Browser, BrowserContext, Page + +from rdmo.accounts.utils import set_group_permissions + +PLAYWRIGHT_TIMEOUT = 10_000 # timeout in ms + + +@pytest.fixture(scope="session", autouse=True) +def _set_django_allow_async_unsafe(): + """pytest-playwright needs this setting to be enabled.""" + os.environ.setdefault("DJANGO_ALLOW_ASYNC_UNSAFE", "true") + + +@pytest.fixture(autouse=True) +def allow_live_server_host(settings, live_server): + """Allow the pytest live server host in Django host validation.""" + hosts = { + *settings.ALLOWED_HOSTS, + urlparse(live_server.url).hostname, + "localhost", + "127.0.0.1", + "testserver", + } + settings.ALLOWED_HOSTS = list(hosts) + + +@pytest.fixture +def django_db_setup(django_db_setup, django_db_blocker, fixtures): + """Set up database and populate it with fixtures for every e2e test.""" + with django_db_blocker.unblock(): + call_command("loaddata", *fixtures, verbosity=0) + set_group_permissions() + + +@pytest.fixture +def authenticated_context(client, login, live_server, browser: Browser, e2e_username) -> BrowserContext: + """Create an authenticated Playwright browser context.""" + login(e2e_username) + session_cookie = client.cookies[settings.SESSION_COOKIE_NAME] + cookie = { + "name": session_cookie.key, + "value": session_cookie.value, + "url": live_server.url, + } + + context = browser.new_context(base_url=live_server.url) + context.add_cookies([cookie]) + try: + yield context + finally: + context.close() + + +@pytest.fixture +def authenticated_page(authenticated_context: BrowserContext) -> Page: + """Create an authenticated Playwright page and collect JS errors.""" + js_errors = [] + ignored_warnings = [ + "legacy childContextTypes API", + "legacy contextTypes API", + "Use React.createContext()", + ] + + def log_console_msg(msg): + if msg.type == "error": + if any(ignored in msg.text for ignored in ignored_warnings): + print(f"Ignoring warning: {msg.text}") + return + js_errors.append(f"console error: {msg.text}") + + def log_page_error(exception): + js_errors.append(f"page error: {exception}") + + page = authenticated_context.new_page() + page.set_default_timeout(PLAYWRIGHT_TIMEOUT) + page.set_default_navigation_timeout(PLAYWRIGHT_TIMEOUT) + page.on("console", log_console_msg) + page.on("pageerror", log_page_error) + try: + yield page + finally: + page.close() + if js_errors: + pytest.fail("\n".join(js_errors)) diff --git a/rdmo/management/tests/e2e/conftest.py b/rdmo/management/tests/e2e/conftest.py index 86ceee0f6c..d84695b52b 100644 --- a/rdmo/management/tests/e2e/conftest.py +++ b/rdmo/management/tests/e2e/conftest.py @@ -1,20 +1,19 @@ import pytest -from _pytest.fixtures import SubRequest -from playwright.sync_api import Page +from playwright.sync_api import Page, expect -from rdmo.core.tests.e2e.conftest import ( # noqa: F401 +from rdmo.core.tests.e2e.fixtures import ( # noqa: F401 PLAYWRIGHT_TIMEOUT, _set_django_allow_async_unsafe, + allow_live_server_host, authenticated_context, authenticated_page, django_db_setup, - fail_on_js_error, ) -@pytest.fixture -def e2e_username(scope="session") -> str: +@pytest.fixture(scope="session") +def e2e_username() -> str: """Fixture to specify which user should be authenticated. This can be overridden in individual test modules or fixtures. """ @@ -22,29 +21,24 @@ def e2e_username(scope="session") -> str: @pytest.fixture -def page_single(django_db_setup, live_server, browser, authenticated_page: Page) -> Page: # noqa: F811 +def page_single(transactional_db, live_server, authenticated_page: Page) -> Page: # noqa: F811 """Navigates the authenticated page to /management.""" authenticated_page.goto("/management") # Navigate to the projects section - authenticated_page.wait_for_load_state() # maybe not needed + expect(authenticated_page.get_by_role("heading", name="Management")).to_be_visible() return authenticated_page @pytest.fixture -def page_multisite(django_db_setup, live_server, browser, authenticated_page: Page, settings) -> Page: # noqa: F811 +def page_multisite(transactional_db, live_server, authenticated_page: Page, settings) -> Page: # noqa: F811 """Enables the multisite and navigates the authenticated page to /management.""" settings.MULTISITE = True authenticated_page.goto("/management") # Navigate to the projects section - authenticated_page.wait_for_load_state() # maybe not needed + expect(authenticated_page.get_by_role("heading", name="Management")).to_be_visible() return authenticated_page @pytest.fixture -def page(request, django_db_setup, authenticated_page) -> Page: # noqa: F811 - """Allows for the specific page fixtures to be parameters and returns a default fixture for page""" - try: - return request.getfixturevalue(request.param) - except AttributeError as e: - if isinstance(request, SubRequest) and request.fixturename == "page": - # set a default return fixture for "page" - return request.getfixturevalue("page_multisite") - raise e from e +def page(request, transactional_db) -> Page: + """Allow specific page fixtures to be selected indirectly and default to multisite.""" + fixture_name = getattr(request, "param", "page_multisite") + return request.getfixturevalue(fixture_name) diff --git a/rdmo/management/tests/e2e/test_frontend_import_options.py b/rdmo/management/tests/e2e/test_frontend_import_options.py index 748e66137a..f0f1916125 100644 --- a/rdmo/management/tests/e2e/test_frontend_import_options.py +++ b/rdmo/management/tests/e2e/test_frontend_import_options.py @@ -6,7 +6,7 @@ from rdmo.management.tests.helpers_import_elements import IMPORT_ELEMENT_PANELS_LOCATOR from rdmo.options.models import Option, OptionSet -pytestmark = pytest.mark.e2e +pytestmark = [pytest.mark.e2e, pytest.mark.django_db(transaction=True)] import_xml = "./testing/xml/elements/optionsets.xml" import_xml_1 = "./testing/xml/elements/updated-and-changed/optionsets-1.xml" diff --git a/rdmo/management/tests/e2e/test_frontend_import_questions.py b/rdmo/management/tests/e2e/test_frontend_import_questions.py index 512e0b5ef9..f03a481c1b 100644 --- a/rdmo/management/tests/e2e/test_frontend_import_questions.py +++ b/rdmo/management/tests/e2e/test_frontend_import_questions.py @@ -7,7 +7,7 @@ from rdmo.questions.models import Page as PageModel from rdmo.questions.models.questionset import QuestionSet -pytestmark = pytest.mark.e2e +pytestmark = [pytest.mark.e2e, pytest.mark.django_db(transaction=True)] def test_import_catalogs_in_management(db, page, delete_all_objects) -> None: diff --git a/rdmo/management/tests/e2e/test_frontend_management_catalog_copy.py b/rdmo/management/tests/e2e/test_frontend_management_catalog_copy.py index ecb2dc9b79..ad93b86056 100644 --- a/rdmo/management/tests/e2e/test_frontend_management_catalog_copy.py +++ b/rdmo/management/tests/e2e/test_frontend_management_catalog_copy.py @@ -4,7 +4,7 @@ from rdmo.questions.models import Catalog -pytestmark = pytest.mark.e2e +pytestmark = [pytest.mark.e2e, pytest.mark.django_db(transaction=True)] @pytest.mark.parametrize("page", ["page_single", "page_multisite"], indirect=True) diff --git a/rdmo/management/tests/e2e/test_frontend_management_elements.py b/rdmo/management/tests/e2e/test_frontend_management_elements.py index 9ca313234b..3ad9b43c27 100644 --- a/rdmo/management/tests/e2e/test_frontend_management_elements.py +++ b/rdmo/management/tests/e2e/test_frontend_management_elements.py @@ -10,7 +10,7 @@ from rdmo.management.tests.helpers_models import ModelHelper, model_helpers from rdmo.questions.models import Catalog -pytestmark = pytest.mark.e2e +pytestmark = [pytest.mark.e2e, pytest.mark.django_db(transaction=True)] @pytest.mark.parametrize("page", ["page_single", "page_multisite"], indirect=True) @@ -41,6 +41,7 @@ def test_management_has_items(page: Page, helper: ModelHelper) -> None: """Test all items in database are visible in management UI.""" num_items_in_database = helper.model.objects.count() page.goto(f"/management/{helper.url}") + expect(page.get_by_role("heading", name="Management")).to_be_visible() items_in_ui = page.locator(".list-group > .list-group-item") expect(items_in_ui).to_have_count(num_items_in_database) diff --git a/rdmo/projects/tests/e2e/conftest.py b/rdmo/projects/tests/e2e/conftest.py index 584bc7f711..27e117b9ec 100644 --- a/rdmo/projects/tests/e2e/conftest.py +++ b/rdmo/projects/tests/e2e/conftest.py @@ -1,15 +1,15 @@ import pytest -from playwright.sync_api import Page +from playwright.sync_api import Page, expect -from rdmo.core.tests.e2e.conftest import ( # noqa: F401 +from rdmo.core.tests.e2e.fixtures import ( # noqa: F401 PLAYWRIGHT_TIMEOUT, _set_django_allow_async_unsafe, + allow_live_server_host, authenticated_context, authenticated_page, django_db_setup, - fail_on_js_error, ) @@ -25,5 +25,5 @@ def e2e_username() -> str: def page(live_server, browser, authenticated_page: Page) -> Page: # noqa: F811 """Navigates the authenticated page to /projects.""" authenticated_page.goto("/projects") # Navigate to the projects section - authenticated_page.wait_for_load_state() + expect(authenticated_page).to_have_url("/projects/") return authenticated_page diff --git a/rdmo/projects/tests/e2e/test_frontend_project_detail.py b/rdmo/projects/tests/e2e/test_frontend_project_detail.py index 565a3b16d7..a7fd92ae8a 100644 --- a/rdmo/projects/tests/e2e/test_frontend_project_detail.py +++ b/rdmo/projects/tests/e2e/test_frontend_project_detail.py @@ -4,7 +4,7 @@ from playwright.sync_api import Page, expect -pytestmark = pytest.mark.e2e +pytestmark = [pytest.mark.e2e, pytest.mark.django_db(transaction=True)] def test_project_detail_page(page: Page): diff --git a/rdmo/projects/tests/e2e/test_frontend_projects.py b/rdmo/projects/tests/e2e/test_frontend_projects.py index 78a59d7606..1e665a23ab 100644 --- a/rdmo/projects/tests/e2e/test_frontend_projects.py +++ b/rdmo/projects/tests/e2e/test_frontend_projects.py @@ -4,7 +4,7 @@ from playwright.sync_api import Page, expect -pytestmark = pytest.mark.e2e +pytestmark = [pytest.mark.e2e, pytest.mark.django_db(transaction=True)] def test_projects_page(page: Page): From a917da8dcc51b459f489abf71c672c8a50a8c174 Mon Sep 17 00:00:00 2001 From: David Wallace Date: Thu, 21 May 2026 17:24:49 +0200 Subject: [PATCH 086/131] Revert "ci: do not use parallel xdist for test-e2e" This reverts commit c7ff02f1486ee5d98c877337544a0dacefb64e17. --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 565fd09a2c..b195f702c4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -152,7 +152,7 @@ jobs: - name: Collect static files into static root (only required if rdmo is installed from wheel) run: python testing/manage.py collectstatic --noinput - name: Run end-to-end tests - run: pytest -p randomly -p no:cacheprovider -m e2e --nomigrations + run: pytest -p randomly -p no:cacheprovider --reuse-db --numprocesses=auto --dist=loadscope -m e2e --nomigrations env: DJANGO_DEBUG: True GITHUB_DB_BACKEND: ${{ matrix.db-backend }} From fa0e7e631b64a6987a3f1b3e521181704b08be7e Mon Sep 17 00:00:00 2001 From: David Wallace Date: Fri, 22 May 2026 09:00:47 +0200 Subject: [PATCH 087/131] Keep borders and spacing on import diff field rows as before Signed-off-by: David Wallace --- .../js/components/import/common/FieldRowDiffs.js | 6 +++++- rdmo/management/assets/scss/management.scss | 15 +++------------ 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/rdmo/management/assets/js/components/import/common/FieldRowDiffs.js b/rdmo/management/assets/js/components/import/common/FieldRowDiffs.js index 93f166a0a3..05e306a3e9 100644 --- a/rdmo/management/assets/js/components/import/common/FieldRowDiffs.js +++ b/rdmo/management/assets/js/components/import/common/FieldRowDiffs.js @@ -40,18 +40,22 @@ const FieldRowDiffs = ({ element, field }) => { textAlign: 'left', overflow: 'visible', pre: { - display: 'inline', + backgroundColor: 'transparent', border: 'none', boxShadow: 'none', lineHeight: '1.6em', overflow: 'visible', + padding: 0, }, }, contentText: { backgroundColor: '#fff !important', + border: '1px solid #ccc', + borderRadius: 4, display: 'block', lineHeight: '1.6em', overflowWrap: 'anywhere', + padding: '5px 10px', wordBreak: 'break-word', }, } diff --git a/rdmo/management/assets/scss/management.scss b/rdmo/management/assets/scss/management.scss index b3d0d3a8a8..47712dbdae 100644 --- a/rdmo/management/assets/scss/management.scss +++ b/rdmo/management/assets/scss/management.scss @@ -242,6 +242,7 @@ border-collapse: separate; // needed for border radius since this is a table border: 1px solid #ccc; border-radius: 4px; + border-spacing: 0; tr { &:not(:last-child) { @@ -262,23 +263,13 @@ padding: 8px 0 8px 8px; pre { - padding: 0; + background-color: transparent; border: none; border-radius: 0; box-shadow: none; font-size: 13px; line-height: 18px; - background-color: transparent; - } - } - - td[class*="content"] { - pre { - padding: 5px 10px; - border-radius: 4px; - font-size: 13px; - line-height: 18px; - background-color: #fff; + padding: 0; } } } From 8693b5cfbdd751695af0998fdbbc84a9cf2cad7f Mon Sep 17 00:00:00 2001 From: David Wallace Date: Fri, 22 May 2026 09:50:59 +0200 Subject: [PATCH 088/131] Refactor and update import diff field rows Signed-off-by: David Wallace --- .../js/components/import/common/FieldRow.js | 18 +++++++++++------- .../components/import/common/FieldRowDiffs.js | 10 ++++++++-- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/rdmo/management/assets/js/components/import/common/FieldRow.js b/rdmo/management/assets/js/components/import/common/FieldRow.js index 33a9229763..87e975d69a 100644 --- a/rdmo/management/assets/js/components/import/common/FieldRow.js +++ b/rdmo/management/assets/js/components/import/common/FieldRow.js @@ -1,23 +1,27 @@ import React from 'react' import PropTypes from 'prop-types' -import uniqueId from 'lodash/uniqueId' import FieldRowValue from './FieldRowValue' import FieldRowDiffs from './FieldRowDiffs' const FieldRow = ({ element, keyName, value }) => { + const fieldDiffData = element.updated_and_changed?.[keyName] + const showDiff = element.updated && element.changed && fieldDiffData?.changed return (
    -
    +
    {keyName}
    -
    - - {element.updated && element.changed && keyName in element.updated_and_changed && ( - - )} +
    + { + showDiff ? ( + + ) : ( + + ) + }
    ) diff --git a/rdmo/management/assets/js/components/import/common/FieldRowDiffs.js b/rdmo/management/assets/js/components/import/common/FieldRowDiffs.js index 05e306a3e9..271ffc7e7a 100644 --- a/rdmo/management/assets/js/components/import/common/FieldRowDiffs.js +++ b/rdmo/management/assets/js/components/import/common/FieldRowDiffs.js @@ -11,9 +11,10 @@ const FieldRowDiffs = ({ element, field }) => { return null } const fieldDiffData = element.updated_and_changed[field] - const newVal = fieldDiffData.newValue.toString() ?? '' - const oldVal = fieldDiffData.oldValue.toString() ?? '' + const newVal = fieldDiffData.newValue ?? '' + const oldVal = fieldDiffData.oldValue ?? '' const changed = fieldDiffData.changed ?? false + const compareMethod = fieldDiffData.compareMethod const splitView = false const hideLineNumbers = true const warnings = fieldDiffData.warnings ?? {} @@ -23,8 +24,12 @@ const FieldRowDiffs = ({ element, field }) => { variables: { light: { diffViewerBackground: '#fff', + addedBackground: '#eafaf0', + removedBackground: '#fff0f2', changedBackground: '#fff', gutterBackground: '#fff', + wordAddedBackground: '#a7e8b8', + wordRemovedBackground: '#f5b5bf', }, }, diffContainer: { @@ -66,6 +71,7 @@ const FieldRowDiffs = ({ element, field }) => { styles={newStyles} oldValue={oldVal} newValue={newVal} + compareMethod={compareMethod} splitView={splitView} hideSummary={true} hideLineNumbers={hideLineNumbers} From 830f0d901afe8a969a52bce132c28cb4b74011eb Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Fri, 22 May 2026 11:54:40 +0200 Subject: [PATCH 089/131] Refactor prefetching --- rdmo/projects/viewsets.py | 8 +- rdmo/questions/managers.py | 49 ++--- rdmo/questions/models/catalog.py | 4 +- rdmo/questions/models/page.py | 4 +- rdmo/questions/models/question.py | 4 +- rdmo/questions/models/questionset.py | 4 +- rdmo/questions/models/section.py | 4 +- rdmo/questions/prefetch.py | 171 +++++++++--------- rdmo/questions/serializers/export.py | 41 ++++- rdmo/questions/utils.py | 44 +++-- rdmo/questions/viewsets.py | 260 +++++++++++++-------------- 11 files changed, 313 insertions(+), 280 deletions(-) diff --git a/rdmo/projects/viewsets.py b/rdmo/projects/viewsets.py index d5082ceed6..0074e4831e 100644 --- a/rdmo/projects/viewsets.py +++ b/rdmo/projects/viewsets.py @@ -25,7 +25,7 @@ from rdmo.core.utils import human2bytes, is_truthy, return_file_response from rdmo.options.models import OptionSet from rdmo.questions.models import Catalog, Page, Question, QuestionSet -from rdmo.questions.prefetch import project_page_prefetch_lookups +from rdmo.questions.prefetch import get_page_prefetch_lookups from rdmo.tasks.models import Task from rdmo.views.models import View @@ -697,8 +697,10 @@ class ProjectPageViewSet(ProjectNestedViewSetMixin, RetrieveModelMixin, GenericV def get_queryset(self): self.project.catalog.prefetch_elements() - return Page.objects.filter_by_catalog(self.project.catalog).prefetch_related( - *project_page_prefetch_lookups() + return ( + Page.objects + .filter_by_catalog(self.project.catalog) + .prefetch_related(*get_page_prefetch_lookups(optionsets=True, optionsets_conditions=True, options=True)) ) def get_serializer_context(self): diff --git a/rdmo/questions/managers.py b/rdmo/questions/managers.py index d9524e22f4..e97469c29b 100644 --- a/rdmo/questions/managers.py +++ b/rdmo/questions/managers.py @@ -10,11 +10,11 @@ ) from .prefetch import ( - catalog_prefetch_lookups, - page_prefetch_lookups, - question_prefetch_lookups, - questionset_prefetch_lookups, - section_prefetch_lookups, + get_catalog_prefetch_lookups, + get_page_prefetch_lookups, + get_question_prefetch_lookups, + get_questionset_prefetch_lookups, + get_section_prefetch_lookups, ) @@ -23,8 +23,8 @@ class CatalogQuerySet(CurrentSiteQuerySetMixin, GroupsQuerySetMixin, Availabilit def filter_catalog(self, catalog): return self.filter(models.Q(catalogs=None) | models.Q(catalogs=catalog)) - def prefetch_elements(self): - return self.prefetch_related(*catalog_prefetch_lookups()) + def prefetch_elements(self, **kwargs): + return self.prefetch_related(*get_catalog_prefetch_lookups(**kwargs)) def filter_for_user(self, user): return ( @@ -43,8 +43,8 @@ def get_queryset(self): def filter_catalog(self, catalog): return self.get_queryset().filter_catalog(catalog) - def prefetch_elements(self): - return self.get_queryset().prefetch_elements() + def prefetch_elements(self, **kwargs): + return self.get_queryset().prefetch_elements(**kwargs) def filter_for_user(self, user): return self.get_queryset().filter_for_user(user) @@ -52,8 +52,8 @@ def filter_for_user(self, user): class SectionQuerySet(models.QuerySet): - def prefetch_elements(self): - return self.prefetch_related(*section_prefetch_lookups()) + def prefetch_elements(self, **kwargs): + return self.prefetch_related(*get_section_prefetch_lookups(**kwargs)) class SectionManager(models.Manager): @@ -61,14 +61,14 @@ class SectionManager(models.Manager): def get_queryset(self): return SectionQuerySet(self.model, using=self._db) - def prefetch_elements(self): - return self.get_queryset().prefetch_elements() + def prefetch_elements(self, **kwargs): + return self.get_queryset().prefetch_elements(**kwargs) class PageQuerySet(models.QuerySet): - def prefetch_elements(self): - return self.prefetch_related(*page_prefetch_lookups()) + def prefetch_elements(self, **kwargs): + return self.prefetch_related(*get_page_prefetch_lookups(**kwargs)) def filter_by_catalog(self, catalog): ids = [descendant.id for descendant in catalog.descendants if isinstance(descendant, self.model)] @@ -83,14 +83,14 @@ def get_queryset(self): def filter_by_catalog(self, catalog): return self.get_queryset().filter_by_catalog(catalog) - def prefetch_elements(self): - return self.get_queryset().prefetch_elements() + def prefetch_elements(self, **kwargs): + return self.get_queryset().prefetch_elements(**kwargs) class QuestionSetQuerySet(models.QuerySet): - def prefetch_elements(self): - return self.prefetch_related(*questionset_prefetch_lookups()) + def prefetch_elements(self, **kwargs): + return self.prefetch_related(*get_questionset_prefetch_lookups(**kwargs)) def filter_by_catalog(self, catalog): ids = [descendant.id for descendant in catalog.descendants if isinstance(descendant, self.model)] @@ -105,11 +105,14 @@ def get_queryset(self): def filter_by_catalog(self, catalog): return self.get_queryset().filter_by_catalog(catalog) + def prefetch_elements(self, **kwargs): + return self.get_queryset().prefetch_elements(**kwargs) + class QuestionQuerySet(models.QuerySet): - def prefetch_elements(self): - return self.prefetch_related(*question_prefetch_lookups()) + def prefetch_elements(self, **kwargs): + return self.prefetch_related(*get_question_prefetch_lookups(**kwargs)) def filter_by_catalog(self, catalog): ids = [descendant.id for descendant in catalog.descendants if isinstance(descendant, self.model)] @@ -124,5 +127,5 @@ def get_queryset(self): def filter_by_catalog(self, catalog): return self.get_queryset().filter_by_catalog(catalog) - def prefetch_elements(self): - return self.get_queryset().prefetch_elements() + def prefetch_elements(self, **kwargs): + return self.get_queryset().prefetch_elements(**kwargs) diff --git a/rdmo/questions/models/catalog.py b/rdmo/questions/models/catalog.py index 9c24dc111b..97d7f18c78 100644 --- a/rdmo/questions/models/catalog.py +++ b/rdmo/questions/models/catalog.py @@ -10,7 +10,7 @@ from rdmo.core.utils import join_url from ..managers import CatalogManager -from ..prefetch import catalog_prefetch_lookups +from ..prefetch import get_catalog_prefetch_lookups class Catalog(Model, TranslationMixin): @@ -198,7 +198,7 @@ def optional_questions(self): return list(filter(lambda q: q.is_optional, self.questions)) def prefetch_elements(self): - models.prefetch_related_objects([self], *catalog_prefetch_lookups()) + models.prefetch_related_objects([self], *get_catalog_prefetch_lookups()) def to_dict(self): elements = [element.to_dict() for element in self.elements] diff --git a/rdmo/questions/models/page.py b/rdmo/questions/models/page.py index ba0e8586f2..a83b5df439 100644 --- a/rdmo/questions/models/page.py +++ b/rdmo/questions/models/page.py @@ -10,7 +10,7 @@ from rdmo.domain.models import Attribute from ..managers import PageManager -from ..prefetch import page_prefetch_lookups +from ..prefetch import get_page_prefetch_lookups class Page(Model, TranslationMixin): @@ -223,7 +223,7 @@ def descendants(self) -> list: return descendants def prefetch_elements(self): - models.prefetch_related_objects([self], *page_prefetch_lookups()) + models.prefetch_related_objects([self], *get_page_prefetch_lookups()) def to_dict(self): return { diff --git a/rdmo/questions/models/question.py b/rdmo/questions/models/question.py index 66c87058b7..788781ca91 100644 --- a/rdmo/questions/models/question.py +++ b/rdmo/questions/models/question.py @@ -13,7 +13,7 @@ from ..constants import WIDGET_TYPE_CHOICES from ..managers import QuestionManager -from ..prefetch import question_prefetch_lookups +from ..prefetch import get_question_prefetch_lookups class Question(Model, TranslationMixin): @@ -264,7 +264,7 @@ def descendants(self) -> list: return [] def prefetch_elements(self): - models.prefetch_related_objects([self], *question_prefetch_lookups()) + models.prefetch_related_objects([self], *get_question_prefetch_lookups()) def to_dict(self, *ancestors): return { diff --git a/rdmo/questions/models/questionset.py b/rdmo/questions/models/questionset.py index e3f0284e90..5f27b302c3 100644 --- a/rdmo/questions/models/questionset.py +++ b/rdmo/questions/models/questionset.py @@ -10,7 +10,7 @@ from rdmo.domain.models import Attribute from ..managers import QuestionSetManager -from ..prefetch import questionset_prefetch_lookups +from ..prefetch import get_questionset_prefetch_lookups class QuestionSet(Model, TranslationMixin): @@ -199,7 +199,7 @@ def descendants(self) -> list: return descendants def prefetch_elements(self): - models.prefetch_related_objects([self], *questionset_prefetch_lookups()) + models.prefetch_related_objects([self], *get_questionset_prefetch_lookups()) def to_dict(self, *ancestors): return { diff --git a/rdmo/questions/models/section.py b/rdmo/questions/models/section.py index 4b1ed425be..c782c7bf98 100644 --- a/rdmo/questions/models/section.py +++ b/rdmo/questions/models/section.py @@ -8,7 +8,7 @@ from rdmo.core.utils import join_url from ..managers import SectionManager -from ..prefetch import section_prefetch_lookups +from ..prefetch import get_section_prefetch_lookups class Section(Model, TranslationMixin): @@ -138,7 +138,7 @@ def descendants(self) -> list: return descendants def prefetch_elements(self): - models.prefetch_related_objects([self], *section_prefetch_lookups()) + models.prefetch_related_objects([self], *get_section_prefetch_lookups()) def to_dict(self): elements = [element.to_dict() for element in self.elements] diff --git a/rdmo/questions/prefetch.py b/rdmo/questions/prefetch.py index bd196bb657..d495958150 100644 --- a/rdmo/questions/prefetch.py +++ b/rdmo/questions/prefetch.py @@ -3,133 +3,144 @@ from rdmo.conditions.models import Condition -def condition_prefetch(path): - return Prefetch( - path, - queryset=Condition.objects.select_related('source', 'source__parent', 'target_option') +def get_catalog_prefetch_lookups(**kwargs): + return ( + section_prefetch('catalog_sections__section', **kwargs), ) -def question_options_prefetch_lookups(): +def get_section_prefetch_lookups(**kwargs): return ( - 'optionsets', - 'optionsets__optionset_options__option', + page_prefetch('section_pages__page', **kwargs), ) -def question_prefetch(path, include_options=False): - from .models import Question +def get_page_prefetch_lookups(**kwargs): + return ( + condition_prefetch('conditions'), + question_prefetch('page_questions__question', **kwargs), + questionset_prefetch('page_questionsets__questionset', **kwargs), + ) + + +def get_questionset_prefetch_lookups(**kwargs): + return ( + condition_prefetch('conditions'), + question_prefetch('questionset_questions__question', **kwargs), + questionset_questionset_prefetch('questionset_questionsets__questionset', **kwargs), + ) + + +def get_question_prefetch_lookups(optionsets=False, optionsets_conditions=False, options=False): + additional_lookups = [] + if optionsets: + additional_lookups += [ + 'optionsets', + ] + if optionsets_conditions: + additional_lookups += [ + condition_prefetch('optionsets__conditions'), + ] + if options: + additional_lookups += [ + 'default_option', + 'optionsets__optionset_options__option', + ] + + return ( + 'attribute', + condition_prefetch('conditions'), + *additional_lookups + ) - optionset_lookups = question_options_prefetch_lookups() if include_options else ('optionsets',) + +def section_prefetch(lookup, **kwargs): + from .models import Section return Prefetch( - path, - queryset=Question.objects.select_related( - 'attribute', - ).prefetch_related( - condition_prefetch('conditions'), - *optionset_lookups, + lookup, + queryset=Section.objects.prefetch_related( + page_prefetch('section_pages__page', **kwargs), ) ) -def questionset_questionset_prefetch(path): - from .models import QuestionSet +def page_prefetch(lookup, **kwargs): + from .models import Page return Prefetch( - path, - queryset=QuestionSet.objects.select_related( + lookup, + queryset=Page.objects.select_related( 'attribute', ).prefetch_related( condition_prefetch('conditions'), - question_prefetch('questionset_questions__question'), + question_prefetch('page_questions__question', **kwargs), + questionset_prefetch('page_questionsets__questionset', **kwargs), ) ) -def questionset_prefetch(path, include_question_options=False): +def questionset_prefetch(lookup, **kwargs): from .models import QuestionSet return Prefetch( - path, + lookup, queryset=QuestionSet.objects.select_related( 'attribute', ).prefetch_related( condition_prefetch('conditions'), - question_prefetch( - 'questionset_questions__question', - include_options=include_question_options - ), - questionset_questionset_prefetch('questionset_questionsets__questionset'), + question_prefetch('questionset_questions__question', **kwargs), + questionset_questionset_prefetch('questionset_questionsets__questionset', **kwargs), ) ) -def page_prefetch(path): - from .models import Page +def questionset_questionset_prefetch(lookup, **kwargs): + from .models import QuestionSet return Prefetch( - path, - queryset=Page.objects.select_related( + lookup, + queryset=QuestionSet.objects.select_related( 'attribute', ).prefetch_related( condition_prefetch('conditions'), - question_prefetch('page_questions__question'), - questionset_prefetch('page_questionsets__questionset'), + question_prefetch('questionset_questions__question', **kwargs), ) ) -def section_prefetch(path): - from .models import Section +def question_prefetch(lookup, optionsets=False, optionsets_conditions=False, options=False): + from .models import Question + + additional_fields = ['default_option'] if options else [] + additional_lookups = [] + if optionsets: + additional_lookups += [ + 'optionsets', + ] + if optionsets_conditions: + additional_lookups += [ + condition_prefetch('optionsets__conditions'), + ] + if options: + additional_lookups += [ + 'optionsets__optionset_options__option', + ] return Prefetch( - path, - queryset=Section.objects.prefetch_related( - page_prefetch('section_pages__page'), + lookup, + queryset=Question.objects.select_related( + 'attribute', + *additional_fields, + ).prefetch_related( + condition_prefetch('conditions'), + *additional_lookups, ) ) -def catalog_prefetch_lookups(): - return ( - section_prefetch('catalog_sections__section'), - ) - - -def section_prefetch_lookups(): - return ( - page_prefetch('section_pages__page'), - ) - - -def page_prefetch_lookups(): - return ( - condition_prefetch('conditions'), - question_prefetch('page_questions__question'), - questionset_prefetch('page_questionsets__questionset'), - ) - - -def project_page_prefetch_lookups(): - return ( - condition_prefetch('conditions'), - question_prefetch('page_questions__question', include_options=True), - questionset_prefetch('page_questionsets__questionset', include_question_options=True), - ) - - -def questionset_prefetch_lookups(): - return ( - condition_prefetch('conditions'), - question_prefetch('questionset_questions__question'), - questionset_questionset_prefetch('questionset_questionsets__questionset'), - ) - - -def question_prefetch_lookups(): - return ( - condition_prefetch('conditions'), - 'optionsets', - 'default_option', +def condition_prefetch(lookup): + return Prefetch( + lookup, + queryset=Condition.objects.select_related('source', 'target_option') ) diff --git a/rdmo/questions/serializers/export.py b/rdmo/questions/serializers/export.py index d07f2aeacf..cbc03552a2 100644 --- a/rdmo/questions/serializers/export.py +++ b/rdmo/questions/serializers/export.py @@ -22,7 +22,7 @@ class QuestionExportSerializer(TranslationSerializerMixin, serializers.ModelSerializer): - attribute = AttributeExportSerializer() + attribute = serializers.SerializerMethodField() default_option = serializers.CharField(source='default_option.uri', default=None, read_only=True) optionsets = serializers.SerializerMethodField() conditions = serializers.SerializerMethodField() @@ -56,6 +56,11 @@ class Meta: 'verbose_name' ) + def get_attribute(self, obj): + attribute = self.context.get('attribute_map', {}).get(obj.attribute_id) + if attribute: + return AttributeExportSerializer(attribute, context=self.context).data + def get_optionsets(self, obj): optionsets = obj.optionsets.all() @@ -90,7 +95,7 @@ def get_questionset(self, obj): class QuestionSetQuestionExportSerializer(serializers.ModelSerializer): - question = QuestionExportSerializer() + question = serializers.SerializerMethodField() class Meta: model = QuestionSetQuestion @@ -99,10 +104,13 @@ class Meta: 'order' ) + def get_question(self, obj): + return QuestionExportSerializer(obj.question, context=self.context).data + class QuestionSetExportSerializer(TranslationSerializerMixin, serializers.ModelSerializer): - attribute = AttributeExportSerializer() + attribute = serializers.SerializerMethodField() conditions = serializers.SerializerMethodField() questionset_questionsets = QuestionSetQuestionSetExportSerializer(many=True) @@ -127,6 +135,11 @@ class Meta: 'verbose_name' ) + def get_attribute(self, obj): + attribute = self.context.get('attribute_map', {}).get(obj.attribute_id) + if attribute: + return AttributeExportSerializer(attribute, context=self.context).data + def get_conditions(self, obj): conditions = obj.conditions.all() @@ -138,7 +151,7 @@ def get_conditions(self, obj): class PageQuestionSetExportSerializer(serializers.ModelSerializer): - questionset = QuestionSetExportSerializer() + questionset = serializers.SerializerMethodField() class Meta: model = PageQuestionSet @@ -147,10 +160,13 @@ class Meta: 'order' ) + def get_questionset(self, obj): + return QuestionSetExportSerializer(obj.questionset, context=self.context).data + class PageQuestionExportSerializer(serializers.ModelSerializer): - question = QuestionExportSerializer() + question = serializers.SerializerMethodField() class Meta: model = PageQuestion @@ -159,10 +175,13 @@ class Meta: 'order' ) + def get_question(self, obj): + return QuestionExportSerializer(obj.question, context=self.context).data + class PageExportSerializer(TranslationSerializerMixin, serializers.ModelSerializer): - attribute = AttributeExportSerializer() + attribute = serializers.SerializerMethodField() conditions = serializers.SerializerMethodField() page_questionsets = PageQuestionSetExportSerializer(many=True) @@ -188,6 +207,11 @@ class Meta: 'verbose_name' ) + def get_attribute(self, obj): + attribute = self.context.get('attribute_map', {}).get(obj.attribute_id) + if attribute: + return AttributeExportSerializer(attribute, context=self.context).data + def get_conditions(self, obj): conditions = obj.conditions.all() @@ -199,7 +223,7 @@ def get_conditions(self, obj): class SectionPageExportSerializer(serializers.ModelSerializer): - page = PageExportSerializer() + page = serializers.SerializerMethodField() class Meta: model = SectionPage @@ -208,6 +232,9 @@ class Meta: 'order' ) + def get_page(self, obj): + return PageExportSerializer(obj.page, context=self.context).data + class SectionExportSerializer(TranslationSerializerMixin, serializers.ModelSerializer): diff --git a/rdmo/questions/utils.py b/rdmo/questions/utils.py index 0e5ac0440b..5c994ce1b0 100644 --- a/rdmo/questions/utils.py +++ b/rdmo/questions/utils.py @@ -1,20 +1,32 @@ from rdmo.conditions.models import Condition +from rdmo.core.utils import is_truthy from rdmo.domain.models import Attribute from rdmo.questions.models import Page, Question, QuestionSet -def get_export_serializer_context(elements, renderer_context): - if not any( - renderer_context.get(key) - for key in ('attributes', 'conditions', 'optionsets') - ): - return {'attribute_map': {}} +def get_export_flags(request): + full = is_truthy(request.GET.get('full')) + return { + 'sections': full or is_truthy(request.GET.get('sections', True)), + 'pages': full or is_truthy(request.GET.get('pages', True)), + 'questionsets': full or is_truthy(request.GET.get('questionsets', True)), + 'questions': full or is_truthy(request.GET.get('questions', True)), + 'attributes': full or is_truthy(request.GET.get('attributes')), + 'optionsets': full or is_truthy(request.GET.get('optionsets')), + 'options': full or is_truthy(request.GET.get('options')), + 'conditions': full or is_truthy(request.GET.get('conditions')), + } + + +def get_serializer_context(elements, export_flags): + if not any(export_flags.get(key) for key in ('attributes', 'conditions', 'optionsets')): + return export_flags attribute_ids = set() question_ids = set() for element in elements: - for descendant in get_element_descendants(element): + for descendant in [element, *element.descendants]: if isinstance(descendant, (Page, QuestionSet, Question)) and descendant.attribute_id: attribute_ids.add(descendant.attribute_id) @@ -27,7 +39,7 @@ def get_export_serializer_context(elements, renderer_context): for condition in descendant.conditions.all() ) - if renderer_context.get('optionsets') and question_ids: + if export_flags.get('optionsets') and question_ids: attribute_ids.update( Condition.objects.filter( optionsets__questions__id__in=question_ids @@ -35,13 +47,11 @@ def get_export_serializer_context(elements, renderer_context): ) return { - 'attribute_map': Attribute.objects.get_queryset_ancestors( - Attribute.objects.filter(id__in=attribute_ids), - include_self=True - ).in_bulk() + **export_flags, + 'attribute_map': ( + Attribute.objects.get_queryset_ancestors( + Attribute.objects.filter(id__in=attribute_ids), + include_self=True + ).in_bulk() + ) } - - -def get_element_descendants(element): - yield element - yield from getattr(element, 'descendants', []) diff --git a/rdmo/questions/viewsets.py b/rdmo/questions/viewsets.py index ebf1cb8308..f54eacf55c 100644 --- a/rdmo/questions/viewsets.py +++ b/rdmo/questions/viewsets.py @@ -11,7 +11,7 @@ from rdmo.core.exports import XMLResponse from rdmo.core.filters import SearchFilter from rdmo.core.permissions import HasModelPermission, HasObjectPermission -from rdmo.core.utils import is_truthy, render_to_format +from rdmo.core.utils import render_to_format from rdmo.core.views import ChoicesViewSet from rdmo.management.viewsets import ElementToggleCurrentSiteViewSetMixin @@ -41,7 +41,7 @@ SectionNestedSerializer, SectionSerializer, ) -from .utils import get_export_serializer_context +from .utils import get_export_flags, get_serializer_context class CatalogViewSet(ElementToggleCurrentSiteViewSetMixin, ModelViewSet): @@ -64,10 +64,21 @@ def get_queryset(self): return queryset queryset = Catalog.objects.annotate(projects_count=models.Count('projects')) - if self.action in ('nested', 'export', 'detail_export'): + if self.action in ['nested']: return queryset.prefetch_elements() + elif self.action in ['export', 'detail_export']: + return queryset.prefetch_elements( + optionsets=True, + optionsets_conditions=get_export_flags(self.request).get('conditions'), + options=get_export_flags(self.request).get('options') + ) else: - return queryset.prefetch_related('sites', 'editors', 'groups', 'catalog_sections__section') + return queryset.prefetch_related( + 'sites', + 'editors', + 'groups', + 'catalog_sections__section', + ) @action(detail=True) def nested(self, request, pk): @@ -84,13 +95,13 @@ def index(self, request): def export(self, request, export_format='xml'): queryset = self.filter_queryset(self.get_queryset()) if export_format == 'xml': - renderer_context = self.get_export_renderer_context(request) - serializer_context = { - **renderer_context, - **get_export_serializer_context(queryset, renderer_context), - } - serializer = CatalogExportSerializer(queryset, many=True, context=serializer_context) - xml = CatalogRenderer().render(serializer.data, context=renderer_context) + export_flags = get_export_flags(self.request) + serializer = CatalogExportSerializer( + queryset, + many=True, + context=get_serializer_context(queryset, export_flags) + ) + xml = CatalogRenderer().render(serializer.data, context=export_flags) return XMLResponse(xml, name='catalogs') else: return render_to_format( @@ -103,13 +114,12 @@ def export(self, request, export_format='xml'): def detail_export(self, request, pk=None, export_format='xml'): instance = self.get_object() if export_format == 'xml': - renderer_context = self.get_export_renderer_context(request) - serializer_context = { - **renderer_context, - **get_export_serializer_context([instance], renderer_context), - } - serializer = CatalogExportSerializer(instance, context=serializer_context) - xml = CatalogRenderer().render([serializer.data], context=renderer_context) + export_flags = get_export_flags(self.request) + serializer = CatalogExportSerializer( + instance, + context=get_serializer_context([instance], export_flags) + ) + xml = CatalogRenderer().render([serializer.data], context=export_flags) return XMLResponse(xml, name=instance.uri_path) else: return render_to_format( @@ -118,19 +128,6 @@ def detail_export(self, request, pk=None, export_format='xml'): } ) - def get_export_renderer_context(self, request): - full = is_truthy(request.GET.get('full')) - return { - 'sections': full or is_truthy(request.GET.get('sections', True)), - 'pages': full or is_truthy(request.GET.get('pages', True)), - 'questionsets': full or is_truthy(request.GET.get('questionsets', True)), - 'questions': full or is_truthy(request.GET.get('questions', True)), - 'attributes': full or is_truthy(request.GET.get('attributes')), - 'optionsets': full or is_truthy(request.GET.get('optionsets')), - 'options': full or is_truthy(request.GET.get('options')), - 'conditions': full or is_truthy(request.GET.get('conditions')), - } - class SectionViewSet(ModelViewSet): permission_classes = (HasModelPermission | HasObjectPermission, ) @@ -149,10 +146,20 @@ def get_queryset(self): queryset = Section.objects.all() if self.action in ['index']: return queryset - elif self.action in ('nested', 'export', 'detail_export'): + if self.action in ['nested']: return queryset.prefetch_elements() + elif self.action in ['export', 'detail_export']: + return queryset.prefetch_elements( + optionsets=True, + optionsets_conditions=get_export_flags(self.request).get('conditions'), + options=get_export_flags(self.request).get('options') + ) else: - return queryset.prefetch_related('catalogs', 'editors', 'section_pages__page') + return queryset.prefetch_related( + 'catalogs', + 'editors', + 'section_pages__page', + ) @action(detail=True) def nested(self, request, pk): @@ -169,13 +176,13 @@ def index(self, request): def export(self, request, export_format='xml'): queryset = self.filter_queryset(self.get_queryset()) if export_format == 'xml': - renderer_context = self.get_export_renderer_context(request) - serializer_context = { - **renderer_context, - **get_export_serializer_context(queryset, renderer_context), - } - serializer = SectionExportSerializer(queryset, many=True, context=serializer_context) - xml = SectionRenderer().render(serializer.data, context=renderer_context) + export_flags = get_export_flags(self.request) + serializer = SectionExportSerializer( + queryset, + many=True, + context=get_serializer_context(queryset, export_flags) + ) + xml = SectionRenderer().render(serializer.data, context=export_flags) return XMLResponse(xml, name='sections') else: return render_to_format( @@ -188,13 +195,12 @@ def export(self, request, export_format='xml'): def detail_export(self, request, pk=None, export_format='xml'): instance = self.get_object() if export_format == 'xml': - renderer_context = self.get_export_renderer_context(request) - serializer_context = { - **renderer_context, - **get_export_serializer_context([instance], renderer_context), - } - serializer = SectionExportSerializer(instance, context=serializer_context) - xml = SectionRenderer().render([serializer.data], context=renderer_context) + export_flags = get_export_flags(self.request) + serializer = SectionExportSerializer( + instance, + context=get_serializer_context([instance], export_flags) + ) + xml = SectionRenderer().render([serializer.data], context=export_flags) return XMLResponse(xml, name=instance.uri_path) else: return render_to_format( @@ -203,18 +209,6 @@ def detail_export(self, request, pk=None, export_format='xml'): } ) - def get_export_renderer_context(self, request): - full = is_truthy(request.GET.get('full')) - return { - 'pages': full or is_truthy(request.GET.get('pages', True)), - 'questionsets': full or is_truthy(request.GET.get('questionsets', True)), - 'questions': full or is_truthy(request.GET.get('questions', True)), - 'attributes': full or is_truthy(request.GET.get('attributes')), - 'optionsets': full or is_truthy(request.GET.get('optionsets')), - 'options': full or is_truthy(request.GET.get('options')), - 'conditions': full or is_truthy(request.GET.get('conditions')), - } - class PageViewSet(ModelViewSet): permission_classes = (HasModelPermission | HasObjectPermission, ) @@ -235,15 +229,21 @@ def get_queryset(self): queryset = Page.objects.all() if self.action in ['index']: return queryset - elif self.action in ['nested', 'export', 'detail_export']: - return queryset.prefetch_elements().select_related('attribute') + if self.action in ['nested']: + return queryset.prefetch_elements() + elif self.action in ['export', 'detail_export']: + return queryset.prefetch_elements( + optionsets=True, + optionsets_conditions=get_export_flags(self.request).get('conditions'), + options=get_export_flags(self.request).get('options') + ) else: return queryset.prefetch_related( 'conditions', 'sections', 'editors', 'page_questionsets__questionset', - 'page_questions__question' + 'page_questions__question', ).select_related('attribute') @action(detail=True) @@ -261,13 +261,13 @@ def index(self, request): def export(self, request, export_format='xml'): queryset = self.filter_queryset(self.get_queryset()) if export_format == 'xml': - renderer_context = self.get_export_renderer_context(request) - serializer_context = { - **renderer_context, - **get_export_serializer_context(queryset, renderer_context), - } - serializer = PageExportSerializer(queryset, many=True, context=serializer_context) - xml = PageRenderer().render(serializer.data, context=renderer_context) + export_flags = get_export_flags(self.request) + serializer = PageExportSerializer( + queryset, + many=True, + context=get_serializer_context(queryset, export_flags) + ) + xml = PageRenderer().render(serializer.data, context=export_flags) return XMLResponse(xml, name='pages') else: return render_to_format( @@ -280,13 +280,12 @@ def export(self, request, export_format='xml'): def detail_export(self, request, pk=None, export_format='xml'): instance = self.get_object() if export_format == 'xml': - renderer_context = self.get_export_renderer_context(request) - serializer_context = { - **renderer_context, - **get_export_serializer_context([instance], renderer_context), - } - serializer = PageExportSerializer(instance, context=serializer_context) - xml = PageRenderer().render([serializer.data], context=renderer_context) + export_flags = get_export_flags(self.request) + serializer = PageExportSerializer( + instance, + context=get_serializer_context([instance], export_flags) + ) + xml = PageRenderer().render([serializer.data], context=export_flags) return XMLResponse(xml, name=instance.uri_path) else: return render_to_format( @@ -295,17 +294,6 @@ def detail_export(self, request, pk=None, export_format='xml'): } ) - def get_export_renderer_context(self, request): - full = is_truthy(request.GET.get('full')) - return { - 'questionsets': full or is_truthy(request.GET.get('questionsets', True)), - 'questions': full or is_truthy(request.GET.get('questions', True)), - 'attributes': full or is_truthy(request.GET.get('attributes')), - 'optionsets': full or is_truthy(request.GET.get('optionsets')), - 'options': full or is_truthy(request.GET.get('options')), - 'conditions': full or is_truthy(request.GET.get('conditions')), - } - class QuestionSetViewSet(ModelViewSet): permission_classes = (HasModelPermission | HasObjectPermission, ) @@ -324,10 +312,17 @@ class QuestionSetViewSet(ModelViewSet): def get_queryset(self): queryset = QuestionSet.objects.all() + if self.action in ['index']: return queryset - elif self.action in ('nested', 'export', 'detail_export'): - return queryset.prefetch_elements().select_related('attribute') + elif self.action in ['nested']: + return queryset.prefetch_elements() + elif self.action in ['export', 'detail_export']: + return queryset.prefetch_elements( + optionsets=True, + optionsets_conditions=get_export_flags(self.request).get('conditions'), + options=get_export_flags(self.request).get('options') + ) else: return queryset.prefetch_related( 'conditions', @@ -335,7 +330,7 @@ def get_queryset(self): 'parents', 'editors', 'questionset_questionsets__questionset', - 'questionset_questions__question' + 'questionset_questions__question', ).select_related('attribute') @action(detail=True) @@ -353,13 +348,13 @@ def index(self, request): def export(self, request, export_format='xml'): queryset = self.filter_queryset(self.get_queryset()) if export_format == 'xml': - renderer_context = self.get_export_renderer_context(request) - serializer_context = { - **renderer_context, - **get_export_serializer_context(queryset, renderer_context), - } - serializer = QuestionSetExportSerializer(queryset, many=True, context=serializer_context) - xml = QuestionSetRenderer().render(serializer.data, context=renderer_context) + export_flags = get_export_flags(self.request) + serializer = QuestionSetExportSerializer( + queryset, + many=True, + context=get_serializer_context(queryset, export_flags) + ) + xml = QuestionSetRenderer().render(serializer.data, context=export_flags) return XMLResponse(xml, name='questionsets') else: return render_to_format( @@ -372,13 +367,12 @@ def export(self, request, export_format='xml'): def detail_export(self, request, pk=None, export_format='xml'): instance = self.get_object() if export_format == 'xml': - renderer_context = self.get_export_renderer_context(request) - serializer_context = { - **renderer_context, - **get_export_serializer_context([instance], renderer_context), - } - serializer = QuestionSetExportSerializer(instance, context=serializer_context) - xml = QuestionSetRenderer().render([serializer.data], context=renderer_context) + export_flags = get_export_flags(self.request) + serializer = QuestionSetExportSerializer( + instance, + context=get_serializer_context([instance], export_flags) + ) + xml = QuestionSetRenderer().render([serializer.data], context=export_flags) return XMLResponse(xml, name=instance.uri_path) else: return render_to_format( @@ -387,17 +381,6 @@ def detail_export(self, request, pk=None, export_format='xml'): } ) - def get_export_renderer_context(self, request): - full = is_truthy(request.GET.get('full')) - return { - 'questionsets': full or is_truthy(request.GET.get('questionsets', True)), - 'questions': full or is_truthy(request.GET.get('questions', True)), - 'attributes': full or is_truthy(request.GET.get('attributes')), - 'optionsets': full or is_truthy(request.GET.get('optionsets')), - 'options': full or is_truthy(request.GET.get('options')), - 'conditions': full or is_truthy(request.GET.get('conditions')), - } - class QuestionViewSet(ModelViewSet): permission_classes = (HasModelPermission | HasObjectPermission, ) @@ -419,10 +402,17 @@ class QuestionViewSet(ModelViewSet): def get_queryset(self): queryset = Question.objects.all() + if self.action in ['index']: return queryset - elif self.action in ('nested', 'export', 'detail_export'): - return queryset.prefetch_elements().select_related('attribute', 'default_option') + elif self.action in ['nested']: + return queryset.prefetch_elements() + elif self.action in ['export', 'detail_export']: + return queryset.prefetch_elements( + optionsets=True, + optionsets_conditions=get_export_flags(self.request).get('conditions'), + options=get_export_flags(self.request).get('options') + ) else: return queryset.prefetch_related( 'conditions', @@ -442,13 +432,13 @@ def index(self, request): def export(self, request, export_format='xml'): queryset = self.filter_queryset(self.get_queryset()) if export_format == 'xml': - renderer_context = self.get_export_renderer_context(request) - serializer_context = { - **renderer_context, - **get_export_serializer_context(queryset, renderer_context), - } - serializer = QuestionExportSerializer(queryset, many=True, context=serializer_context) - xml = QuestionRenderer().render(serializer.data, context=renderer_context) + export_flags = get_export_flags(self.request) + serializer = QuestionExportSerializer( + queryset, + many=True, + context=get_serializer_context(queryset, export_flags) + ) + xml = QuestionRenderer().render(serializer.data, context=export_flags) return XMLResponse(xml, name='questions') else: return render_to_format( @@ -461,13 +451,12 @@ def export(self, request, export_format='xml'): def detail_export(self, request, pk=None, export_format='xml'): instance = self.get_object() if export_format == 'xml': - renderer_context = self.get_export_renderer_context(request) - serializer_context = { - **renderer_context, - **get_export_serializer_context([instance], renderer_context), - } - serializer = QuestionExportSerializer(instance, context=serializer_context) - xml = QuestionRenderer().render([serializer.data], context=renderer_context) + export_flags = get_export_flags(self.request) + serializer = QuestionExportSerializer( + instance, + context=get_serializer_context([instance], export_flags) + ) + xml = QuestionRenderer().render([serializer.data], context=export_flags) return XMLResponse(xml, name=instance.uri_path) else: return render_to_format( @@ -476,15 +465,6 @@ def detail_export(self, request, pk=None, export_format='xml'): } ) - def get_export_renderer_context(self, request): - full = is_truthy(request.GET.get('full')) - return { - 'attributes': full or is_truthy(request.GET.get('attributes')), - 'optionsets': full or is_truthy(request.GET.get('optionsets')), - 'options': full or is_truthy(request.GET.get('options')), - 'conditions': full or is_truthy(request.GET.get('conditions')), - } - class WidgetTypeViewSet(ChoicesViewSet): permission_classes = (IsAuthenticated, ) From da59051460ed2d3aafca2fa64a4a0e1e5a04e03f Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Fri, 22 May 2026 11:55:00 +0200 Subject: [PATCH 090/131] Improve get_section_for_page --- rdmo/questions/models/catalog.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/rdmo/questions/models/catalog.py b/rdmo/questions/models/catalog.py index 97d7f18c78..85a14513bc 100644 --- a/rdmo/questions/models/catalog.py +++ b/rdmo/questions/models/catalog.py @@ -212,11 +212,11 @@ def to_dict(self): } def get_section_for_page(self, page): - from . import Section - try: - return Section.objects.get(catalogs=self, pages=page) - except (Section.DoesNotExist, Section.MultipleObjectsReturned): - return None + for catalog_section in self.catalog_sections.all(): + for section_page in catalog_section.section.section_pages.all(): + if section_page.page.id == page.id: + return catalog_section.section + return None def get_prev_page(self, page): try: From 6e78ab3e2258cd59384002c20332623eb449cd3f Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Fri, 22 May 2026 11:55:47 +0200 Subject: [PATCH 091/131] Rename and adjust query tests --- .../tests/test_api_performance_condition.py | 25 --------- rdmo/conditions/tests/test_queries.py | 31 +++++++++++ .../tests/test_api_performance_attribute.py | 27 --------- rdmo/domain/tests/test_queries.py | 31 +++++++++++ .../tests/test_api_performance_option.py | 29 ---------- .../tests/test_api_performance_optionset.py | 29 ---------- rdmo/options/tests/test_queries_option.py | 31 +++++++++++ rdmo/options/tests/test_queries_optionset.py | 31 +++++++++++ .../tests/test_api_performance_project.py | 55 ------------------- rdmo/projects/tests/test_queries.py | 30 ++++++++++ .../tests/test_api_performance_catalog.py | 28 ---------- .../tests/test_api_performance_page.py | 28 ---------- .../tests/test_api_performance_question.py | 28 ---------- .../tests/test_api_performance_questionset.py | 28 ---------- .../tests/test_api_performance_section.py | 28 ---------- rdmo/questions/tests/test_queries_catalog.py | 31 +++++++++++ rdmo/questions/tests/test_queries_page.py | 31 +++++++++++ rdmo/questions/tests/test_queries_question.py | 31 +++++++++++ .../tests/test_queries_questionset.py | 31 +++++++++++ rdmo/questions/tests/test_queries_section.py | 31 +++++++++++ rdmo/tasks/tests/test_api_performance_task.py | 30 ---------- rdmo/tasks/tests/test_queries.py | 31 +++++++++++ rdmo/views/tests/test_api_performance_view.py | 31 ----------- rdmo/views/tests/test_queries.py | 31 +++++++++++ 24 files changed, 371 insertions(+), 366 deletions(-) delete mode 100644 rdmo/conditions/tests/test_api_performance_condition.py create mode 100644 rdmo/conditions/tests/test_queries.py delete mode 100644 rdmo/domain/tests/test_api_performance_attribute.py create mode 100644 rdmo/domain/tests/test_queries.py delete mode 100644 rdmo/options/tests/test_api_performance_option.py delete mode 100644 rdmo/options/tests/test_api_performance_optionset.py create mode 100644 rdmo/options/tests/test_queries_option.py create mode 100644 rdmo/options/tests/test_queries_optionset.py delete mode 100644 rdmo/projects/tests/test_api_performance_project.py create mode 100644 rdmo/projects/tests/test_queries.py delete mode 100644 rdmo/questions/tests/test_api_performance_catalog.py delete mode 100644 rdmo/questions/tests/test_api_performance_page.py delete mode 100644 rdmo/questions/tests/test_api_performance_question.py delete mode 100644 rdmo/questions/tests/test_api_performance_questionset.py delete mode 100644 rdmo/questions/tests/test_api_performance_section.py create mode 100644 rdmo/questions/tests/test_queries_catalog.py create mode 100644 rdmo/questions/tests/test_queries_page.py create mode 100644 rdmo/questions/tests/test_queries_question.py create mode 100644 rdmo/questions/tests/test_queries_questionset.py create mode 100644 rdmo/questions/tests/test_queries_section.py delete mode 100644 rdmo/tasks/tests/test_api_performance_task.py create mode 100644 rdmo/tasks/tests/test_queries.py delete mode 100644 rdmo/views/tests/test_api_performance_view.py create mode 100644 rdmo/views/tests/test_queries.py diff --git a/rdmo/conditions/tests/test_api_performance_condition.py b/rdmo/conditions/tests/test_api_performance_condition.py deleted file mode 100644 index 62f5af1170..0000000000 --- a/rdmo/conditions/tests/test_api_performance_condition.py +++ /dev/null @@ -1,25 +0,0 @@ -import pytest - -from django.urls import reverse - -from .test_viewset_condition import urlnames - -max_queries = [ - # action, max_queries, url_kwargs - ('list', 9, {}), - ('index', 3, {}), - ('export', 11, {'export_format': 'xml'}), - ('detail', 9, {'pk': 1}), - ('detail_export', 9, {'pk': 1, 'export_format': 'xml'}), -] - - -@pytest.mark.performance -@pytest.mark.parametrize('action,max_queries,url_kwargs', max_queries) -def test_actions_max_query_counts(db, admin_client, django_assert_max_num_queries, action, max_queries, url_kwargs): - url = reverse(urlnames[action], kwargs=url_kwargs) - - with django_assert_max_num_queries(max_queries): - response = admin_client.get(url) - - assert response.status_code == 200 diff --git a/rdmo/conditions/tests/test_queries.py b/rdmo/conditions/tests/test_queries.py new file mode 100644 index 0000000000..ee6e5947e0 --- /dev/null +++ b/rdmo/conditions/tests/test_queries.py @@ -0,0 +1,31 @@ +from urllib.parse import urlencode + +import pytest + +from django.urls import reverse + +from .test_viewset_condition import urlnames + +max_queries = [ + # action, max_queries, url_kwargs, url_params + ('list', 9, {}, {}), + ('index', 3, {}, {}), + ('export', 5, {'export_format': 'xml'}, {}), + ('export', 5, {'export_format': 'xml'}, {'full': '1'}), + ('detail', 9, {'pk': 1}, {}), + ('detail_export', 5, {'pk': 1, 'export_format': 'xml'}, {}), + ('detail_export', 5, {'pk': 1, 'export_format': 'xml'}, {'full': '1'}), +] + + +@pytest.mark.performance +@pytest.mark.parametrize('action,max_queries,url_kwargs,url_params', max_queries) +def test_queries(db, admin_client, django_assert_max_num_queries, action, max_queries, url_kwargs, url_params): + url = reverse(urlnames[action], kwargs=url_kwargs) + if url_params: + url += f'?{urlencode(url_params)}' + + with django_assert_max_num_queries(max_queries): + response = admin_client.get(url) + + assert response.status_code == 200 diff --git a/rdmo/domain/tests/test_api_performance_attribute.py b/rdmo/domain/tests/test_api_performance_attribute.py deleted file mode 100644 index d95b59b369..0000000000 --- a/rdmo/domain/tests/test_api_performance_attribute.py +++ /dev/null @@ -1,27 +0,0 @@ -import pytest - -from django.urls import reverse - -from .test_viewset_attribute import urlnames - -max_queries = [ - # action, max_queries, url_kwargs - ('list', 10, {}), - ('index', 3, {}), - ('export', 12, {'export_format': 'xml'}), - ('detail', 11, {'pk': 1}), - ('detail_export', 12, {'pk': 1, 'export_format': 'xml'}), -] - - -@pytest.mark.performance -@pytest.mark.parametrize('action,max_queries,url_kwargs', max_queries) -def test_domain_endpoints_query_counts( - db, admin_client, django_assert_max_num_queries, - action, max_queries, url_kwargs, -): - url = reverse(urlnames[action], kwargs=url_kwargs) - - with django_assert_max_num_queries(max_queries): - response = admin_client.get(url) - assert response.status_code == 200 diff --git a/rdmo/domain/tests/test_queries.py b/rdmo/domain/tests/test_queries.py new file mode 100644 index 0000000000..6bbc233806 --- /dev/null +++ b/rdmo/domain/tests/test_queries.py @@ -0,0 +1,31 @@ +from urllib.parse import urlencode + +import pytest + +from django.urls import reverse + +from .test_viewset_attribute import urlnames + +max_queries = [ + # action, max_queries, url_kwargs, url_params + ('list', 10, {}, {}), + ('index', 3, {}, {}), + ('export', 3, {'export_format': 'xml'}, {}), + ('export', 3, {'export_format': 'xml'}, {'full': '1'}), + ('detail', 11, {'pk': 1}, {}), + ('detail_export', 4, {'pk': 1, 'export_format': 'xml'}, {}), + ('detail_export', 4, {'pk': 1, 'export_format': 'xml'}, {'full': '1'}), +] + + +@pytest.mark.performance +@pytest.mark.parametrize("action,max_queries,url_kwargs,url_params", max_queries) +def test_queries(db, admin_client, django_assert_max_num_queries, action, max_queries, url_kwargs, url_params): + url = reverse(urlnames[action], kwargs=url_kwargs) + if url_params: + url += f'?{urlencode(url_params)}' + + with django_assert_max_num_queries(max_queries): + response = admin_client.get(url) + + assert response.status_code == 200 diff --git a/rdmo/options/tests/test_api_performance_option.py b/rdmo/options/tests/test_api_performance_option.py deleted file mode 100644 index f9026e5e5f..0000000000 --- a/rdmo/options/tests/test_api_performance_option.py +++ /dev/null @@ -1,29 +0,0 @@ -import pytest - -from django.urls import reverse - -from .test_viewset_options import urlnames - -max_queries = [ - # action, max_queries, url_kwargs - ('list', 10, {}), - ('index', 3, {}), - ('export', 12, {'export_format': 'xml'}), - ('detail', 10, {'pk': 1}), - ('detail_export', 12, {'pk': 1, 'export_format': 'xml'}), -] - - -@pytest.mark.performance -@pytest.mark.parametrize('action,max_queries,url_kwargs', max_queries) -def test_option_endpoints_query_counts( - admin_client, - django_assert_max_num_queries, - action, - max_queries, - url_kwargs, -): - url = reverse(urlnames[action], kwargs=url_kwargs) - with django_assert_max_num_queries(max_queries): - response = admin_client.get(url) - assert response.status_code == 200 diff --git a/rdmo/options/tests/test_api_performance_optionset.py b/rdmo/options/tests/test_api_performance_optionset.py deleted file mode 100644 index 9660e59164..0000000000 --- a/rdmo/options/tests/test_api_performance_optionset.py +++ /dev/null @@ -1,29 +0,0 @@ -import pytest - -from django.urls import reverse - -from .test_viewset_optionsets import urlnames - -max_queries = [ - # action, max_queries, url_kwargs - ('list', 8, {}), - ('index', 3, {}), - ('export', 11, {'export_format': 'xml'}), - ('detail', 8, {'pk': 1}), - ('detail_export', 10, {'pk': 1, 'export_format': 'xml'}), -] - - -@pytest.mark.performance -@pytest.mark.parametrize('action,max_queries,url_kwargs', max_queries) -def test_optionset_endpoints_query_counts( - admin_client, - django_assert_max_num_queries, - action, - max_queries, - url_kwargs, -): - url = reverse(urlnames[action], kwargs=url_kwargs) - with django_assert_max_num_queries(max_queries): - response = admin_client.get(url) - assert response.status_code == 200 diff --git a/rdmo/options/tests/test_queries_option.py b/rdmo/options/tests/test_queries_option.py new file mode 100644 index 0000000000..38caa19f24 --- /dev/null +++ b/rdmo/options/tests/test_queries_option.py @@ -0,0 +1,31 @@ +from urllib.parse import urlencode + +import pytest + +from django.urls import reverse + +from .test_viewset_options import urlnames + +max_queries = [ + # action, max_queries, url_kwargs, url_params + ('list', 6, {}, {}), + ('index', 3, {}, {}), + ('export', 6, {'export_format': 'xml'}, {}), + ('export', 6, {'export_format': 'xml'}, {'full': '1'}), + ('detail', 6, {'pk': 1}, {}), + ('detail_export', 6, {'pk': 1, 'export_format': 'xml'}, {}), + ('detail_export', 6, {'pk': 1, 'export_format': 'xml'}, {'full': '1'}), +] + + +@pytest.mark.performance +@pytest.mark.parametrize('action,max_queries,url_kwargs,url_params', max_queries) +def test_queries(db, admin_client, django_assert_max_num_queries, action, max_queries, url_kwargs, url_params): + url = reverse(urlnames[action], kwargs=url_kwargs) + if url_params: + url += f'?{urlencode(url_params)}' + + with django_assert_max_num_queries(max_queries): + response = admin_client.get(url) + + assert response.status_code == 200 diff --git a/rdmo/options/tests/test_queries_optionset.py b/rdmo/options/tests/test_queries_optionset.py new file mode 100644 index 0000000000..aa3925bb0b --- /dev/null +++ b/rdmo/options/tests/test_queries_optionset.py @@ -0,0 +1,31 @@ +from urllib.parse import urlencode + +import pytest + +from django.urls import reverse + +from .test_viewset_optionsets import urlnames + +max_queries = [ + # action, max_queries, url_kwargs, url_params + ('list', 8, {}, {}), + ('index', 3, {}, {}), + ('export', 8, {'export_format': 'xml'}, {}), + ('export', 8, {'export_format': 'xml'}, {'full': '1'}), + ('detail', 8, {'pk': 1}, {}), + ('detail_export', 6, {'pk': 1, 'export_format': 'xml'}, {}), + ('detail_export', 6, {'pk': 1, 'export_format': 'xml'}, {'full': '1'}), +] + + +@pytest.mark.performance +@pytest.mark.parametrize('action,max_queries,url_kwargs,url_params', max_queries) +def test_queries(db, admin_client, django_assert_max_num_queries, action, max_queries, url_kwargs, url_params): + url = reverse(urlnames[action], kwargs=url_kwargs) + if url_params: + url += f'?{urlencode(url_params)}' + + with django_assert_max_num_queries(max_queries): + response = admin_client.get(url) + + assert response.status_code == 200 diff --git a/rdmo/projects/tests/test_api_performance_project.py b/rdmo/projects/tests/test_api_performance_project.py deleted file mode 100644 index 233e9dea55..0000000000 --- a/rdmo/projects/tests/test_api_performance_project.py +++ /dev/null @@ -1,55 +0,0 @@ -import pytest - -from django.urls import reverse - -max_queries = [ - # urlname, max_queries, url_args - ('project_answers', 41, [1]), - ('project_answers_export', 34, [1, 'html']), -] - -answer_tree_max_queries = [ - # method, urlname, max_queries, url_args - ('get', 'v1-projects:project-navigation', 46, [1]), - ('post', 'v1-projects:project-progress', 47, [1]), - ('get', 'v1-projects:project-page-detail', 60, [1, 1]), -] - - -@pytest.mark.performance -@pytest.mark.parametrize('urlname,max_queries,url_args', max_queries) -def test_project_answer_endpoints_query_counts( - db, - client, - django_assert_max_num_queries, - urlname, - max_queries, - url_args, -): - client.login(username='owner', password='owner') - url = reverse(urlname, args=url_args) - - with django_assert_max_num_queries(max_queries): - response = client.get(url) - - assert response.status_code == 200 - - -@pytest.mark.performance -@pytest.mark.parametrize('method,urlname,max_queries,url_args', answer_tree_max_queries) -def test_project_answer_tree_endpoints_query_counts( - db, - client, - django_assert_max_num_queries, - method, - urlname, - max_queries, - url_args, -): - client.login(username='owner', password='owner') - url = reverse(urlname, args=url_args) - - with django_assert_max_num_queries(max_queries): - response = getattr(client, method)(url) - - assert response.status_code == 200 diff --git a/rdmo/projects/tests/test_queries.py b/rdmo/projects/tests/test_queries.py new file mode 100644 index 0000000000..f297b390b1 --- /dev/null +++ b/rdmo/projects/tests/test_queries.py @@ -0,0 +1,30 @@ +import pytest + +from django.urls import reverse + +max_queries = [ + # method, urlname, max_queries, url_args + ('get', 'project_answers', 38, [1]), + ('get', 'project_answers_export', 31, [1, 'html']), + ('get', 'v1-projects:project-navigation', 43, [1]), + ('get', 'v1-projects:project-answers', 43, [1]), + ('post', 'v1-projects:project-progress', 44, [1]), + ('get', 'v1-projects:project-page-detail', 46, [1, 1]), + ('get', 'v1-projects:project-page-detail', 50, [1, 42]), + ('get', 'v1-projects:project-page-detail', 62, [1, 87]), +] + + +@pytest.mark.performance +@pytest.mark.parametrize('method,urlname,max_queries,url_args', max_queries) +def test_queries(db, client, django_assert_max_num_queries, method, urlname, max_queries, url_args): + client.login(username='owner', password='owner') + url = reverse(urlname, args=url_args) + + with django_assert_max_num_queries(max_queries): + if method == 'get': + response = client.get(url) + elif method == 'post': + response = client.post(url) + + assert response.status_code == 200 diff --git a/rdmo/questions/tests/test_api_performance_catalog.py b/rdmo/questions/tests/test_api_performance_catalog.py deleted file mode 100644 index 0bf08d602c..0000000000 --- a/rdmo/questions/tests/test_api_performance_catalog.py +++ /dev/null @@ -1,28 +0,0 @@ -import pytest - -from django.urls import reverse - -from .test_viewset_catalog import urlnames - -max_queries = [ - # action, max_queries url_kwargs - ('list', 8, {}), - ('index', 3, {}), - ('export', 32, {'export_format': 'xml'}), - ('detail', 8, {'pk': 1}), - ('detail_export', 32, {'pk': 1, 'export_format': 'xml'}), -] - - -@pytest.mark.performance -@pytest.mark.parametrize('action,max_queries,url_kwargs', max_queries) -def test_catalog_endpoints_query_counts( - db, admin_client, django_assert_max_num_queries, - action, max_queries, url_kwargs, -): - url = reverse(urlnames[action], kwargs=url_kwargs) - - with django_assert_max_num_queries(max_queries): - response = admin_client.get(url) - - assert response.status_code == 200 diff --git a/rdmo/questions/tests/test_api_performance_page.py b/rdmo/questions/tests/test_api_performance_page.py deleted file mode 100644 index ff7d3bdabd..0000000000 --- a/rdmo/questions/tests/test_api_performance_page.py +++ /dev/null @@ -1,28 +0,0 @@ -import pytest - -from django.urls import reverse - -from .test_viewset_page import urlnames - -max_queries = [ - # action, max_queries, url_kwargs - ('list', 10, {}), - ('index', 3, {}), - ('export', 28, {'export_format': 'xml'}), - ('detail', 10, {'pk': 1}), - ('detail_export', 13, {'pk': 1, 'export_format': 'xml'}), -] - - -@pytest.mark.performance -@pytest.mark.parametrize('action,max_queries,url_kwargs', max_queries) -def test_page_endpoints_query_counts( - db, admin_client, django_assert_max_num_queries, - action, max_queries, url_kwargs, -): - url = reverse(urlnames[action], kwargs=url_kwargs) - - with django_assert_max_num_queries(max_queries): - response = admin_client.get(url) - - assert response.status_code == 200 diff --git a/rdmo/questions/tests/test_api_performance_question.py b/rdmo/questions/tests/test_api_performance_question.py deleted file mode 100644 index 2c29595820..0000000000 --- a/rdmo/questions/tests/test_api_performance_question.py +++ /dev/null @@ -1,28 +0,0 @@ -import pytest - -from django.urls import reverse - -from .test_viewset_question import urlnames - -max_queries = [ - # action, max_queries, url_kwargs - ('list', 10, {}), - ('index', 3, {}), - ('export', 12, {'export_format': 'xml'}), - ('detail', 10, {'pk': 1}), - ('detail_export', 12, {'pk': 1, 'export_format': 'xml'}), -] - - -@pytest.mark.performance -@pytest.mark.parametrize('action,max_queries,url_kwargs', max_queries) -def test_question_endpoints_query_counts( - db, admin_client, django_assert_max_num_queries, - action, max_queries, url_kwargs, -): - url = reverse(urlnames[action], kwargs=url_kwargs) - - with django_assert_max_num_queries(max_queries): - response = admin_client.get(url) - - assert response.status_code == 200 diff --git a/rdmo/questions/tests/test_api_performance_questionset.py b/rdmo/questions/tests/test_api_performance_questionset.py deleted file mode 100644 index af70dbdcd2..0000000000 --- a/rdmo/questions/tests/test_api_performance_questionset.py +++ /dev/null @@ -1,28 +0,0 @@ -import pytest - -from django.urls import reverse - -from .test_viewset_questionset import urlnames - -max_queries = [ - # action, max_queries, url_kwargs - ('list', 11, {}), - ('index', 3, {}), - ('export', 19, {'export_format': 'xml'}), - ('detail', 10, {'pk': 89}), - ('detail_export', 13, {'pk': 89, 'export_format': 'xml'}), -] - - -@pytest.mark.performance -@pytest.mark.parametrize('action,max_queries,url_kwargs', max_queries) -def test_questionset_endpoints_query_counts( - db, admin_client, django_assert_max_num_queries, - action, max_queries, url_kwargs, -): - url = reverse(urlnames[action], kwargs=url_kwargs) - - with django_assert_max_num_queries(max_queries): - response = admin_client.get(url) - - assert response.status_code == 200 diff --git a/rdmo/questions/tests/test_api_performance_section.py b/rdmo/questions/tests/test_api_performance_section.py deleted file mode 100644 index 78e0967478..0000000000 --- a/rdmo/questions/tests/test_api_performance_section.py +++ /dev/null @@ -1,28 +0,0 @@ -import pytest - -from django.urls import reverse - -from .test_viewset_section import urlnames - -max_queries = [ - # action, max_queries, url_kwargs - ('list', 8, {}), - ('index', 3, {}), - ('export', 30, {'export_format': 'xml'}), - ('detail', 8, {'pk': 1}), - ('detail_export', 14, {'pk': 1, 'export_format': 'xml'}), -] - - -@pytest.mark.performance -@pytest.mark.parametrize('action,max_queries,url_kwargs', max_queries) -def test_section_endpoints_query_counts( - db, admin_client, django_assert_max_num_queries, - action, max_queries, url_kwargs, -): - url = reverse(urlnames[action], kwargs=url_kwargs) - - with django_assert_max_num_queries(max_queries): - response = admin_client.get(url) - - assert response.status_code == 200 diff --git a/rdmo/questions/tests/test_queries_catalog.py b/rdmo/questions/tests/test_queries_catalog.py new file mode 100644 index 0000000000..9b885cf5f3 --- /dev/null +++ b/rdmo/questions/tests/test_queries_catalog.py @@ -0,0 +1,31 @@ +from urllib.parse import urlencode + +import pytest + +from django.urls import reverse + +from .test_viewset_catalog import urlnames + +max_queries = [ + # action, max_queries, url_kwargs, url_params + ('list', 8, {}, {}), + ('index', 3, {}, {}), + ('export', 32, {'export_format': 'xml'}, {}), + ('export', 42, {'export_format': 'xml'}, {'full': '1'}), + ('detail', 8, {'pk': 1}, {}), + ('detail_export', 32, {'pk': 1, 'export_format': 'xml'}, {}), + ('detail_export', 39, {'pk': 1, 'export_format': 'xml'}, {'full': '1'}), +] + + +@pytest.mark.performance +@pytest.mark.parametrize('action,max_queries,url_kwargs,url_params', max_queries) +def test_queries(db, admin_client, django_assert_max_num_queries, action, max_queries, url_kwargs, url_params): + url = reverse(urlnames[action], kwargs=url_kwargs) + if url_params: + url += f'?{urlencode(url_params)}' + + with django_assert_max_num_queries(max_queries): + response = admin_client.get(url) + + assert response.status_code == 200 diff --git a/rdmo/questions/tests/test_queries_page.py b/rdmo/questions/tests/test_queries_page.py new file mode 100644 index 0000000000..7f94e69156 --- /dev/null +++ b/rdmo/questions/tests/test_queries_page.py @@ -0,0 +1,31 @@ +from urllib.parse import urlencode + +import pytest + +from django.urls import reverse + +from .test_viewset_page import urlnames + +max_queries = [ + # action, max_queries, url_kwargs, url_params + ('list', 10, {}, {}), + ('index', 3, {}, {}), + ('export', 28, {'export_format': 'xml'}, {}), + ('export', 38, {'export_format': 'xml'}, {'full': '1'}), + ('detail', 9, {'pk': 1}, {}), + ('detail_export', 9, {'pk': 1, 'export_format': 'xml'}, {}), + ('detail_export', 12, {'pk': 1, 'export_format': 'xml'}, {'full': '1'}), +] + + +@pytest.mark.performance +@pytest.mark.parametrize('action,max_queries,url_kwargs,url_params', max_queries) +def test_queries(db, admin_client, django_assert_max_num_queries, action, max_queries, url_kwargs, url_params): + url = reverse(urlnames[action], kwargs=url_kwargs) + if url_params: + url += f'?{urlencode(url_params)}' + + with django_assert_max_num_queries(max_queries): + response = admin_client.get(url) + + assert response.status_code == 200 diff --git a/rdmo/questions/tests/test_queries_question.py b/rdmo/questions/tests/test_queries_question.py new file mode 100644 index 0000000000..0a4b1cc3d0 --- /dev/null +++ b/rdmo/questions/tests/test_queries_question.py @@ -0,0 +1,31 @@ +from urllib.parse import urlencode + +import pytest + +from django.urls import reverse + +from .test_viewset_question import urlnames + +max_queries = [ + # action, max_queries, url_kwargs, url_params + ('list', 8, {}, {}), + ('index', 3, {}, {}), + ('export', 10, {'export_format': 'xml'}, {}), + ('export', 13, {'export_format': 'xml'}, {'full': '1'}), + ('detail', 8, {'pk': 1}, {}), + ('detail_export', 6, {'pk': 1, 'export_format': 'xml'}, {}), + ('detail_export', 9, {'pk': 1, 'export_format': 'xml'}, {'full': '1'}), +] + + +@pytest.mark.performance +@pytest.mark.parametrize('action,max_queries,url_kwargs,url_params', max_queries) +def test_queries(db, admin_client, django_assert_max_num_queries, action, max_queries, url_kwargs, url_params): + url = reverse(urlnames[action], kwargs=url_kwargs) + if url_params: + url += f'?{urlencode(url_params)}' + + with django_assert_max_num_queries(max_queries): + response = admin_client.get(url) + + assert response.status_code == 200 diff --git a/rdmo/questions/tests/test_queries_questionset.py b/rdmo/questions/tests/test_queries_questionset.py new file mode 100644 index 0000000000..a6d43bee8b --- /dev/null +++ b/rdmo/questions/tests/test_queries_questionset.py @@ -0,0 +1,31 @@ +from urllib.parse import urlencode + +import pytest + +from django.urls import reverse + +from .test_viewset_questionset import urlnames + +max_queries = [ + # action, max_queries, url_kwargs, url_params + ('list', 11, {}, {}), + ('index', 3, {}, {}), + ('export', 19, {'export_format': 'xml'}, {}), + ('export', 28, {'export_format': 'xml'}, {'full': '1'}), + ('detail', 11, {'pk': 90}, {}), + ('detail_export', 18, {'pk': 90, 'export_format': 'xml'}, {}), + ('detail_export', 25, {'pk': 90, 'export_format': 'xml'}, {'full': '1'}), +] + + +@pytest.mark.performance +@pytest.mark.parametrize('action,max_queries,url_kwargs,url_params', max_queries) +def test_queries(db, admin_client, django_assert_max_num_queries, action, max_queries, url_kwargs, url_params): + url = reverse(urlnames[action], kwargs=url_kwargs) + if url_params: + url += f'?{urlencode(url_params)}' + + with django_assert_max_num_queries(max_queries): + response = admin_client.get(url) + + assert response.status_code == 200 diff --git a/rdmo/questions/tests/test_queries_section.py b/rdmo/questions/tests/test_queries_section.py new file mode 100644 index 0000000000..a41ca73b88 --- /dev/null +++ b/rdmo/questions/tests/test_queries_section.py @@ -0,0 +1,31 @@ +from urllib.parse import urlencode + +import pytest + +from django.urls import reverse + +from .test_viewset_section import urlnames + +max_queries = [ + # action, max_queries, url_kwargs, url_params + ('list', 7, {}, {}), + ('index', 3, {}, {}), + ('export', 30, {'export_format': 'xml'}, {}), + ('export', 40, {'export_format': 'xml'}, {'full': '1'}), + ('detail', 7, {'pk': 1}, {}), + ('detail_export', 14, {'pk': 1, 'export_format': 'xml'}, {}), + ('detail_export', 17, {'pk': 1, 'export_format': 'xml'}, {'full': '1'}), +] + + +@pytest.mark.performance +@pytest.mark.parametrize('action,max_queries,url_kwargs,url_params', max_queries) +def test_queries(db, admin_client, django_assert_max_num_queries, action, max_queries, url_kwargs, url_params): + url = reverse(urlnames[action], kwargs=url_kwargs) + if url_params: + url += f'?{urlencode(url_params)}' + + with django_assert_max_num_queries(max_queries): + response = admin_client.get(url) + + assert response.status_code == 200 diff --git a/rdmo/tasks/tests/test_api_performance_task.py b/rdmo/tasks/tests/test_api_performance_task.py deleted file mode 100644 index 8af150fb32..0000000000 --- a/rdmo/tasks/tests/test_api_performance_task.py +++ /dev/null @@ -1,30 +0,0 @@ -import pytest - -from django.urls import reverse - -from .test_viewset_task import urlnames - -max_queries = [ - # action, max_queries, url_kwargs - ('list', 10, {}), - ('index', 3, {}), - ('export', 12, {'export_format': 'xml'}), - ('detail', 10, {'pk': 1}), - ('detail_export', 12, {'pk': 1, 'export_format': 'xml'}), -] - - -@pytest.mark.performance -@pytest.mark.parametrize('action,max_queries,url_kwargs', max_queries) -def test_tasks_endpoints_query_counts( - db, - admin_client, - django_assert_max_num_queries, - action, - max_queries, - url_kwargs, -): - url = reverse(urlnames[action], kwargs=url_kwargs) - with django_assert_max_num_queries(max_queries): - response = admin_client.get(url) - assert response.status_code == 200 diff --git a/rdmo/tasks/tests/test_queries.py b/rdmo/tasks/tests/test_queries.py new file mode 100644 index 0000000000..c7ddd7c541 --- /dev/null +++ b/rdmo/tasks/tests/test_queries.py @@ -0,0 +1,31 @@ +from urllib.parse import urlencode + +import pytest + +from django.urls import reverse + +from .test_viewset_task import urlnames + +max_queries = [ + # action, max_queries, url_kwargs, url_params + ('list', 8, {}, {}), + ('index', 3, {}, {}), + ('export', 9, {'export_format': 'xml'}, {}), + ('export', 9, {'export_format': 'xml'}, {'full': '1'}), + ('detail', 8, {'pk': 1}, {}), + ('detail_export', 9, {'pk': 1, 'export_format': 'xml'}, {}), + ('detail_export', 9, {'pk': 1, 'export_format': 'xml'}, {'full': '1'}), +] + + +@pytest.mark.performance +@pytest.mark.parametrize('action,max_queries,url_kwargs,url_params', max_queries) +def test_queries(db, admin_client, django_assert_max_num_queries, action, max_queries, url_kwargs, url_params): + url = reverse(urlnames[action], kwargs=url_kwargs) + if url_params: + url += f'?{urlencode(url_params)}' + + with django_assert_max_num_queries(max_queries): + response = admin_client.get(url) + + assert response.status_code == 200 diff --git a/rdmo/views/tests/test_api_performance_view.py b/rdmo/views/tests/test_api_performance_view.py deleted file mode 100644 index 9d28eb5d6f..0000000000 --- a/rdmo/views/tests/test_api_performance_view.py +++ /dev/null @@ -1,31 +0,0 @@ -import pytest - -from django.urls import reverse - -from .test_viewset_view import urlnames - -max_queries = [ - # action, max_queries, url_kwargs - ('list', 8, {}), - ('index', 3, {}), - ('export', 10, {'export_format': 'xml'}), - ('detail', 8, {'pk': 1}), - ('detail_export', 10, {'pk': 1, 'export_format': 'xml'}), -] - -@pytest.mark.performance -@pytest.mark.parametrize('action,max_queries,url_kwargs', max_queries) -def test_views_endpoints_query_counts( - db, - admin_client, - django_assert_max_num_queries, - action, - max_queries, - url_kwargs, -): - url = reverse(urlnames[action], kwargs=url_kwargs) - - with django_assert_max_num_queries(max_queries): - response = admin_client.get(url) - - assert response.status_code == 200 diff --git a/rdmo/views/tests/test_queries.py b/rdmo/views/tests/test_queries.py new file mode 100644 index 0000000000..c4c2116352 --- /dev/null +++ b/rdmo/views/tests/test_queries.py @@ -0,0 +1,31 @@ +from urllib.parse import urlencode + +import pytest + +from django.urls import reverse + +from .test_viewset_view import urlnames + +max_queries = [ + # action, max_queries, url_kwargs, url_params + ('list', 7, {}, {}), + ('index', 3, {}, {}), + ('export', 7, {'export_format': 'xml'}, {}), + ('export', 7, {'export_format': 'xml'}, {'full': '1'}), + ('detail', 7, {'pk': 1}, {}), + ('detail_export', 7, {'pk': 1, 'export_format': 'xml'}, {}), + ('detail_export', 7, {'pk': 1, 'export_format': 'xml'}, {'full': '1'}), +] + + +@pytest.mark.performance +@pytest.mark.parametrize('action,max_queries,url_kwargs,url_params', max_queries) +def test_queries(db, admin_client, django_assert_max_num_queries, action, max_queries, url_kwargs, url_params): + url = reverse(urlnames[action], kwargs=url_kwargs) + if url_params: + url += f'?{urlencode(url_params)}' + + with django_assert_max_num_queries(max_queries): + response = admin_client.get(url) + + assert response.status_code == 200 From 63f10b360fea816ec1e618a6111f4097b6da9560 Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Fri, 22 May 2026 12:50:46 +0200 Subject: [PATCH 092/131] Refactor nested serializers --- rdmo/questions/serializers/v1/catalog.py | 30 ++++++++-- rdmo/questions/serializers/v1/page.py | 43 +++++++++++++-- rdmo/questions/serializers/v1/question.py | 55 ++++++++++++++++++- rdmo/questions/serializers/v1/questionset.py | 44 +++++++++++++-- rdmo/questions/serializers/v1/section.py | 30 ++++++++-- rdmo/questions/tests/test_queries_catalog.py | 1 + rdmo/questions/tests/test_queries_page.py | 1 + .../tests/test_queries_questionset.py | 1 + rdmo/questions/tests/test_queries_section.py | 1 + rdmo/questions/viewsets.py | 10 ++-- 10 files changed, 189 insertions(+), 27 deletions(-) diff --git a/rdmo/questions/serializers/v1/catalog.py b/rdmo/questions/serializers/v1/catalog.py index 23e4a8e362..6991a60a05 100644 --- a/rdmo/questions/serializers/v1/catalog.py +++ b/rdmo/questions/serializers/v1/catalog.py @@ -81,14 +81,36 @@ class Meta: ) -class CatalogNestedSerializer(CatalogSerializer): +class CatalogNestedSerializer( + ElementModelSerializerMixin, + ElementWarningSerializerMixin, + ReadOnlyObjectPermissionSerializerMixin, + MarkdownSerializerMixin, + serializers.ModelSerializer +): + markdown_fields = ('title',) + + model = serializers.SerializerMethodField() + + warning = serializers.SerializerMethodField() + read_only = serializers.SerializerMethodField() elements = serializers.SerializerMethodField() - class Meta(CatalogSerializer.Meta): + class Meta: + model = Catalog fields = ( - *CatalogSerializer.Meta.fields, - 'elements' + 'id', + 'model', + 'uri', + 'locked', + 'title', + 'warning', + 'read_only', + 'elements', + ) + warning_fields = ( + 'title', ) def get_elements(self, obj): diff --git a/rdmo/questions/serializers/v1/page.py b/rdmo/questions/serializers/v1/page.py index 5ce00195bf..93da654ed6 100644 --- a/rdmo/questions/serializers/v1/page.py +++ b/rdmo/questions/serializers/v1/page.py @@ -11,8 +11,7 @@ from ...models import Page, PageQuestion, PageQuestionSet, QuestionSet, Section from ...validators import PageLockedValidator, PageUniqueURIValidator -from .question import QuestionSerializer -from .questionset import QuestionSetNestedSerializer +from .questionset import QuestionNestedSerializer, QuestionSetNestedSerializer class PageQuestionSetSerializer(serializers.ModelSerializer): @@ -108,22 +107,54 @@ def get_condition_uris(self, obj) -> list: return [condition.uri for condition in obj.conditions.all()] -class PageNestedSerializer(PageSerializer): +class PageNestedSerializer( + ElementModelSerializerMixin, + ElementWarningSerializerMixin, + ReadOnlyObjectPermissionSerializerMixin, + MarkdownSerializerMixin, + serializers.ModelSerializer +): + markdown_fields = ('title',) + + model = serializers.SerializerMethodField() + + warning = serializers.SerializerMethodField() + read_only = serializers.SerializerMethodField() + + attribute_uri = serializers.CharField(source='attribute.uri', read_only=True) + condition_uris = serializers.SerializerMethodField() elements = serializers.SerializerMethodField() - class Meta(PageSerializer.Meta): + class Meta: + model = Page fields = ( - *PageSerializer.Meta.fields, + 'id', + 'model', + 'uri', + 'locked', + 'attribute', + 'title', + 'conditions', + 'warning', + 'read_only', + 'attribute_uri', + 'condition_uris', 'elements' ) + warning_fields = ( + 'title', + ) def get_elements(self, obj): for element in obj.elements: if isinstance(element, QuestionSet): yield QuestionSetNestedSerializer(element, context=self.context).data else: - yield QuestionSerializer(element, context=self.context).data + yield QuestionNestedSerializer(element, context=self.context).data + + def get_condition_uris(self, obj) -> list: + return [condition.uri for condition in obj.conditions.all()] class PageIndexSerializer(serializers.ModelSerializer): diff --git a/rdmo/questions/serializers/v1/question.py b/rdmo/questions/serializers/v1/question.py index 00f75dbff7..453e0695bc 100644 --- a/rdmo/questions/serializers/v1/question.py +++ b/rdmo/questions/serializers/v1/question.py @@ -51,6 +51,7 @@ class Meta: 'maximum', 'minimum', 'step', + 'default_text', 'default_option', 'default_external_id', 'widget_type', @@ -59,7 +60,6 @@ class Meta: 'width', 'text', 'help', - 'default_text', 'verbose_name', 'pages', 'questionsets', @@ -134,6 +134,59 @@ def validate(self, data): return data +class QuestionNestedSerializer( + ElementModelSerializerMixin, + ElementWarningSerializerMixin, + ReadOnlyObjectPermissionSerializerMixin, + MarkdownSerializerMixin, + serializers.ModelSerializer +): + markdown_fields = ('text',) + + model = serializers.SerializerMethodField() + + warning = serializers.SerializerMethodField() + read_only = serializers.SerializerMethodField() + + attribute_uri = serializers.CharField(source='attribute.uri', read_only=True) + condition_uris = serializers.SerializerMethodField() + optionset_uris = serializers.SerializerMethodField() + + class Meta: + model = Question + fields = ( + 'id', + 'model', + 'uri', + 'locked', + 'attribute', + 'is_collection', + 'is_optional', + 'default_text', + 'default_option', + 'default_external_id', + 'widget_type', + 'value_type', + 'text', + 'optionsets', + 'conditions', + 'warning', + 'read_only', + 'attribute_uri', + 'condition_uris', + 'optionset_uris' + ) + warning_fields = ( + 'text', + ) + + def get_condition_uris(self, obj) -> list: + return [condition.uri for condition in obj.conditions.all()] + + def get_optionset_uris(self, obj) -> list: + return [optionset.uri for optionset in obj.optionsets.all()] + + class QuestionIndexSerializer(serializers.ModelSerializer): class Meta: diff --git a/rdmo/questions/serializers/v1/questionset.py b/rdmo/questions/serializers/v1/questionset.py index 9385c85934..59e4ac1cf3 100644 --- a/rdmo/questions/serializers/v1/questionset.py +++ b/rdmo/questions/serializers/v1/questionset.py @@ -11,7 +11,7 @@ from ...models import Page, QuestionSet, QuestionSetQuestion, QuestionSetQuestionSet from ...validators import QuestionSetLockedValidator, QuestionSetQuestionSetValidator, QuestionSetUniqueURIValidator -from .question import QuestionSerializer +from .question import QuestionNestedSerializer class QuestionSetQuestionSetSerializer(serializers.ModelSerializer): @@ -111,14 +111,43 @@ def get_condition_uris(self, obj) -> list: return [condition.uri for condition in obj.conditions.all()] -class QuestionSetNestedSerializer(QuestionSetSerializer): +class QuestionSetNestedSerializer( + ElementModelSerializerMixin, + ElementWarningSerializerMixin, + ReadOnlyObjectPermissionSerializerMixin, + MarkdownSerializerMixin, + serializers.ModelSerializer +): + markdown_fields = ('title',) + + model = serializers.SerializerMethodField() + + warning = serializers.SerializerMethodField() + read_only = serializers.SerializerMethodField() + + attribute_uri = serializers.CharField(source='attribute.uri', read_only=True) + condition_uris = serializers.SerializerMethodField() elements = serializers.SerializerMethodField() - class Meta(QuestionSetSerializer.Meta): + class Meta: + model = QuestionSet fields = ( - *QuestionSetSerializer.Meta.fields, - 'elements' + 'id', + 'model', + 'uri', + 'locked', + 'attribute', + 'title', + 'conditions', + 'warning', + 'read_only', + 'attribute_uri', + 'condition_uris', + 'elements', + ) + warning_fields = ( + 'title', ) def get_elements(self, obj): @@ -126,7 +155,10 @@ def get_elements(self, obj): if isinstance(element, QuestionSet): yield QuestionSetNestedSerializer(element, context=self.context).data else: - yield QuestionSerializer(element, context=self.context).data + yield QuestionNestedSerializer(element, context=self.context).data + + def get_condition_uris(self, obj) -> list: + return [condition.uri for condition in obj.conditions.all()] class QuestionSetIndexSerializer(serializers.ModelSerializer): diff --git a/rdmo/questions/serializers/v1/section.py b/rdmo/questions/serializers/v1/section.py index ebedc8081a..4443ae7a82 100644 --- a/rdmo/questions/serializers/v1/section.py +++ b/rdmo/questions/serializers/v1/section.py @@ -79,14 +79,36 @@ class Meta: ) -class SectionNestedSerializer(SectionSerializer): +class SectionNestedSerializer( + ElementModelSerializerMixin, + ElementWarningSerializerMixin, + ReadOnlyObjectPermissionSerializerMixin, + MarkdownSerializerMixin, + serializers.ModelSerializer +): + markdown_fields = ('title',) + + model = serializers.SerializerMethodField() + + warning = serializers.SerializerMethodField() + read_only = serializers.SerializerMethodField() elements = serializers.SerializerMethodField() - class Meta(SectionSerializer.Meta): + class Meta: + model = Section fields = ( - *SectionSerializer.Meta.fields, - 'elements' + 'id', + 'model', + 'uri', + 'locked', + 'title', + 'warning', + 'read_only', + 'elements', + ) + warning_fields = ( + 'title', ) def get_elements(self, obj): diff --git a/rdmo/questions/tests/test_queries_catalog.py b/rdmo/questions/tests/test_queries_catalog.py index 9b885cf5f3..7d02c15224 100644 --- a/rdmo/questions/tests/test_queries_catalog.py +++ b/rdmo/questions/tests/test_queries_catalog.py @@ -10,6 +10,7 @@ # action, max_queries, url_kwargs, url_params ('list', 8, {}, {}), ('index', 3, {}, {}), + ('nested', 28, {'pk': 1}, {}), ('export', 32, {'export_format': 'xml'}, {}), ('export', 42, {'export_format': 'xml'}, {'full': '1'}), ('detail', 8, {'pk': 1}, {}), diff --git a/rdmo/questions/tests/test_queries_page.py b/rdmo/questions/tests/test_queries_page.py index 7f94e69156..bd54e1e897 100644 --- a/rdmo/questions/tests/test_queries_page.py +++ b/rdmo/questions/tests/test_queries_page.py @@ -10,6 +10,7 @@ # action, max_queries, url_kwargs, url_params ('list', 10, {}, {}), ('index', 3, {}, {}), + ('nested', 9, {'pk': 1}, {}), ('export', 28, {'export_format': 'xml'}, {}), ('export', 38, {'export_format': 'xml'}, {'full': '1'}), ('detail', 9, {'pk': 1}, {}), diff --git a/rdmo/questions/tests/test_queries_questionset.py b/rdmo/questions/tests/test_queries_questionset.py index a6d43bee8b..8e855eddeb 100644 --- a/rdmo/questions/tests/test_queries_questionset.py +++ b/rdmo/questions/tests/test_queries_questionset.py @@ -10,6 +10,7 @@ # action, max_queries, url_kwargs, url_params ('list', 11, {}, {}), ('index', 3, {}, {}), + ('nested', 17, {'pk': 90}, {}), ('export', 19, {'export_format': 'xml'}, {}), ('export', 28, {'export_format': 'xml'}, {'full': '1'}), ('detail', 11, {'pk': 90}, {}), diff --git a/rdmo/questions/tests/test_queries_section.py b/rdmo/questions/tests/test_queries_section.py index a41ca73b88..3dd17ac0b6 100644 --- a/rdmo/questions/tests/test_queries_section.py +++ b/rdmo/questions/tests/test_queries_section.py @@ -10,6 +10,7 @@ # action, max_queries, url_kwargs, url_params ('list', 7, {}, {}), ('index', 3, {}, {}), + ('nested', 11, {'pk': 1}, {}), ('export', 30, {'export_format': 'xml'}, {}), ('export', 40, {'export_format': 'xml'}, {'full': '1'}), ('detail', 7, {'pk': 1}, {}), diff --git a/rdmo/questions/viewsets.py b/rdmo/questions/viewsets.py index f54eacf55c..d8f5921b9a 100644 --- a/rdmo/questions/viewsets.py +++ b/rdmo/questions/viewsets.py @@ -65,7 +65,7 @@ def get_queryset(self): queryset = Catalog.objects.annotate(projects_count=models.Count('projects')) if self.action in ['nested']: - return queryset.prefetch_elements() + return queryset.prefetch_elements(optionsets=True) elif self.action in ['export', 'detail_export']: return queryset.prefetch_elements( optionsets=True, @@ -147,7 +147,7 @@ def get_queryset(self): if self.action in ['index']: return queryset if self.action in ['nested']: - return queryset.prefetch_elements() + return queryset.prefetch_elements(optionsets=True) elif self.action in ['export', 'detail_export']: return queryset.prefetch_elements( optionsets=True, @@ -230,7 +230,7 @@ def get_queryset(self): if self.action in ['index']: return queryset if self.action in ['nested']: - return queryset.prefetch_elements() + return queryset.prefetch_elements(optionsets=True) elif self.action in ['export', 'detail_export']: return queryset.prefetch_elements( optionsets=True, @@ -316,7 +316,7 @@ def get_queryset(self): if self.action in ['index']: return queryset elif self.action in ['nested']: - return queryset.prefetch_elements() + return queryset.prefetch_elements(optionsets=True) elif self.action in ['export', 'detail_export']: return queryset.prefetch_elements( optionsets=True, @@ -405,8 +405,6 @@ def get_queryset(self): if self.action in ['index']: return queryset - elif self.action in ['nested']: - return queryset.prefetch_elements() elif self.action in ['export', 'detail_export']: return queryset.prefetch_elements( optionsets=True, From 14929098a27d2f8d18798334149828dcb19b8246 Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Fri, 22 May 2026 13:34:16 +0200 Subject: [PATCH 093/131] Refactor OptionSetNestedSerializer and add OptionNestedSerializer --- rdmo/options/serializers/v1/option.py | 30 +++++++++++++++++ rdmo/options/serializers/v1/optionset.py | 34 ++++++++++++++++---- rdmo/options/tests/test_queries_optionset.py | 1 + rdmo/options/viewsets.py | 22 ++++++------- 4 files changed, 69 insertions(+), 18 deletions(-) diff --git a/rdmo/options/serializers/v1/option.py b/rdmo/options/serializers/v1/option.py index 8fecf0d123..055ddbb731 100644 --- a/rdmo/options/serializers/v1/option.py +++ b/rdmo/options/serializers/v1/option.py @@ -76,6 +76,36 @@ class Meta: ) +class OptionNestedSerializer( + ElementModelSerializerMixin, + ElementWarningSerializerMixin, + ReadOnlyObjectPermissionSerializerMixin, + MarkdownSerializerMixin, + serializers.ModelSerializer +): + markdown_fields = ('text',) + + model = serializers.SerializerMethodField() + + warning = serializers.SerializerMethodField() + read_only = serializers.SerializerMethodField() + + class Meta: + model = Option + fields = ( + 'id', + 'model', + 'uri', + 'locked', + 'text', + 'warning', + 'read_only', + ) + warning_fields = ( + 'text', + ) + + class OptionIndexSerializer(serializers.ModelSerializer): class Meta: diff --git a/rdmo/options/serializers/v1/optionset.py b/rdmo/options/serializers/v1/optionset.py index 14a4b5169e..188c9ba882 100644 --- a/rdmo/options/serializers/v1/optionset.py +++ b/rdmo/options/serializers/v1/optionset.py @@ -9,7 +9,7 @@ from ...models import OptionSet, OptionSetOption from ...validators import OptionSetLockedValidator, OptionSetUniqueURIValidator -from .option import OptionSerializer +from .option import OptionNestedSerializer class OptionSetOptionSerializer(serializers.ModelSerializer): @@ -44,7 +44,6 @@ class Meta: 'uri_path', 'comment', 'locked', - 'read_only', 'order', 'provider_key', 'options', @@ -69,19 +68,40 @@ def get_condition_uris(self, obj) -> list[str]: return [condition.uri for condition in obj.conditions.all()] -class OptionSetNestedSerializer(OptionSetSerializer): +class OptionSetNestedSerializer( + ElementModelSerializerMixin, + ReadOnlyObjectPermissionSerializerMixin, + serializers.ModelSerializer, +): + model = serializers.SerializerMethodField() + + read_only = serializers.SerializerMethodField() + + condition_uris = serializers.SerializerMethodField() elements = serializers.SerializerMethodField() - class Meta(OptionSetSerializer.Meta): + class Meta: + model = OptionSet fields = ( - *OptionSetSerializer.Meta.fields, - 'elements' + 'id', + 'model', + 'uri', + 'locked', + 'order', + 'provider_key', + 'conditions', + 'read_only', + 'condition_uris', + 'elements', ) def get_elements(self, obj) -> list[dict]: for element in obj.elements: - yield OptionSerializer(element, context=self.context).data + yield OptionNestedSerializer(element, context=self.context).data + + def get_condition_uris(self, obj) -> list[str]: + return [condition.uri for condition in obj.conditions.all()] class OptionSetIndexSerializer(serializers.ModelSerializer): diff --git a/rdmo/options/tests/test_queries_optionset.py b/rdmo/options/tests/test_queries_optionset.py index aa3925bb0b..17090e0bc3 100644 --- a/rdmo/options/tests/test_queries_optionset.py +++ b/rdmo/options/tests/test_queries_optionset.py @@ -10,6 +10,7 @@ # action, max_queries, url_kwargs, url_params ('list', 8, {}, {}), ('index', 3, {}, {}), + ('nested', 6, {'pk': 1}, {}), ('export', 8, {'export_format': 'xml'}, {}), ('export', 8, {'export_format': 'xml'}, {'full': '1'}), ('detail', 8, {'pk': 1}, {}), diff --git a/rdmo/options/viewsets.py b/rdmo/options/viewsets.py index 5ea8ecb239..e4d8f9d46e 100644 --- a/rdmo/options/viewsets.py +++ b/rdmo/options/viewsets.py @@ -44,7 +44,7 @@ def get_queryset(self): queryset = OptionSet.objects.all() if self.action in ['index']: return queryset - elif self.action in ['export', 'detail_export']: + elif self.action in ['nested', 'export', 'detail_export']: return queryset.prefetch_related( 'optionset_options__option', 'conditions', @@ -77,7 +77,7 @@ def export(self, request, export_format='xml'): many=True, context=self.get_export_serializer_context(queryset), ) - xml = OptionSetRenderer().render(serializer.data, context=self.get_export_renderer_context(request)) + xml = OptionSetRenderer().render(serializer.data, context=self.get_export_flags()) return XMLResponse(xml, name='optionsets') else: return render_to_format(self.request, export_format, 'optionsets', 'options/export/optionsets.html', { @@ -92,7 +92,7 @@ def detail_export(self, request, pk=None, export_format='xml'): instance, context=self.get_export_serializer_context([instance]), ) - xml = OptionSetRenderer().render([serializer.data], context=self.get_export_renderer_context(request)) + xml = OptionSetRenderer().render([serializer.data], context=self.get_export_flags()) return XMLResponse(xml, name=instance.uri_path) else: return render_to_format( @@ -101,6 +101,14 @@ def detail_export(self, request, pk=None, export_format='xml'): } ) + def get_export_flags(self): + full = is_truthy(self.request.GET.get('full')) + return { + 'attributes': full or is_truthy(self.request.GET.get('attributes')), + 'conditions': full or is_truthy(self.request.GET.get('conditions')), + 'options': full or is_truthy(self.request.GET.get('options')) + } + def get_export_serializer_context(self, optionsets): return { 'attribute_map': Attribute.objects.get_queryset_ancestors( @@ -113,14 +121,6 @@ def get_export_serializer_context(self, optionsets): ).in_bulk() } - def get_export_renderer_context(self, request): - full = is_truthy(request.GET.get('full')) - return { - 'attributes': full or is_truthy(request.GET.get('attributes')), - 'conditions': full or is_truthy(request.GET.get('conditions')), - 'options': full or is_truthy(request.GET.get('options')) - } - class OptionViewSet(ModelViewSet): permission_classes = (HasModelPermission | HasObjectPermission, ) From 2f89b1b6eeea156fdefbf0125bb68ba6326c2b2e Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Fri, 22 May 2026 13:34:43 +0200 Subject: [PATCH 094/131] Rename remaining get_export_renderer_context methods --- rdmo/conditions/viewsets.py | 18 +++++++++--------- rdmo/tasks/viewsets.py | 20 ++++++++++---------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/rdmo/conditions/viewsets.py b/rdmo/conditions/viewsets.py index 7776f0dcb6..c59596b7c9 100644 --- a/rdmo/conditions/viewsets.py +++ b/rdmo/conditions/viewsets.py @@ -71,7 +71,7 @@ def export(self, request, export_format='xml'): many=True, context=self.get_export_serializer_context(queryset), ) - xml = ConditionRenderer().render(serializer.data, context=self.get_export_renderer_context(request)) + xml = ConditionRenderer().render(serializer.data, context=self.get_export_flags()) return XMLResponse(xml, name='conditions') else: return render_to_format(self.request, export_format, 'conditions', 'conditions/export/conditions.html', { @@ -86,7 +86,7 @@ def detail_export(self, request, pk=None, export_format='xml'): instance, context=self.get_export_serializer_context([instance]), ) - xml = ConditionRenderer().render([serializer.data], context=self.get_export_renderer_context(request)) + xml = ConditionRenderer().render([serializer.data], context=self.get_export_flags()) return XMLResponse(xml, name=instance.uri_path) else: return render_to_format( @@ -95,6 +95,13 @@ def detail_export(self, request, pk=None, export_format='xml'): } ) + def get_export_flags(self): + full = is_truthy(self.request.GET.get('full')) + return { + 'attributes': full or is_truthy(self.request.GET.get('attributes')), + 'options': full or is_truthy(self.request.GET.get('options')), + } + def get_export_serializer_context(self, conditions): return { 'attribute_map': Attribute.objects.get_queryset_ancestors( @@ -103,13 +110,6 @@ def get_export_serializer_context(self, conditions): ).in_bulk() } - def get_export_renderer_context(self, request): - full = is_truthy(request.GET.get('full')) - return { - 'attributes': full or is_truthy(request.GET.get('attributes')), - 'options': full or is_truthy(request.GET.get('options')), - } - class RelationViewSet(ChoicesViewSet): permission_classes = (IsAuthenticated, ) diff --git a/rdmo/tasks/viewsets.py b/rdmo/tasks/viewsets.py index cdcc009864..7adea1650c 100644 --- a/rdmo/tasks/viewsets.py +++ b/rdmo/tasks/viewsets.py @@ -72,7 +72,7 @@ def export(self, request, export_format='xml'): many=True, context=self.get_export_serializer_context(queryset), ) - xml = TaskRenderer().render(serializer.data, context=self.get_export_renderer_context(request)) + xml = TaskRenderer().render(serializer.data, context=self.get_export_flags()) return XMLResponse(xml, name='tasks') else: return render_to_format(self.request, export_format, 'tasks', 'tasks/export/tasks.html', { @@ -87,7 +87,7 @@ def detail_export(self, request, pk=None, export_format='xml'): instance, context=self.get_export_serializer_context([instance]), ) - xml = TaskRenderer().render([serializer.data], context=self.get_export_renderer_context(request)) + xml = TaskRenderer().render([serializer.data], context=self.get_export_flags()) return XMLResponse(xml, name=instance.uri_path) else: return render_to_format( @@ -96,6 +96,14 @@ def detail_export(self, request, pk=None, export_format='xml'): } ) + def get_export_flags(self): + full = is_truthy(self.request.GET.get('full')) + return { + 'conditions': full or is_truthy(self.request.GET.get('conditions')), + 'attributes': full or is_truthy(self.request.GET.get('attributes')), + 'options': full or is_truthy(self.request.GET.get('options')) + } + def get_export_serializer_context(self, tasks): attribute_ids = set() for task in tasks: @@ -112,11 +120,3 @@ def get_export_serializer_context(self, tasks): include_self=True ).in_bulk() } - - def get_export_renderer_context(self, request): - full = is_truthy(request.GET.get('full')) - return { - 'conditions': full or is_truthy(request.GET.get('conditions')), - 'attributes': full or is_truthy(request.GET.get('attributes')), - 'options': full or is_truthy(request.GET.get('options')) - } From 3d993d8f00ebd521d9a92b60290269c3db9a72ed Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Fri, 22 May 2026 13:54:50 +0200 Subject: [PATCH 095/131] Move get_condition_uris and get_condition_uris to models --- rdmo/options/models.py | 4 ++++ rdmo/options/serializers/v1/optionset.py | 10 ---------- rdmo/questions/models/page.py | 8 ++++++++ rdmo/questions/models/question.py | 12 ++++++++++++ rdmo/questions/models/questionset.py | 8 ++++++++ rdmo/questions/serializers/v1/page.py | 12 ------------ rdmo/questions/serializers/v1/question.py | 20 -------------------- rdmo/questions/serializers/v1/questionset.py | 12 ------------ 8 files changed, 32 insertions(+), 54 deletions(-) diff --git a/rdmo/options/models.py b/rdmo/options/models.py index 1aa9315132..9f4c4e25e2 100644 --- a/rdmo/options/models.py +++ b/rdmo/options/models.py @@ -109,6 +109,10 @@ def is_locked(self) -> bool: def elements(self) -> list[Option]: return [element.option for element in sorted(self.optionset_options.all(), key=lambda e: e.order)] + @property + def condition_uris(self): + return [condition.uri for condition in self.conditions.all()] + @classmethod def build_uri(cls, uri_prefix, uri_path): if not uri_path: diff --git a/rdmo/options/serializers/v1/optionset.py b/rdmo/options/serializers/v1/optionset.py index 188c9ba882..4189ba46b3 100644 --- a/rdmo/options/serializers/v1/optionset.py +++ b/rdmo/options/serializers/v1/optionset.py @@ -32,8 +32,6 @@ class OptionSetSerializer(ThroughModelSerializerMixin, ElementModelSerializerMix read_only = serializers.SerializerMethodField() - condition_uris = serializers.SerializerMethodField() - class Meta: model = OptionSet fields = ( @@ -64,9 +62,6 @@ class Meta: OptionSetLockedValidator() ) - def get_condition_uris(self, obj) -> list[str]: - return [condition.uri for condition in obj.conditions.all()] - class OptionSetNestedSerializer( ElementModelSerializerMixin, @@ -77,8 +72,6 @@ class OptionSetNestedSerializer( read_only = serializers.SerializerMethodField() - condition_uris = serializers.SerializerMethodField() - elements = serializers.SerializerMethodField() class Meta: @@ -100,9 +93,6 @@ def get_elements(self, obj) -> list[dict]: for element in obj.elements: yield OptionNestedSerializer(element, context=self.context).data - def get_condition_uris(self, obj) -> list[str]: - return [condition.uri for condition in obj.conditions.all()] - class OptionSetIndexSerializer(serializers.ModelSerializer): diff --git a/rdmo/questions/models/page.py b/rdmo/questions/models/page.py index a83b5df439..69504c2e82 100644 --- a/rdmo/questions/models/page.py +++ b/rdmo/questions/models/page.py @@ -206,10 +206,18 @@ def verbose_name(self) -> str: def is_locked(self) -> bool: return self.locked or any(section.is_locked for section in self.sections.all()) + @property + def attribute_uri(self) -> str: + return self.attribute.uri + @cached_property def has_conditions(self) -> bool: return self.conditions.exists() + @property + def condition_uris(self): + return [condition.uri for condition in self.conditions.all()] + @cached_property def elements(self) -> list: page_elements = list(self.page_questionsets.all()) + list(self.page_questions.all()) diff --git a/rdmo/questions/models/question.py b/rdmo/questions/models/question.py index 788781ca91..e44a8f104b 100644 --- a/rdmo/questions/models/question.py +++ b/rdmo/questions/models/question.py @@ -255,10 +255,22 @@ def is_locked(self) -> bool: any(page.is_locked for page in self.pages.all()) or \ any(questionset.is_locked for questionset in self.questionsets.all()) + @property + def attribute_uri(self) -> str: + return self.attribute.uri + @cached_property def has_conditions(self) -> bool: return self.conditions.exists() + @property + def condition_uris(self) -> list: + return [condition.uri for condition in self.conditions.all()] + + @property + def optionset_uris(self) -> list: + return [optionset.uri for optionset in self.optionsets.all()] + @cached_property def descendants(self) -> list: return [] diff --git a/rdmo/questions/models/questionset.py b/rdmo/questions/models/questionset.py index 5f27b302c3..12acf60f68 100644 --- a/rdmo/questions/models/questionset.py +++ b/rdmo/questions/models/questionset.py @@ -179,10 +179,18 @@ def is_locked(self) -> bool: any(page.is_locked for page in self.pages.all()) or \ any(questionset.is_locked for questionset in self.questionsets.all()) + @property + def attribute_uri(self) -> str: + return self.attribute.uri + @cached_property def has_conditions(self) -> bool: return self.conditions.exists() + @property + def condition_uris(self): + return [condition.uri for condition in self.conditions.all()] + @cached_property def elements(self) -> list: questionset_elements = list(self.questionset_questionsets.all()) + list(self.questionset_questions.all()) diff --git a/rdmo/questions/serializers/v1/page.py b/rdmo/questions/serializers/v1/page.py index 93da654ed6..954920ee17 100644 --- a/rdmo/questions/serializers/v1/page.py +++ b/rdmo/questions/serializers/v1/page.py @@ -50,9 +50,6 @@ class PageSerializer(ThroughModelSerializerMixin, TranslationSerializerMixin, warning = serializers.SerializerMethodField() read_only = serializers.SerializerMethodField() - attribute_uri = serializers.CharField(source='attribute.uri', read_only=True) - condition_uris = serializers.SerializerMethodField() - class Meta: model = Page fields = ( @@ -103,9 +100,6 @@ class Meta: 'title', ) - def get_condition_uris(self, obj) -> list: - return [condition.uri for condition in obj.conditions.all()] - class PageNestedSerializer( ElementModelSerializerMixin, @@ -121,9 +115,6 @@ class PageNestedSerializer( warning = serializers.SerializerMethodField() read_only = serializers.SerializerMethodField() - attribute_uri = serializers.CharField(source='attribute.uri', read_only=True) - condition_uris = serializers.SerializerMethodField() - elements = serializers.SerializerMethodField() class Meta: @@ -153,9 +144,6 @@ def get_elements(self, obj): else: yield QuestionNestedSerializer(element, context=self.context).data - def get_condition_uris(self, obj) -> list: - return [condition.uri for condition in obj.conditions.all()] - class PageIndexSerializer(serializers.ModelSerializer): diff --git a/rdmo/questions/serializers/v1/question.py b/rdmo/questions/serializers/v1/question.py index 453e0695bc..a9a488aa55 100644 --- a/rdmo/questions/serializers/v1/question.py +++ b/rdmo/questions/serializers/v1/question.py @@ -31,10 +31,6 @@ class QuestionSerializer(ThroughModelSerializerMixin, TranslationSerializerMixin warning = serializers.SerializerMethodField() read_only = serializers.SerializerMethodField() - attribute_uri = serializers.CharField(source='attribute.uri', read_only=True) - condition_uris = serializers.SerializerMethodField() - optionset_uris = serializers.SerializerMethodField() - class Meta: model = Question fields = ( @@ -101,12 +97,6 @@ def to_internal_value(self, data): return super().to_internal_value(data) - def get_condition_uris(self, obj) -> list: - return [condition.uri for condition in obj.conditions.all()] - - def get_optionset_uris(self, obj) -> list: - return [optionset.uri for optionset in obj.optionsets.all()] - def validate(self, data): is_collection = data.get('is_collection') widget_type = data.get('widget_type') @@ -148,10 +138,6 @@ class QuestionNestedSerializer( warning = serializers.SerializerMethodField() read_only = serializers.SerializerMethodField() - attribute_uri = serializers.CharField(source='attribute.uri', read_only=True) - condition_uris = serializers.SerializerMethodField() - optionset_uris = serializers.SerializerMethodField() - class Meta: model = Question fields = ( @@ -180,12 +166,6 @@ class Meta: 'text', ) - def get_condition_uris(self, obj) -> list: - return [condition.uri for condition in obj.conditions.all()] - - def get_optionset_uris(self, obj) -> list: - return [optionset.uri for optionset in obj.optionsets.all()] - class QuestionIndexSerializer(serializers.ModelSerializer): diff --git a/rdmo/questions/serializers/v1/questionset.py b/rdmo/questions/serializers/v1/questionset.py index 59e4ac1cf3..0d7c24aa8c 100644 --- a/rdmo/questions/serializers/v1/questionset.py +++ b/rdmo/questions/serializers/v1/questionset.py @@ -53,9 +53,6 @@ class QuestionSetSerializer(ThroughModelSerializerMixin, TranslationSerializerMi warning = serializers.SerializerMethodField() read_only = serializers.SerializerMethodField() - attribute_uri = serializers.CharField(source='attribute.uri', read_only=True) - condition_uris = serializers.SerializerMethodField() - class Meta: model = QuestionSet fields = ( @@ -107,9 +104,6 @@ class Meta: 'title', ) - def get_condition_uris(self, obj) -> list: - return [condition.uri for condition in obj.conditions.all()] - class QuestionSetNestedSerializer( ElementModelSerializerMixin, @@ -125,9 +119,6 @@ class QuestionSetNestedSerializer( warning = serializers.SerializerMethodField() read_only = serializers.SerializerMethodField() - attribute_uri = serializers.CharField(source='attribute.uri', read_only=True) - condition_uris = serializers.SerializerMethodField() - elements = serializers.SerializerMethodField() class Meta: @@ -157,9 +148,6 @@ def get_elements(self, obj): else: yield QuestionNestedSerializer(element, context=self.context).data - def get_condition_uris(self, obj) -> list: - return [condition.uri for condition in obj.conditions.all()] - class QuestionSetIndexSerializer(serializers.ModelSerializer): From 7407b3188599fbdcd14d69defa9b59402396bc95 Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Fri, 22 May 2026 14:10:17 +0200 Subject: [PATCH 096/131] Fix nested serializers --- rdmo/questions/serializers/v1/catalog.py | 5 +++++ rdmo/questions/serializers/v1/page.py | 5 +++++ rdmo/questions/serializers/v1/questionset.py | 15 +++++++++++++++ rdmo/questions/serializers/v1/section.py | 3 +++ rdmo/questions/tests/test_queries_catalog.py | 2 +- rdmo/questions/tests/test_queries_questionset.py | 2 +- 6 files changed, 30 insertions(+), 2 deletions(-) diff --git a/rdmo/questions/serializers/v1/catalog.py b/rdmo/questions/serializers/v1/catalog.py index 6991a60a05..370db96b2e 100644 --- a/rdmo/questions/serializers/v1/catalog.py +++ b/rdmo/questions/serializers/v1/catalog.py @@ -92,6 +92,8 @@ class CatalogNestedSerializer( model = serializers.SerializerMethodField() + sections = CatalogSectionSerializer(source='catalog_sections', read_only=False, required=False, many=True) + warning = serializers.SerializerMethodField() read_only = serializers.SerializerMethodField() @@ -104,7 +106,10 @@ class Meta: 'model', 'uri', 'locked', + 'available', 'title', + 'sections', + 'sites', 'warning', 'read_only', 'elements', diff --git a/rdmo/questions/serializers/v1/page.py b/rdmo/questions/serializers/v1/page.py index 954920ee17..0b3971a829 100644 --- a/rdmo/questions/serializers/v1/page.py +++ b/rdmo/questions/serializers/v1/page.py @@ -112,6 +112,9 @@ class PageNestedSerializer( model = serializers.SerializerMethodField() + questionsets = PageQuestionSetSerializer(source='page_questionsets', read_only=False, required=False, many=True) + questions = PageQuestionSerializer(source='page_questions', read_only=False, required=False, many=True) + warning = serializers.SerializerMethodField() read_only = serializers.SerializerMethodField() @@ -126,6 +129,8 @@ class Meta: 'locked', 'attribute', 'title', + 'questionsets', + 'questions', 'conditions', 'warning', 'read_only', diff --git a/rdmo/questions/serializers/v1/questionset.py b/rdmo/questions/serializers/v1/questionset.py index 0d7c24aa8c..d8ffe8ebc0 100644 --- a/rdmo/questions/serializers/v1/questionset.py +++ b/rdmo/questions/serializers/v1/questionset.py @@ -116,6 +116,19 @@ class QuestionSetNestedSerializer( model = serializers.SerializerMethodField() + questionsets = QuestionSetQuestionSetSerializer( + source="questionset_questionsets", + read_only=False, + required=False, + many=True, + ) + questions = QuestionSetQuestionSerializer( + source="questionset_questions", + read_only=False, + required=False, + many=True, + ) + warning = serializers.SerializerMethodField() read_only = serializers.SerializerMethodField() @@ -130,6 +143,8 @@ class Meta: 'locked', 'attribute', 'title', + 'questionsets', + 'questions', 'conditions', 'warning', 'read_only', diff --git a/rdmo/questions/serializers/v1/section.py b/rdmo/questions/serializers/v1/section.py index 4443ae7a82..df23eceda0 100644 --- a/rdmo/questions/serializers/v1/section.py +++ b/rdmo/questions/serializers/v1/section.py @@ -90,6 +90,8 @@ class SectionNestedSerializer( model = serializers.SerializerMethodField() + pages = SectionPageSerializer(source='section_pages', read_only=False, required=False, many=True) + warning = serializers.SerializerMethodField() read_only = serializers.SerializerMethodField() @@ -103,6 +105,7 @@ class Meta: 'uri', 'locked', 'title', + 'pages', 'warning', 'read_only', 'elements', diff --git a/rdmo/questions/tests/test_queries_catalog.py b/rdmo/questions/tests/test_queries_catalog.py index 7d02c15224..fe79ac2991 100644 --- a/rdmo/questions/tests/test_queries_catalog.py +++ b/rdmo/questions/tests/test_queries_catalog.py @@ -10,7 +10,7 @@ # action, max_queries, url_kwargs, url_params ('list', 8, {}, {}), ('index', 3, {}, {}), - ('nested', 28, {'pk': 1}, {}), + ('nested', 31, {'pk': 1}, {}), ('export', 32, {'export_format': 'xml'}, {}), ('export', 42, {'export_format': 'xml'}, {'full': '1'}), ('detail', 8, {'pk': 1}, {}), diff --git a/rdmo/questions/tests/test_queries_questionset.py b/rdmo/questions/tests/test_queries_questionset.py index 8e855eddeb..c5dc48d3f2 100644 --- a/rdmo/questions/tests/test_queries_questionset.py +++ b/rdmo/questions/tests/test_queries_questionset.py @@ -10,7 +10,7 @@ # action, max_queries, url_kwargs, url_params ('list', 11, {}, {}), ('index', 3, {}, {}), - ('nested', 17, {'pk': 90}, {}), + ('nested', 19, {'pk': 90}, {}), ('export', 19, {'export_format': 'xml'}, {}), ('export', 28, {'export_format': 'xml'}, {'full': '1'}), ('detail', 11, {'pk': 90}, {}), From 418d73ccfe3416c2e764c5085365736d3ab27c3d Mon Sep 17 00:00:00 2001 From: David Wallace Date: Fri, 22 May 2026 14:29:34 +0200 Subject: [PATCH 097/131] build(deps): bump js-cookie from 3.0.5 to 3.0.7 Signed-off-by: David Wallace --- package-lock.json | 86 +++++++++++++++++++++++------------------------ package.json | 2 +- 2 files changed, 44 insertions(+), 44 deletions(-) diff --git a/package-lock.json b/package-lock.json index ddb843eb76..1713b81081 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,7 @@ "font-awesome": "4.7.0", "html-to-text": "^10.0.0", "jquery": "^3.7.1", - "js-cookie": "^3.0.5", + "js-cookie": "^3.0.7", "lodash": "^4.18.1", "popper.js": "^1.16.1", "prop-types": "^15.7.2", @@ -2770,9 +2770,9 @@ "license": "MIT" }, "node_modules/@uiw/codemirror-extensions-basic-setup": { - "version": "4.25.9", - "resolved": "https://registry.npmjs.org/@uiw/codemirror-extensions-basic-setup/-/codemirror-extensions-basic-setup-4.25.9.tgz", - "integrity": "sha512-QFAqr+pu6lDmNpAlecODcF49TlsrZ0bj15zPzfhiqSDl+Um3EsDLFLppixC7kFLn+rdDM2LTvVjn5CPvefpRgw==", + "version": "4.25.10", + "resolved": "https://registry.npmjs.org/@uiw/codemirror-extensions-basic-setup/-/codemirror-extensions-basic-setup-4.25.10.tgz", + "integrity": "sha512-P3vytLlpE62KYSWrMUnwDCv2lvaQDuDZzyj03mHntuHo5bSl34fRZpjTY3kQTPGuXHxkGSYpoPFFj+hMTqaaMQ==", "license": "MIT", "dependencies": { "@codemirror/autocomplete": "^6.0.0", @@ -2797,16 +2797,16 @@ } }, "node_modules/@uiw/react-codemirror": { - "version": "4.25.9", - "resolved": "https://registry.npmjs.org/@uiw/react-codemirror/-/react-codemirror-4.25.9.tgz", - "integrity": "sha512-HftqCBUYShAOH0pGi1CHP8vfm5L8fQ3+0j0VI6lQD6QpK+UBu3J7nxfEN5O/BXMilMNf9ZyFJRvRcuMMOLHMng==", + "version": "4.25.10", + "resolved": "https://registry.npmjs.org/@uiw/react-codemirror/-/react-codemirror-4.25.10.tgz", + "integrity": "sha512-DzgSMwM5qzB7v1FIb4gEeriYt67iiay756/HIOM9mAbeOVK0MO7rqefHf0O5c0269pJKMW7AH9FjclExD23V9w==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.18.6", "@codemirror/commands": "^6.1.0", "@codemirror/state": "^6.1.1", "@codemirror/theme-one-dark": "^6.0.0", - "@uiw/codemirror-extensions-basic-setup": "4.25.9", + "@uiw/codemirror-extensions-basic-setup": "4.25.10", "codemirror": "^6.0.0" }, "funding": { @@ -3883,9 +3883,9 @@ } }, "node_modules/css-loader/node_modules/semver": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", - "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz", + "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==", "dev": true, "license": "ISC", "bin": { @@ -4178,9 +4178,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.360", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.360.tgz", - "integrity": "sha512-GkcBt6YYAw9SxFWn+xVar4cLVGlXVuswwtRLBozi2zp0GjXs4ZnOrqV4zbXzg35n7w81hCkyJNYicgXlVHAmBA==", + "version": "1.5.361", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.361.tgz", + "integrity": "sha512-Q6Hts7N9FnJc5LeGRINFvLhCI9xZmNtTDe5ZbcVezQz7cU4a8Aua3GH1b8J2XY8Al9PF+OCwYqhgsOOheMdvkA==", "dev": true, "license": "ISC" }, @@ -4195,9 +4195,9 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.21.6", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.21.6.tgz", - "integrity": "sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ==", + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.22.0.tgz", + "integrity": "sha512-xYcDWrpELkFzz9SpZ3PlI6Eu6eD93Yf0WLDRxikGhWJ3MAir2SNZTIVCVZqZ/NUyx8AdMc2gT9C0gPiw18kG+A==", "dev": true, "license": "MIT", "dependencies": { @@ -6346,9 +6346,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.45", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.45.tgz", - "integrity": "sha512-iIbHXV9eBB2nB0wa7oTsrrXq+qQt+9SIlx9AX3T96YgobtEQfis5n6TJ6vV+3QP8DwdriEAcGhARaFCu37peBg==", + "version": "2.0.46", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.46.tgz", + "integrity": "sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ==", "dev": true, "license": "MIT", "engines": { @@ -7588,13 +7588,13 @@ } }, "node_modules/sass": { - "version": "1.99.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.99.0.tgz", - "integrity": "sha512-kgW13M54DUB7IsIRM5LvJkNlpH+WhMpooUcaWGFARkF1Tc82v9mIWkCbCYf+MBvpIUBSeSOTilpZjEPr2VYE6Q==", + "version": "1.100.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.100.0.tgz", + "integrity": "sha512-B5j0rYMlinhhOo9tjQebMVVn0TfyXAF+wB3b2ggZUuJ/is/Y+7+JGjirAMxHZ9Z3hIP98NPfamlAkBHa1lAaXQ==", "dev": true, "license": "MIT", "dependencies": { - "chokidar": "^4.0.0", + "chokidar": "^5.0.0", "immutable": "^5.1.5", "source-map-js": ">=0.6.2 <2.0.0" }, @@ -7602,7 +7602,7 @@ "sass": "sass.js" }, "engines": { - "node": ">=14.0.0" + "node": ">=20.19.0" }, "optionalDependencies": { "@parcel/watcher": "^2.4.1" @@ -7650,29 +7650,29 @@ } }, "node_modules/sass/node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", "dev": true, "license": "MIT", "dependencies": { - "readdirp": "^4.0.1" + "readdirp": "^5.0.0" }, "engines": { - "node": ">= 14.16.0" + "node": ">= 20.19.0" }, "funding": { "url": "https://paulmillr.com/funding/" } }, "node_modules/sass/node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", + "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", "dev": true, "license": "MIT", "engines": { - "node": ">= 14.18.0" + "node": ">= 20.19.0" }, "funding": { "type": "individual", @@ -8184,9 +8184,9 @@ } }, "node_modules/terser": { - "version": "5.47.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.47.1.tgz", - "integrity": "sha512-tPbLXTI6ohPASb/1YViL428oEHu6/qv1OxqYnfaonVCFHqx4+wCd95pHrQWsL5X4pl90CTyW9piSAsS2L0VoMw==", + "version": "5.48.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.48.0.tgz", + "integrity": "sha512-J/9An6vs9Us6wKRriSFXBWdRZapREHqFzdNUKk0pmu804EMR6dr6winwo7e5JDxN4xahxQsuysyYFwlwj4XN/Q==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -8647,9 +8647,9 @@ } }, "node_modules/webpack": { - "version": "5.107.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.107.0.tgz", - "integrity": "sha512-PSxeHk/dmLYZlnTU+vL1Gej6Evg5RNtl3flhxBresfznFnzxinHMzHKloHnywM/3ouQv7/AlZCswWDIkNSggUA==", + "version": "5.107.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.107.1.tgz", + "integrity": "sha512-mvdIWxj/H6QsfgDdH9djne3a5dYcmEmtsXGESkypaGN5jXjF/b+9KDlmTDQ2TKlFUeA2fI9Y65kihD30JOdB+Q==", "dev": true, "license": "MIT", "dependencies": { @@ -8760,9 +8760,9 @@ } }, "node_modules/webpack-sources": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.4.1.tgz", - "integrity": "sha512-eACpxRN02yaawnt+uUNIF7Qje6A9zArxBbcAJjK1PK3S9Ycg5jIuJ8pW4q8EMnwNZCEGltcjkRx1QzOxOkKD8A==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.5.0.tgz", + "integrity": "sha512-HPuy+uuoTCaaoEoI1LQ3JN9+vrPBvEesnnX1jADHy728cHSMlq4wUc4afYqahq2B1mhQVZxCXOkNTnXltr+2vQ==", "dev": true, "license": "MIT", "engines": { diff --git a/package.json b/package.json index af6aec0972..33ff2219df 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "font-awesome": "4.7.0", "html-to-text": "^10.0.0", "jquery": "^3.7.1", - "js-cookie": "^3.0.5", + "js-cookie": "^3.0.7", "lodash": "^4.18.1", "popper.js": "^1.16.1", "prop-types": "^15.7.2", From bc94b27bb713880bb2870810d07e952b60b4dae7 Mon Sep 17 00:00:00 2001 From: David Wallace Date: Wed, 27 May 2026 10:23:30 +0200 Subject: [PATCH 098/131] Add is_collection to PageNested and QuestionSetNested serializers Signed-off-by: David Wallace --- rdmo/questions/serializers/v1/page.py | 1 + rdmo/questions/serializers/v1/questionset.py | 1 + 2 files changed, 2 insertions(+) diff --git a/rdmo/questions/serializers/v1/page.py b/rdmo/questions/serializers/v1/page.py index 0b3971a829..b861a40607 100644 --- a/rdmo/questions/serializers/v1/page.py +++ b/rdmo/questions/serializers/v1/page.py @@ -128,6 +128,7 @@ class Meta: 'uri', 'locked', 'attribute', + 'is_collection', 'title', 'questionsets', 'questions', diff --git a/rdmo/questions/serializers/v1/questionset.py b/rdmo/questions/serializers/v1/questionset.py index d8ffe8ebc0..054064659c 100644 --- a/rdmo/questions/serializers/v1/questionset.py +++ b/rdmo/questions/serializers/v1/questionset.py @@ -142,6 +142,7 @@ class Meta: 'uri', 'locked', 'attribute', + 'is_collection', 'title', 'questionsets', 'questions', From 0e33c71b626fe0dfe9ee98341ce51284104a1f2f Mon Sep 17 00:00:00 2001 From: David Wallace Date: Fri, 29 May 2026 12:34:59 +0200 Subject: [PATCH 099/131] Add test for checking permissions on ancestor elements #1628 Signed-off-by: David Wallace --- ..._viewset_ancestor_permissions_multisite.py | 68 +++++++++++++++++++ .../tests/test_viewset_page_multisite.py | 7 +- .../tests/test_viewset_question_multisite.py | 14 +++- .../test_viewset_questionset_multisite.py | 7 +- .../tests/test_viewset_section_multisite.py | 7 +- 5 files changed, 98 insertions(+), 5 deletions(-) create mode 100644 rdmo/questions/tests/test_viewset_ancestor_permissions_multisite.py diff --git a/rdmo/questions/tests/test_viewset_ancestor_permissions_multisite.py b/rdmo/questions/tests/test_viewset_ancestor_permissions_multisite.py new file mode 100644 index 0000000000..2c30601494 --- /dev/null +++ b/rdmo/questions/tests/test_viewset_ancestor_permissions_multisite.py @@ -0,0 +1,68 @@ +import pytest + +from django.urls import reverse + +from ..models import Catalog, Page, Question, QuestionSet, Section + + +@pytest.mark.parametrize( + 'descendant_model,descendant_uri,ancestor_model,ancestor_uri,urlname,ancestor_field,relation_name', + [ + ( + Section, 'foo-section', + Catalog, 'foo-catalog', + 'v1-questions:section-list', 'catalogs', 'catalog_sections' + ), + ( + Page, 'foo-page', + Section, 'foo-section', + 'v1-questions:page-list', 'sections', 'section_pages' + ), + ( + QuestionSet, 'foo-questionset', + Page, 'foo-page', + 'v1-questions:questionset-list', 'pages', 'page_questionsets' + ), + ( + QuestionSet, 'foo-questionset', + QuestionSet, 'foo-questionset', + 'v1-questions:questionset-list', 'parents', 'questionset_questionsets' + ), + ( + Question, 'foo-question', + Page, 'foo-page', + 'v1-questions:question-list', 'pages', 'page_questions' + ), + ( + Question, 'foo-question', + QuestionSet, 'foo-questionset', + 'v1-questions:question-list', 'questionsets', 'questionset_questions' + ), + ], + ids=[ + 'catalog-to-section', + 'section-to-page', + 'page-to-questionset', + 'questionset-to-questionset', + 'page-to-question', + 'questionset-to-question', + ], +) +def test_foo_editor_cannot_create_descendant_on_foo_ancestor( + db, client, descendant_model, descendant_uri, ancestor_model, ancestor_uri, urlname, ancestor_field, relation_name +): + client.login(username='foo-editor', password='foo-editor') + + descendant = descendant_model.objects.get(uri_path=descendant_uri) + ancestor = ancestor_model.objects.get(uri_path=ancestor_uri) + before_count = getattr(ancestor, relation_name).count() + + data = { + 'uri_prefix': 'https://example.com/terms', + 'uri_path': f'{descendant.uri_path}-ancestor-denied', + ancestor_field: [ancestor.id] + } + response = client.post(reverse(urlname), data, content_type='application/json') + + assert response.status_code == 403, response.json() + assert getattr(ancestor, relation_name).count() == before_count diff --git a/rdmo/questions/tests/test_viewset_page_multisite.py b/rdmo/questions/tests/test_viewset_page_multisite.py index e50cfbc3cb..a249e21cab 100644 --- a/rdmo/questions/tests/test_viewset_page_multisite.py +++ b/rdmo/questions/tests/test_viewset_page_multisite.py @@ -120,7 +120,12 @@ def test_create_section(db, client, username, password): 'sections': [section.id] } response = client.post(url, data, content_type='application/json') - assert response.status_code == status_map['create'][username], response.json() + expected_status_code = status_map['create'][username] + if expected_status_code == 201 and not response.wsgi_request.user.has_perm( + 'questions.change_section_object', section + ): + expected_status_code = 403 + assert response.status_code == expected_status_code, response.json() if response.status_code == 201: new_instance = Page.objects.get(id=response.json().get('id')) diff --git a/rdmo/questions/tests/test_viewset_question_multisite.py b/rdmo/questions/tests/test_viewset_question_multisite.py index 82e3a399a4..51b381ca52 100644 --- a/rdmo/questions/tests/test_viewset_question_multisite.py +++ b/rdmo/questions/tests/test_viewset_question_multisite.py @@ -121,7 +121,12 @@ def test_create_page(db, client, username, password): 'pages': [page.id] } response = client.post(url, data, content_type='application/json') - assert response.status_code == status_map['create'][username], response.json() + expected_status_code = status_map['create'][username] + if expected_status_code == 201 and not response.wsgi_request.user.has_perm( + 'questions.change_page_object', page + ): + expected_status_code = 403 + assert response.status_code == expected_status_code, response.json() if response.status_code == 201: new_instance = Question.objects.get(id=response.json().get('id')) @@ -163,7 +168,12 @@ def test_create_questionset(db, client, username, password): 'questionsets': [questionset.id] } response = client.post(url, data, content_type='application/json') - assert response.status_code == status_map['create'][username], response.json() + expected_status_code = status_map['create'][username] + if expected_status_code == 201 and not response.wsgi_request.user.has_perm( + 'questions.change_questionset_object', questionset + ): + expected_status_code = 403 + assert response.status_code == expected_status_code, response.json() if response.status_code == 201: new_instance = Question.objects.get(id=response.json().get('id')) diff --git a/rdmo/questions/tests/test_viewset_questionset_multisite.py b/rdmo/questions/tests/test_viewset_questionset_multisite.py index d37e93117a..5557f31243 100644 --- a/rdmo/questions/tests/test_viewset_questionset_multisite.py +++ b/rdmo/questions/tests/test_viewset_questionset_multisite.py @@ -120,7 +120,12 @@ def test_create_page(db, client, username, password): 'pages': [page.id] } response = client.post(url, data, content_type='application/json') - assert response.status_code == status_map['create'][username], response.json() + expected_status_code = status_map['create'][username] + if expected_status_code == 201 and not response.wsgi_request.user.has_perm( + 'questions.change_page_object', page + ): + expected_status_code = 403 + assert response.status_code == expected_status_code, response.json() if response.status_code == 201: new_instance = QuestionSet.objects.get(id=response.json().get('id')) diff --git a/rdmo/questions/tests/test_viewset_section_multisite.py b/rdmo/questions/tests/test_viewset_section_multisite.py index c74f56e4d1..7d6c2b975b 100644 --- a/rdmo/questions/tests/test_viewset_section_multisite.py +++ b/rdmo/questions/tests/test_viewset_section_multisite.py @@ -108,7 +108,12 @@ def test_create_catalog(db, client, username, password): 'catalogs': [catalog.id] } response = client.post(url, data, content_type='application/json') - assert response.status_code == status_map['create'][username], response.json() + expected_status_code = status_map['create'][username] + if expected_status_code == 201 and not response.wsgi_request.user.has_perm( + 'questions.change_catalog_object', catalog + ): + expected_status_code = 403 + assert response.status_code == expected_status_code, response.json() if response.status_code == 201: new_instance = Section.objects.get(id=response.json().get('id')) From eea8c8a8ab08e3b99c2178e71ee849709966598d Mon Sep 17 00:00:00 2001 From: David Wallace Date: Fri, 29 May 2026 13:11:54 +0200 Subject: [PATCH 100/131] Fix permission checks on parent field in ThroughModelSerializerMixin #1628 Signed-off-by: David Wallace --- rdmo/core/serializers.py | 30 +++++++++++++++++++++++ rdmo/questions/serializers/v1/question.py | 1 + 2 files changed, 31 insertions(+) diff --git a/rdmo/core/serializers.py b/rdmo/core/serializers.py index 48a75f86ac..3f0274a420 100644 --- a/rdmo/core/serializers.py +++ b/rdmo/core/serializers.py @@ -6,6 +6,7 @@ from django.db.models import Max from rest_framework import serializers +from rest_framework.exceptions import PermissionDenied from rest_framework.utils import model_meta from rdmo.core.utils import get_language_warning, get_languages, markdown2html @@ -74,6 +75,35 @@ def __init__(self, *args, **kwargs): class ThroughModelSerializerMixin: + def validate(self, attrs): + attrs = super().validate(attrs) + self.check_parent_object_permissions(attrs) + return attrs + + def check_parent_object_permissions(self, attrs): + try: + parent_fields = self.Meta.parent_fields + except AttributeError: + return + + # Parent fields create through-model rows on existing parent elements when this + # serializer creates a new child element. That effectively changes the + # parent element (for example adding a section to an existing catalog), so + # require object-level change permission for each supplied parent. + if self.instance is not None: + return + + request = self.context.get('request') + if request is None: + return + + for field_name, _source_name, _target_name, _through_name in parent_fields: + for parent in attrs.get(field_name) or []: + opts = parent._meta + permission = f'{opts.app_label}.change_{opts.model_name}_object' + if not request.user.has_perm(permission, parent): + raise PermissionDenied() + def create(self, validated_data): parent_fields = self.get_parent_fields(validated_data) through_fields = self.get_through_fields(validated_data) diff --git a/rdmo/questions/serializers/v1/question.py b/rdmo/questions/serializers/v1/question.py index a9a488aa55..62bd1a476c 100644 --- a/rdmo/questions/serializers/v1/question.py +++ b/rdmo/questions/serializers/v1/question.py @@ -98,6 +98,7 @@ def to_internal_value(self, data): return super().to_internal_value(data) def validate(self, data): + data = super().validate(data) is_collection = data.get('is_collection') widget_type = data.get('widget_type') value_type = data.get('value_type') From 42700bb0dfe501b7965c0fa7c774c13b373bf8ae Mon Sep 17 00:00:00 2001 From: David Wallace Date: Mon, 1 Jun 2026 13:18:31 +0200 Subject: [PATCH 101/131] Add and use create-with-parent from status map for test assertions Signed-off-by: David Wallace --- rdmo/core/tests/constants.py | 23 ++++ .../tests/test_viewset_page_multisite.py | 9 +- ...st_viewset_parent_permissions_multisite.py | 119 ++++++++++++++++++ .../tests/test_viewset_question_multisite.py | 18 +-- .../test_viewset_questionset_multisite.py | 13 +- .../tests/test_viewset_section_multisite.py | 9 +- 6 files changed, 160 insertions(+), 31 deletions(-) create mode 100644 rdmo/questions/tests/test_viewset_parent_permissions_multisite.py diff --git a/rdmo/core/tests/constants.py b/rdmo/core/tests/constants.py index 0789bf4f64..21b9d5f139 100644 --- a/rdmo/core/tests/constants.py +++ b/rdmo/core/tests/constants.py @@ -24,6 +24,12 @@ 'user': 403, 'example-reviewer': 403, 'example-editor': 201, 'anonymous': 401, 'reviewer': 403, 'editor': 201, }, + 'create-with-parent': { + 'foo-user': 403, 'foo-reviewer': 403, 'foo-editor': 403, + 'bar-user': 403, 'bar-reviewer': 403, 'bar-editor': 403, + 'user': 403, 'example-reviewer': 403, 'example-editor': 201, + 'anonymous': 401, 'reviewer': 403, 'editor': 201, + }, 'copy': { 'foo-user': 404, 'foo-reviewer': 403, 'foo-editor': 201, 'bar-user': 404, 'bar-reviewer': 403, 'bar-editor': 201, @@ -111,6 +117,23 @@ 'example-reviewer': 200, 'example-editor': 200, } }, + 'create-with-parent': { + 'foo-element': { + 'foo-reviewer': 403, 'foo-editor': 403, + 'bar-reviewer': 403, 'bar-editor': 403, + 'example-reviewer': 403, 'example-editor': 403, + }, + 'bar-element': { + 'foo-reviewer': 403, 'foo-editor': 403, + 'bar-reviewer': 403, 'bar-editor': 403, + 'example-reviewer': 403, 'example-editor': 403, + }, + 'example-element': { + 'foo-reviewer': 403, 'foo-editor': 403, + 'bar-reviewer': 403, 'bar-editor': 403, + 'example-reviewer': 403, 'example-editor': 201, + } + }, 'update': { 'all-element': { 'foo-reviewer': 404, 'foo-editor': 404, diff --git a/rdmo/questions/tests/test_viewset_page_multisite.py b/rdmo/questions/tests/test_viewset_page_multisite.py index a249e21cab..bfaf3000e0 100644 --- a/rdmo/questions/tests/test_viewset_page_multisite.py +++ b/rdmo/questions/tests/test_viewset_page_multisite.py @@ -120,12 +120,9 @@ def test_create_section(db, client, username, password): 'sections': [section.id] } response = client.post(url, data, content_type='application/json') - expected_status_code = status_map['create'][username] - if expected_status_code == 201 and not response.wsgi_request.user.has_perm( - 'questions.change_section_object', section - ): - expected_status_code = 403 - assert response.status_code == expected_status_code, response.json() + assert response.status_code == get_obj_perms_status_code( + section, username, 'create-with-parent' + ), response.json() if response.status_code == 201: new_instance = Page.objects.get(id=response.json().get('id')) diff --git a/rdmo/questions/tests/test_viewset_parent_permissions_multisite.py b/rdmo/questions/tests/test_viewset_parent_permissions_multisite.py new file mode 100644 index 0000000000..28de7abf71 --- /dev/null +++ b/rdmo/questions/tests/test_viewset_parent_permissions_multisite.py @@ -0,0 +1,119 @@ +import pytest + +from django.contrib.sites.models import Site +from django.urls import reverse + +from rdmo.core.tests.constants import status_map_object_permissions as status_map + +from ..models import Catalog, Page, Question, QuestionSet, Section + + +@pytest.mark.parametrize('model,parent_model,urlname,payload_builder', [ + ( + Section, Catalog, 'v1-questions:section-list', + lambda instance, parent: { + 'uri_prefix': 'https://bar.com/terms', + 'uri_path': f'{instance.uri_path}-bar-parent-denied', + 'comment': instance.comment, + 'title_en': instance.title_lang1, + 'title_de': instance.title_lang2, + 'catalogs': [parent.id] + } + ), + ( + Page, Section, 'v1-questions:page-list', + lambda instance, parent: { + 'uri_prefix': 'https://bar.com/terms', + 'uri_path': f'{instance.uri_path}-bar-parent-denied', + 'comment': instance.comment, + 'attribute': instance.attribute_id, + 'is_collection': instance.is_collection, + 'title_en': instance.title_lang1, + 'title_de': instance.title_lang2, + 'sections': [parent.id] + } + ), + ( + QuestionSet, Page, 'v1-questions:questionset-list', + lambda instance, parent: { + 'uri_prefix': 'https://bar.com/terms', + 'uri_path': f'{instance.uri_path}-bar-parent-denied', + 'comment': instance.comment, + 'attribute': instance.attribute_id, + 'is_collection': instance.is_collection, + 'title_en': instance.title_lang1, + 'title_de': instance.title_lang2, + 'pages': [parent.id] + } + ), + ( + QuestionSet, QuestionSet, 'v1-questions:questionset-list', + lambda instance, parent: { + 'uri_prefix': 'https://bar.com/terms', + 'uri_path': f'{instance.uri_path}-bar-parent-denied-parent', + 'comment': instance.comment, + 'attribute': instance.attribute_id, + 'is_collection': instance.is_collection, + 'title_en': instance.title_lang1, + 'title_de': instance.title_lang2, + 'parents': [parent.id] + } + ), + ( + Question, Page, 'v1-questions:question-list', + lambda instance, parent: { + 'uri_prefix': 'https://bar.com/terms', + 'uri_path': f'{instance.uri_path}-bar-parent-denied', + 'comment': instance.comment, + 'attribute': instance.attribute_id, + 'is_collection': instance.is_collection, + 'is_optional': instance.is_optional, + 'widget_type': instance.widget_type, + 'value_type': instance.value_type, + 'text_en': instance.text_lang1, + 'text_de': instance.text_lang2, + 'pages': [parent.id] + } + ), + ( + Question, QuestionSet, 'v1-questions:question-list', + lambda instance, parent: { + 'uri_prefix': 'https://bar.com/terms', + 'uri_path': f'{instance.uri_path}-bar-parent-denied-questionset', + 'comment': instance.comment, + 'attribute': instance.attribute_id, + 'is_collection': instance.is_collection, + 'is_optional': instance.is_optional, + 'widget_type': instance.widget_type, + 'value_type': instance.value_type, + 'text_en': instance.text_lang1, + 'text_de': instance.text_lang2, + 'questionsets': [parent.id] + } + ), +]) +def test_bar_editor_cannot_create_child_on_foo_parent( + db, client, settings, model, parent_model, urlname, payload_builder +): + settings.SITE_ID = Site.objects.get(domain='bar.com').id + Site.objects.clear_cache() + client.login(username='bar-editor', password='bar-editor') + + parent = parent_model.objects.get(uri_path=f'foo-{parent_model._meta.model_name}') + instance = model.objects.filter(uri_path=f'foo-{model._meta.model_name}').first() + if instance is None and model == QuestionSet: + instance = model.objects.get(uri_path='foo-questionset') + if instance is None and model == Question: + instance = model.objects.get(uri_path='foo-question') + + through_name = { + Catalog: 'catalog_sections', + Section: 'section_pages', + Page: 'page_questionsets' if model == QuestionSet else 'page_questions', + QuestionSet: 'questionset_questionsets' if model == QuestionSet else 'questionset_questions', + }[parent_model] + before_count = getattr(parent, through_name).count() + response = client.post(reverse(urlname), payload_builder(instance, parent), content_type='application/json') + + assert response.status_code == status_map['create-with-parent']['foo-element']['bar-editor'], response.json() + assert getattr(parent, through_name).count() == before_count diff --git a/rdmo/questions/tests/test_viewset_question_multisite.py b/rdmo/questions/tests/test_viewset_question_multisite.py index 51b381ca52..5e35d50c27 100644 --- a/rdmo/questions/tests/test_viewset_question_multisite.py +++ b/rdmo/questions/tests/test_viewset_question_multisite.py @@ -121,12 +121,9 @@ def test_create_page(db, client, username, password): 'pages': [page.id] } response = client.post(url, data, content_type='application/json') - expected_status_code = status_map['create'][username] - if expected_status_code == 201 and not response.wsgi_request.user.has_perm( - 'questions.change_page_object', page - ): - expected_status_code = 403 - assert response.status_code == expected_status_code, response.json() + assert response.status_code == get_obj_perms_status_code( + page, username, 'create-with-parent' + ), response.json() if response.status_code == 201: new_instance = Question.objects.get(id=response.json().get('id')) @@ -168,12 +165,9 @@ def test_create_questionset(db, client, username, password): 'questionsets': [questionset.id] } response = client.post(url, data, content_type='application/json') - expected_status_code = status_map['create'][username] - if expected_status_code == 201 and not response.wsgi_request.user.has_perm( - 'questions.change_questionset_object', questionset - ): - expected_status_code = 403 - assert response.status_code == expected_status_code, response.json() + assert response.status_code == get_obj_perms_status_code( + questionset, username, 'create-with-parent' + ), response.json() if response.status_code == 201: new_instance = Question.objects.get(id=response.json().get('id')) diff --git a/rdmo/questions/tests/test_viewset_questionset_multisite.py b/rdmo/questions/tests/test_viewset_questionset_multisite.py index 5557f31243..6c37e88ded 100644 --- a/rdmo/questions/tests/test_viewset_questionset_multisite.py +++ b/rdmo/questions/tests/test_viewset_questionset_multisite.py @@ -120,12 +120,9 @@ def test_create_page(db, client, username, password): 'pages': [page.id] } response = client.post(url, data, content_type='application/json') - expected_status_code = status_map['create'][username] - if expected_status_code == 201 and not response.wsgi_request.user.has_perm( - 'questions.change_page_object', page - ): - expected_status_code = 403 - assert response.status_code == expected_status_code, response.json() + assert response.status_code == get_obj_perms_status_code( + page, username, 'create-with-parent' + ), response.json() if response.status_code == 201: new_instance = QuestionSet.objects.get(id=response.json().get('id')) @@ -161,7 +158,9 @@ def test_create_parent(db, client, username, password): 'parents': [parent.id] } response = client.post(url, data, content_type='application/json') - assert response.status_code == status_map['create'][username], response.json() + assert response.status_code == get_obj_perms_status_code( + parent, username, 'create-with-parent' + ), response.json() if response.status_code == 201: new_instance = QuestionSet.objects.get(id=response.json().get('id')) diff --git a/rdmo/questions/tests/test_viewset_section_multisite.py b/rdmo/questions/tests/test_viewset_section_multisite.py index 7d6c2b975b..e22d30d826 100644 --- a/rdmo/questions/tests/test_viewset_section_multisite.py +++ b/rdmo/questions/tests/test_viewset_section_multisite.py @@ -108,12 +108,9 @@ def test_create_catalog(db, client, username, password): 'catalogs': [catalog.id] } response = client.post(url, data, content_type='application/json') - expected_status_code = status_map['create'][username] - if expected_status_code == 201 and not response.wsgi_request.user.has_perm( - 'questions.change_catalog_object', catalog - ): - expected_status_code = 403 - assert response.status_code == expected_status_code, response.json() + assert response.status_code == get_obj_perms_status_code( + catalog, username, 'create-with-parent' + ), response.json() if response.status_code == 201: new_instance = Section.objects.get(id=response.json().get('id')) From 6030bd0db26b8d46b5d206da383e87b47987d5e3 Mon Sep 17 00:00:00 2001 From: David Wallace Date: Mon, 1 Jun 2026 13:20:05 +0200 Subject: [PATCH 102/131] Refactor get_parent_fields in ThroughModelSerializerMixin for re-use Signed-off-by: David Wallace --- rdmo/core/serializers.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/rdmo/core/serializers.py b/rdmo/core/serializers.py index 3f0274a420..853a676fa0 100644 --- a/rdmo/core/serializers.py +++ b/rdmo/core/serializers.py @@ -81,11 +81,6 @@ def validate(self, attrs): return attrs def check_parent_object_permissions(self, attrs): - try: - parent_fields = self.Meta.parent_fields - except AttributeError: - return - # Parent fields create through-model rows on existing parent elements when this # serializer creates a new child element. That effectively changes the # parent element (for example adding a section to an existing catalog), so @@ -97,8 +92,8 @@ def check_parent_object_permissions(self, attrs): if request is None: return - for field_name, _source_name, _target_name, _through_name in parent_fields: - for parent in attrs.get(field_name) or []: + for _field_name, _through_model, parents in self.get_parent_field_data(attrs): + for parent in parents or []: opts = parent._meta permission = f'{opts.app_label}.change_{opts.model_name}_object' if not request.user.has_perm(permission, parent): @@ -173,21 +168,27 @@ def set_through_fields(self, instance, through_fields): return instance def get_parent_fields(self, validated_data): + parent_fields = self.get_parent_field_data(validated_data) + for field_name, _through_model, _parents in parent_fields: + validated_data.pop(field_name, None) + return parent_fields + + def get_parent_field_data(self, data): try: self.Meta.parent_fields # noqa: B018 except AttributeError: - return None + return [] model_info = model_meta.get_field_info(self.Meta.model) - parent_fields = {} + parent_fields = [] for field_name, _source_name, _target_name, through_name in self.Meta.parent_fields: parent_model = model_info.reverse_relations[field_name].related_model parent_model_info = model_meta.get_field_info(parent_model) through_model = parent_model_info.reverse_relations[through_name].related_model - parent_fields[field_name] = (through_model, validated_data.pop(field_name, None)) + parent_fields.append((field_name, through_model, data.get(field_name))) return parent_fields @@ -197,8 +198,9 @@ def set_parent_fields(self, instance, parent_fields): except AttributeError: return instance - for field_name, source_name, target_name, through_name in self.Meta.parent_fields: - through_model, validated_data = parent_fields[field_name] + for field_data, parent_field in zip(parent_fields, self.Meta.parent_fields, strict=True): + _field_name, through_model, validated_data = field_data + _field_name, source_name, target_name, through_name = parent_field if validated_data is None: continue From f31d4a268858670cd99ee42a12ff99a2b12d01b9 Mon Sep 17 00:00:00 2001 From: David Wallace Date: Mon, 1 Jun 2026 14:43:55 +0200 Subject: [PATCH 103/131] Add tests for checking foreign-site parent questions elements at create Signed-off-by: David Wallace --- rdmo/questions/tests/conftest.py | 18 +++ .../tests/test_viewset_page_multisite.py | 35 ++++++ ...st_viewset_parent_permissions_multisite.py | 119 ------------------ .../tests/test_viewset_question_multisite.py | 84 +++++++++++++ .../test_viewset_questionset_multisite.py | 70 +++++++++++ .../tests/test_viewset_section_multisite.py | 29 +++++ 6 files changed, 236 insertions(+), 119 deletions(-) create mode 100644 rdmo/questions/tests/conftest.py delete mode 100644 rdmo/questions/tests/test_viewset_parent_permissions_multisite.py diff --git a/rdmo/questions/tests/conftest.py b/rdmo/questions/tests/conftest.py new file mode 100644 index 0000000000..1b2ea14e3e --- /dev/null +++ b/rdmo/questions/tests/conftest.py @@ -0,0 +1,18 @@ +import pytest + +from django.contrib.sites.models import Site + + +@pytest.fixture +def site_settings(settings): + Site.objects.clear_cache() + + def set_site(domain): + site = Site.objects.get(domain=domain) + settings.SITE_ID = site.id + Site.objects.clear_cache() + return site + + yield set_site + + Site.objects.clear_cache() diff --git a/rdmo/questions/tests/test_viewset_page_multisite.py b/rdmo/questions/tests/test_viewset_page_multisite.py index bfaf3000e0..2130132483 100644 --- a/rdmo/questions/tests/test_viewset_page_multisite.py +++ b/rdmo/questions/tests/test_viewset_page_multisite.py @@ -131,6 +131,41 @@ def test_create_section(db, client, username, password): list(section.section_pages.values_list('page', 'order')) +def test_create_section_rejects_foreign_site_parent(db, client, site_settings): + site_settings('bar.com') + client.login(username='bar-editor', password='bar-editor') + + instance = Page.objects.get(uri_path='foo-page') + section = instance.sections.get(uri_path='foo-section') + + section_pages = list(section.section_pages.values_list('page', 'order')) + + url = reverse(urlnames['list']) + data = { + 'uri_prefix': 'https://bar.com/terms', + 'uri_path': f'{instance.uri_path}-bar-parent-denied', + 'comment': instance.comment, + 'attribute': instance.attribute.pk if instance.attribute else '', + 'is_collection': instance.is_collection, + 'title_en': instance.title_lang1, + 'title_de': instance.title_lang2, + 'help_en': instance.help_lang1, + 'help_de': instance.help_lang2, + 'verbose_name_en': instance.verbose_name_lang1, + 'verbose_name_de': instance.verbose_name_lang2, + 'sections': [section.id] + } + + response = client.post(url, data, content_type='application/json') + + assert response.status_code == get_obj_perms_status_code( + section, 'bar-editor', 'create-with-parent' + ), response.json() + + section.refresh_from_db() + assert section_pages == list(section.section_pages.values_list('page', 'order')) + + @pytest.mark.parametrize('username,password', users) def test_create_m2m(db, client, username, password): client.login(username=username, password=password) diff --git a/rdmo/questions/tests/test_viewset_parent_permissions_multisite.py b/rdmo/questions/tests/test_viewset_parent_permissions_multisite.py deleted file mode 100644 index 28de7abf71..0000000000 --- a/rdmo/questions/tests/test_viewset_parent_permissions_multisite.py +++ /dev/null @@ -1,119 +0,0 @@ -import pytest - -from django.contrib.sites.models import Site -from django.urls import reverse - -from rdmo.core.tests.constants import status_map_object_permissions as status_map - -from ..models import Catalog, Page, Question, QuestionSet, Section - - -@pytest.mark.parametrize('model,parent_model,urlname,payload_builder', [ - ( - Section, Catalog, 'v1-questions:section-list', - lambda instance, parent: { - 'uri_prefix': 'https://bar.com/terms', - 'uri_path': f'{instance.uri_path}-bar-parent-denied', - 'comment': instance.comment, - 'title_en': instance.title_lang1, - 'title_de': instance.title_lang2, - 'catalogs': [parent.id] - } - ), - ( - Page, Section, 'v1-questions:page-list', - lambda instance, parent: { - 'uri_prefix': 'https://bar.com/terms', - 'uri_path': f'{instance.uri_path}-bar-parent-denied', - 'comment': instance.comment, - 'attribute': instance.attribute_id, - 'is_collection': instance.is_collection, - 'title_en': instance.title_lang1, - 'title_de': instance.title_lang2, - 'sections': [parent.id] - } - ), - ( - QuestionSet, Page, 'v1-questions:questionset-list', - lambda instance, parent: { - 'uri_prefix': 'https://bar.com/terms', - 'uri_path': f'{instance.uri_path}-bar-parent-denied', - 'comment': instance.comment, - 'attribute': instance.attribute_id, - 'is_collection': instance.is_collection, - 'title_en': instance.title_lang1, - 'title_de': instance.title_lang2, - 'pages': [parent.id] - } - ), - ( - QuestionSet, QuestionSet, 'v1-questions:questionset-list', - lambda instance, parent: { - 'uri_prefix': 'https://bar.com/terms', - 'uri_path': f'{instance.uri_path}-bar-parent-denied-parent', - 'comment': instance.comment, - 'attribute': instance.attribute_id, - 'is_collection': instance.is_collection, - 'title_en': instance.title_lang1, - 'title_de': instance.title_lang2, - 'parents': [parent.id] - } - ), - ( - Question, Page, 'v1-questions:question-list', - lambda instance, parent: { - 'uri_prefix': 'https://bar.com/terms', - 'uri_path': f'{instance.uri_path}-bar-parent-denied', - 'comment': instance.comment, - 'attribute': instance.attribute_id, - 'is_collection': instance.is_collection, - 'is_optional': instance.is_optional, - 'widget_type': instance.widget_type, - 'value_type': instance.value_type, - 'text_en': instance.text_lang1, - 'text_de': instance.text_lang2, - 'pages': [parent.id] - } - ), - ( - Question, QuestionSet, 'v1-questions:question-list', - lambda instance, parent: { - 'uri_prefix': 'https://bar.com/terms', - 'uri_path': f'{instance.uri_path}-bar-parent-denied-questionset', - 'comment': instance.comment, - 'attribute': instance.attribute_id, - 'is_collection': instance.is_collection, - 'is_optional': instance.is_optional, - 'widget_type': instance.widget_type, - 'value_type': instance.value_type, - 'text_en': instance.text_lang1, - 'text_de': instance.text_lang2, - 'questionsets': [parent.id] - } - ), -]) -def test_bar_editor_cannot_create_child_on_foo_parent( - db, client, settings, model, parent_model, urlname, payload_builder -): - settings.SITE_ID = Site.objects.get(domain='bar.com').id - Site.objects.clear_cache() - client.login(username='bar-editor', password='bar-editor') - - parent = parent_model.objects.get(uri_path=f'foo-{parent_model._meta.model_name}') - instance = model.objects.filter(uri_path=f'foo-{model._meta.model_name}').first() - if instance is None and model == QuestionSet: - instance = model.objects.get(uri_path='foo-questionset') - if instance is None and model == Question: - instance = model.objects.get(uri_path='foo-question') - - through_name = { - Catalog: 'catalog_sections', - Section: 'section_pages', - Page: 'page_questionsets' if model == QuestionSet else 'page_questions', - QuestionSet: 'questionset_questionsets' if model == QuestionSet else 'questionset_questions', - }[parent_model] - before_count = getattr(parent, through_name).count() - response = client.post(reverse(urlname), payload_builder(instance, parent), content_type='application/json') - - assert response.status_code == status_map['create-with-parent']['foo-element']['bar-editor'], response.json() - assert getattr(parent, through_name).count() == before_count diff --git a/rdmo/questions/tests/test_viewset_question_multisite.py b/rdmo/questions/tests/test_viewset_question_multisite.py index 5e35d50c27..9c0b0d71b0 100644 --- a/rdmo/questions/tests/test_viewset_question_multisite.py +++ b/rdmo/questions/tests/test_viewset_question_multisite.py @@ -132,6 +132,47 @@ def test_create_page(db, client, username, password): list(page.page_questions.values_list('question', 'order')) +def test_create_page_rejects_foreign_site_parent(db, client, site_settings): + site_settings('bar.com') + client.login(username='bar-editor', password='bar-editor') + + instance = Question.objects.get(uri_path='foo-question') + page = instance.pages.get(uri_path='foo-page') + + page_questions = list(page.page_questions.values_list('question', 'order')) + + url = reverse(urlnames['list']) + data = { + 'uri_prefix': 'https://bar.com/terms', + 'uri_path': f'{instance.uri_path}-bar-parent-denied', + 'comment': instance.comment or '', + 'attribute': instance.attribute.pk if instance.attribute else '', + 'is_collection': instance.is_collection, + 'help_en': instance.help_lang1 or '', + 'help_de': instance.help_lang2 or '', + 'text_en': instance.text_lang1 or '', + 'text_de': instance.text_lang2 or '', + 'verbose_name_en': instance.verbose_name_lang1 or '', + 'verbose_name_de': instance.verbose_name_lang2 or '', + 'widget_type': instance.widget_type, + 'value_type': instance.value_type, + 'minimum': instance.minimum or '', + 'maximum': instance.maximum or '', + 'step': instance.step or '', + 'unit': instance.unit or '', + 'pages': [page.id] + } + + response = client.post(url, data, content_type='application/json') + + assert response.status_code == get_obj_perms_status_code( + page, 'bar-editor', 'create-with-parent' + ), response.json() + + page.refresh_from_db() + assert page_questions == list(page.page_questions.values_list('question', 'order')) + + @pytest.mark.parametrize('username,password', users) def test_create_questionset(db, client, username, password): client.login(username=username, password=password) @@ -176,6 +217,49 @@ def test_create_questionset(db, client, username, password): list(questionset.questionset_questions.values_list('question', 'order')) +def test_create_questionset_rejects_foreign_site_parent(db, client, site_settings): + site_settings('bar.com') + client.login(username='bar-editor', password='bar-editor') + + instance = Question.objects.get(uri_path='foo-question') + questionset = instance.questionsets.get(uri_path='foo-questionset') + + questionset_questions = list(questionset.questionset_questions.values_list('question', 'order')) + + url = reverse(urlnames['list']) + data = { + 'uri_prefix': 'https://bar.com/terms', + 'uri_path': f'{instance.uri_path}-bar-parent-denied-questionset', + 'comment': instance.comment or '', + 'attribute': instance.attribute.pk if instance.attribute else '', + 'is_collection': instance.is_collection, + 'help_en': instance.help_lang1 or '', + 'help_de': instance.help_lang2 or '', + 'text_en': instance.text_lang1 or '', + 'text_de': instance.text_lang2 or '', + 'verbose_name_en': instance.verbose_name_lang1 or '', + 'verbose_name_de': instance.verbose_name_lang2 or '', + 'widget_type': instance.widget_type, + 'value_type': instance.value_type, + 'minimum': instance.minimum or '', + 'maximum': instance.maximum or '', + 'step': instance.step or '', + 'unit': instance.unit or '', + 'questionsets': [questionset.id] + } + + response = client.post(url, data, content_type='application/json') + + assert response.status_code == get_obj_perms_status_code( + questionset, 'bar-editor', 'create-with-parent' + ), response.json() + + questionset.refresh_from_db() + assert questionset_questions == list( + questionset.questionset_questions.values_list('question', 'order') + ) + + @pytest.mark.parametrize('username,password', users) def test_create_m2m(db, client, username, password): client.login(username=username, password=password) diff --git a/rdmo/questions/tests/test_viewset_questionset_multisite.py b/rdmo/questions/tests/test_viewset_questionset_multisite.py index 6c37e88ded..1ce9d52767 100644 --- a/rdmo/questions/tests/test_viewset_questionset_multisite.py +++ b/rdmo/questions/tests/test_viewset_questionset_multisite.py @@ -131,6 +131,41 @@ def test_create_page(db, client, username, password): list(page.page_questionsets.values_list('questionset', 'order')) +def test_create_page_rejects_foreign_site_parent(db, client, site_settings): + site_settings('bar.com') + client.login(username='bar-editor', password='bar-editor') + + instance = QuestionSet.objects.get(uri_path='foo-questionset') + page = instance.pages.get(uri_path='foo-page') + + page_questionsets = list(page.page_questionsets.values_list('questionset', 'order')) + + url = reverse(urlnames['list']) + data = { + 'uri_prefix': 'https://bar.com/terms', + 'uri_path': f'{instance.uri_path}-bar-parent-denied', + 'comment': instance.comment, + 'attribute': instance.attribute.pk if instance.attribute else '', + 'is_collection': instance.is_collection, + 'title_en': instance.title_lang1, + 'title_de': instance.title_lang2, + 'help_en': instance.help_lang1, + 'help_de': instance.help_lang2, + 'verbose_name_en': instance.verbose_name_lang1, + 'verbose_name_de': instance.verbose_name_lang2, + 'pages': [page.id] + } + + response = client.post(url, data, content_type='application/json') + + assert response.status_code == get_obj_perms_status_code( + page, 'bar-editor', 'create-with-parent' + ), response.json() + + page.refresh_from_db() + assert page_questionsets == list(page.page_questionsets.values_list('questionset', 'order')) + + @pytest.mark.parametrize('username,password', users) def test_create_parent(db, client, username, password): client.login(username=username, password=password) @@ -169,6 +204,41 @@ def test_create_parent(db, client, username, password): list(parent.questionset_questionsets.values_list('questionset', 'order')) +def test_create_parent_rejects_foreign_site_parent(db, client, site_settings): + site_settings('bar.com') + client.login(username='bar-editor', password='bar-editor') + + instance = QuestionSet.objects.get(uri_path='foo-questionset') + parent = QuestionSet.objects.get(uri_path='foo-questionset-parent') + + parent_questionsets = list(parent.questionset_questionsets.values_list('questionset', 'order')) + + url = reverse(urlnames['list']) + data = { + 'uri_prefix': 'https://bar.com/terms', + 'uri_path': f'{instance.uri_path}-bar-parent-denied-parent', + 'comment': instance.comment, + 'attribute': instance.attribute.pk if instance.attribute else '', + 'is_collection': instance.is_collection, + 'title_en': instance.title_lang1, + 'title_de': instance.title_lang2, + 'help_en': instance.help_lang1, + 'help_de': instance.help_lang2, + 'verbose_name_en': instance.verbose_name_lang1, + 'verbose_name_de': instance.verbose_name_lang2, + 'parents': [parent.id] + } + + response = client.post(url, data, content_type='application/json') + + assert response.status_code == get_obj_perms_status_code( + parent, 'bar-editor', 'create-with-parent' + ), response.json() + + parent.refresh_from_db() + assert parent_questionsets == list(parent.questionset_questionsets.values_list('questionset', 'order')) + + @pytest.mark.parametrize('username,password', users) def test_create_m2m(db, client, username, password): client.login(username=username, password=password) diff --git a/rdmo/questions/tests/test_viewset_section_multisite.py b/rdmo/questions/tests/test_viewset_section_multisite.py index e22d30d826..e245da6fb9 100644 --- a/rdmo/questions/tests/test_viewset_section_multisite.py +++ b/rdmo/questions/tests/test_viewset_section_multisite.py @@ -119,6 +119,35 @@ def test_create_catalog(db, client, username, password): list(catalog.catalog_sections.values_list('section', 'order')) +def test_create_catalog_rejects_foreign_site_parent(db, client, site_settings): + site_settings('bar.com') + client.login(username='bar-editor', password='bar-editor') + + instance = Section.objects.get(uri_path='foo-section') + catalog = instance.catalogs.get(uri_path='foo-catalog') + + catalog_sections = list(catalog.catalog_sections.values_list('section', 'order')) + + url = reverse(urlnames['list']) + data = { + 'uri_prefix': 'https://bar.com/terms', + 'uri_path': f'{instance.uri_path}-bar-parent-denied', + 'comment': instance.comment, + 'title_en': instance.title_lang1, + 'title_de': instance.title_lang2, + 'catalogs': [catalog.id] + } + + response = client.post(url, data, content_type='application/json') + + assert response.status_code == get_obj_perms_status_code( + catalog, 'bar-editor', 'create-with-parent' + ), response.json() + + catalog.refresh_from_db() + assert catalog_sections == list(catalog.catalog_sections.values_list('section', 'order')) + + @pytest.mark.parametrize('username,password', users) def test_create_m2m(db, client, username, password): client.login(username=username, password=password) From e54613249761ab4dd2f81cc461b953f5cf5e1c8a Mon Sep 17 00:00:00 2001 From: David Wallace Date: Mon, 1 Jun 2026 14:45:28 +0200 Subject: [PATCH 104/131] Add foo and bar parent questions elements fixtures and increase max query counts Signed-off-by: David Wallace --- .../tests/test_queries_questionset.py | 4 +- testing/fixtures/questions.json | 104 ++++++++++++++++++ 2 files changed, 106 insertions(+), 2 deletions(-) diff --git a/rdmo/questions/tests/test_queries_questionset.py b/rdmo/questions/tests/test_queries_questionset.py index c5dc48d3f2..5fd726bad7 100644 --- a/rdmo/questions/tests/test_queries_questionset.py +++ b/rdmo/questions/tests/test_queries_questionset.py @@ -11,8 +11,8 @@ ('list', 11, {}, {}), ('index', 3, {}, {}), ('nested', 19, {'pk': 90}, {}), - ('export', 19, {'export_format': 'xml'}, {}), - ('export', 28, {'export_format': 'xml'}, {'full': '1'}), + ('export', 21, {'export_format': 'xml'}, {}), + ('export', 32, {'export_format': 'xml'}, {'full': '1'}), ('detail', 11, {'pk': 90}, {}), ('detail_export', 18, {'pk': 90, 'export_format': 'xml'}, {}), ('detail_export', 25, {'pk': 90, 'export_format': 'xml'}, {'full': '1'}), diff --git a/testing/fixtures/questions.json b/testing/fixtures/questions.json index 99d5a3c1c4..eb85b26d86 100644 --- a/testing/fixtures/questions.json +++ b/testing/fixtures/questions.json @@ -3404,6 +3404,24 @@ "order": 0 } }, + { + "model": "questions.pagequestion", + "pk": 97, + "fields": { + "page": 97, + "question": 109, + "order": 0 + } + }, + { + "model": "questions.pagequestion", + "pk": 98, + "fields": { + "page": 98, + "question": 110, + "order": 0 + } + }, { "model": "questions.pagequestionset", "pk": 1, @@ -8929,6 +8947,74 @@ "conditions": [] } }, + { + "model": "questions.questionset", + "pk": 104, + "fields": { + "created": "2026-06-01T14:10:07.972Z", + "updated": "2026-06-01T14:51:30.505Z", + "uri": "https://foo.com/terms/questions/foo-questionset-parent", + "uri_prefix": "https://foo.com/terms", + "uri_path": "foo-questionset-parent", + "comment": "", + "locked": false, + "attribute": 122, + "is_collection": false, + "title_lang1": "foo-questionset-parent", + "title_lang2": "foo-questionset-parent", + "title_lang3": "", + "title_lang4": "", + "title_lang5": "", + "help_lang1": "", + "help_lang2": "", + "help_lang3": "", + "help_lang4": "", + "help_lang5": "", + "verbose_name_lang1": "", + "verbose_name_lang2": "", + "verbose_name_lang3": "", + "verbose_name_lang4": "", + "verbose_name_lang5": "", + "editors": [ + 2 + ], + "conditions": [] + } + }, + { + "model": "questions.questionset", + "pk": 105, + "fields": { + "created": "2026-06-01T14:37:35.454Z", + "updated": "2026-06-01T14:38:57.402Z", + "uri": "https://bar.com/terms/questions/bar-questionset-parent", + "uri_prefix": "https://bar.com/terms", + "uri_path": "bar-questionset-parent", + "comment": "", + "locked": false, + "attribute": 123, + "is_collection": false, + "title_lang1": "bar-questionset-parent", + "title_lang2": "bar-questionset-parent", + "title_lang3": "", + "title_lang4": "", + "title_lang5": "", + "help_lang1": "", + "help_lang2": "", + "help_lang3": "", + "help_lang4": "", + "help_lang5": "", + "verbose_name_lang1": "", + "verbose_name_lang2": "", + "verbose_name_lang3": "", + "verbose_name_lang4": "", + "verbose_name_lang5": "", + "editors": [ + 3 + ], + "conditions": [] + } + }, { "model": "questions.questionset", "pk": 97, @@ -9306,6 +9392,24 @@ "order": 11 } }, + { + "model": "questions.questionsetquestionset", + "pk": 3, + "fields": { + "parent": 104, + "questionset": 95, + "order": 0 + } + }, + { + "model": "questions.questionsetquestionset", + "pk": 4, + "fields": { + "parent": 105, + "questionset": 96, + "order": 0 + } + }, { "model": "questions.section", "pk": 1, From fa413f0825467580681623cd5d5b49a50ea8d2b2 Mon Sep 17 00:00:00 2001 From: David Wallace Date: Mon, 1 Jun 2026 15:12:20 +0200 Subject: [PATCH 105/131] Refactor test helper for object permission status code Signed-off-by: David Wallace --- rdmo/core/tests/utils.py | 94 ++++++++++++++++++++++------------------ 1 file changed, 53 insertions(+), 41 deletions(-) diff --git a/rdmo/core/tests/utils.py b/rdmo/core/tests/utils.py index 9594fa609d..8ea1f8587f 100644 --- a/rdmo/core/tests/utils.py +++ b/rdmo/core/tests/utils.py @@ -5,63 +5,75 @@ from rdmo.core.tests.constants import multisite_status_map, status_map_object_permissions -def get_obj_perms_status_code(instance, username, method, editors=None): +def get_obj_perms_status_code(instance, username, action, editors=None): ''' looks for the object permissions of the instance and returns the status code ''' - if (isinstance(instance, Model) or hasattr(instance, 'editors')) and hasattr(instance, 'uri'): - instance_uri = instance.uri - instance_editors = instance.editors.values_list('domain', flat=True) - elif isinstance(instance, str): - instance_uri = instance - instance_editors = [] - else: - raise TypeError(f'instance {instance} should be a str or a Model (and have an uri)') + instance_uri, instance_editors = get_uri_and_editors_from_instance(instance) - if editors is not None and method == 'delete': + if editors is not None and action == 'delete': # override in case of deleted instance instance_editors = editors - if 'foo-' in instance_uri: - instance_obj_perms_key = 'foo-element' - assert_editors = ['foo.com'] - elif 'bar-' in instance_uri: - instance_obj_perms_key = 'bar-element' - assert_editors = ['bar.com'] - elif 'example-' in instance_uri: - instance_obj_perms_key = 'example-element' - assert_editors = ['example.com'] - else: - if instance_editors: - raise ValueError(f"uri {instance_uri} should contain the domain for {instance_editors}") - instance_obj_perms_key = 'all-element' - assert_editors = ['foo.com', 'bar.com', 'example.com'] - - if not instance_editors and instance_obj_perms_key != 'all-element': - if 'import' in method: # override for import when only uri is passed - instance_editors = assert_editors - else: - raise ValueError(f"instance_editors should be specified on {instance_uri}") - elif instance_editors: - assert all( - i in instance_editors for i in assert_editors - ), f"{assert_editors} should be specified on {instance_uri}" - + instance_obj_perms_key, expected_editors = get_obj_perms_key_and_expected_editors_from_uri(instance_uri) + instance_editors = validate_or_infer_instance_editors( + instance_uri, instance_editors, instance_obj_perms_key, expected_editors, action + ) if not instance_editors: - return multisite_status_map[method][username] + return multisite_status_map[action][username] try: - method_instance_obj_perms_map = status_map_object_permissions[method][instance_obj_perms_key] + action_instance_obj_perms_map = status_map_object_permissions[action][instance_obj_perms_key] except KeyError as e: raise KeyError( - f'instance (uri={instance_uri}, editors={instance_editors}) for {method} ({instance_obj_perms_key})' + f'instance (uri={instance_uri}, editors={instance_editors}) for {action} ({instance_obj_perms_key})' f' should be defined in status_map_object_permissions' ) from e try: - return method_instance_obj_perms_map[username] + return action_instance_obj_perms_map[username] except KeyError: - # not all users are defined in the method_instance_perms_map - return multisite_status_map[method][username] + # not all users are defined in the object-permission status map + return multisite_status_map[action][username] + + +def get_uri_and_editors_from_instance(instance): + if isinstance(instance, str): + return instance, [] + + if isinstance(instance, Model) and hasattr(instance, 'uri') and hasattr(instance, 'editors'): + return instance.uri, instance.editors.values_list('domain', flat=True) + + raise TypeError(f'instance {instance} should be a str or a Model with uri and editors') + + +def get_obj_perms_key_and_expected_editors_from_uri(instance_uri): + if 'foo-' in instance_uri: + return 'foo-element', ['foo.com'] + if 'bar-' in instance_uri: + return 'bar-element', ['bar.com'] + if 'example-' in instance_uri: + return 'example-element', ['example.com'] + + return 'all-element', ['foo.com', 'bar.com', 'example.com'] + + +def validate_or_infer_instance_editors(instance_uri, instance_editors, obj_perms_key, expected_editors, action): + if instance_editors and obj_perms_key == 'all-element': + raise ValueError(f"uri {instance_uri} should contain the domain for {instance_editors}") + + if not instance_editors and obj_perms_key != 'all-element': + if 'import' in action: + # override for import when only uri is passed + return expected_editors + + raise ValueError(f"instance_editors should be specified on {instance_uri}") + + if instance_editors: + assert all( + editor in instance_editors for editor in expected_editors + ), f"{expected_editors} should be specified on {instance_uri}" + + return instance_editors def compute_checksum(string): From 8a5a17a6cc587f78d77dfdab2255f4b86beb0f3d Mon Sep 17 00:00:00 2001 From: David Wallace Date: Mon, 1 Jun 2026 16:09:01 +0200 Subject: [PATCH 106/131] build(deps): bump the prod-dependency date-fns to 4.4.0 Signed-off-by: David Wallace --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1713b81081..9e31cea142 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "@uiw/react-codemirror": "^4.25.1", "bootstrap-sass": "^3.4.1", "classnames": "^2.5.1", - "date-fns": "^4.1.0", + "date-fns": "^4.4.0", "font-awesome": "4.7.0", "html-to-text": "^10.0.0", "jquery": "^3.7.1", @@ -3969,9 +3969,9 @@ } }, "node_modules/date-fns": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.2.1.tgz", - "integrity": "sha512-37RhSdxaG1suen6VDCza6rNrQfooyQh57HFVPwQGEq2QWliVLzPQZ8Oa017weOu+HZCnzI7N3Pf/wyoBKfEqrA==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.4.0.tgz", + "integrity": "sha512-+1UMbeh68lH1SegH83CGWwpb6OHHbpSgr3+s5Eww5M4CAgswBpoWS0AjTOfEJ33HiYKz1hdj/KTFprzXHmq/6w==", "license": "MIT", "funding": { "type": "github", diff --git a/package.json b/package.json index 33ff2219df..43f365f0db 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "@uiw/react-codemirror": "^4.25.1", "bootstrap-sass": "^3.4.1", "classnames": "^2.5.1", - "date-fns": "^4.1.0", + "date-fns": "^4.4.0", "font-awesome": "4.7.0", "html-to-text": "^10.0.0", "jquery": "^3.7.1", From a4009ddcc1bc9e4dcd73ab7793aeb4d5ddbc6ccb Mon Sep 17 00:00:00 2001 From: David Wallace Date: Mon, 1 Jun 2026 16:18:46 +0200 Subject: [PATCH 107/131] build(deps-dev): bump the dev-dependency sass-loader to 17.0.0 Signed-off-by: David Wallace --- package-lock.json | 17 +++++------------ package.json | 2 +- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9e31cea142..9d800d5a43 100644 --- a/package-lock.json +++ b/package-lock.json @@ -48,7 +48,7 @@ "file-loader": "^6.2.0", "mini-css-extract-plugin": "^2.10.2", "sass": "^1.99.0", - "sass-loader": "^16.0.0", + "sass-loader": "^17.0.0", "webpack": "^5.106.2", "webpack-cli": "^7.0.2", "webpack-merge": "6.0.1" @@ -7609,16 +7609,13 @@ } }, "node_modules/sass-loader": { - "version": "16.0.8", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-16.0.8.tgz", - "integrity": "sha512-hcov4ZwZJIGbEuyNr9EmiTmZueyrxSToE6GOzoZnq5JM7ecRO7ttyvilPn+VmRsqiP16+VYZzVnGZj/hzZgKBA==", + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-17.0.0.tgz", + "integrity": "sha512-0Ybm8ohBQ9LcrycVrFQp/KQBNX5a3Wda9/smS0mE/xLffzEnwvV8nykOzrbiSWNzTE3IB/jiXx8O4QmDPG2+Gw==", "dev": true, "license": "MIT", - "dependencies": { - "neo-async": "^2.6.2" - }, "engines": { - "node": ">= 18.12.0" + "node": ">= 22.11.0" }, "funding": { "type": "opencollective", @@ -7626,7 +7623,6 @@ }, "peerDependencies": { "@rspack/core": "0.x || ^1.0.0 || ^2.0.0-0", - "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0", "sass": "^1.3.0", "sass-embedded": "*", "webpack": "^5.0.0" @@ -7635,9 +7631,6 @@ "@rspack/core": { "optional": true }, - "node-sass": { - "optional": true - }, "sass": { "optional": true }, diff --git a/package.json b/package.json index 43f365f0db..373eec7f17 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "file-loader": "^6.2.0", "mini-css-extract-plugin": "^2.10.2", "sass": "^1.99.0", - "sass-loader": "^16.0.0", + "sass-loader": "^17.0.0", "webpack": "^5.106.2", "webpack-cli": "^7.0.2", "webpack-merge": "6.0.1" From b8e2a976310c9e83643b8f55534af26322ffb168 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jun 2026 01:26:17 +0000 Subject: [PATCH 108/131] build(deps-dev): bump the optional group with 2 updates Updates the requirements on [django-allauth](https://github.com/sponsors/pennersr) and [pytest-playwright](https://github.com/microsoft/playwright-pytest) to permit the latest version. Updates `django-allauth` to 65.17.0 - [Commits](https://github.com/sponsors/pennersr/commits) Updates `pytest-playwright` to 0.8.0 - [Release notes](https://github.com/microsoft/playwright-pytest/releases) - [Commits](https://github.com/microsoft/playwright-pytest/compare/v0.7.2...v0.8.0) --- updated-dependencies: - dependency-name: django-allauth dependency-version: 65.17.0 dependency-type: direct:development dependency-group: optional - dependency-name: pytest-playwright dependency-version: 0.8.0 dependency-type: direct:development dependency-group: optional ... Signed-off-by: dependabot[bot] --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 1f5c874652..22eda2ff41 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -74,7 +74,7 @@ ci = [ "pytest-github-actions-annotate-failures>=0.2.0,<0.5.0", ] allauth = [ - "django-allauth[socialaccount,openid]>=64.1.0,<65.17.0", + "django-allauth[socialaccount,openid]>=64.1.0,<65.18.0", ] dev = [ "pipdeptree>=2.13,<3.0", @@ -101,7 +101,7 @@ pytest = [ "pytest-cov>=7.0,<8.0", "pytest-django>=4.11,<5.0", "pytest-mock>=3.15,<4.0", - "pytest-playwright>=0.7.2,<0.8.0", + "pytest-playwright>=0.7.2,<0.9.0", "pytest-randomly>=4.0,<5.0", "pytest-xdist>=3.8,<4.0", ] From dac47208b806dd7814d5540d4722267282f155d7 Mon Sep 17 00:00:00 2001 From: David Wallace Date: Mon, 1 Jun 2026 16:46:12 +0200 Subject: [PATCH 109/131] Remove redundant test module, refactored into *_rejects_foreign_site_parent test functions Signed-off-by: David Wallace --- ..._viewset_ancestor_permissions_multisite.py | 68 ------------------- 1 file changed, 68 deletions(-) delete mode 100644 rdmo/questions/tests/test_viewset_ancestor_permissions_multisite.py diff --git a/rdmo/questions/tests/test_viewset_ancestor_permissions_multisite.py b/rdmo/questions/tests/test_viewset_ancestor_permissions_multisite.py deleted file mode 100644 index 2c30601494..0000000000 --- a/rdmo/questions/tests/test_viewset_ancestor_permissions_multisite.py +++ /dev/null @@ -1,68 +0,0 @@ -import pytest - -from django.urls import reverse - -from ..models import Catalog, Page, Question, QuestionSet, Section - - -@pytest.mark.parametrize( - 'descendant_model,descendant_uri,ancestor_model,ancestor_uri,urlname,ancestor_field,relation_name', - [ - ( - Section, 'foo-section', - Catalog, 'foo-catalog', - 'v1-questions:section-list', 'catalogs', 'catalog_sections' - ), - ( - Page, 'foo-page', - Section, 'foo-section', - 'v1-questions:page-list', 'sections', 'section_pages' - ), - ( - QuestionSet, 'foo-questionset', - Page, 'foo-page', - 'v1-questions:questionset-list', 'pages', 'page_questionsets' - ), - ( - QuestionSet, 'foo-questionset', - QuestionSet, 'foo-questionset', - 'v1-questions:questionset-list', 'parents', 'questionset_questionsets' - ), - ( - Question, 'foo-question', - Page, 'foo-page', - 'v1-questions:question-list', 'pages', 'page_questions' - ), - ( - Question, 'foo-question', - QuestionSet, 'foo-questionset', - 'v1-questions:question-list', 'questionsets', 'questionset_questions' - ), - ], - ids=[ - 'catalog-to-section', - 'section-to-page', - 'page-to-questionset', - 'questionset-to-questionset', - 'page-to-question', - 'questionset-to-question', - ], -) -def test_foo_editor_cannot_create_descendant_on_foo_ancestor( - db, client, descendant_model, descendant_uri, ancestor_model, ancestor_uri, urlname, ancestor_field, relation_name -): - client.login(username='foo-editor', password='foo-editor') - - descendant = descendant_model.objects.get(uri_path=descendant_uri) - ancestor = ancestor_model.objects.get(uri_path=ancestor_uri) - before_count = getattr(ancestor, relation_name).count() - - data = { - 'uri_prefix': 'https://example.com/terms', - 'uri_path': f'{descendant.uri_path}-ancestor-denied', - ancestor_field: [ancestor.id] - } - response = client.post(reverse(urlname), data, content_type='application/json') - - assert response.status_code == 403, response.json() - assert getattr(ancestor, relation_name).count() == before_count From 5bd560aa70ce897e3c5421337f9fcdaa43186c6e Mon Sep 17 00:00:00 2001 From: David Wallace Date: Mon, 1 Jun 2026 17:33:47 +0200 Subject: [PATCH 110/131] Refactor parent field methods and get_object_permission helper function Signed-off-by: David Wallace --- rdmo/core/permissions.py | 5 +++++ rdmo/core/serializers.py | 31 +++++++++++-------------------- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/rdmo/core/permissions.py b/rdmo/core/permissions.py index dd37b3870a..b3234ea92c 100644 --- a/rdmo/core/permissions.py +++ b/rdmo/core/permissions.py @@ -96,3 +96,8 @@ def has_permission(self, request, view) -> bool: # the viewset needs to set permission_required return request.user.has_perm(view.permission_required) + + +def get_object_permission(model_or_instance, action): + model = model_or_instance if isinstance(model_or_instance, type) else model_or_instance._meta.model + return f'{model._meta.app_label}.{action}_{model._meta.model_name}_object' diff --git a/rdmo/core/serializers.py b/rdmo/core/serializers.py index 853a676fa0..0a7f74cd98 100644 --- a/rdmo/core/serializers.py +++ b/rdmo/core/serializers.py @@ -9,6 +9,7 @@ from rest_framework.exceptions import PermissionDenied from rest_framework.utils import model_meta +from rdmo.core.permissions import get_object_permission from rdmo.core.utils import get_language_warning, get_languages, markdown2html logger = logging.getLogger(__name__) @@ -92,10 +93,10 @@ def check_parent_object_permissions(self, attrs): if request is None: return - for _field_name, _through_model, parents in self.get_parent_field_data(attrs): + for parent_field in self.get_parent_field_data(attrs): + _field_name, _source_name, _target_name, _through_name, _through_model, parents = parent_field for parent in parents or []: - opts = parent._meta - permission = f'{opts.app_label}.change_{opts.model_name}_object' + permission = get_object_permission(parent, 'change') if not request.user.has_perm(permission, parent): raise PermissionDenied() @@ -169,7 +170,7 @@ def set_through_fields(self, instance, through_fields): def get_parent_fields(self, validated_data): parent_fields = self.get_parent_field_data(validated_data) - for field_name, _through_model, _parents in parent_fields: + for field_name, _source_name, _target_name, _through_name, _through_model, _parents in parent_fields: validated_data.pop(field_name, None) return parent_fields @@ -182,13 +183,13 @@ def get_parent_field_data(self, data): model_info = model_meta.get_field_info(self.Meta.model) parent_fields = [] - for field_name, _source_name, _target_name, through_name in self.Meta.parent_fields: + for field_name, source_name, target_name, through_name in self.Meta.parent_fields: parent_model = model_info.reverse_relations[field_name].related_model parent_model_info = model_meta.get_field_info(parent_model) through_model = parent_model_info.reverse_relations[through_name].related_model - parent_fields.append((field_name, through_model, data.get(field_name))) + parent_fields.append((field_name, source_name, target_name, through_name, through_model, data.get(field_name))) # noqa: E501 return parent_fields @@ -198,14 +199,11 @@ def set_parent_fields(self, instance, parent_fields): except AttributeError: return instance - for field_data, parent_field in zip(parent_fields, self.Meta.parent_fields, strict=True): - _field_name, through_model, validated_data = field_data - _field_name, source_name, target_name, through_name = parent_field - - if validated_data is None: + for _field_name, source_name, target_name, through_name, through_model, parents in parent_fields: + if parents is None: continue - for parent in validated_data: + for parent in parents: order = (getattr(parent, through_name).aggregate(order=Max('order')).get('order') or 0) + 1 through_model(**{ source_name: parent, @@ -249,19 +247,12 @@ class ReadOnlyObjectPermissionSerializerMixin: OBJECT_PERMISSION_ACTION_NAMES = ('change', 'delete') - @staticmethod - def construct_object_permission(model, action_name: str) -> str: - model_app_label = model._meta.app_label - model_name = model._meta.model_name - perm = f'{model_app_label}.{action_name}_{model_name}_object' - return perm - def get_read_only(self, obj) -> bool: request = self.context.get('request') if request is None: return False user = request.user - perms = (self.construct_object_permission(self.Meta.model, action_name) + perms = (get_object_permission(self.Meta.model, action_name) for action_name in self.OBJECT_PERMISSION_ACTION_NAMES) return not all(user.has_perm(perm, obj) for perm in perms) From 209911dfa875540d0f3ba754836152e92fa229ee Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Mon, 1 Jun 2026 19:55:02 +0200 Subject: [PATCH 111/131] Adjust .field-diff styling --- rdmo/management/assets/scss/management.scss | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/rdmo/management/assets/scss/management.scss b/rdmo/management/assets/scss/management.scss index 47712dbdae..c1f98b247c 100644 --- a/rdmo/management/assets/scss/management.scss +++ b/rdmo/management/assets/scss/management.scss @@ -255,22 +255,32 @@ td[class*="marker"], td[class*="content"] { - padding: 8px 12px; vertical-align: top; } td[class*="marker"] { - padding: 8px 0 8px 8px; + padding-top: 4px; + padding-left: 8px; pre { background-color: transparent; border: none; border-radius: 0; box-shadow: none; - font-size: 13px; - line-height: 18px; padding: 0; } } + + td[class*="content"] { + padding: 6px 12px; + + pre { + padding: 2px; + } + + [class*="word-diff"] { + border-radius: 0; + } + } } } From 79bf4b92aeb905c37235ed1789d1a71d172dd281 Mon Sep 17 00:00:00 2001 From: David Wallace Date: Tue, 2 Jun 2026 10:50:40 +0200 Subject: [PATCH 112/131] build(pre-commit): apply pre-commit autoupdate --freeze Signed-off-by: David Wallace --- .pre-commit-config.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ed892222fb..37978f0cf5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,7 +9,7 @@ repos: hooks: - id: check-hooks-apply - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v6.0.0 + rev: 3e8a8703264a2f4a69428a0aa4dcb512790b2c8c # frozen: v6.0.0 hooks: - id: check-ast - id: check-json @@ -22,12 +22,12 @@ repos: exclude: \.dot$ - id: debug-statements - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.15.12 + rev: 0671d8ab202c4ac093b78433ae5baf74f3fc7246 # frozen: v0.15.15 hooks: - id: ruff-check args: [--fix, --exit-non-zero-on-fix] - repo: https://github.com/pre-commit/mirrors-eslint - rev: v10.3.0 + rev: 61e8b06e669ce52c94348373faab214beaa8cd4c # frozen: v10.4.1 hooks: - id: eslint args: [--fix, --color] @@ -36,7 +36,7 @@ repos: - eslint-plugin-react@7.37.0 - react@18.3.1 - repo: https://github.com/crate-ci/typos - rev: v1 + rev: 0758ccd3261dc7b0594e9b698113eb08d3ba25b5 # frozen: v1 hooks: - id: typos exclude: | @@ -50,7 +50,7 @@ repos: testing/.*.xml )$ - repo: https://github.com/zizmorcore/zizmor-pre-commit - rev: v1.24.1 + rev: 9257c6050c0261b8c57e712f632dc4a8010109a9 # frozen: v1.25.2 hooks: - id: zizmor args: [--fix, --offline] From e539de6991409fffe6d244c9f5e8212b7a310e2e Mon Sep 17 00:00:00 2001 From: David Wallace Date: Tue, 2 Jun 2026 10:52:57 +0200 Subject: [PATCH 113/131] build(pre-commit): update typos to frozen 1.47.0 Signed-off-by: David Wallace --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 37978f0cf5..5ad5909bd6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -36,7 +36,7 @@ repos: - eslint-plugin-react@7.37.0 - react@18.3.1 - repo: https://github.com/crate-ci/typos - rev: 0758ccd3261dc7b0594e9b698113eb08d3ba25b5 # frozen: v1 + rev: f8a58b6b53f2279f71eb605f03a4ae4d10608f45 # frozen: v1.47.0 hooks: - id: typos exclude: | From 86308db88a761f1466915a93c978d3becf61f546 Mon Sep 17 00:00:00 2001 From: David Wallace Date: Tue, 2 Jun 2026 10:58:08 +0200 Subject: [PATCH 114/131] build(pre-commit): remove autoupdate from ci and add comment for manual autoupdate Signed-off-by: David Wallace --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5ad5909bd6..6a59bb10f5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,5 +1,8 @@ # See https://pre-commit.com for more information # See https://pre-commit.com/hooks.html for more hooks +# Run manual autoupdate with: +# pre-commit autoupdate --freeze +# pre-commit run --all-files # do not touch the migration files at all: these are auto-generated by Django exclude: \/migrations\/ @@ -55,7 +58,4 @@ repos: - id: zizmor args: [--fix, --offline] ci: - autoupdate_schedule: monthly autofix_prs: false - autoupdate_branch: 'dependency-updates' - autoupdate_commit_msg: 'build(pre-commit): pre-commit autoupdate by ci' From 1a3268c74d9efb8ac2a3083895c6cb64d0c06928 Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Tue, 2 Jun 2026 16:22:08 +0200 Subject: [PATCH 115/131] Rename and move sites fixture --- conftest.py | 17 +++++++++++++++++ rdmo/core/tests/constants.py | 1 - rdmo/questions/tests/conftest.py | 18 ------------------ .../tests/test_viewset_page_multisite.py | 4 ++-- .../tests/test_viewset_question_multisite.py | 8 ++++---- .../test_viewset_questionset_multisite.py | 8 ++++---- .../tests/test_viewset_section_multisite.py | 4 ++-- 7 files changed, 29 insertions(+), 31 deletions(-) delete mode 100644 rdmo/questions/tests/conftest.py diff --git a/conftest.py b/conftest.py index 01fad1f47f..a0dd6360bb 100644 --- a/conftest.py +++ b/conftest.py @@ -1,11 +1,13 @@ import json import shutil from pathlib import Path +from types import SimpleNamespace import pytest from django.conf import settings from django.contrib.auth.models import User +from django.contrib.sites.models import Site from django.core.management import call_command from rdmo.accounts.utils import set_group_permissions @@ -75,3 +77,18 @@ def delete_all(*models): for model in models: model.objects.all().delete() return delete_all + + +@pytest.fixture +def sites(settings): + Site.objects.clear_cache() + + def activate(domain): + site = Site.objects.get(domain=domain) + settings.SITE_ID = site.id + Site.objects.clear_cache() + return site + + yield SimpleNamespace(activate=activate) + + Site.objects.clear_cache() diff --git a/rdmo/core/tests/constants.py b/rdmo/core/tests/constants.py index 21b9d5f139..aff454d605 100644 --- a/rdmo/core/tests/constants.py +++ b/rdmo/core/tests/constants.py @@ -1,4 +1,3 @@ - multisite_status_map = { 'list': { 'foo-user': 403, 'foo-reviewer': 403, 'foo-editor': 403, diff --git a/rdmo/questions/tests/conftest.py b/rdmo/questions/tests/conftest.py deleted file mode 100644 index 1b2ea14e3e..0000000000 --- a/rdmo/questions/tests/conftest.py +++ /dev/null @@ -1,18 +0,0 @@ -import pytest - -from django.contrib.sites.models import Site - - -@pytest.fixture -def site_settings(settings): - Site.objects.clear_cache() - - def set_site(domain): - site = Site.objects.get(domain=domain) - settings.SITE_ID = site.id - Site.objects.clear_cache() - return site - - yield set_site - - Site.objects.clear_cache() diff --git a/rdmo/questions/tests/test_viewset_page_multisite.py b/rdmo/questions/tests/test_viewset_page_multisite.py index 2130132483..cfa2d35dd1 100644 --- a/rdmo/questions/tests/test_viewset_page_multisite.py +++ b/rdmo/questions/tests/test_viewset_page_multisite.py @@ -131,8 +131,8 @@ def test_create_section(db, client, username, password): list(section.section_pages.values_list('page', 'order')) -def test_create_section_rejects_foreign_site_parent(db, client, site_settings): - site_settings('bar.com') +def test_create_section_rejects_foreign_site_parent(db, client, sites): + sites.activate('bar.com') client.login(username='bar-editor', password='bar-editor') instance = Page.objects.get(uri_path='foo-page') diff --git a/rdmo/questions/tests/test_viewset_question_multisite.py b/rdmo/questions/tests/test_viewset_question_multisite.py index 9c0b0d71b0..8d40497926 100644 --- a/rdmo/questions/tests/test_viewset_question_multisite.py +++ b/rdmo/questions/tests/test_viewset_question_multisite.py @@ -132,8 +132,8 @@ def test_create_page(db, client, username, password): list(page.page_questions.values_list('question', 'order')) -def test_create_page_rejects_foreign_site_parent(db, client, site_settings): - site_settings('bar.com') +def test_create_page_rejects_foreign_site_parent(db, client, sites): + sites.activate('bar.com') client.login(username='bar-editor', password='bar-editor') instance = Question.objects.get(uri_path='foo-question') @@ -217,8 +217,8 @@ def test_create_questionset(db, client, username, password): list(questionset.questionset_questions.values_list('question', 'order')) -def test_create_questionset_rejects_foreign_site_parent(db, client, site_settings): - site_settings('bar.com') +def test_create_questionset_rejects_foreign_site_parent(db, client, sites): + sites.activate('bar.com') client.login(username='bar-editor', password='bar-editor') instance = Question.objects.get(uri_path='foo-question') diff --git a/rdmo/questions/tests/test_viewset_questionset_multisite.py b/rdmo/questions/tests/test_viewset_questionset_multisite.py index 1ce9d52767..aec4cd889f 100644 --- a/rdmo/questions/tests/test_viewset_questionset_multisite.py +++ b/rdmo/questions/tests/test_viewset_questionset_multisite.py @@ -131,8 +131,8 @@ def test_create_page(db, client, username, password): list(page.page_questionsets.values_list('questionset', 'order')) -def test_create_page_rejects_foreign_site_parent(db, client, site_settings): - site_settings('bar.com') +def test_create_page_rejects_foreign_site_parent(db, client, sites): + sites.activate('bar.com') client.login(username='bar-editor', password='bar-editor') instance = QuestionSet.objects.get(uri_path='foo-questionset') @@ -204,8 +204,8 @@ def test_create_parent(db, client, username, password): list(parent.questionset_questionsets.values_list('questionset', 'order')) -def test_create_parent_rejects_foreign_site_parent(db, client, site_settings): - site_settings('bar.com') +def test_create_parent_rejects_foreign_site_parent(db, client, sites): + sites.activate('bar.com') client.login(username='bar-editor', password='bar-editor') instance = QuestionSet.objects.get(uri_path='foo-questionset') diff --git a/rdmo/questions/tests/test_viewset_section_multisite.py b/rdmo/questions/tests/test_viewset_section_multisite.py index e245da6fb9..6a4b0872e9 100644 --- a/rdmo/questions/tests/test_viewset_section_multisite.py +++ b/rdmo/questions/tests/test_viewset_section_multisite.py @@ -119,8 +119,8 @@ def test_create_catalog(db, client, username, password): list(catalog.catalog_sections.values_list('section', 'order')) -def test_create_catalog_rejects_foreign_site_parent(db, client, site_settings): - site_settings('bar.com') +def test_create_catalog_rejects_foreign_site_parent(db, client, sites): + sites.activate('bar.com') client.login(username='bar-editor', password='bar-editor') instance = Section.objects.get(uri_path='foo-section') From bbb5e45f78a4712229a4695c95578559ce0ff87f Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Tue, 2 Jun 2026 17:05:55 +0200 Subject: [PATCH 116/131] Slightly refactor ThroughModelSerializerMixin --- rdmo/core/serializers.py | 86 +++++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 40 deletions(-) diff --git a/rdmo/core/serializers.py b/rdmo/core/serializers.py index 0a7f74cd98..bc44ef259e 100644 --- a/rdmo/core/serializers.py +++ b/rdmo/core/serializers.py @@ -81,25 +81,6 @@ def validate(self, attrs): self.check_parent_object_permissions(attrs) return attrs - def check_parent_object_permissions(self, attrs): - # Parent fields create through-model rows on existing parent elements when this - # serializer creates a new child element. That effectively changes the - # parent element (for example adding a section to an existing catalog), so - # require object-level change permission for each supplied parent. - if self.instance is not None: - return - - request = self.context.get('request') - if request is None: - return - - for parent_field in self.get_parent_field_data(attrs): - _field_name, _source_name, _target_name, _through_name, _through_model, parents = parent_field - for parent in parents or []: - permission = get_object_permission(parent, 'change') - if not request.user.has_perm(permission, parent): - raise PermissionDenied() - def create(self, validated_data): parent_fields = self.get_parent_fields(validated_data) through_fields = self.get_through_fields(validated_data) @@ -169,30 +150,11 @@ def set_through_fields(self, instance, through_fields): return instance def get_parent_fields(self, validated_data): - parent_fields = self.get_parent_field_data(validated_data) - for field_name, _source_name, _target_name, _through_name, _through_model, _parents in parent_fields: + parent_fields = self.resolve_parent_fields(validated_data) + for field_name, *_ in parent_fields: validated_data.pop(field_name, None) return parent_fields - def get_parent_field_data(self, data): - try: - self.Meta.parent_fields # noqa: B018 - except AttributeError: - return [] - - model_info = model_meta.get_field_info(self.Meta.model) - - parent_fields = [] - for field_name, source_name, target_name, through_name in self.Meta.parent_fields: - parent_model = model_info.reverse_relations[field_name].related_model - parent_model_info = model_meta.get_field_info(parent_model) - - through_model = parent_model_info.reverse_relations[through_name].related_model - - parent_fields.append((field_name, source_name, target_name, through_name, through_model, data.get(field_name))) # noqa: E501 - - return parent_fields - def set_parent_fields(self, instance, parent_fields): try: self.Meta.parent_fields # noqa: B018 @@ -213,6 +175,50 @@ def set_parent_fields(self, instance, parent_fields): return instance + def resolve_parent_fields(self, data): + try: + self.Meta.parent_fields # noqa: B018 + except AttributeError: + return [] + + model_info = model_meta.get_field_info(self.Meta.model) + + parent_fields = [] + for field_name, source_name, target_name, through_name in self.Meta.parent_fields: + parent_model = model_info.reverse_relations[field_name].related_model + parent_model_info = model_meta.get_field_info(parent_model) + + through_model = parent_model_info.reverse_relations[through_name].related_model + + parent_fields.append(( + field_name, + source_name, + target_name, + through_name, + through_model, + data.get(field_name), + )) + + return parent_fields + + def check_parent_object_permissions(self, attrs): + # Parent fields create through-model rows on existing parent elements when this + # serializer creates a new child element. That effectively changes the + # parent element (for example adding a section to an existing catalog), so + # require object-level change permission for each supplied parent. + if self.instance is not None: + return + + request = self.context.get('request') + if request is None: + return + + for *_, parents in self.resolve_parent_fields(attrs): + for parent in parents or []: + permission = get_object_permission(parent, 'change') + if not request.user.has_perm(permission, parent): + raise PermissionDenied() + class ElementModelSerializerMixin(serializers.ModelSerializer): From cbb853c53b209e5ff3d927a28330328c23bbaba5 Mon Sep 17 00:00:00 2001 From: David Wallace Date: Wed, 3 Jun 2026 11:14:34 +0200 Subject: [PATCH 117/131] Use status code mappings in multisite tests Signed-off-by: David Wallace --- .../tests/test_viewset_condition_multisite.py | 62 +++++- rdmo/core/tests/constants.py | 135 ------------ rdmo/core/tests/utils.py | 75 ------- .../tests/test_viewset_attribute_multisite.py | 80 +++++++- rdmo/management/tests/test_view_multisite.py | 3 +- .../tests/test_viewset_import_multisite.py | 87 +++++++- .../tests/test_viewset_options_multisite.py | 96 ++++++++- .../test_viewset_optionsets_multisite.py | 82 +++++++- .../tests/test_viewset_catalog_multisite.py | 88 +++++++- .../tests/test_viewset_page_multisite.py | 94 +++++++-- .../tests/test_viewset_question_multisite.py | 126 ++++++++++-- .../test_viewset_questionset_multisite.py | 192 ++++++++++++++++-- .../tests/test_viewset_section_multisite.py | 108 ++++++++-- .../tests/test_viewset_task_multisite.py | 82 +++++++- .../tests/test_viewset_view_multisite.py | 86 ++++++-- 15 files changed, 1065 insertions(+), 331 deletions(-) diff --git a/rdmo/conditions/tests/test_viewset_condition_multisite.py b/rdmo/conditions/tests/test_viewset_condition_multisite.py index f3adabb87e..594cf61f07 100644 --- a/rdmo/conditions/tests/test_viewset_condition_multisite.py +++ b/rdmo/conditions/tests/test_viewset_condition_multisite.py @@ -6,11 +6,56 @@ from rdmo.core.tests.constants import multisite_status_map as status_map from rdmo.core.tests.constants import multisite_users as users -from rdmo.core.tests.utils import get_obj_perms_status_code from ..models import Condition from .test_viewset_condition import export_formats, urlnames +STATUS_CODES = { + 'detail': { + 'https://foo.com/terms/conditions/foo-condition': { + 'user': 404, 'reviewer': 200, 'editor': 200, + 'example-reviewer': 200, 'example-editor': 200, 'foo-user': 404, + 'foo-reviewer': 200, 'foo-editor': 200, 'bar-user': 404, + 'bar-reviewer': 404, 'bar-editor': 404, 'anonymous': 401, + }, + 'https://bar.com/terms/conditions/bar-condition': { + 'user': 404, 'reviewer': 200, 'editor': 200, + 'example-reviewer': 200, 'example-editor': 200, 'foo-user': 404, + 'foo-reviewer': 404, 'foo-editor': 404, 'bar-user': 404, + 'bar-reviewer': 200, 'bar-editor': 200, 'anonymous': 401, + }, + }, + 'update': { + 'https://foo.com/terms/conditions/foo-condition': { + 'user': 404, 'reviewer': 403, 'editor': 200, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 403, 'foo-editor': 200, 'bar-user': 404, + 'bar-reviewer': 404, 'bar-editor': 404, 'anonymous': 401, + }, + 'https://bar.com/terms/conditions/bar-condition': { + 'user': 404, 'reviewer': 403, 'editor': 200, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 404, 'foo-editor': 404, 'bar-user': 404, + 'bar-reviewer': 403, 'bar-editor': 200, 'anonymous': 401, + }, + }, + 'delete': { + 'https://foo.com/terms/conditions/foo-condition': { + 'user': 404, 'reviewer': 403, 'editor': 204, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 403, 'foo-editor': 204, 'bar-user': 404, + 'bar-reviewer': 404, 'bar-editor': 404, 'anonymous': 401, + }, + 'https://bar.com/terms/conditions/bar-condition': { + 'user': 404, 'reviewer': 403, 'editor': 204, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 404, 'foo-editor': 404, 'bar-user': 404, + 'bar-reviewer': 403, 'bar-editor': 204, 'anonymous': 401, + }, + }, +} + + @pytest.mark.parametrize('username,password', users) def test_list(db, client, username, password): @@ -54,9 +99,9 @@ def test_detail(db, client, username, password): for instance in instances: url = reverse(urlnames['detail'], args=[instance.pk]) response = client.get(url) - assert response.status_code == get_obj_perms_status_code( - instance, username, 'detail' - ), (response.json(), instance.editors.all()) + assert response.status_code == STATUS_CODES['detail'].get(instance.uri, status_map['detail'])[username], ( + response.json(), instance.editors.all() + ) @pytest.mark.parametrize('username,password', users) @@ -231,9 +276,9 @@ def test_update(db, client, username, password): 'target_option': instance.target_option.pk if instance.target_option else None } response = client.put(url, data, content_type='application/json') - assert response.status_code == get_obj_perms_status_code( - instance, username, 'update' - ), (response.json(), instance.editors.all()) + assert response.status_code == STATUS_CODES['update'].get(instance.uri, status_map['update'])[username], ( + response.json(), instance.editors.all() + ) @pytest.mark.parametrize('username,password', users) @@ -242,10 +287,9 @@ def test_delete(db, client, username, password): instances = Condition.objects.all() for instance in instances: - editors = list(instance.editors.values_list('domain', flat=True)) url = reverse(urlnames['detail'], args=[instance.pk]) response = client.delete(url) - assert response.status_code == get_obj_perms_status_code(instance, username, 'delete', editors=editors) + assert response.status_code == STATUS_CODES['delete'].get(instance.uri, status_map['delete'])[username] @pytest.mark.parametrize('username,password', users) diff --git a/rdmo/core/tests/constants.py b/rdmo/core/tests/constants.py index aff454d605..df14b2948a 100644 --- a/rdmo/core/tests/constants.py +++ b/rdmo/core/tests/constants.py @@ -69,141 +69,6 @@ }, } -status_map_object_permissions = { - 'copy': { - 'foo-element': { - 'foo-reviewer': 403, 'foo-editor': 201, - 'bar-reviewer': 404, 'bar-editor': 404, - 'example-reviewer': 404, 'example-editor': 404, - }, - 'bar-element': { - 'foo-reviewer': 404, 'foo-editor': 404, - 'bar-reviewer': 403, 'bar-editor': 201, - 'example-reviewer': 404, 'example-editor': 404, - } - }, - 'detail': { - 'foo-element': { - 'foo-reviewer': 200, 'foo-editor': 200, - 'bar-reviewer': 404, 'bar-editor': 404, - 'example-reviewer': 200, 'example-editor': 200, # because current site is example.com - }, - 'bar-element': { - 'foo-reviewer': 404, 'foo-editor': 404, - 'bar-reviewer': 200, 'bar-editor': 200, - 'example-reviewer': 200, 'example-editor': 200, # because current site is example.com - }, - 'example-element': { - 'foo-reviewer': 404, 'foo-editor': 404, - 'bar-reviewer': 404, 'bar-editor': 404, - 'example-reviewer': 200, 'example-editor': 200, - } - }, - 'nested': { - 'foo-element': { - 'foo-reviewer': 200, 'foo-editor': 200, - 'bar-reviewer': 404, 'bar-editor': 404, - 'example-reviewer': 200, 'example-editor': 200, # because current site is example.com - }, - 'bar-element': { - 'foo-reviewer': 404, 'foo-editor': 404, - 'bar-reviewer': 200, 'bar-editor': 200, - 'example-reviewer': 200, 'example-editor': 200, # because current site is example.com - }, - 'example-element': { - 'foo-reviewer': 404, 'foo-editor': 404, - 'bar-reviewer': 404, 'bar-editor': 404, - 'example-reviewer': 200, 'example-editor': 200, - } - }, - 'create-with-parent': { - 'foo-element': { - 'foo-reviewer': 403, 'foo-editor': 403, - 'bar-reviewer': 403, 'bar-editor': 403, - 'example-reviewer': 403, 'example-editor': 403, - }, - 'bar-element': { - 'foo-reviewer': 403, 'foo-editor': 403, - 'bar-reviewer': 403, 'bar-editor': 403, - 'example-reviewer': 403, 'example-editor': 403, - }, - 'example-element': { - 'foo-reviewer': 403, 'foo-editor': 403, - 'bar-reviewer': 403, 'bar-editor': 403, - 'example-reviewer': 403, 'example-editor': 201, - } - }, - 'update': { - 'all-element': { - 'foo-reviewer': 404, 'foo-editor': 404, - 'bar-reviewer': 404, 'bar-editor': 404, - 'example-reviewer': 403, 'example-editor': 200, - }, - 'foo-element': { - 'foo-reviewer': 403, 'foo-editor': 200, - 'bar-reviewer': 404, 'bar-editor': 404, - 'example-reviewer': 404, 'example-editor': 404, - }, - 'bar-element': { - 'foo-reviewer': 404, 'foo-editor': 404, - 'bar-reviewer': 403, 'bar-editor': 200, - 'example-reviewer': 404, 'example-editor': 404, - } - }, - 'upload-import': { - 'all-element': { - 'foo-reviewer': 404, 'foo-editor': 404, - 'bar-reviewer': 404, 'bar-editor': 404, - 'example-reviewer': 403, 'example-editor': 200, - }, - 'foo-element': { - 'foo-reviewer': 403, 'foo-editor': 200, - 'bar-reviewer': 404, 'bar-editor': 404, - 'example-reviewer': 404, 'example-editor': 404, - }, - 'bar-element': { - 'foo-reviewer': 404, 'foo-editor': 404, - 'bar-reviewer': 403, 'bar-editor': 200, - 'example-reviewer': 404, 'example-editor': 404, - } - }, - 'delete': { - 'all-element': { - 'foo-reviewer': 403, 'foo-editor': 204, - 'bar-reviewer': 403, 'bar-editor': 204, - 'example-reviewer': 403, 'example-editor': 204, - }, - 'foo-element': { - 'foo-reviewer': 403, 'foo-editor': 204, - 'bar-reviewer': 404, 'bar-editor': 404, - 'example-reviewer': 404, 'example-editor': 404, - }, - 'bar-element': { - 'foo-reviewer': 404, 'foo-editor': 404, - 'bar-reviewer': 403, 'bar-editor': 204, - 'example-reviewer': 404, 'example-editor': 404, - } - }, - 'toggle-site': { - 'all-element': { - # foo-editor can not apply own site(foo.com) in test run(example.com) - 'foo-reviewer': 403, 'foo-editor': 403, - # bar-editor can not apply own site(bar.com) in test run(example.com) - 'bar-reviewer': 403, 'bar-editor': 403, - 'example-reviewer': 403, 'example-editor': 200, - }, - 'foo-element': { - 'foo-reviewer': 403, 'foo-editor': 403, - 'bar-reviewer': 403, 'bar-editor': 403, - 'example-reviewer': 403, 'example-editor': 200, - }, - 'bar-element': { - 'foo-reviewer': 403, 'foo-editor': 403, - 'bar-reviewer': 403, 'bar-editor': 403, - 'example-reviewer': 403, 'example-editor': 200, - } - } -} multisite_users = ( ('user', 'user'), ('reviewer', 'reviewer'), diff --git a/rdmo/core/tests/utils.py b/rdmo/core/tests/utils.py index 8ea1f8587f..ed7a8b1f6e 100644 --- a/rdmo/core/tests/utils.py +++ b/rdmo/core/tests/utils.py @@ -1,80 +1,5 @@ import hashlib -from django.db.models import Model - -from rdmo.core.tests.constants import multisite_status_map, status_map_object_permissions - - -def get_obj_perms_status_code(instance, username, action, editors=None): - ''' looks for the object permissions of the instance and returns the status code ''' - - instance_uri, instance_editors = get_uri_and_editors_from_instance(instance) - - if editors is not None and action == 'delete': - # override in case of deleted instance - instance_editors = editors - - instance_obj_perms_key, expected_editors = get_obj_perms_key_and_expected_editors_from_uri(instance_uri) - instance_editors = validate_or_infer_instance_editors( - instance_uri, instance_editors, instance_obj_perms_key, expected_editors, action - ) - - if not instance_editors: - return multisite_status_map[action][username] - - try: - action_instance_obj_perms_map = status_map_object_permissions[action][instance_obj_perms_key] - except KeyError as e: - raise KeyError( - f'instance (uri={instance_uri}, editors={instance_editors}) for {action} ({instance_obj_perms_key})' - f' should be defined in status_map_object_permissions' - ) from e - try: - return action_instance_obj_perms_map[username] - except KeyError: - # not all users are defined in the object-permission status map - return multisite_status_map[action][username] - - -def get_uri_and_editors_from_instance(instance): - if isinstance(instance, str): - return instance, [] - - if isinstance(instance, Model) and hasattr(instance, 'uri') and hasattr(instance, 'editors'): - return instance.uri, instance.editors.values_list('domain', flat=True) - - raise TypeError(f'instance {instance} should be a str or a Model with uri and editors') - - -def get_obj_perms_key_and_expected_editors_from_uri(instance_uri): - if 'foo-' in instance_uri: - return 'foo-element', ['foo.com'] - if 'bar-' in instance_uri: - return 'bar-element', ['bar.com'] - if 'example-' in instance_uri: - return 'example-element', ['example.com'] - - return 'all-element', ['foo.com', 'bar.com', 'example.com'] - - -def validate_or_infer_instance_editors(instance_uri, instance_editors, obj_perms_key, expected_editors, action): - if instance_editors and obj_perms_key == 'all-element': - raise ValueError(f"uri {instance_uri} should contain the domain for {instance_editors}") - - if not instance_editors and obj_perms_key != 'all-element': - if 'import' in action: - # override for import when only uri is passed - return expected_editors - - raise ValueError(f"instance_editors should be specified on {instance_uri}") - - if instance_editors: - assert all( - editor in instance_editors for editor in expected_editors - ), f"{expected_editors} should be specified on {instance_uri}" - - return instance_editors - def compute_checksum(string): return hashlib.sha1(string).hexdigest() diff --git a/rdmo/domain/tests/test_viewset_attribute_multisite.py b/rdmo/domain/tests/test_viewset_attribute_multisite.py index 092be6ae68..507d71f443 100644 --- a/rdmo/domain/tests/test_viewset_attribute_multisite.py +++ b/rdmo/domain/tests/test_viewset_attribute_multisite.py @@ -6,11 +6,70 @@ from rdmo.core.tests.constants import multisite_status_map as status_map from rdmo.core.tests.constants import multisite_users as users -from rdmo.core.tests.utils import get_obj_perms_status_code from ..models import Attribute from .test_viewset_attribute import urlnames +STATUS_CODES = { + 'nested': { + 'https://foo.com/terms/domain/foo-attribute': { + 'user': 404, 'reviewer': 200, 'editor': 200, + 'example-reviewer': 200, 'example-editor': 200, 'foo-user': 404, + 'foo-reviewer': 200, 'foo-editor': 200, 'bar-user': 404, + 'bar-reviewer': 404, 'bar-editor': 404, 'anonymous': 401, + }, + 'https://bar.com/terms/domain/bar-attribute': { + 'user': 404, 'reviewer': 200, 'editor': 200, + 'example-reviewer': 200, 'example-editor': 200, 'foo-user': 404, + 'foo-reviewer': 404, 'foo-editor': 404, 'bar-user': 404, + 'bar-reviewer': 200, 'bar-editor': 200, 'anonymous': 401, + }, + }, + 'detail': { + 'https://foo.com/terms/domain/foo-attribute': { + 'user': 404, 'reviewer': 200, 'editor': 200, + 'example-reviewer': 200, 'example-editor': 200, 'foo-user': 404, + 'foo-reviewer': 200, 'foo-editor': 200, 'bar-user': 404, + 'bar-reviewer': 404, 'bar-editor': 404, 'anonymous': 401, + }, + 'https://bar.com/terms/domain/bar-attribute': { + 'user': 404, 'reviewer': 200, 'editor': 200, + 'example-reviewer': 200, 'example-editor': 200, 'foo-user': 404, + 'foo-reviewer': 404, 'foo-editor': 404, 'bar-user': 404, + 'bar-reviewer': 200, 'bar-editor': 200, 'anonymous': 401, + }, + }, + 'update': { + 'https://foo.com/terms/domain/foo-attribute': { + 'user': 404, 'reviewer': 403, 'editor': 200, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 403, 'foo-editor': 200, 'bar-user': 404, + 'bar-reviewer': 404, 'bar-editor': 404, 'anonymous': 401, + }, + 'https://bar.com/terms/domain/bar-attribute': { + 'user': 404, 'reviewer': 403, 'editor': 200, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 404, 'foo-editor': 404, 'bar-user': 404, + 'bar-reviewer': 403, 'bar-editor': 200, 'anonymous': 401, + }, + }, + 'delete': { + 'https://foo.com/terms/domain/foo-attribute': { + 'user': 404, 'reviewer': 403, 'editor': 204, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 403, 'foo-editor': 204, 'bar-user': 404, + 'bar-reviewer': 404, 'bar-editor': 404, 'anonymous': 401, + }, + 'https://bar.com/terms/domain/bar-attribute': { + 'user': 404, 'reviewer': 403, 'editor': 204, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 404, 'foo-editor': 404, 'bar-user': 404, + 'bar-reviewer': 403, 'bar-editor': 204, 'anonymous': 401, + }, + }, +} + + @pytest.mark.parametrize('username,password', users) def test_list(db, client, username, password): @@ -30,7 +89,9 @@ def test_nested(db, client, username, password): for instance in instances: url = reverse(urlnames['nested'], args=[instance.pk]) response = client.get(url) - assert response.status_code == get_obj_perms_status_code(instance, username, 'nested'), response.json() + assert response.status_code == STATUS_CODES['nested'].get(instance.uri, status_map['nested'])[username], ( + response.json() + ) @pytest.mark.parametrize('username,password', users) @@ -58,7 +119,9 @@ def test_detail(db, client, username, password): for instance in instances: url = reverse(urlnames['detail'], args=[instance.pk]) response = client.get(url) - assert response.status_code == get_obj_perms_status_code(instance, username, 'detail'), response.json() + assert response.status_code == ( + STATUS_CODES['detail'].get(instance.uri, status_map['detail'])[username] + ), response.json() @pytest.mark.parametrize('username,password', users) @@ -95,7 +158,9 @@ def test_update(db, client, username, password): } response = client.put(url, data, content_type='application/json') - assert response.status_code == get_obj_perms_status_code(instance, username, 'update'), response.json() + assert response.status_code == ( + STATUS_CODES['update'].get(instance.uri, status_map['update'])[username] + ), response.json() @pytest.mark.parametrize('username,password', users) @@ -104,10 +169,9 @@ def test_delete(db, client, username, password): instances = Attribute.objects.order_by('-level') for instance in instances: - editors = list(instance.editors.values_list('domain', flat=True)) url = reverse(urlnames['detail'], args=[instance.pk]) response = client.delete(url) - assert response.status_code == get_obj_perms_status_code(instance, username, 'delete', editors=editors) + assert response.status_code == STATUS_CODES['delete'].get(instance.uri, status_map['delete'])[username] @pytest.mark.parametrize('username,password', users) @@ -118,7 +182,9 @@ def test_detail_export(db, client, username, password): for instance in instances: url = reverse(urlnames['detail_export'], args=[instance.pk]) response = client.get(url) - assert response.status_code == get_obj_perms_status_code(instance, username, 'detail'), response.json() + assert response.status_code == ( + STATUS_CODES['detail'].get(instance.uri, status_map['detail'])[username] + ), response.json() if response.status_code == 200: root = et.fromstring(response.content) diff --git a/rdmo/management/tests/test_view_multisite.py b/rdmo/management/tests/test_view_multisite.py index 9d2d5b2b17..df840a7ea3 100644 --- a/rdmo/management/tests/test_view_multisite.py +++ b/rdmo/management/tests/test_view_multisite.py @@ -2,8 +2,7 @@ from django.urls import reverse -from rdmo.core.tests.constants import multisite_users -from rdmo.core.tests.utils import multisite_status_map +from rdmo.core.tests.constants import multisite_status_map, multisite_users @pytest.mark.parametrize('username,password', multisite_users) diff --git a/rdmo/management/tests/test_viewset_import_multisite.py b/rdmo/management/tests/test_viewset_import_multisite.py index 06697ddf28..391ce63906 100644 --- a/rdmo/management/tests/test_viewset_import_multisite.py +++ b/rdmo/management/tests/test_viewset_import_multisite.py @@ -4,9 +4,85 @@ from rdmo.core.tests.constants import multisite_status_map as status_map from rdmo.core.tests.constants import multisite_users as users -from rdmo.core.tests.utils import get_obj_perms_status_code from rdmo.questions.models import Catalog, Page, Question, QuestionSet, Section +STATUS_CODES = { + 'upload-import': { + 'foo-catalog': { + 'user': 403, 'reviewer': 403, 'editor': 200, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 403, + 'foo-reviewer': 403, 'foo-editor': 200, 'bar-user': 403, + 'bar-reviewer': 404, 'bar-editor': 404, 'anonymous': 401, + }, + 'bar-catalog': { + 'user': 403, 'reviewer': 403, 'editor': 200, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 403, + 'foo-reviewer': 404, 'foo-editor': 404, 'bar-user': 403, + 'bar-reviewer': 403, 'bar-editor': 200, 'anonymous': 401, + }, + 'foo-section': { + 'user': 403, 'reviewer': 403, 'editor': 200, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 403, + 'foo-reviewer': 403, 'foo-editor': 200, 'bar-user': 403, + 'bar-reviewer': 404, 'bar-editor': 404, 'anonymous': 401, + }, + 'bar-section': { + 'user': 403, 'reviewer': 403, 'editor': 200, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 403, + 'foo-reviewer': 404, 'foo-editor': 404, 'bar-user': 403, + 'bar-reviewer': 403, 'bar-editor': 200, 'anonymous': 401, + }, + 'foo-page': { + 'user': 403, 'reviewer': 403, 'editor': 200, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 403, + 'foo-reviewer': 403, 'foo-editor': 200, 'bar-user': 403, + 'bar-reviewer': 404, 'bar-editor': 404, 'anonymous': 401, + }, + 'bar-page': { + 'user': 403, 'reviewer': 403, 'editor': 200, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 403, + 'foo-reviewer': 404, 'foo-editor': 404, 'bar-user': 403, + 'bar-reviewer': 403, 'bar-editor': 200, 'anonymous': 401, + }, + 'foo-questionset': { + 'user': 403, 'reviewer': 403, 'editor': 200, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 403, + 'foo-reviewer': 403, 'foo-editor': 200, 'bar-user': 403, + 'bar-reviewer': 404, 'bar-editor': 404, 'anonymous': 401, + }, + 'bar-questionset': { + 'user': 403, 'reviewer': 403, 'editor': 200, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 403, + 'foo-reviewer': 404, 'foo-editor': 404, 'bar-user': 403, + 'bar-reviewer': 403, 'bar-editor': 200, 'anonymous': 401, + }, + 'foo-questionset-parent': { + 'user': 403, 'reviewer': 403, 'editor': 200, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 403, + 'foo-reviewer': 403, 'foo-editor': 200, 'bar-user': 403, + 'bar-reviewer': 404, 'bar-editor': 404, 'anonymous': 401, + }, + 'bar-questionset-parent': { + 'user': 403, 'reviewer': 403, 'editor': 200, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 403, + 'foo-reviewer': 404, 'foo-editor': 404, 'bar-user': 403, + 'bar-reviewer': 403, 'bar-editor': 200, 'anonymous': 401, + }, + 'foo-question': { + 'user': 403, 'reviewer': 403, 'editor': 200, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 403, + 'foo-reviewer': 403, 'foo-editor': 200, 'bar-user': 403, + 'bar-reviewer': 404, 'bar-editor': 404, 'anonymous': 401, + }, + 'bar-question': { + 'user': 403, 'reviewer': 403, 'editor': 200, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 403, + 'foo-reviewer': 404, 'foo-editor': 404, 'bar-user': 403, + 'bar-reviewer': 403, 'bar-editor': 200, 'anonymous': 401, + }, + }, +} + catalog_uri_paths = [ 'catalog', 'catalog2', @@ -19,6 +95,7 @@ } + @pytest.mark.parametrize('username,password', users) def test_list(db, client, username, password): client.login(username=username, password=password) @@ -63,7 +140,9 @@ def test_create_update(db, client, username, password, json_data): if response.status_code == 200: for element in response.json(): assert element.get('created') is False - obj_perm_status_code = get_obj_perms_status_code(element.get('uri_path'), username, 'upload-import') + obj_perm_status_code = STATUS_CODES['upload-import'].get( + element['uri_path'], status_map['upload-import'] + )[username] if obj_perm_status_code == 200: assert element.get('updated') is True else: @@ -85,7 +164,9 @@ def test_create_update_certain_catalog(db, client, username, password, catalog_u assert response.status_code == status_map['upload-import'].get(username), response.json() if response.status_code == 200: - obj_perm_status_code = get_obj_perms_status_code(catalog_uri_path, username, 'upload-import') + obj_perm_status_code = STATUS_CODES['upload-import'].get( + catalog_uri_path, status_map['upload-import'] + )[username] for element in response.json(): assert element.get('created') is False if obj_perm_status_code == 200: diff --git a/rdmo/options/tests/test_viewset_options_multisite.py b/rdmo/options/tests/test_viewset_options_multisite.py index f3060223ce..463c5fa7f0 100644 --- a/rdmo/options/tests/test_viewset_options_multisite.py +++ b/rdmo/options/tests/test_viewset_options_multisite.py @@ -6,11 +6,92 @@ from rdmo.core.tests.constants import multisite_status_map as status_map from rdmo.core.tests.constants import multisite_users as users -from rdmo.core.tests.utils import get_obj_perms_status_code from ..models import Option from .test_viewset_options import urlnames +STATUS_CODES = { + 'detail': { + 'https://foo.com/terms/options/foo-option-1': { + 'user': 404, 'reviewer': 200, 'editor': 200, + 'example-reviewer': 200, 'example-editor': 200, 'foo-user': 404, + 'foo-reviewer': 200, 'foo-editor': 200, 'bar-user': 404, + 'bar-reviewer': 404, 'bar-editor': 404, 'anonymous': 401, + }, + 'https://foo.com/terms/options/foo-option-2': { + 'user': 404, 'reviewer': 200, 'editor': 200, + 'example-reviewer': 200, 'example-editor': 200, 'foo-user': 404, + 'foo-reviewer': 200, 'foo-editor': 200, 'bar-user': 404, + 'bar-reviewer': 404, 'bar-editor': 404, 'anonymous': 401, + }, + 'https://bar.com/terms/options/bar-option-1': { + 'user': 404, 'reviewer': 200, 'editor': 200, + 'example-reviewer': 200, 'example-editor': 200, 'foo-user': 404, + 'foo-reviewer': 404, 'foo-editor': 404, 'bar-user': 404, + 'bar-reviewer': 200, 'bar-editor': 200, 'anonymous': 401, + }, + 'https://bar.com/terms/options/bar-option-2': { + 'user': 404, 'reviewer': 200, 'editor': 200, + 'example-reviewer': 200, 'example-editor': 200, 'foo-user': 404, + 'foo-reviewer': 404, 'foo-editor': 404, 'bar-user': 404, + 'bar-reviewer': 200, 'bar-editor': 200, 'anonymous': 401, + }, + }, + 'update': { + 'https://foo.com/terms/options/foo-option-1': { + 'user': 404, 'reviewer': 403, 'editor': 200, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 403, 'foo-editor': 200, 'bar-user': 404, + 'bar-reviewer': 404, 'bar-editor': 404, 'anonymous': 401, + }, + 'https://foo.com/terms/options/foo-option-2': { + 'user': 404, 'reviewer': 403, 'editor': 200, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 403, 'foo-editor': 200, 'bar-user': 404, + 'bar-reviewer': 404, 'bar-editor': 404, 'anonymous': 401, + }, + 'https://bar.com/terms/options/bar-option-1': { + 'user': 404, 'reviewer': 403, 'editor': 200, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 404, 'foo-editor': 404, 'bar-user': 404, + 'bar-reviewer': 403, 'bar-editor': 200, 'anonymous': 401, + }, + 'https://bar.com/terms/options/bar-option-2': { + 'user': 404, 'reviewer': 403, 'editor': 200, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 404, 'foo-editor': 404, 'bar-user': 404, + 'bar-reviewer': 403, 'bar-editor': 200, 'anonymous': 401, + }, + }, + 'delete': { + 'https://foo.com/terms/options/foo-option-1': { + 'user': 404, 'reviewer': 403, 'editor': 204, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 403, 'foo-editor': 204, 'bar-user': 404, + 'bar-reviewer': 404, 'bar-editor': 404, 'anonymous': 401, + }, + 'https://foo.com/terms/options/foo-option-2': { + 'user': 404, 'reviewer': 403, 'editor': 204, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 403, 'foo-editor': 204, 'bar-user': 404, + 'bar-reviewer': 404, 'bar-editor': 404, 'anonymous': 401, + }, + 'https://bar.com/terms/options/bar-option-1': { + 'user': 404, 'reviewer': 403, 'editor': 204, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 404, 'foo-editor': 404, 'bar-user': 404, + 'bar-reviewer': 403, 'bar-editor': 204, 'anonymous': 401, + }, + 'https://bar.com/terms/options/bar-option-2': { + 'user': 404, 'reviewer': 403, 'editor': 204, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 404, 'foo-editor': 404, 'bar-user': 404, + 'bar-reviewer': 403, 'bar-editor': 204, 'anonymous': 401, + }, + }, +} + + @pytest.mark.parametrize('username,password', users) def test_list(db, client, username, password): @@ -53,7 +134,9 @@ def test_detail(db, client, username, password): for instance in instances: url = reverse(urlnames['detail'], args=[instance.pk]) response = client.get(url) - assert response.status_code == get_obj_perms_status_code(instance, username, 'detail'), response.json() + assert response.status_code == ( + STATUS_CODES['detail'].get(instance.uri, status_map['detail'])[username] + ), response.json() @pytest.mark.parametrize('username,password', users) @@ -91,7 +174,9 @@ def test_update_multisite(db, client, username, password): 'text_de': instance.text_lang2 } response = client.put(url, data, content_type='application/json') - assert response.status_code == get_obj_perms_status_code(instance, username, 'update'), response.json() + assert response.status_code == ( + STATUS_CODES['update'].get(instance.uri, status_map['update'])[username] + ), response.json() instance.refresh_from_db() assert optionsets == [optionset.id for optionset in instance.optionsets.all()] @@ -103,9 +188,8 @@ def test_delete(db, client, username, password): instances = Option.objects.all() for instance in instances: - editors = list(instance.editors.values_list('domain', flat=True)) url = reverse(urlnames['detail'], args=[instance.pk]) response = client.delete(url) - assert response.status_code == get_obj_perms_status_code( - instance, username, 'delete', editors=editors + assert response.status_code == ( + STATUS_CODES['delete'].get(instance.uri, status_map['delete'])[username] ), response.json() diff --git a/rdmo/options/tests/test_viewset_optionsets_multisite.py b/rdmo/options/tests/test_viewset_optionsets_multisite.py index 9c32a3c947..db6ced4c36 100644 --- a/rdmo/options/tests/test_viewset_optionsets_multisite.py +++ b/rdmo/options/tests/test_viewset_optionsets_multisite.py @@ -6,11 +6,70 @@ from rdmo.core.tests.constants import multisite_status_map as status_map from rdmo.core.tests.constants import multisite_users as users -from rdmo.core.tests.utils import get_obj_perms_status_code from ..models import OptionSet from .test_viewset_optionsets import urlnames +STATUS_CODES = { + 'detail': { + 'https://foo.com/terms/options/foo-optionset': { + 'user': 404, 'reviewer': 200, 'editor': 200, + 'example-reviewer': 200, 'example-editor': 200, 'foo-user': 404, + 'foo-reviewer': 200, 'foo-editor': 200, 'bar-user': 404, + 'bar-reviewer': 404, 'bar-editor': 404, 'anonymous': 401, + }, + 'https://bar.com/terms/options/bar-optionset': { + 'user': 404, 'reviewer': 200, 'editor': 200, + 'example-reviewer': 200, 'example-editor': 200, 'foo-user': 404, + 'foo-reviewer': 404, 'foo-editor': 404, 'bar-user': 404, + 'bar-reviewer': 200, 'bar-editor': 200, 'anonymous': 401, + }, + }, + 'nested': { + 'https://foo.com/terms/options/foo-optionset': { + 'user': 404, 'reviewer': 200, 'editor': 200, + 'example-reviewer': 200, 'example-editor': 200, 'foo-user': 404, + 'foo-reviewer': 200, 'foo-editor': 200, 'bar-user': 404, + 'bar-reviewer': 404, 'bar-editor': 404, 'anonymous': 401, + }, + 'https://bar.com/terms/options/bar-optionset': { + 'user': 404, 'reviewer': 200, 'editor': 200, + 'example-reviewer': 200, 'example-editor': 200, 'foo-user': 404, + 'foo-reviewer': 404, 'foo-editor': 404, 'bar-user': 404, + 'bar-reviewer': 200, 'bar-editor': 200, 'anonymous': 401, + }, + }, + 'update': { + 'https://foo.com/terms/options/foo-optionset': { + 'user': 404, 'reviewer': 403, 'editor': 200, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 403, 'foo-editor': 200, 'bar-user': 404, + 'bar-reviewer': 404, 'bar-editor': 404, 'anonymous': 401, + }, + 'https://bar.com/terms/options/bar-optionset': { + 'user': 404, 'reviewer': 403, 'editor': 200, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 404, 'foo-editor': 404, 'bar-user': 404, + 'bar-reviewer': 403, 'bar-editor': 200, 'anonymous': 401, + }, + }, + 'delete': { + 'https://foo.com/terms/options/foo-optionset': { + 'user': 404, 'reviewer': 403, 'editor': 204, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 403, 'foo-editor': 204, 'bar-user': 404, + 'bar-reviewer': 404, 'bar-editor': 404, 'anonymous': 401, + }, + 'https://bar.com/terms/options/bar-optionset': { + 'user': 404, 'reviewer': 403, 'editor': 204, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 404, 'foo-editor': 404, 'bar-user': 404, + 'bar-reviewer': 403, 'bar-editor': 204, 'anonymous': 401, + }, + }, +} + + @pytest.mark.parametrize('username,password', users) def test_list(db, client, username, password): @@ -29,7 +88,9 @@ def test_detail(db, client, username, password): for instance in instances: url = reverse(urlnames['detail'], args=[instance.pk]) response = client.get(url) - assert response.status_code == get_obj_perms_status_code(instance, username, 'detail'), response.json() + assert response.status_code == ( + STATUS_CODES['detail'].get(instance.uri, status_map['detail'])[username] + ), response.json() @pytest.mark.parametrize('username,password', users) @@ -40,7 +101,9 @@ def test_nested(db, client, username, password): for instance in instances: url = reverse(urlnames['nested'], args=[instance.pk]) response = client.get(url) - assert response.status_code == get_obj_perms_status_code(instance, username, 'nested'), response.json() + assert response.status_code == ( + STATUS_CODES['nested'].get(instance.uri, status_map['nested'])[username] + ), response.json() @pytest.mark.parametrize('username,password', users) @@ -107,7 +170,9 @@ def test_update_m2m_multisite(db, client, username, password): 'conditions': conditions, } response = client.put(url, data, content_type='application/json') - assert response.status_code == get_obj_perms_status_code(instance, username, 'update'), response.json() + assert response.status_code == ( + STATUS_CODES['update'].get(instance.uri, status_map['update'])[username] + ), response.json() if response.status_code == 200: instance.refresh_from_db() @@ -124,11 +189,10 @@ def test_delete(db, client, username, password): instances = OptionSet.objects.all() for instance in instances: - editors = list(instance.editors.values_list('domain', flat=True)) url = reverse(urlnames['detail'], args=[instance.pk]) response = client.delete(url) - assert response.status_code == get_obj_perms_status_code( - instance, username, 'delete', editors=editors + assert response.status_code == ( + STATUS_CODES['delete'].get(instance.uri, status_map['delete'])[username] ), response.json() @@ -140,7 +204,9 @@ def test_detail_export(db, client, username, password): for instance in instances: url = reverse(urlnames['detail_export'], args=[instance.pk]) response = client.get(url) - assert response.status_code == get_obj_perms_status_code(instance, username, 'detail'), response.json() + assert response.status_code == ( + STATUS_CODES['detail'].get(instance.uri, status_map['detail'])[username] + ), response.json() if response.status_code == 200: root = et.fromstring(response.content) diff --git a/rdmo/questions/tests/test_viewset_catalog_multisite.py b/rdmo/questions/tests/test_viewset_catalog_multisite.py index e2f0e04059..b24c39e6d4 100644 --- a/rdmo/questions/tests/test_viewset_catalog_multisite.py +++ b/rdmo/questions/tests/test_viewset_catalog_multisite.py @@ -7,14 +7,75 @@ from rdmo.core.tests.constants import multisite_status_map as status_map from rdmo.core.tests.constants import multisite_users as users -from rdmo.core.tests.utils import get_obj_perms_status_code from ..models import Catalog from .test_viewset_catalog import export_formats, urlnames +STATUS_CODES = { + 'detail': { + 'https://foo.com/terms/questions/foo-catalog': { + 'user': 404, 'reviewer': 200, 'editor': 200, + 'example-reviewer': 200, 'example-editor': 200, 'foo-user': 404, + 'foo-reviewer': 200, 'foo-editor': 200, 'bar-user': 404, + 'bar-reviewer': 404, 'bar-editor': 404, 'anonymous': 401, + }, + 'https://bar.com/terms/questions/bar-catalog': { + 'user': 404, 'reviewer': 200, 'editor': 200, + 'example-reviewer': 200, 'example-editor': 200, 'foo-user': 404, + 'foo-reviewer': 404, 'foo-editor': 404, 'bar-user': 404, + 'bar-reviewer': 200, 'bar-editor': 200, 'anonymous': 401, + }, + }, + 'update': { + 'https://foo.com/terms/questions/foo-catalog': { + 'user': 404, 'reviewer': 403, 'editor': 200, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 403, 'foo-editor': 200, 'bar-user': 404, + 'bar-reviewer': 404, 'bar-editor': 404, 'anonymous': 401, + }, + 'https://bar.com/terms/questions/bar-catalog': { + 'user': 404, 'reviewer': 403, 'editor': 200, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 404, 'foo-editor': 404, 'bar-user': 404, + 'bar-reviewer': 403, 'bar-editor': 200, 'anonymous': 401, + }, + }, + 'delete': { + 'https://foo.com/terms/questions/foo-catalog': { + 'user': 404, 'reviewer': 403, 'editor': 204, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 403, 'foo-editor': 204, 'bar-user': 404, + 'bar-reviewer': 404, 'bar-editor': 404, 'anonymous': 401, + }, + 'https://bar.com/terms/questions/bar-catalog': { + 'user': 404, 'reviewer': 403, 'editor': 204, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 404, 'foo-editor': 404, 'bar-user': 404, + 'bar-reviewer': 403, 'bar-editor': 204, 'anonymous': 401, + }, + }, + 'toggle-site': { + 'https://foo.com/terms/questions/foo-catalog': { + 'user': 403, 'reviewer': 403, 'editor': 200, + 'example-reviewer': 403, 'example-editor': 200, 'foo-user': 403, + 'foo-reviewer': 403, 'foo-editor': 403, 'bar-user': 403, + 'bar-reviewer': 403, 'bar-editor': 403, 'anonymous': 401, + }, + 'https://bar.com/terms/questions/bar-catalog': { + 'user': 403, 'reviewer': 403, 'editor': 200, + 'example-reviewer': 403, 'example-editor': 200, 'foo-user': 403, + 'foo-reviewer': 403, 'foo-editor': 403, 'bar-user': 403, + 'bar-reviewer': 403, 'bar-editor': 403, 'anonymous': 401, + }, + }, +} + + urlnames['catalog-toggle-site'] = 'v1-questions:catalog-toggle-site' + + @pytest.mark.parametrize('username,password', users) def test_list(db, client, username, password): client.login(username=username, password=password) @@ -57,7 +118,9 @@ def test_detail(db, client, username, password): for instance in instances: url = reverse(urlnames['detail'], args=[instance.pk]) response = client.get(url) - assert response.status_code == get_obj_perms_status_code(instance, username, 'detail'), response.json() + assert response.status_code == ( + STATUS_CODES['detail'].get(instance.uri, status_map['detail'])[username] + ), response.json() @pytest.mark.parametrize('username,password', users) @@ -68,7 +131,9 @@ def test_nested(db, client, username, password): for instance in instances: url = reverse(urlnames['nested'], args=[instance.pk]) response = client.get(url) - assert response.status_code == get_obj_perms_status_code(instance, username, 'detail'), response.json() + assert response.status_code == ( + STATUS_CODES['detail'].get(instance.uri, status_map['detail'])[username] + ), response.json() @pytest.mark.parametrize('username,password', users) @@ -143,7 +208,9 @@ def test_update(db, client, username, password): 'title_de': instance.title_lang2 } response = client.put(url, data, content_type='application/json') - assert response.status_code == get_obj_perms_status_code(instance, username, 'update'), response.json() + assert response.status_code == ( + STATUS_CODES['update'].get(instance.uri, status_map['update'])[username] + ), response.json() instance.refresh_from_db() assert catalog_sections == [{ @@ -174,7 +241,9 @@ def test_update_m2m(db, client, username, password): 'sections': catalog_sections } response = client.put(url, data, content_type='application/json') - assert response.status_code == get_obj_perms_status_code(instance, username, 'update'), response.json() + assert response.status_code == ( + STATUS_CODES['update'].get(instance.uri, status_map['update'])[username] + ), response.json() if response.status_code == 200: instance.refresh_from_db() @@ -190,11 +259,10 @@ def test_delete(db, client, username, password): instances = Catalog.objects.all() for instance in instances: - editors = list(instance.editors.values_list('domain', flat=True)) url = reverse(urlnames['detail'], args=[instance.pk]) response = client.delete(url) - assert response.status_code == get_obj_perms_status_code( - instance, username, 'delete',editors=editors + assert response.status_code == ( + STATUS_CODES['delete'].get(instance.uri, status_map['delete'])[username] ), response.json() @@ -238,7 +306,9 @@ def test_update_catalog_toggle_site(db, client, username, password, add_or_remov url = reverse(urlnames['catalog-toggle-site'], kwargs={'pk': instance.pk}) response = client.put(url, {}, content_type='application/json') - assert response.status_code == get_obj_perms_status_code(instance, username, 'toggle-site'), response.json() + assert response.status_code == ( + STATUS_CODES['toggle-site'].get(instance.uri, status_map['toggle-site'])[username] + ), response.json() instance.refresh_from_db() after_put_has_current_site = instance.sites.filter(id=current_site.id).exists() if response.status_code == 200: diff --git a/rdmo/questions/tests/test_viewset_page_multisite.py b/rdmo/questions/tests/test_viewset_page_multisite.py index cfa2d35dd1..c11c26f9e8 100644 --- a/rdmo/questions/tests/test_viewset_page_multisite.py +++ b/rdmo/questions/tests/test_viewset_page_multisite.py @@ -7,11 +7,70 @@ from rdmo.core.tests.constants import multisite_status_map as status_map from rdmo.core.tests.constants import multisite_users as users -from rdmo.core.tests.utils import get_obj_perms_status_code from ..models import Page from .test_viewset_page import export_formats, urlnames +STATUS_CODES = { + 'detail': { + 'https://foo.com/terms/questions/foo-page': { + 'user': 404, 'reviewer': 200, 'editor': 200, + 'example-reviewer': 200, 'example-editor': 200, 'foo-user': 404, + 'foo-reviewer': 200, 'foo-editor': 200, 'bar-user': 404, + 'bar-reviewer': 404, 'bar-editor': 404, 'anonymous': 401, + }, + 'https://bar.com/terms/questions/bar-page': { + 'user': 404, 'reviewer': 200, 'editor': 200, + 'example-reviewer': 200, 'example-editor': 200, 'foo-user': 404, + 'foo-reviewer': 404, 'foo-editor': 404, 'bar-user': 404, + 'bar-reviewer': 200, 'bar-editor': 200, 'anonymous': 401, + }, + }, + 'create-with-parent': { + 'https://foo.com/terms/questions/foo-section': { + 'user': 403, 'reviewer': 403, 'editor': 201, + 'example-reviewer': 403, 'example-editor': 403, 'foo-user': 403, + 'foo-reviewer': 403, 'foo-editor': 403, 'bar-user': 403, + 'bar-reviewer': 403, 'bar-editor': 403, 'anonymous': 401, + }, + 'https://bar.com/terms/questions/bar-section': { + 'user': 403, 'reviewer': 403, 'editor': 201, + 'example-reviewer': 403, 'example-editor': 403, 'foo-user': 403, + 'foo-reviewer': 403, 'foo-editor': 403, 'bar-user': 403, + 'bar-reviewer': 403, 'bar-editor': 403, 'anonymous': 401, + }, + }, + 'update': { + 'https://foo.com/terms/questions/foo-page': { + 'user': 404, 'reviewer': 403, 'editor': 200, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 403, 'foo-editor': 200, 'bar-user': 404, + 'bar-reviewer': 404, 'bar-editor': 404, 'anonymous': 401, + }, + 'https://bar.com/terms/questions/bar-page': { + 'user': 404, 'reviewer': 403, 'editor': 200, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 404, 'foo-editor': 404, 'bar-user': 404, + 'bar-reviewer': 403, 'bar-editor': 200, 'anonymous': 401, + }, + }, + 'delete': { + 'https://foo.com/terms/questions/foo-page': { + 'user': 404, 'reviewer': 403, 'editor': 204, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 403, 'foo-editor': 204, 'bar-user': 404, + 'bar-reviewer': 404, 'bar-editor': 404, 'anonymous': 401, + }, + 'https://bar.com/terms/questions/bar-page': { + 'user': 404, 'reviewer': 403, 'editor': 204, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 404, 'foo-editor': 404, 'bar-user': 404, + 'bar-reviewer': 403, 'bar-editor': 204, 'anonymous': 401, + }, + }, +} + + @pytest.mark.parametrize('username,password', users) def test_list(db, client, username, password): @@ -55,7 +114,9 @@ def test_detail(db, client, username, password): for instance in instances: url = reverse(urlnames['detail'], args=[instance.pk]) response = client.get(url) - assert response.status_code == get_obj_perms_status_code(instance, username, 'detail'), response.json() + assert response.status_code == ( + STATUS_CODES['detail'].get(instance.uri, status_map['detail'])[username] + ), response.json() @pytest.mark.parametrize('username,password', users) @@ -66,7 +127,9 @@ def test_nested(db, client, username, password): for instance in instances: url = reverse(urlnames['nested'], args=[instance.pk]) response = client.get(url) - assert response.status_code == get_obj_perms_status_code(instance, username, 'detail'), response.json() + assert response.status_code == ( + STATUS_CODES['detail'].get(instance.uri, status_map['detail'])[username] + ), response.json() @pytest.mark.parametrize('username,password', users) @@ -120,8 +183,10 @@ def test_create_section(db, client, username, password): 'sections': [section.id] } response = client.post(url, data, content_type='application/json') - assert response.status_code == get_obj_perms_status_code( - section, username, 'create-with-parent' + assert response.status_code == ( + STATUS_CODES['create-with-parent'].get( + section.uri, status_map['create-with-parent'] + )[username] ), response.json() if response.status_code == 201: @@ -158,8 +223,10 @@ def test_create_section_rejects_foreign_site_parent(db, client, sites): response = client.post(url, data, content_type='application/json') - assert response.status_code == get_obj_perms_status_code( - section, 'bar-editor', 'create-with-parent' + assert response.status_code == ( + STATUS_CODES['create-with-parent'].get( + section.uri, status_map['create-with-parent'] + )['bar-editor'] ), response.json() section.refresh_from_db() @@ -240,7 +307,9 @@ def test_update(db, client, username, password): 'verbose_name_de': instance.verbose_name_lang2 } response = client.put(url, data, content_type='application/json') - assert response.status_code == get_obj_perms_status_code(instance, username, 'update'), response.json() + assert response.status_code == ( + STATUS_CODES['update'].get(instance.uri, status_map['update'])[username] + ), response.json() instance.refresh_from_db() assert questionsets == [questionset.id for questionset in instance.questionsets.all()] @@ -282,7 +351,9 @@ def test_update_m2m(db, client, username, password): 'conditions': conditions } response = client.put(url, data, content_type='application/json') - assert response.status_code == get_obj_perms_status_code(instance, username, 'update'), response.json() + assert response.status_code == ( + STATUS_CODES['update'].get(instance.uri, status_map['update'])[username] + ), response.json() if response.status_code == 200: instance.refresh_from_db() @@ -303,11 +374,10 @@ def test_delete(db, client, username, password): instances = Page.objects.all() for instance in instances: - editors = list(instance.editors.values_list('domain', flat=True)) url = reverse(urlnames['detail'], args=[instance.pk]) response = client.delete(url) - assert response.status_code == get_obj_perms_status_code( - instance, username, 'delete',editors=editors + assert response.status_code == ( + STATUS_CODES['delete'].get(instance.uri, status_map['delete'])[username] ), response.json() diff --git a/rdmo/questions/tests/test_viewset_question_multisite.py b/rdmo/questions/tests/test_viewset_question_multisite.py index 8d40497926..34e8b878c4 100644 --- a/rdmo/questions/tests/test_viewset_question_multisite.py +++ b/rdmo/questions/tests/test_viewset_question_multisite.py @@ -7,11 +7,94 @@ from rdmo.core.tests.constants import multisite_status_map as status_map from rdmo.core.tests.constants import multisite_users as users -from rdmo.core.tests.utils import get_obj_perms_status_code from ..models import Question from .test_viewset_question import export_formats, urlnames +STATUS_CODES = { + 'detail': { + 'https://foo.com/terms/questions/foo-question': { + 'user': 404, 'reviewer': 200, 'editor': 200, + 'example-reviewer': 200, 'example-editor': 200, 'foo-user': 404, + 'foo-reviewer': 200, 'foo-editor': 200, 'bar-user': 404, + 'bar-reviewer': 404, 'bar-editor': 404, 'anonymous': 401, + }, + 'https://bar.com/terms/questions/bar-question': { + 'user': 404, 'reviewer': 200, 'editor': 200, + 'example-reviewer': 200, 'example-editor': 200, 'foo-user': 404, + 'foo-reviewer': 404, 'foo-editor': 404, 'bar-user': 404, + 'bar-reviewer': 200, 'bar-editor': 200, 'anonymous': 401, + }, + }, + 'create-with-parent': { + 'https://foo.com/terms/questions/foo-page': { + 'user': 403, 'reviewer': 403, 'editor': 201, + 'example-reviewer': 403, 'example-editor': 403, 'foo-user': 403, + 'foo-reviewer': 403, 'foo-editor': 403, 'bar-user': 403, + 'bar-reviewer': 403, 'bar-editor': 403, 'anonymous': 401, + }, + 'https://bar.com/terms/questions/bar-page': { + 'user': 403, 'reviewer': 403, 'editor': 201, + 'example-reviewer': 403, 'example-editor': 403, 'foo-user': 403, + 'foo-reviewer': 403, 'foo-editor': 403, 'bar-user': 403, + 'bar-reviewer': 403, 'bar-editor': 403, 'anonymous': 401, + }, + 'https://foo.com/terms/questions/foo-questionset': { + 'user': 403, 'reviewer': 403, 'editor': 201, + 'example-reviewer': 403, 'example-editor': 403, 'foo-user': 403, + 'foo-reviewer': 403, 'foo-editor': 403, 'bar-user': 403, + 'bar-reviewer': 403, 'bar-editor': 403, 'anonymous': 401, + }, + 'https://bar.com/terms/questions/bar-questionset': { + 'user': 403, 'reviewer': 403, 'editor': 201, + 'example-reviewer': 403, 'example-editor': 403, 'foo-user': 403, + 'foo-reviewer': 403, 'foo-editor': 403, 'bar-user': 403, + 'bar-reviewer': 403, 'bar-editor': 403, 'anonymous': 401, + }, + 'https://foo.com/terms/questions/foo-questionset-parent': { + 'user': 403, 'reviewer': 403, 'editor': 201, + 'example-reviewer': 403, 'example-editor': 403, 'foo-user': 403, + 'foo-reviewer': 403, 'foo-editor': 403, 'bar-user': 403, + 'bar-reviewer': 403, 'bar-editor': 403, 'anonymous': 401, + }, + 'https://bar.com/terms/questions/bar-questionset-parent': { + 'user': 403, 'reviewer': 403, 'editor': 201, + 'example-reviewer': 403, 'example-editor': 403, 'foo-user': 403, + 'foo-reviewer': 403, 'foo-editor': 403, 'bar-user': 403, + 'bar-reviewer': 403, 'bar-editor': 403, 'anonymous': 401, + }, + }, + 'update': { + 'https://foo.com/terms/questions/foo-question': { + 'user': 404, 'reviewer': 403, 'editor': 200, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 403, 'foo-editor': 200, 'bar-user': 404, + 'bar-reviewer': 404, 'bar-editor': 404, 'anonymous': 401, + }, + 'https://bar.com/terms/questions/bar-question': { + 'user': 404, 'reviewer': 403, 'editor': 200, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 404, 'foo-editor': 404, 'bar-user': 404, + 'bar-reviewer': 403, 'bar-editor': 200, 'anonymous': 401, + }, + }, + 'delete': { + 'https://foo.com/terms/questions/foo-question': { + 'user': 404, 'reviewer': 403, 'editor': 204, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 403, 'foo-editor': 204, 'bar-user': 404, + 'bar-reviewer': 404, 'bar-editor': 404, 'anonymous': 401, + }, + 'https://bar.com/terms/questions/bar-question': { + 'user': 404, 'reviewer': 403, 'editor': 204, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 404, 'foo-editor': 404, 'bar-user': 404, + 'bar-reviewer': 403, 'bar-editor': 204, 'anonymous': 401, + }, + }, +} + + @pytest.mark.parametrize('username,password', users) def test_list(db, client, username, password): @@ -55,7 +138,9 @@ def test_detail(db, client, username, password): for instance in instances: url = reverse(urlnames['detail'], args=[instance.pk]) response = client.get(url) - assert response.status_code == get_obj_perms_status_code(instance, username, 'detail'), response.json() + assert response.status_code == ( + STATUS_CODES['detail'].get(instance.uri, status_map['detail'])[username] + ), response.json() @pytest.mark.parametrize('username,password', users) @@ -121,8 +206,10 @@ def test_create_page(db, client, username, password): 'pages': [page.id] } response = client.post(url, data, content_type='application/json') - assert response.status_code == get_obj_perms_status_code( - page, username, 'create-with-parent' + assert response.status_code == ( + STATUS_CODES['create-with-parent'].get( + page.uri, status_map['create-with-parent'] + )[username] ), response.json() if response.status_code == 201: @@ -165,8 +252,10 @@ def test_create_page_rejects_foreign_site_parent(db, client, sites): response = client.post(url, data, content_type='application/json') - assert response.status_code == get_obj_perms_status_code( - page, 'bar-editor', 'create-with-parent' + assert response.status_code == ( + STATUS_CODES['create-with-parent'].get( + page.uri, status_map['create-with-parent'] + )['bar-editor'] ), response.json() page.refresh_from_db() @@ -206,8 +295,10 @@ def test_create_questionset(db, client, username, password): 'questionsets': [questionset.id] } response = client.post(url, data, content_type='application/json') - assert response.status_code == get_obj_perms_status_code( - questionset, username, 'create-with-parent' + assert response.status_code == ( + STATUS_CODES['create-with-parent'].get( + questionset.uri, status_map['create-with-parent'] + )[username] ), response.json() if response.status_code == 201: @@ -250,8 +341,10 @@ def test_create_questionset_rejects_foreign_site_parent(db, client, sites): response = client.post(url, data, content_type='application/json') - assert response.status_code == get_obj_perms_status_code( - questionset, 'bar-editor', 'create-with-parent' + assert response.status_code == ( + STATUS_CODES['create-with-parent'].get( + questionset.uri, status_map['create-with-parent'] + )['bar-editor'] ), response.json() questionset.refresh_from_db() @@ -334,7 +427,9 @@ def test_update(db, client, username, password): 'conditions': [condition.pk for condition in instance.conditions.all()], } response = client.put(url, data, content_type='application/json') - assert response.status_code == get_obj_perms_status_code(instance, username, 'update'), response.json() + assert response.status_code == ( + STATUS_CODES['update'].get(instance.uri, status_map['update'])[username] + ), response.json() instance.refresh_from_db() assert pages == [page.id for page in instance.pages.all()] @@ -375,7 +470,9 @@ def test_update_m2m(db, client, username, password): 'conditions': conditions } response = client.put(url, data, content_type='application/json') - assert response.status_code == get_obj_perms_status_code(instance, username, 'update'), response.json() + assert response.status_code == ( + STATUS_CODES['update'].get(instance.uri, status_map['update'])[username] + ), response.json() if response.status_code == 200: instance.refresh_from_db() @@ -389,11 +486,10 @@ def test_delete(db, client, username, password): instances = Question.objects.all() for instance in instances: - editors = list(instance.editors.values_list('domain', flat=True)) url = reverse(urlnames['detail'], args=[instance.pk]) response = client.delete(url) - assert response.status_code == get_obj_perms_status_code( - instance, username, 'delete', editors=editors + assert response.status_code == ( + STATUS_CODES['delete'].get(instance.uri, status_map['delete'])[username] ), response.json() diff --git a/rdmo/questions/tests/test_viewset_questionset_multisite.py b/rdmo/questions/tests/test_viewset_questionset_multisite.py index aec4cd889f..ff4240a80d 100644 --- a/rdmo/questions/tests/test_viewset_questionset_multisite.py +++ b/rdmo/questions/tests/test_viewset_questionset_multisite.py @@ -7,11 +7,156 @@ from rdmo.core.tests.constants import multisite_status_map as status_map from rdmo.core.tests.constants import multisite_users as users -from rdmo.core.tests.utils import get_obj_perms_status_code from ..models import QuestionSet from .test_viewset_questionset import export_formats, urlnames +STATUS_CODES = { + 'detail': { + 'https://foo.com/terms/questions/foo-questionset': { + 'user': 404, 'reviewer': 200, 'editor': 200, + 'example-reviewer': 200, 'example-editor': 200, 'foo-user': 404, + 'foo-reviewer': 200, 'foo-editor': 200, 'bar-user': 404, + 'bar-reviewer': 404, 'bar-editor': 404, 'anonymous': 401, + }, + 'https://bar.com/terms/questions/bar-questionset': { + 'user': 404, 'reviewer': 200, 'editor': 200, + 'example-reviewer': 200, 'example-editor': 200, 'foo-user': 404, + 'foo-reviewer': 404, 'foo-editor': 404, 'bar-user': 404, + 'bar-reviewer': 200, 'bar-editor': 200, 'anonymous': 401, + }, + 'https://foo.com/terms/questions/foo-questionset-parent': { + 'user': 404, 'reviewer': 200, 'editor': 200, + 'example-reviewer': 200, 'example-editor': 200, 'foo-user': 404, + 'foo-reviewer': 200, 'foo-editor': 200, 'bar-user': 404, + 'bar-reviewer': 404, 'bar-editor': 404, 'anonymous': 401, + }, + 'https://bar.com/terms/questions/bar-questionset-parent': { + 'user': 404, 'reviewer': 200, 'editor': 200, + 'example-reviewer': 200, 'example-editor': 200, 'foo-user': 404, + 'foo-reviewer': 404, 'foo-editor': 404, 'bar-user': 404, + 'bar-reviewer': 200, 'bar-editor': 200, 'anonymous': 401, + }, + }, + 'nested': { + 'https://foo.com/terms/questions/foo-questionset': { + 'user': 404, 'reviewer': 200, 'editor': 200, + 'example-reviewer': 200, 'example-editor': 200, 'foo-user': 404, + 'foo-reviewer': 200, 'foo-editor': 200, 'bar-user': 404, + 'bar-reviewer': 404, 'bar-editor': 404, 'anonymous': 401, + }, + 'https://bar.com/terms/questions/bar-questionset': { + 'user': 404, 'reviewer': 200, 'editor': 200, + 'example-reviewer': 200, 'example-editor': 200, 'foo-user': 404, + 'foo-reviewer': 404, 'foo-editor': 404, 'bar-user': 404, + 'bar-reviewer': 200, 'bar-editor': 200, 'anonymous': 401, + }, + 'https://foo.com/terms/questions/foo-questionset-parent': { + 'user': 404, 'reviewer': 200, 'editor': 200, + 'example-reviewer': 200, 'example-editor': 200, 'foo-user': 404, + 'foo-reviewer': 200, 'foo-editor': 200, 'bar-user': 404, + 'bar-reviewer': 404, 'bar-editor': 404, 'anonymous': 401, + }, + 'https://bar.com/terms/questions/bar-questionset-parent': { + 'user': 404, 'reviewer': 200, 'editor': 200, + 'example-reviewer': 200, 'example-editor': 200, 'foo-user': 404, + 'foo-reviewer': 404, 'foo-editor': 404, 'bar-user': 404, + 'bar-reviewer': 200, 'bar-editor': 200, 'anonymous': 401, + }, + }, + 'create-with-parent': { + 'https://foo.com/terms/questions/foo-page': { + 'user': 403, 'reviewer': 403, 'editor': 201, + 'example-reviewer': 403, 'example-editor': 403, 'foo-user': 403, + 'foo-reviewer': 403, 'foo-editor': 403, 'bar-user': 403, + 'bar-reviewer': 403, 'bar-editor': 403, 'anonymous': 401, + }, + 'https://bar.com/terms/questions/bar-page': { + 'user': 403, 'reviewer': 403, 'editor': 201, + 'example-reviewer': 403, 'example-editor': 403, 'foo-user': 403, + 'foo-reviewer': 403, 'foo-editor': 403, 'bar-user': 403, + 'bar-reviewer': 403, 'bar-editor': 403, 'anonymous': 401, + }, + 'https://foo.com/terms/questions/foo-questionset': { + 'user': 403, 'reviewer': 403, 'editor': 201, + 'example-reviewer': 403, 'example-editor': 403, 'foo-user': 403, + 'foo-reviewer': 403, 'foo-editor': 403, 'bar-user': 403, + 'bar-reviewer': 403, 'bar-editor': 403, 'anonymous': 401, + }, + 'https://bar.com/terms/questions/bar-questionset': { + 'user': 403, 'reviewer': 403, 'editor': 201, + 'example-reviewer': 403, 'example-editor': 403, 'foo-user': 403, + 'foo-reviewer': 403, 'foo-editor': 403, 'bar-user': 403, + 'bar-reviewer': 403, 'bar-editor': 403, 'anonymous': 401, + }, + 'https://foo.com/terms/questions/foo-questionset-parent': { + 'user': 403, 'reviewer': 403, 'editor': 201, + 'example-reviewer': 403, 'example-editor': 403, 'foo-user': 403, + 'foo-reviewer': 403, 'foo-editor': 403, 'bar-user': 403, + 'bar-reviewer': 403, 'bar-editor': 403, 'anonymous': 401, + }, + 'https://bar.com/terms/questions/bar-questionset-parent': { + 'user': 403, 'reviewer': 403, 'editor': 201, + 'example-reviewer': 403, 'example-editor': 403, 'foo-user': 403, + 'foo-reviewer': 403, 'foo-editor': 403, 'bar-user': 403, + 'bar-reviewer': 403, 'bar-editor': 403, 'anonymous': 401, + }, + }, + 'update': { + 'https://foo.com/terms/questions/foo-questionset': { + 'user': 404, 'reviewer': 403, 'editor': 200, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 403, 'foo-editor': 200, 'bar-user': 404, + 'bar-reviewer': 404, 'bar-editor': 404, 'anonymous': 401, + }, + 'https://bar.com/terms/questions/bar-questionset': { + 'user': 404, 'reviewer': 403, 'editor': 200, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 404, 'foo-editor': 404, 'bar-user': 404, + 'bar-reviewer': 403, 'bar-editor': 200, 'anonymous': 401, + }, + 'https://foo.com/terms/questions/foo-questionset-parent': { + 'user': 404, 'reviewer': 403, 'editor': 200, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 403, 'foo-editor': 200, 'bar-user': 404, + 'bar-reviewer': 404, 'bar-editor': 404, 'anonymous': 401, + }, + 'https://bar.com/terms/questions/bar-questionset-parent': { + 'user': 404, 'reviewer': 403, 'editor': 200, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 404, 'foo-editor': 404, 'bar-user': 404, + 'bar-reviewer': 403, 'bar-editor': 200, 'anonymous': 401, + }, + }, + 'delete': { + 'https://foo.com/terms/questions/foo-questionset': { + 'user': 404, 'reviewer': 403, 'editor': 204, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 403, 'foo-editor': 204, 'bar-user': 404, + 'bar-reviewer': 404, 'bar-editor': 404, 'anonymous': 401, + }, + 'https://bar.com/terms/questions/bar-questionset': { + 'user': 404, 'reviewer': 403, 'editor': 204, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 404, 'foo-editor': 404, 'bar-user': 404, + 'bar-reviewer': 403, 'bar-editor': 204, 'anonymous': 401, + }, + 'https://foo.com/terms/questions/foo-questionset-parent': { + 'user': 404, 'reviewer': 403, 'editor': 204, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 403, 'foo-editor': 204, 'bar-user': 404, + 'bar-reviewer': 404, 'bar-editor': 404, 'anonymous': 401, + }, + 'https://bar.com/terms/questions/bar-questionset-parent': { + 'user': 404, 'reviewer': 403, 'editor': 204, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 404, 'foo-editor': 404, 'bar-user': 404, + 'bar-reviewer': 403, 'bar-editor': 204, 'anonymous': 401, + }, + }, +} + + @pytest.mark.parametrize('username,password', users) def test_list(db, client, username, password): @@ -55,7 +200,9 @@ def test_detail(db, client, username, password): for instance in instances: url = reverse(urlnames['detail'], args=[instance.pk]) response = client.get(url) - assert response.status_code == get_obj_perms_status_code(instance, username, 'detail'), response.json() + assert response.status_code == ( + STATUS_CODES['detail'].get(instance.uri, status_map['detail'])[username] + ), response.json() @pytest.mark.parametrize('username,password', users) @@ -66,7 +213,9 @@ def test_nested(db, client, username, password): for instance in instances: url = reverse(urlnames['detail'], args=[instance.pk]) response = client.get(url) - assert response.status_code == get_obj_perms_status_code(instance, username, 'nested'), response.json() + assert response.status_code == ( + STATUS_CODES['nested'].get(instance.uri, status_map['nested'])[username] + ), response.json() @pytest.mark.parametrize('username,password', users) @@ -120,8 +269,10 @@ def test_create_page(db, client, username, password): 'pages': [page.id] } response = client.post(url, data, content_type='application/json') - assert response.status_code == get_obj_perms_status_code( - page, username, 'create-with-parent' + assert response.status_code == ( + STATUS_CODES['create-with-parent'].get( + page.uri, status_map['create-with-parent'] + )[username] ), response.json() if response.status_code == 201: @@ -158,8 +309,10 @@ def test_create_page_rejects_foreign_site_parent(db, client, sites): response = client.post(url, data, content_type='application/json') - assert response.status_code == get_obj_perms_status_code( - page, 'bar-editor', 'create-with-parent' + assert response.status_code == ( + STATUS_CODES['create-with-parent'].get( + page.uri, status_map['create-with-parent'] + )['bar-editor'] ), response.json() page.refresh_from_db() @@ -193,8 +346,10 @@ def test_create_parent(db, client, username, password): 'parents': [parent.id] } response = client.post(url, data, content_type='application/json') - assert response.status_code == get_obj_perms_status_code( - parent, username, 'create-with-parent' + assert response.status_code == ( + STATUS_CODES['create-with-parent'].get( + parent.uri, status_map['create-with-parent'] + )[username] ), response.json() if response.status_code == 201: @@ -231,8 +386,10 @@ def test_create_parent_rejects_foreign_site_parent(db, client, sites): response = client.post(url, data, content_type='application/json') - assert response.status_code == get_obj_perms_status_code( - parent, 'bar-editor', 'create-with-parent' + assert response.status_code == ( + STATUS_CODES['create-with-parent'].get( + parent.uri, status_map['create-with-parent'] + )['bar-editor'] ), response.json() parent.refresh_from_db() @@ -315,7 +472,9 @@ def test_update(db, client, username, password): 'verbose_name_de': instance.verbose_name_lang2 } response = client.put(url, data, content_type='application/json') - assert response.status_code == get_obj_perms_status_code(instance, username, 'update'), response.json() + assert response.status_code == ( + STATUS_CODES['update'].get(instance.uri, status_map['update'])[username] + ), response.json() instance.refresh_from_db() assert pages == [page.id for page in instance.pages.all()] @@ -359,7 +518,9 @@ def test_update_m2m(db, client, username, password): 'conditions': conditions } response = client.put(url, data, content_type='application/json') - assert response.status_code == get_obj_perms_status_code(instance, username, 'update'), response.json() + assert response.status_code == ( + STATUS_CODES['update'].get(instance.uri, status_map['update'])[username] + ), response.json() if response.status_code == 200: instance.refresh_from_db() @@ -380,11 +541,10 @@ def test_delete(db, client, username, password): instances = QuestionSet.objects.all() for instance in instances: - editors = list(instance.editors.values_list('domain', flat=True)) url = reverse(urlnames['detail'], args=[instance.pk]) response = client.delete(url) - assert response.status_code == get_obj_perms_status_code( - instance, username, 'delete', editors=editors + assert response.status_code == ( + STATUS_CODES['delete'].get(instance.uri, status_map['delete'])[username] ), response.json() diff --git a/rdmo/questions/tests/test_viewset_section_multisite.py b/rdmo/questions/tests/test_viewset_section_multisite.py index 6a4b0872e9..51f0977727 100644 --- a/rdmo/questions/tests/test_viewset_section_multisite.py +++ b/rdmo/questions/tests/test_viewset_section_multisite.py @@ -7,11 +7,84 @@ from rdmo.core.tests.constants import multisite_status_map as status_map from rdmo.core.tests.constants import multisite_users as users -from rdmo.core.tests.utils import get_obj_perms_status_code from ..models import Section from .test_viewset_section import export_formats, urlnames +STATUS_CODES = { + 'detail': { + 'https://foo.com/terms/questions/foo-section': { + 'user': 404, 'reviewer': 200, 'editor': 200, + 'example-reviewer': 200, 'example-editor': 200, 'foo-user': 404, + 'foo-reviewer': 200, 'foo-editor': 200, 'bar-user': 404, + 'bar-reviewer': 404, 'bar-editor': 404, 'anonymous': 401, + }, + 'https://bar.com/terms/questions/bar-section': { + 'user': 404, 'reviewer': 200, 'editor': 200, + 'example-reviewer': 200, 'example-editor': 200, 'foo-user': 404, + 'foo-reviewer': 404, 'foo-editor': 404, 'bar-user': 404, + 'bar-reviewer': 200, 'bar-editor': 200, 'anonymous': 401, + }, + }, + 'nested': { + 'https://foo.com/terms/questions/foo-section': { + 'user': 404, 'reviewer': 200, 'editor': 200, + 'example-reviewer': 200, 'example-editor': 200, 'foo-user': 404, + 'foo-reviewer': 200, 'foo-editor': 200, 'bar-user': 404, + 'bar-reviewer': 404, 'bar-editor': 404, 'anonymous': 401, + }, + 'https://bar.com/terms/questions/bar-section': { + 'user': 404, 'reviewer': 200, 'editor': 200, + 'example-reviewer': 200, 'example-editor': 200, 'foo-user': 404, + 'foo-reviewer': 404, 'foo-editor': 404, 'bar-user': 404, + 'bar-reviewer': 200, 'bar-editor': 200, 'anonymous': 401, + }, + }, + 'create-with-parent': { + 'https://foo.com/terms/questions/foo-catalog': { + 'user': 403, 'reviewer': 403, 'editor': 201, + 'example-reviewer': 403, 'example-editor': 403, 'foo-user': 403, + 'foo-reviewer': 403, 'foo-editor': 403, 'bar-user': 403, + 'bar-reviewer': 403, 'bar-editor': 403, 'anonymous': 401, + }, + 'https://bar.com/terms/questions/bar-catalog': { + 'user': 403, 'reviewer': 403, 'editor': 201, + 'example-reviewer': 403, 'example-editor': 403, 'foo-user': 403, + 'foo-reviewer': 403, 'foo-editor': 403, 'bar-user': 403, + 'bar-reviewer': 403, 'bar-editor': 403, 'anonymous': 401, + }, + }, + 'update': { + 'https://foo.com/terms/questions/foo-section': { + 'user': 404, 'reviewer': 403, 'editor': 200, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 403, 'foo-editor': 200, 'bar-user': 404, + 'bar-reviewer': 404, 'bar-editor': 404, 'anonymous': 401, + }, + 'https://bar.com/terms/questions/bar-section': { + 'user': 404, 'reviewer': 403, 'editor': 200, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 404, 'foo-editor': 404, 'bar-user': 404, + 'bar-reviewer': 403, 'bar-editor': 200, 'anonymous': 401, + }, + }, + 'delete': { + 'https://foo.com/terms/questions/foo-section': { + 'user': 404, 'reviewer': 403, 'editor': 204, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 403, 'foo-editor': 204, 'bar-user': 404, + 'bar-reviewer': 404, 'bar-editor': 404, 'anonymous': 401, + }, + 'https://bar.com/terms/questions/bar-section': { + 'user': 404, 'reviewer': 403, 'editor': 204, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 404, 'foo-editor': 404, 'bar-user': 404, + 'bar-reviewer': 403, 'bar-editor': 204, 'anonymous': 401, + }, + }, +} + + @pytest.mark.parametrize('username,password', users) def test_list(db, client, username, password): @@ -55,7 +128,9 @@ def test_detail(db, client, username, password): for instance in instances: url = reverse(urlnames['detail'], args=[instance.pk]) response = client.get(url) - assert response.status_code == get_obj_perms_status_code(instance, username, 'detail'), response.json() + assert response.status_code == ( + STATUS_CODES['detail'].get(instance.uri, status_map['detail'])[username] + ), response.json() @pytest.mark.parametrize('username,password', users) @@ -66,7 +141,9 @@ def test_nested(db, client, username, password): for instance in instances: url = reverse(urlnames['nested'], args=[instance.pk]) response = client.get(url) - assert response.status_code == get_obj_perms_status_code(instance, username, 'nested'), response.json() + assert response.status_code == ( + STATUS_CODES['nested'].get(instance.uri, status_map['nested'])[username] + ), response.json() @pytest.mark.parametrize('username,password', users) @@ -108,8 +185,10 @@ def test_create_catalog(db, client, username, password): 'catalogs': [catalog.id] } response = client.post(url, data, content_type='application/json') - assert response.status_code == get_obj_perms_status_code( - catalog, username, 'create-with-parent' + assert response.status_code == ( + STATUS_CODES['create-with-parent'].get( + catalog.uri, status_map['create-with-parent'] + )[username] ), response.json() if response.status_code == 201: @@ -140,8 +219,10 @@ def test_create_catalog_rejects_foreign_site_parent(db, client, sites): response = client.post(url, data, content_type='application/json') - assert response.status_code == get_obj_perms_status_code( - catalog, 'bar-editor', 'create-with-parent' + assert response.status_code == ( + STATUS_CODES['create-with-parent'].get( + catalog.uri, status_map['create-with-parent'] + )['bar-editor'] ), response.json() catalog.refresh_from_db() @@ -198,7 +279,9 @@ def test_update(db, client, username, password): 'title_de': instance.title_lang2 } response = client.put(url, data, content_type='application/json') - assert response.status_code == get_obj_perms_status_code(instance, username, 'update'), response.json() + assert response.status_code == ( + STATUS_CODES['update'].get(instance.uri, status_map['update'])[username] + ), response.json() instance.refresh_from_db() assert catalogs == [catalog.id for catalog in instance.catalogs.all()] @@ -226,7 +309,9 @@ def test_update_m2m(db, client, username, password): 'title_de': instance.title_lang2 } response = client.put(url, data, content_type='application/json') - assert response.status_code == get_obj_perms_status_code(instance, username, 'update'), response.json() + assert response.status_code == ( + STATUS_CODES['update'].get(instance.uri, status_map['update'])[username] + ), response.json() if response.status_code == 200: instance.refresh_from_db() @@ -242,11 +327,10 @@ def test_delete(db, client, username, password): instances = Section.objects.all() for instance in instances: - editors = list(instance.editors.values_list('domain', flat=True)) url = reverse(urlnames['detail'], args=[instance.pk]) response = client.delete(url) - assert response.status_code == get_obj_perms_status_code( - instance, username, 'delete', editors=editors + assert response.status_code == ( + STATUS_CODES['delete'].get(instance.uri, status_map['delete'])[username] ), response.json() diff --git a/rdmo/tasks/tests/test_viewset_task_multisite.py b/rdmo/tasks/tests/test_viewset_task_multisite.py index 76d910c054..e54721375e 100644 --- a/rdmo/tasks/tests/test_viewset_task_multisite.py +++ b/rdmo/tasks/tests/test_viewset_task_multisite.py @@ -7,14 +7,75 @@ from rdmo.core.tests.constants import multisite_status_map as status_map from rdmo.core.tests.constants import multisite_users as users -from rdmo.core.tests.utils import get_obj_perms_status_code from ..models import Task from .test_viewset_task import export_formats, urlnames +STATUS_CODES = { + 'detail': { + 'https://foo.com/terms/tasks/foo-task': { + 'user': 404, 'reviewer': 200, 'editor': 200, + 'example-reviewer': 200, 'example-editor': 200, 'foo-user': 404, + 'foo-reviewer': 200, 'foo-editor': 200, 'bar-user': 404, + 'bar-reviewer': 404, 'bar-editor': 404, 'anonymous': 401, + }, + 'https://bar.com/terms/tasks/bar-task': { + 'user': 404, 'reviewer': 200, 'editor': 200, + 'example-reviewer': 200, 'example-editor': 200, 'foo-user': 404, + 'foo-reviewer': 404, 'foo-editor': 404, 'bar-user': 404, + 'bar-reviewer': 200, 'bar-editor': 200, 'anonymous': 401, + }, + }, + 'update': { + 'https://foo.com/terms/tasks/foo-task': { + 'user': 404, 'reviewer': 403, 'editor': 200, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 403, 'foo-editor': 200, 'bar-user': 404, + 'bar-reviewer': 404, 'bar-editor': 404, 'anonymous': 401, + }, + 'https://bar.com/terms/tasks/bar-task': { + 'user': 404, 'reviewer': 403, 'editor': 200, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 404, 'foo-editor': 404, 'bar-user': 404, + 'bar-reviewer': 403, 'bar-editor': 200, 'anonymous': 401, + }, + }, + 'delete': { + 'https://foo.com/terms/tasks/foo-task': { + 'user': 404, 'reviewer': 403, 'editor': 204, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 403, 'foo-editor': 204, 'bar-user': 404, + 'bar-reviewer': 404, 'bar-editor': 404, 'anonymous': 401, + }, + 'https://bar.com/terms/tasks/bar-task': { + 'user': 404, 'reviewer': 403, 'editor': 204, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 404, 'foo-editor': 404, 'bar-user': 404, + 'bar-reviewer': 403, 'bar-editor': 204, 'anonymous': 401, + }, + }, + 'toggle-site': { + 'https://foo.com/terms/tasks/foo-task': { + 'user': 403, 'reviewer': 403, 'editor': 200, + 'example-reviewer': 403, 'example-editor': 200, 'foo-user': 403, + 'foo-reviewer': 403, 'foo-editor': 403, 'bar-user': 403, + 'bar-reviewer': 403, 'bar-editor': 403, 'anonymous': 401, + }, + 'https://bar.com/terms/tasks/bar-task': { + 'user': 403, 'reviewer': 403, 'editor': 200, + 'example-reviewer': 403, 'example-editor': 200, 'foo-user': 403, + 'foo-reviewer': 403, 'foo-editor': 403, 'bar-user': 403, + 'bar-reviewer': 403, 'bar-editor': 403, 'anonymous': 401, + }, + }, +} + + urlnames['task-toggle-site'] = 'v1-tasks:task-toggle-site' + + @pytest.mark.parametrize('username,password', users) def test_list(db, client, username, password): client.login(username=username, password=password) @@ -57,7 +118,9 @@ def test_detail(db, client, username, password): for instance in instances: url = reverse(urlnames['detail'], args=[instance.pk]) response = client.get(url) - assert response.status_code == get_obj_perms_status_code(instance, username, 'detail'), response.json() + assert response.status_code == ( + STATUS_CODES['detail'].get(instance.uri, status_map['detail'])[username] + ), response.json() @pytest.mark.parametrize('username,password', users) @@ -107,7 +170,9 @@ def test_update(db, client, username, password): 'conditions': [condition.pk for condition in instance.conditions.all()] } response = client.put(url, data, content_type='application/json') - assert response.status_code == get_obj_perms_status_code(instance, username, 'update'), response.json() + assert response.status_code == ( + STATUS_CODES['update'].get(instance.uri, status_map['update'])[username] + ), response.json() @pytest.mark.parametrize('username,password', users) @@ -116,12 +181,11 @@ def test_delete(db, client, username, password): instances = Task.objects.all() for instance in instances: - editors = list(instance.editors.values_list('domain', flat=True)) url = reverse(urlnames['detail'], args=[instance.pk]) response = client.delete(url) - assert response.status_code == get_obj_perms_status_code( - instance, username, 'delete',editors=editors - ),response.json() + assert response.status_code == ( + STATUS_CODES['delete'].get(instance.uri, status_map['delete'])[username] + ), response.json() @pytest.mark.parametrize('username,password', users) @@ -165,7 +229,9 @@ def test_update_task_toggle_site(db, client, username, password, add_or_remove, url = reverse(urlnames['task-toggle-site'], kwargs={'pk': instance.pk}) response = client.put(url, {}, content_type='application/json') - assert response.status_code == get_obj_perms_status_code(instance, username, 'toggle-site'), response.json() + assert response.status_code == STATUS_CODES['toggle-site'].get(instance.uri, status_map['toggle-site'])[username], ( # noqa: E501 + response.json() + ) instance.refresh_from_db() after_has_current_site = instance.sites.filter(id=current_site.id).exists() if response.status_code == 200: diff --git a/rdmo/views/tests/test_viewset_view_multisite.py b/rdmo/views/tests/test_viewset_view_multisite.py index d5465f3d43..45d6f4156d 100644 --- a/rdmo/views/tests/test_viewset_view_multisite.py +++ b/rdmo/views/tests/test_viewset_view_multisite.py @@ -7,20 +7,73 @@ from rdmo.core.tests.constants import multisite_status_map as status_map from rdmo.core.tests.constants import multisite_users as users -from rdmo.core.tests.utils import get_obj_perms_status_code from ..models import View from .test_viewset_view import export_formats, urlnames +STATUS_CODES = { + 'detail': { + 'https://foo.com/terms/views/foo-view': { + 'user': 404, 'reviewer': 200, 'editor': 200, + 'example-reviewer': 200, 'example-editor': 200, 'foo-user': 404, + 'foo-reviewer': 200, 'foo-editor': 200, 'bar-user': 404, + 'bar-reviewer': 404, 'bar-editor': 404, 'anonymous': 401, + }, + 'https://bar.com/terms/views/bar-view': { + 'user': 404, 'reviewer': 200, 'editor': 200, + 'example-reviewer': 200, 'example-editor': 200, 'foo-user': 404, + 'foo-reviewer': 404, 'foo-editor': 404, 'bar-user': 404, + 'bar-reviewer': 200, 'bar-editor': 200, 'anonymous': 401, + }, + }, + 'update': { + 'https://foo.com/terms/views/foo-view': { + 'user': 404, 'reviewer': 403, 'editor': 200, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 403, 'foo-editor': 200, 'bar-user': 404, + 'bar-reviewer': 404, 'bar-editor': 404, 'anonymous': 401, + }, + 'https://bar.com/terms/views/bar-view': { + 'user': 404, 'reviewer': 403, 'editor': 200, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 404, 'foo-editor': 404, 'bar-user': 404, + 'bar-reviewer': 403, 'bar-editor': 200, 'anonymous': 401, + }, + }, + 'delete': { + 'https://foo.com/terms/views/foo-view': { + 'user': 404, 'reviewer': 403, 'editor': 204, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 403, 'foo-editor': 204, 'bar-user': 404, + 'bar-reviewer': 404, 'bar-editor': 404, 'anonymous': 401, + }, + 'https://bar.com/terms/views/bar-view': { + 'user': 404, 'reviewer': 403, 'editor': 204, + 'example-reviewer': 404, 'example-editor': 404, 'foo-user': 404, + 'foo-reviewer': 404, 'foo-editor': 404, 'bar-user': 404, + 'bar-reviewer': 403, 'bar-editor': 204, 'anonymous': 401, + }, + }, + 'toggle-site': { + 'https://foo.com/terms/views/foo-view': { + 'user': 403, 'reviewer': 403, 'editor': 200, + 'example-reviewer': 403, 'example-editor': 200, 'foo-user': 403, + 'foo-reviewer': 403, 'foo-editor': 403, 'bar-user': 403, + 'bar-reviewer': 403, 'bar-editor': 403, 'anonymous': 401, + }, + 'https://bar.com/terms/views/bar-view': { + 'user': 403, 'reviewer': 403, 'editor': 200, + 'example-reviewer': 403, 'example-editor': 200, 'foo-user': 403, + 'foo-reviewer': 403, 'foo-editor': 403, 'bar-user': 403, + 'bar-reviewer': 403, 'bar-editor': 403, 'anonymous': 401, + }, + }, +} + + urlnames['view-toggle-site'] = 'v1-views:view-toggle-site' -@pytest.mark.parametrize('username,password', users) -def test_list(db, client, username, password): - client.login(username=username, password=password) - url = reverse(urlnames['list']) - response = client.get(url) - assert response.status_code == status_map['list'][username], response.json() @pytest.mark.parametrize('username,password', users) @@ -56,7 +109,9 @@ def test_detail(db, client, username, password): for instance in instances: url = reverse(urlnames['detail'], args=[instance.pk]) response = client.get(url) - assert response.status_code == get_obj_perms_status_code(instance, username, 'detail'), response.json() + assert response.status_code == ( + STATUS_CODES['detail'].get(instance.uri, status_map['detail'])[username] + ), response.json() @pytest.mark.parametrize('username,password', users) @@ -98,7 +153,9 @@ def test_update(db, client, username, password): 'help_de': instance.help_lang2 } response = client.put(url, data, content_type='application/json') - assert response.status_code == get_obj_perms_status_code(instance, username, 'update'), response.json() + assert response.status_code == ( + STATUS_CODES['update'].get(instance.uri, status_map['update'])[username] + ), response.json() @pytest.mark.parametrize('username,password', users) @@ -107,12 +164,11 @@ def test_delete(db, client, username, password): instances = View.objects.all() for instance in instances: - editors = list(instance.editors.values_list('domain', flat=True)) url = reverse(urlnames['detail'], args=[instance.pk]) response = client.delete(url) - assert response.status_code == get_obj_perms_status_code( - instance, username, 'delete', editors=editors - ), response.json() + assert response.status_code == STATUS_CODES['delete'].get(instance.uri, status_map['delete'])[username], ( + response.json() + ) @pytest.mark.parametrize('username,password', users) @@ -155,7 +211,9 @@ def test_update_view_toggle_site(db, client, username, password, add_or_remove, url = reverse(urlnames['view-toggle-site'], kwargs={'pk': instance.pk}) response = client.put(url, {}, content_type='application/json') - assert response.status_code == get_obj_perms_status_code(instance, username, 'toggle-site'), response.json() + assert response.status_code == ( + STATUS_CODES['toggle-site'].get(instance.uri, status_map['toggle-site'])[username] + ), response.json() instance.refresh_from_db() after_has_current_site = instance.sites.filter(id=current_site.id).exists() if response.status_code == 200: From 0789c66d7a332fad9657bd9046c322a75657cd7e Mon Sep 17 00:00:00 2001 From: David Wallace Date: Wed, 3 Jun 2026 11:44:35 +0200 Subject: [PATCH 118/131] Add multisite tests for options and optionsets Signed-off-by: David Wallace --- .../tests/test_viewset_options_multisite.py | 82 ++++++++++++++++++- .../test_viewset_optionsets_multisite.py | 33 ++++++++ 2 files changed, 114 insertions(+), 1 deletion(-) diff --git a/rdmo/options/tests/test_viewset_options_multisite.py b/rdmo/options/tests/test_viewset_options_multisite.py index 463c5fa7f0..8b05e2410a 100644 --- a/rdmo/options/tests/test_viewset_options_multisite.py +++ b/rdmo/options/tests/test_viewset_options_multisite.py @@ -2,12 +2,13 @@ import pytest +from django.db.models import Max from django.urls import reverse from rdmo.core.tests.constants import multisite_status_map as status_map from rdmo.core.tests.constants import multisite_users as users -from ..models import Option +from ..models import Option, OptionSet from .test_viewset_options import urlnames STATUS_CODES = { @@ -37,6 +38,20 @@ 'bar-reviewer': 200, 'bar-editor': 200, 'anonymous': 401, }, }, + 'create-with-parent': { + 'https://foo.com/terms/options/foo-optionset': { + 'user': 403, 'reviewer': 403, 'editor': 201, + 'example-reviewer': 403, 'example-editor': 403, 'foo-user': 403, + 'foo-reviewer': 403, 'foo-editor': 403, 'bar-user': 403, + 'bar-reviewer': 403, 'bar-editor': 403, 'anonymous': 401, + }, + 'https://bar.com/terms/options/bar-optionset': { + 'user': 403, 'reviewer': 403, 'editor': 201, + 'example-reviewer': 403, 'example-editor': 403, 'foo-user': 403, + 'foo-reviewer': 403, 'foo-editor': 403, 'bar-user': 403, + 'bar-reviewer': 403, 'bar-editor': 403, 'anonymous': 401, + }, + }, 'update': { 'https://foo.com/terms/options/foo-option-1': { 'user': 404, 'reviewer': 403, 'editor': 200, @@ -157,6 +172,71 @@ def test_create(db, client, username, password): assert response.status_code == status_map['create'][username], response.json() +@pytest.mark.parametrize('username,password', users) +def test_create_optionset(db, client, username, password): + client.login(username=username, password=password) + instances = Option.objects.all() + + for instance in instances: + optionset = instance.optionsets.first() + if optionset is not None: + optionset_options = list(optionset.optionset_options.values_list('option', 'order')) + order = optionset.optionset_options.aggregate(order=Max('order')).get('order') + 1 + + url = reverse(urlnames['list']) + data = { + 'uri_prefix': instance.uri_prefix, + 'uri_path': f'{instance.uri_path}_new_{username}', + 'comment': instance.comment, + 'text_en': instance.text_lang1, + 'text_de': instance.text_lang2, + 'optionsets': [optionset.id] + } + response = client.post(url, data, content_type='application/json') + assert response.status_code == ( + STATUS_CODES['create-with-parent'].get( + optionset.uri, status_map['create-with-parent'] + )[username] + ), response.json() + + if response.status_code == 201: + new_instance = Option.objects.get(id=response.json().get('id')) + optionset.refresh_from_db() + assert [*optionset_options, (new_instance.id, order)] == \ + list(optionset.optionset_options.values_list('option', 'order')) + + +def test_create_optionset_rejects_foreign_site_parent(db, client, sites): + sites.activate('bar.com') + client.login(username='bar-editor', password='bar-editor') + + instance = Option.objects.get(uri_path='foo-option-1') + optionset = OptionSet.objects.get(uri_path='foo-optionset') + + optionset_options = list(optionset.optionset_options.values_list('option', 'order')) + + url = reverse(urlnames['list']) + data = { + 'uri_prefix': 'https://bar.com/terms', + 'uri_path': f'{instance.uri_path}-bar-parent-denied', + 'comment': instance.comment, + 'text_en': instance.text_lang1, + 'text_de': instance.text_lang2, + 'optionsets': [optionset.id] + } + + response = client.post(url, data, content_type='application/json') + + assert response.status_code == ( + STATUS_CODES['create-with-parent'].get( + optionset.uri, status_map['create-with-parent'] + )['bar-editor'] + ), response.json() + + optionset.refresh_from_db() + assert optionset_options == list(optionset.optionset_options.values_list('option', 'order')) + + @pytest.mark.parametrize('username,password', users) def test_update_multisite(db, client, username, password): client.login(username=username, password=password) diff --git a/rdmo/options/tests/test_viewset_optionsets_multisite.py b/rdmo/options/tests/test_viewset_optionsets_multisite.py index db6ced4c36..a66245353b 100644 --- a/rdmo/options/tests/test_viewset_optionsets_multisite.py +++ b/rdmo/options/tests/test_viewset_optionsets_multisite.py @@ -148,6 +148,39 @@ def test_create(db, client, username, password): assert response.status_code == status_map['create'][username], response.json() +@pytest.mark.parametrize('username,password', users) +def test_create_m2m(db, client, username, password): + client.login(username=username, password=password) + instances = OptionSet.objects.all() + + for instance in instances: + optionset_options = [{ + 'option': optionset_option.option.id, + 'order': optionset_option.order + } for optionset_option in instance.optionset_options.all()[:1]] + conditions = [condition.pk for condition in instance.conditions.all()[:1]] + + url = reverse(urlnames['list']) + data = { + 'uri_prefix': instance.uri_prefix, + 'uri_path': f'{instance.uri_path}_new_{username}', + 'comment': instance.comment, + 'order': instance.order, + 'options': optionset_options, + 'conditions': conditions, + } + response = client.post(url, data, content_type='application/json') + assert response.status_code == status_map['create'][username], response.json() + + if response.status_code == 201: + new_instance = OptionSet.objects.get(id=response.json().get('id')) + assert optionset_options == [{ + 'option': optionset_option.option.id, + 'order': optionset_option.order + } for optionset_option in new_instance.optionset_options.all()] + assert conditions == [condition.pk for condition in new_instance.conditions.all()] + + @pytest.mark.parametrize('username,password', users) def test_update_m2m_multisite(db, client, username, password): client.login(username=username, password=password) From 47f8377bc0a34f3130beb043d145b84e4c199ae4 Mon Sep 17 00:00:00 2001 From: David Wallace Date: Wed, 3 Jun 2026 11:52:10 +0200 Subject: [PATCH 119/131] Simplify multisite create-with-parent tests Signed-off-by: David Wallace --- .../tests/test_viewset_options_multisite.py | 7 +-- .../tests/test_viewset_page_multisite.py | 18 ++----- .../tests/test_viewset_question_multisite.py | 50 +++++-------------- .../test_viewset_questionset_multisite.py | 32 +++--------- .../tests/test_viewset_section_multisite.py | 11 ++-- 5 files changed, 32 insertions(+), 86 deletions(-) diff --git a/rdmo/options/tests/test_viewset_options_multisite.py b/rdmo/options/tests/test_viewset_options_multisite.py index 8b05e2410a..32ff66986b 100644 --- a/rdmo/options/tests/test_viewset_options_multisite.py +++ b/rdmo/options/tests/test_viewset_options_multisite.py @@ -210,7 +210,6 @@ def test_create_optionset_rejects_foreign_site_parent(db, client, sites): sites.activate('bar.com') client.login(username='bar-editor', password='bar-editor') - instance = Option.objects.get(uri_path='foo-option-1') optionset = OptionSet.objects.get(uri_path='foo-optionset') optionset_options = list(optionset.optionset_options.values_list('option', 'order')) @@ -218,10 +217,8 @@ def test_create_optionset_rejects_foreign_site_parent(db, client, sites): url = reverse(urlnames['list']) data = { 'uri_prefix': 'https://bar.com/terms', - 'uri_path': f'{instance.uri_path}-bar-parent-denied', - 'comment': instance.comment, - 'text_en': instance.text_lang1, - 'text_de': instance.text_lang2, + 'uri_path': 'bar-option-with-foo-optionset-denied', + 'text_en': 'Bar option with foo optionset denied', 'optionsets': [optionset.id] } diff --git a/rdmo/questions/tests/test_viewset_page_multisite.py b/rdmo/questions/tests/test_viewset_page_multisite.py index c11c26f9e8..ae7e802366 100644 --- a/rdmo/questions/tests/test_viewset_page_multisite.py +++ b/rdmo/questions/tests/test_viewset_page_multisite.py @@ -8,7 +8,7 @@ from rdmo.core.tests.constants import multisite_status_map as status_map from rdmo.core.tests.constants import multisite_users as users -from ..models import Page +from ..models import Page, Section from .test_viewset_page import export_formats, urlnames STATUS_CODES = { @@ -200,24 +200,16 @@ def test_create_section_rejects_foreign_site_parent(db, client, sites): sites.activate('bar.com') client.login(username='bar-editor', password='bar-editor') - instance = Page.objects.get(uri_path='foo-page') - section = instance.sections.get(uri_path='foo-section') + section = Section.objects.get(uri_path='foo-section') section_pages = list(section.section_pages.values_list('page', 'order')) url = reverse(urlnames['list']) data = { 'uri_prefix': 'https://bar.com/terms', - 'uri_path': f'{instance.uri_path}-bar-parent-denied', - 'comment': instance.comment, - 'attribute': instance.attribute.pk if instance.attribute else '', - 'is_collection': instance.is_collection, - 'title_en': instance.title_lang1, - 'title_de': instance.title_lang2, - 'help_en': instance.help_lang1, - 'help_de': instance.help_lang2, - 'verbose_name_en': instance.verbose_name_lang1, - 'verbose_name_de': instance.verbose_name_lang2, + 'uri_path': 'bar-page-with-foo-section-denied', + 'is_collection': False, + 'title_en': 'Bar page with foo section denied', 'sections': [section.id] } diff --git a/rdmo/questions/tests/test_viewset_question_multisite.py b/rdmo/questions/tests/test_viewset_question_multisite.py index 34e8b878c4..80f51cef18 100644 --- a/rdmo/questions/tests/test_viewset_question_multisite.py +++ b/rdmo/questions/tests/test_viewset_question_multisite.py @@ -8,7 +8,7 @@ from rdmo.core.tests.constants import multisite_status_map as status_map from rdmo.core.tests.constants import multisite_users as users -from ..models import Question +from ..models import Page, Question, QuestionSet from .test_viewset_question import export_formats, urlnames STATUS_CODES = { @@ -223,30 +223,18 @@ def test_create_page_rejects_foreign_site_parent(db, client, sites): sites.activate('bar.com') client.login(username='bar-editor', password='bar-editor') - instance = Question.objects.get(uri_path='foo-question') - page = instance.pages.get(uri_path='foo-page') + page = Page.objects.get(uri_path='foo-page') page_questions = list(page.page_questions.values_list('question', 'order')) url = reverse(urlnames['list']) data = { 'uri_prefix': 'https://bar.com/terms', - 'uri_path': f'{instance.uri_path}-bar-parent-denied', - 'comment': instance.comment or '', - 'attribute': instance.attribute.pk if instance.attribute else '', - 'is_collection': instance.is_collection, - 'help_en': instance.help_lang1 or '', - 'help_de': instance.help_lang2 or '', - 'text_en': instance.text_lang1 or '', - 'text_de': instance.text_lang2 or '', - 'verbose_name_en': instance.verbose_name_lang1 or '', - 'verbose_name_de': instance.verbose_name_lang2 or '', - 'widget_type': instance.widget_type, - 'value_type': instance.value_type, - 'minimum': instance.minimum or '', - 'maximum': instance.maximum or '', - 'step': instance.step or '', - 'unit': instance.unit or '', + 'uri_path': 'bar-question-with-foo-page-denied', + 'is_collection': False, + 'text_en': 'Bar question with foo page denied', + 'widget_type': 'text', + 'value_type': 'text', 'pages': [page.id] } @@ -312,30 +300,18 @@ def test_create_questionset_rejects_foreign_site_parent(db, client, sites): sites.activate('bar.com') client.login(username='bar-editor', password='bar-editor') - instance = Question.objects.get(uri_path='foo-question') - questionset = instance.questionsets.get(uri_path='foo-questionset') + questionset = QuestionSet.objects.get(uri_path='foo-questionset') questionset_questions = list(questionset.questionset_questions.values_list('question', 'order')) url = reverse(urlnames['list']) data = { 'uri_prefix': 'https://bar.com/terms', - 'uri_path': f'{instance.uri_path}-bar-parent-denied-questionset', - 'comment': instance.comment or '', - 'attribute': instance.attribute.pk if instance.attribute else '', - 'is_collection': instance.is_collection, - 'help_en': instance.help_lang1 or '', - 'help_de': instance.help_lang2 or '', - 'text_en': instance.text_lang1 or '', - 'text_de': instance.text_lang2 or '', - 'verbose_name_en': instance.verbose_name_lang1 or '', - 'verbose_name_de': instance.verbose_name_lang2 or '', - 'widget_type': instance.widget_type, - 'value_type': instance.value_type, - 'minimum': instance.minimum or '', - 'maximum': instance.maximum or '', - 'step': instance.step or '', - 'unit': instance.unit or '', + 'uri_path': 'bar-question-with-foo-questionset-denied', + 'is_collection': False, + 'text_en': 'Bar question with foo questionset denied', + 'widget_type': 'text', + 'value_type': 'text', 'questionsets': [questionset.id] } diff --git a/rdmo/questions/tests/test_viewset_questionset_multisite.py b/rdmo/questions/tests/test_viewset_questionset_multisite.py index ff4240a80d..c744a8e090 100644 --- a/rdmo/questions/tests/test_viewset_questionset_multisite.py +++ b/rdmo/questions/tests/test_viewset_questionset_multisite.py @@ -8,7 +8,7 @@ from rdmo.core.tests.constants import multisite_status_map as status_map from rdmo.core.tests.constants import multisite_users as users -from ..models import QuestionSet +from ..models import Page, QuestionSet from .test_viewset_questionset import export_formats, urlnames STATUS_CODES = { @@ -286,24 +286,16 @@ def test_create_page_rejects_foreign_site_parent(db, client, sites): sites.activate('bar.com') client.login(username='bar-editor', password='bar-editor') - instance = QuestionSet.objects.get(uri_path='foo-questionset') - page = instance.pages.get(uri_path='foo-page') + page = Page.objects.get(uri_path='foo-page') page_questionsets = list(page.page_questionsets.values_list('questionset', 'order')) url = reverse(urlnames['list']) data = { 'uri_prefix': 'https://bar.com/terms', - 'uri_path': f'{instance.uri_path}-bar-parent-denied', - 'comment': instance.comment, - 'attribute': instance.attribute.pk if instance.attribute else '', - 'is_collection': instance.is_collection, - 'title_en': instance.title_lang1, - 'title_de': instance.title_lang2, - 'help_en': instance.help_lang1, - 'help_de': instance.help_lang2, - 'verbose_name_en': instance.verbose_name_lang1, - 'verbose_name_de': instance.verbose_name_lang2, + 'uri_path': 'bar-questionset-with-foo-page-denied', + 'is_collection': False, + 'title_en': 'Bar questionset with foo page denied', 'pages': [page.id] } @@ -363,7 +355,6 @@ def test_create_parent_rejects_foreign_site_parent(db, client, sites): sites.activate('bar.com') client.login(username='bar-editor', password='bar-editor') - instance = QuestionSet.objects.get(uri_path='foo-questionset') parent = QuestionSet.objects.get(uri_path='foo-questionset-parent') parent_questionsets = list(parent.questionset_questionsets.values_list('questionset', 'order')) @@ -371,16 +362,9 @@ def test_create_parent_rejects_foreign_site_parent(db, client, sites): url = reverse(urlnames['list']) data = { 'uri_prefix': 'https://bar.com/terms', - 'uri_path': f'{instance.uri_path}-bar-parent-denied-parent', - 'comment': instance.comment, - 'attribute': instance.attribute.pk if instance.attribute else '', - 'is_collection': instance.is_collection, - 'title_en': instance.title_lang1, - 'title_de': instance.title_lang2, - 'help_en': instance.help_lang1, - 'help_de': instance.help_lang2, - 'verbose_name_en': instance.verbose_name_lang1, - 'verbose_name_de': instance.verbose_name_lang2, + 'uri_path': 'bar-questionset-with-foo-parent-denied', + 'is_collection': False, + 'title_en': 'Bar questionset with foo parent denied', 'parents': [parent.id] } diff --git a/rdmo/questions/tests/test_viewset_section_multisite.py b/rdmo/questions/tests/test_viewset_section_multisite.py index 51f0977727..6aa2a3fb18 100644 --- a/rdmo/questions/tests/test_viewset_section_multisite.py +++ b/rdmo/questions/tests/test_viewset_section_multisite.py @@ -8,7 +8,7 @@ from rdmo.core.tests.constants import multisite_status_map as status_map from rdmo.core.tests.constants import multisite_users as users -from ..models import Section +from ..models import Catalog, Section from .test_viewset_section import export_formats, urlnames STATUS_CODES = { @@ -202,18 +202,15 @@ def test_create_catalog_rejects_foreign_site_parent(db, client, sites): sites.activate('bar.com') client.login(username='bar-editor', password='bar-editor') - instance = Section.objects.get(uri_path='foo-section') - catalog = instance.catalogs.get(uri_path='foo-catalog') + catalog = Catalog.objects.get(uri_path='foo-catalog') catalog_sections = list(catalog.catalog_sections.values_list('section', 'order')) url = reverse(urlnames['list']) data = { 'uri_prefix': 'https://bar.com/terms', - 'uri_path': f'{instance.uri_path}-bar-parent-denied', - 'comment': instance.comment, - 'title_en': instance.title_lang1, - 'title_de': instance.title_lang2, + 'uri_path': 'bar-section-with-foo-catalog-denied', + 'title_en': 'Bar section with foo catalog denied', 'catalogs': [catalog.id] } From c1f5e32f5f6e4fb5f3c94de524cc56cff9642619 Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Mon, 22 Dec 2025 13:47:51 +0100 Subject: [PATCH 120/131] Update CHANGELOG.md --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e6778018f7..987f15e604 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog 📔 +## [RDMO 2.5.0](https://github.com/rdmorganiser/rdmo/releases/tag/2.5.0) + +**Milestone**: [2.5.0](https://github.com/rdmorganiser/rdmo/milestone/26) + +**Commit history**: [2.4.0...2.5.0](https://github.com/rdmorganiser/rdmo/compare/2.4.0...2.5.0) + ## [RDMO 2.4.4](https://github.com/rdmorganiser/rdmo/releases/tag/2.4.4) (March 26, 2026) ### Bug fixes 🐛 From 8462e9bf598ada19e289a3029a63526c0de5bff6 Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Thu, 8 Jan 2026 11:53:04 +0100 Subject: [PATCH 121/131] Remove unwanted spaces in view tag templates (#556) --- rdmo/views/templates/views/tags/value.html | 6 +----- rdmo/views/templates/views/tags/value_inline_list.html | 4 +--- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/rdmo/views/templates/views/tags/value.html b/rdmo/views/templates/views/tags/value.html index 0e307fa2bd..61f4826ffe 100644 --- a/rdmo/views/templates/views/tags/value.html +++ b/rdmo/views/templates/views/tags/value.html @@ -1,5 +1 @@ -{% if value.file_url %} -{% include 'views/tags/value_file.html' %} -{% else %} -{{ value.value_and_unit }} -{% endif %} +{% if value.file_url %}{% include 'views/tags/value_file.html' %}{% else %}{{ value.value_and_unit }}{% endif %} \ No newline at end of file diff --git a/rdmo/views/templates/views/tags/value_inline_list.html b/rdmo/views/templates/views/tags/value_inline_list.html index b446d846c4..b7ee8b014f 100644 --- a/rdmo/views/templates/views/tags/value_inline_list.html +++ b/rdmo/views/templates/views/tags/value_inline_list.html @@ -1,3 +1 @@ -{% for value in values %} - {{ value.value_and_unit }}{% if not forloop.last %}; {% endif %} -{% endfor %} +{% for value in values %}{{ value.value_and_unit }}{% if not forloop.last %}; {% endif %}{% endfor %} \ No newline at end of file From 8def7d794ac0ebd4c18962f53229b842d7c5feef Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Mon, 19 Jan 2026 11:36:45 +0100 Subject: [PATCH 122/131] Fix project.html test files --- testing/export/project.html | 242 ++++++++++++++++++------------------ 1 file changed, 121 insertions(+), 121 deletions(-) diff --git a/testing/export/project.html b/testing/export/project.html index 956c01e3ce..2094cb854e 100644 --- a/testing/export/project.html +++ b/testing/export/project.html @@ -8,34 +8,34 @@

    Single questions

    Text

    Text?

    -Lorem ipsum dolor sit amet + Lorem ipsum dolor sit amet

    Textarea

    Textarea?

    -Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est. Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est. Lorem ipsum dolor sit amet. + Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est. Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est. Lorem ipsum dolor sit amet.

    Yes or no

    Yes or no?

    -Yes + Yes

    Radio buttons

    Radio buttons?

    -Text: Lorem ipsum + Text: Lorem ipsum

    Select drop-down

    Select drop-down?

    -One + One

    Select drop-down (free)

    Select drop-down (free)?

    Range slider

    Range slider?

    -37 + 37

    File

    File?

    @@ -45,66 +45,66 @@

    File

    Datetime

    Date picker?

    -Jan. 1, 2018 + Jan. 1, 2018

    Collections

    Text

    Text?

    • -Lorem ipsum dolor sit amet, consetetur sadipscing elitr + Lorem ipsum dolor sit amet, consetetur sadipscing elitr
    • -sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua + sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua

    Textarea

    Textarea?

    • -Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est. Lorem ipsum dolor sit amet. + Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est. Lorem ipsum dolor sit amet.
    • -Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est. Lorem ipsum dolor sit amet. + Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est. Lorem ipsum dolor sit amet.

    Yes or no

    Yes or no?

    • -Yes + Yes
    • -No + No
    • -Yes + Yes

    Radio buttons

    Radio buttons?

    • -One + One
    • -Two + Two
    • -Three + Three

    Select drop-down

    Select drop-down?

    • -One + One
    • -Two + Two
    • -Three + Three

    Select drop-down (free)

    @@ -113,26 +113,26 @@

    Range slider

    Range slider?

    • -0 + 0
    • -50 + 50
    • -100 + 100

    Date picker

    Date picker?

    • -April 1, 2017 + April 1, 2017
    • -April 2, 2017 + April 2, 2017
    • -April 3, 2017 + April 3, 2017

    File

    @@ -149,42 +149,42 @@

    Checkbox

    Checkbox?

    • -One + One
    • -Three + Three

    Sets

    Individual sets I

    Text?

    -Lorem ipsum dolor sit amet + Lorem ipsum dolor sit amet

    Textarea?

    -Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est. Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est. Lorem ipsum dolor sit amet. + Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est. Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est. Lorem ipsum dolor sit amet.

    Yes or no?

    -Yes + Yes

    Radio buttons?

    -Text: Lorem ipsum + Text: Lorem ipsum

    Select drop-down?

    -One + One

    Select drop-down (free)?

    Range slider?

    -37 + 37

    Date picker?

    -Jan. 1, 2018 + Jan. 1, 2018

    File?

    @@ -194,80 +194,80 @@

    Individual sets II

    Text?

    • -Lorem ipsum dolor sit amet, consetetur sadipscing elitr + Lorem ipsum dolor sit amet, consetetur sadipscing elitr
    • -sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua + sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua

    Textarea?

    • -Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est. Lorem ipsum dolor sit amet. + Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est. Lorem ipsum dolor sit amet.
    • -Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est. Lorem ipsum dolor sit amet. + Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est. Lorem ipsum dolor sit amet.

    Yes or no?

    • -Yes + Yes
    • -No + No
    • -Yes + Yes

    Radio buttons?

    • -One + One
    • -Two + Two
    • -Three + Three

    Select drop-down?

    • -One + One
    • -Two + Two
    • -Three + Three

    Select drop-down (free)?

    Range slider?

    • -0 + 0
    • -50 + 50
    • -100 + 100

    Date picker?

    • -April 1, 2017 + April 1, 2017
    • -April 2, 2017 + April 2, 2017
    • -April 3, 2017 + April 3, 2017

    File?

    @@ -282,96 +282,96 @@

    Individual sets II

    Checkbox?

    • -One + One
    • -Three + Three

    Set collections I

    Text?

    Set "First":  -Lorem ipsum dolor sit amet, consetetur sadipscing elitr + Lorem ipsum dolor sit amet, consetetur sadipscing elitr

    Set "Second":  -Lorem ipsum dolor sit amet, consetetur sadipscing elitr + Lorem ipsum dolor sit amet, consetetur sadipscing elitr

    Textarea?

    Set "First":  -Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est. Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est. Lorem ipsum dolor sit amet. + Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est. Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est. Lorem ipsum dolor sit amet.

    Set "Second":  -Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est. Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est. Lorem ipsum dolor sit amet. + Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est. Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est. Lorem ipsum dolor sit amet.

    Yes or no?

    Set "First":  -Yes + Yes

    Set "Second":  -No + No

    Radio buttons?

    Set "First":  -One + One

    Set "Second":  -Two + Two

    Select drop-down?

    Set "First":  -One + One

    Set "Second":  -Two + Two

    Select drop-down (free)?

    Range slider?

    Set "First":  -1 + 1

    Set "Second":  -2 + 2

    Date picker?

    Set "First":  -Jan. 7, 2018 + Jan. 7, 2018

    Set "Second":  -Feb. 7, 2018 + Feb. 7, 2018

    File?

    Set collections II

    Text?

    Set "First":  -Lorem ipsum dolor sit amet, consetetur sadipscing elitr + Lorem ipsum dolor sit amet, consetetur sadipscing elitr

    Set "Second":  -Lorem ipsum dolor sit amet, consetetur sadipscing elitr + Lorem ipsum dolor sit amet, consetetur sadipscing elitr

    Textarea?

    Set "First":  -Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est. Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est. Lorem ipsum dolor sit amet. + Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est. Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est. Lorem ipsum dolor sit amet.

    Set "Second":  -Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est. Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est. Lorem ipsum dolor sit amet. + Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est. Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est. Lorem ipsum dolor sit amet.

    Yes or no?

    @@ -379,13 +379,13 @@

    Set collections II

    • -Yes + Yes
    • -No + No
    • -Yes + Yes

    @@ -393,13 +393,13 @@

    Set collections II

    • -No + No
    • -Yes + Yes
    • -No + No

    Radio buttons?

    @@ -408,13 +408,13 @@

    Set collections II

    • -One + One
    • -Two + Two
    • -Three + Three

    @@ -422,13 +422,13 @@

    Set collections II

    • -Three + Three
    • -Two + Two
    • -One + One

    Select drop-down?

    @@ -437,15 +437,15 @@

    Set collections II

    • -One + One
    • -Two + Two

    Set "Second":  -Three + Three

    Select drop-down (free)?

    Range slider?

    @@ -454,15 +454,15 @@

    Set collections II

    • -16 + 16
    • -31 + 31

    Set "Second":  -86 + 86

    Date picker?

    @@ -470,10 +470,10 @@

    Set collections II

    • -Jan. 7, 2018 + Jan. 7, 2018
    • -Feb. 7, 2018 + Feb. 7, 2018

    @@ -481,10 +481,10 @@

    Set collections II

    • -Oct. 7, 2018 + Oct. 7, 2018
    • -Nov. 7, 2018 + Nov. 7, 2018

    File?

    @@ -494,37 +494,37 @@

    Set collections II

    • -Two + Two
    • -Three + Three

    Set "Second":  -One + One

    Conditions

    Input

    Text

    -test + test

    Option

    -One + One

    Text I

    text_contains?

    -test + test

    Text II

    text empty?

    Text III

    text_equal?

    -test + test

    Text IV

    text_greater_than?

    @@ -537,7 +537,7 @@

    Text VII

    Text VIII

    text_not_empty?

    -test + test

    Text IX

    text_not_equal?

    @@ -546,12 +546,12 @@

    Options I

    Options II

    option_equal?

    -One + One

    Options III

    option_not_empty?

    -One + One

    Options IV

    option_not_equal?

    @@ -594,20 +594,20 @@

    A set of questionsets and questions

    A?

    Set "First", Block #1:  -a0 + a0

    Set "First", Block #2:  -a1 + a1

    B?

    Set "First", Block #1:  -b1 + b1

    Set "First", Block #2:  -b1 + b1

    C?

    @@ -615,10 +615,10 @@

    A set of questionsets and questions

    • -c01 + c01
    • -c02 + c02

    @@ -626,40 +626,40 @@

    A set of questionsets and questions

    • -c10 + c10
    • -c11 + c11

    Y?

    Set "First", Block #1, Set #1:  -Three + Three

    Set "First", Block #2, Set #1:  -Three + Three

    Set "Second", Block #1, Set #1:  -Three + Three

    Set "Second", Block #2, Set #1:  -Three + Three

    Set "Second", Block #3, Set #1:  -One + One

    Set "Second", Block #3, Set #2:  -Two + Two

    Set "Second", Block #3, Set #3:  -Three + Three

    Question

    Block with optional questions

    From 4e28053d6a214bd97918faeb480ade224b035dfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Heinz-Alexander=20F=C3=BCtterer?= <35225576+afuetterer@users.noreply.github.com> Date: Tue, 28 Oct 2025 09:34:25 +0100 Subject: [PATCH 123/131] build(deps): bump django to >= 5.2.8 --- pyproject.toml | 12 ++++-------- testing/config/settings/base.py | 11 +++++++++++ 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 5c178e0773..d91f1563ea 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,7 +22,7 @@ requires-python = ">=3.10" classifiers = [ "Development Status :: 5 - Production/Stable", "Environment :: Web Environment", - "Framework :: Django :: 4.2", + "Framework :: Django :: 5.2", "Intended Audience :: Science/Research", "Operating System :: OS Independent", "Programming Language :: Python", @@ -41,7 +41,7 @@ dependencies = [ # in minor version updates anytime "defusedcsv>=2.0,<4.0", "defusedxml>=0.7.1,<1.0", - "django>=4.2,<5.0", + "django>=5.2.8,<6.0", "django-cleanup>=8.0,<10.0", "django-compressor>=4.4,<5.0", "django-extensions>=3.2,<5.0", @@ -205,15 +205,11 @@ markers = [ "performance: marks query-count regression tests", ] filterwarnings = [ - # fail on RemovedInDjango50Warning exception - "error::django.utils.deprecation.RemovedInDjango50Warning", + # throw an error when using methods deprecated in the next django version + "error::django.utils.deprecation.RemovedInNextVersionWarning", # ignore warnings raised by widget_tweaks.py "ignore:'maxsplit' is passed as positional argument", - - # ignore warnings raised from within django itself - # django/core/files/storage/__init__.py - "ignore:django.core.files.storage.get_storage_class is deprecated:django.utils.deprecation.RemovedInDjango51Warning", ] [tool.coverage.run] diff --git a/testing/config/settings/base.py b/testing/config/settings/base.py index f081074c5a..f4d3f82f9e 100644 --- a/testing/config/settings/base.py +++ b/testing/config/settings/base.py @@ -1,4 +1,5 @@ import os +from warnings import filterwarnings from django.utils.translation import gettext_lazy as _ @@ -105,3 +106,13 @@ PROJECT_CONTACT = True PROJECT_CONTACT_RECIPIENTS = ['email@example.com'] + +# Ref: https://adamj.eu/tech/2023/12/07/django-fix-urlfield-assume-scheme-warnings +filterwarnings( + "ignore", "The FORMS_URLFIELD_ASSUME_HTTPS transitional setting is deprecated." +) +# This value will change from False to True in Django 6.0 +# Refs: +# - https://docs.djangoproject.com/en/5.2/ref/settings/#forms-urlfield-assume-https +# - https://docs.djangoproject.com/en/5.2/ref/forms/fields/#django.forms.URLField.assume_scheme +FORMS_URLFIELD_ASSUME_HTTPS = True From f9afca6a9d297dfd692262756ad86ba6566d299b Mon Sep 17 00:00:00 2001 From: David Wallace Date: Fri, 16 Jan 2026 14:18:50 +0100 Subject: [PATCH 124/131] tests: make utils parse date test compatible with python 3.14 Signed-off-by: David Wallace --- rdmo/core/tests/test_utils.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/rdmo/core/tests/test_utils.py b/rdmo/core/tests/test_utils.py index 5943f6c940..7ebd02d7cf 100644 --- a/rdmo/core/tests/test_utils.py +++ b/rdmo/core/tests/test_utils.py @@ -42,7 +42,11 @@ ] invalid_date_strings = [ - ("2025-02-31","day is out of range for month"), + ("2025-02-31", ( + "day is out of range for month", # Python 3.10 + "day 31 must be in range 1..28 for month 2 in year 2025" # Python 3.14 + ) + ), ("2025-17-02", "month must be in 1..12"), ("99/99/9999", "Invalid date format"), ("abcd-ef-gh", "Invalid date format"), @@ -91,11 +95,14 @@ def test_parse_date_from_string_valid_formats(settings, locale, date_string, exp @pytest.mark.parametrize("invalid_date, error_msg", invalid_date_strings) def test_parse_date_from_string_invalid_formats(settings, invalid_date, error_msg): - if not isinstance(invalid_date,str): - with pytest.raises(TypeError, match=error_msg): + patterns = error_msg if isinstance(error_msg, (tuple, list)) else (error_msg,) + match = "|".join(f"(?:{pattern})" for pattern in patterns) + + if not isinstance(invalid_date, str): + with pytest.raises(TypeError, match=match): parse_date_from_string(invalid_date) else: - with pytest.raises(ValueError,match=error_msg): + with pytest.raises(ValueError, match=match): parse_date_from_string(invalid_date) From 18b6174d2c9a742a9cb9ec4153455edb3e22e51f Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Thu, 8 Jan 2026 17:20:14 +0100 Subject: [PATCH 125/131] Add MESSAGE_STORAGE to settings --- rdmo/core/settings.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rdmo/core/settings.py b/rdmo/core/settings.py index 14bb9920d3..f1e40ee556 100644 --- a/rdmo/core/settings.py +++ b/rdmo/core/settings.py @@ -75,6 +75,8 @@ }, ] +MESSAGE_STORAGE = "django.contrib.messages.storage.session.SessionStorage" + COMPRESS_PRECOMPILERS = ( ('text/x-scss', 'django_libsass.SassCompiler'), ) From 884c0e41697fe5b97ef67a87cdc69d63fb2b8927 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Heinz-Alexander=20F=C3=BCtterer?= <35225576+afuetterer@users.noreply.github.com> Date: Wed, 21 Jan 2026 13:04:39 +0100 Subject: [PATCH 126/131] feat: add support for python 3.14 --- .github/workflows/ci.yml | 10 +++++----- pyproject.toml | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a795ba2d22..39cf154d34 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -56,7 +56,7 @@ jobs: runs-on: ubuntu-24.04 strategy: matrix: - python-version: ['3.10', '3.13'] + python-version: ['3.10', '3.14'] db-backend: [mysql, postgres] steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -96,7 +96,7 @@ jobs: - name: Run package status tests first run: | pytest rdmo/core/tests/test_package_status.py --nomigrations --verbose - if: matrix.python-version == '3.13' && matrix.db-backend == 'postgres' + if: matrix.python-version == '3.14' && matrix.db-backend == 'postgres' - name: Run Tests run: | pytest -p randomly -p no:cacheprovider --cov --reuse-db --numprocesses=auto --dist=loadscope @@ -116,7 +116,7 @@ jobs: runs-on: ubuntu-24.04 strategy: matrix: - python-version: ['3.13'] + python-version: ['3.14'] db-backend: [postgres] steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -186,7 +186,7 @@ jobs: persist-credentials: false - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: - python-version: "3.13" + python-version: "3.14" cache: pip - run: python -Im pip install --editable .[dev] - run: python -Ic 'import rdmo; print(rdmo.__version__)' @@ -201,7 +201,7 @@ jobs: persist-credentials: false - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: - python-version: "3.13" + python-version: "3.14" cache: pip - name: Download package uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 diff --git a/pyproject.toml b/pyproject.toml index d91f1563ea..e2ce226497 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,6 +31,7 @@ classifiers = [ "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", ] dynamic = [ "version", From 85afef3502dd796eeb0e1899d546b124d78835a1 Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Fri, 16 Jan 2026 13:18:35 +0100 Subject: [PATCH 127/131] Overwrite static tag to include version --- rdmo/core/templatetags/static.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 rdmo/core/templatetags/static.py diff --git a/rdmo/core/templatetags/static.py b/rdmo/core/templatetags/static.py new file mode 100644 index 0000000000..ea9454e647 --- /dev/null +++ b/rdmo/core/templatetags/static.py @@ -0,0 +1,23 @@ +from django import template +from django.conf import settings +from django.templatetags.static import static as django_static + +from rdmo import __version__ + +register = template.Library() + +@register.simple_tag +def static(path): + """ + Custom static tag that appends ?v={version} to all urls created with `{% static ... %}`. + This overwrites the original django.templatetags.static.static. + """ + static_url = django_static(path) + + if settings.DEBUG: + return static_url + else: + if '?' in static_url: + return f'{static_url}&v={__version__}' + else: + return f'{static_url}?v={__version__}' From 2d18928c8d627cfd7c981035ff918ae1fd3bf298 Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Fri, 16 Jan 2026 13:50:49 +0100 Subject: [PATCH 128/131] Use custom storage instead of static tag --- rdmo/core/settings.py | 2 ++ rdmo/core/storage.py | 18 ++++++++++++++++++ rdmo/core/templatetags/static.py | 23 ----------------------- 3 files changed, 20 insertions(+), 23 deletions(-) create mode 100644 rdmo/core/storage.py delete mode 100644 rdmo/core/templatetags/static.py diff --git a/rdmo/core/settings.py b/rdmo/core/settings.py index f1e40ee556..d0827534a1 100644 --- a/rdmo/core/settings.py +++ b/rdmo/core/settings.py @@ -165,6 +165,8 @@ 'compressor.finders.CompressorFinder', ) +STATICFILES_STORAGE = 'rdmo.core.storage.VersionedStaticFilesStorage' + DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', diff --git a/rdmo/core/storage.py b/rdmo/core/storage.py new file mode 100644 index 0000000000..6039f1c8d4 --- /dev/null +++ b/rdmo/core/storage.py @@ -0,0 +1,18 @@ +from django.conf import settings +from django.contrib.staticfiles.storage import StaticFilesStorage + +from rdmo import __version__ + + +class VersionedStaticFilesStorage(StaticFilesStorage): + + def url(self, name): + url = super().url(name) + + if settings.DEBUG: + return url + else: + if '?' in url: + return f'{url}&v={__version__}' + else: + return f'{url}?v={__version__}' diff --git a/rdmo/core/templatetags/static.py b/rdmo/core/templatetags/static.py deleted file mode 100644 index ea9454e647..0000000000 --- a/rdmo/core/templatetags/static.py +++ /dev/null @@ -1,23 +0,0 @@ -from django import template -from django.conf import settings -from django.templatetags.static import static as django_static - -from rdmo import __version__ - -register = template.Library() - -@register.simple_tag -def static(path): - """ - Custom static tag that appends ?v={version} to all urls created with `{% static ... %}`. - This overwrites the original django.templatetags.static.static. - """ - static_url = django_static(path) - - if settings.DEBUG: - return static_url - else: - if '?' in static_url: - return f'{static_url}&v={__version__}' - else: - return f'{static_url}?v={__version__}' From 8c2811bf0f92c02968387ab524d460b186cdb9c3 Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Thu, 8 Jan 2026 12:29:29 +0100 Subject: [PATCH 129/131] Add nh3 to clean markdown content (#453) --- pyproject.toml | 1 + rdmo/core/settings.py | 3 +++ rdmo/core/utils.py | 7 ++++++- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index e2ce226497..4682f43f9f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -57,6 +57,7 @@ dependencies = [ "drf-extensions>=0.7.1,<1.0", "iso8601>=2.0,<3.0", "markdown>=3.4,<4.0", + "nh3>=0.3<1.0", "packaging>=23.2,<27.0", "pypandoc>=1.11,<2.0", "requests-toolbelt>=1.0,<2.0", diff --git a/rdmo/core/settings.py b/rdmo/core/settings.py index d0827534a1..4e38903dfd 100644 --- a/rdmo/core/settings.py +++ b/rdmo/core/settings.py @@ -335,6 +335,9 @@ # for example: 'not_empty': 'core/text_blocks/template_for_not_empty.html', } +MARKDOWN_CLEAN = True +MARKDOWN_CLEAN_KWARGS = {} # see https://nh3.readthedocs.io for available kwargs + PROJECT_TABLE_PAGE_SIZE = 20 PROJECT_VISIBILITY = True diff --git a/rdmo/core/utils.py b/rdmo/core/utils.py index d198111ca8..a287f54899 100644 --- a/rdmo/core/utils.py +++ b/rdmo/core/utils.py @@ -15,6 +15,7 @@ from django.utils.formats import get_format from django.utils.translation import gettext_lazy as _ +import nh3 from defusedcsv import csv from markdown import markdown @@ -253,7 +254,11 @@ def markdown2html(markdown_string): # textblocks (e.g. for help texts) can be injected into free text fields as small templates via Markdown html = inject_textblocks(html) - return html + if settings.MARKDOWN_CLEAN: + # use nh3/ammonia to clean the html string + return nh3.clean(html, **settings.MARKDOWN_CLEAN_KWARGS) + else: + return html def inject_textblocks(html): From e43641a77aa7aca419baa8b1ad40681daab19a51 Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Tue, 2 Jun 2026 17:15:19 +0200 Subject: [PATCH 130/131] Disable MARKDOWN_CLEAN by default --- rdmo/core/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rdmo/core/settings.py b/rdmo/core/settings.py index 4e38903dfd..788a9ba816 100644 --- a/rdmo/core/settings.py +++ b/rdmo/core/settings.py @@ -335,7 +335,7 @@ # for example: 'not_empty': 'core/text_blocks/template_for_not_empty.html', } -MARKDOWN_CLEAN = True +MARKDOWN_CLEAN = False MARKDOWN_CLEAN_KWARGS = {} # see https://nh3.readthedocs.io for available kwargs PROJECT_TABLE_PAGE_SIZE = 20 From a351d2a8d157b0a96142eac16af7eac4d0d2c294 Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Mon, 8 Jun 2026 11:33:43 +0200 Subject: [PATCH 131/131] Fix pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 4682f43f9f..ae610fcbf1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -57,7 +57,7 @@ dependencies = [ "drf-extensions>=0.7.1,<1.0", "iso8601>=2.0,<3.0", "markdown>=3.4,<4.0", - "nh3>=0.3<1.0", + "nh3>=0.3,<1.0", "packaging>=23.2,<27.0", "pypandoc>=1.11,<2.0", "requests-toolbelt>=1.0,<2.0",