Repository: obsidian-tasks-group/obsidian-tasks
Pull request #2418 (2023-11-17 15:59:13)

feat: Add support for case-insensitive queries
This PR makes the queries case insensitive.

# Description

I made the regex case insensitive and for places where simple comparisons were made, I simply lower-cased the strings. 
As explained in #1612 by @claremacrae.

I just didn't found the file to put the changes for the release, please let me know in the comments where I can find it :smile: 

## Motivation and Context
Fixes: #1612  
Fixes: #1503

## How has this been tested?

For the tests, I duplicated all the query line to test them in the uppercase format.

## Screenshots (if appropriate)

## Types of changes

<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->

Changes visible to users:

- [ ] **Bug fix** (prefix: `fix` - non-breaking change which fixes an issue)
- [x] **New feature** (prefix: `feat` - non-breaking change which adds functionality)
- [ ] **Breaking change** (prefix: `feat!!` or `fix!!` - fix or feature that would cause existing functionality to not work as expected)
- [x] **Documentation** (prefix: `docs` - improvements to any documentation content **for users**)
- [ ] **Sample vault** (prefix: `vault` - improvements to the [Tasks-Demo sample vault](https://github.com/obsidian-tasks-group/obsidian-tasks/tree/main/resources/sample_vaults/Tasks-Demo))
- [ ] **Contributing Guidelines** (prefix: `contrib` - any improvements to documentation content **for contributors** - see [Contributing to Tasks](https://publish.obsidian.md/tasks-contributing/))

Internal changes:

- [ ] **Refactor** (prefix: `refactor` - non-breaking change which only improves the design or structure of existing code, and making no changes to its external behaviour)
- [x] **Tests** (prefix: `test` - additions and improvements to unit tests and the smoke tests)
- [ ] **Infrastructure** (prefix: `chore` - examples include GitHub Actions, issue templates)

## Checklist

<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->

- [x] My code follows the code style of this project and passes `yarn run lint`.
- [x] My change requires a change to the documentation.
- [x] I have [updated the documentation](https://publish.obsidian.md/tasks-contributing/Documentation/About+Documentation) accordingly.
- [x] My change has adequate [Unit Test coverage](https://publish.obsidian.md/tasks-contributing/Testing/About+Testing).

## Terms

<!--
By submitting this pull request, you must agree to follow our
[contributing guide](https://publish.obsidian.md/tasks-contributing) and
[Code of Conduct](https://github.com/obsidian-tasks-group/obsidian-tasks/blob/main/CODE_OF_CONDUCT.md).
Put an x in the boxes to confirm you agree.
-->

- [x] My contribution follow this project's [contributing guide](https://publish.obsidian.md/tasks-contributing)
- [x] I agree to follow this project's [Code of Conduct](https://github.com/obsidian-tasks-group/obsidian-tasks/blob/main/CODE_OF_CONDUCT.md)



Patch:
diff --git a/contributing/Code/How do I add a new field to the Task class.md b/contributing/Code/How do I add a new field to the Task class.md
index 2aa2f3ab..b851c5b1 100644
--- a/contributing/Code/How do I add a new field to the Task class.md
+++ b/contributing/Code/How do I add a new field to the Task class.md
@@ -4,6 +4,9 @@
 
 The many steps below can be split over several PRs, to make work - and code review - manageable.
 
+> [!Tip]
+> Search for `NEW_TASK_FIELD_EDIT_REQUIRED` through the code to find locations that are likely to need edits.
+
 ## Releasing a partial implementation
 
 For example, It's fine to have a first release of a feature without `sort by` and `group by`. In this case, add a feature request issue for the missing capabilities, and note in the documentation, for example:
diff --git a/contributing/Code/How do I add a new task filter.md b/contributing/Code/How do I add a new task filter.md
index 4cf14481..30868d78 100644
--- a/contributing/Code/How do I add a new task filter.md
+++ b/contributing/Code/How do I add a new task filter.md
@@ -2,6 +2,9 @@
 
 All the following steps would be done in the same branch, for inclusion in the same pull request.
 
+> [!Tip]
+> Search for `NEW_QUERY_INSTRUCTION_EDIT_REQUIRED` through the code to find locations that are likely to need edits.
+
 ## Update src/
 
 - Implement the search filter:
diff --git a/contributing/Code/How does Tasks handle status changes.md b/contributing/Code/How does Tasks handle status changes.md
index b39325a1..6909ecce 100644
--- a/contributing/Code/How does Tasks handle status changes.md
+++ b/contributing/Code/How does Tasks handle status changes.md
@@ -2,12 +2,12 @@
 
 You can toggle a task‘s status by:
 
-| #   | User mechanism                                                                    | Source-code location                                                                                                                                                                                                                                | Toggle behaviour                                                                                                                                                                                 |
-| --- | --------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
-| 1   | using the command (may be bound to a hotkey).                                     | [src/Commands/ToggleDone.ts](https://github.com/obsidian-tasks-group/obsidian-tasks/blob/main/src/Commands/ToggleDone.ts)                                                                                                                           | toggles the line directly where the cursor is in the file inside Obsidian's vault.                                                                                                               |
-| 2   | clicking on a checkbox of an inline task in Live Preview.                         | [src/LivePreviewExtension.ts](https://github.com/obsidian-tasks-group/obsidian-tasks/blob/main/src/LivePreviewExtension.ts)                                                                                                                         | toggles the line directly where the checkbox is on the "document" of CodeMirror (the library that Obsidian uses to show text on screen).<br>That, in turn, updates the file in Obsidian's Vault. |
-| 3   | clicking on a checkbox of an inline task in Reading mode.                         | uses a checkbox created by `TaskLineRenderer.renderTaskLine`.<br>There, the checkbox gets a click event handler.                                                                                                                                    | The click event listener of 3. and 4. uses `File::replaceTaskWithTasks()`.<br>That, in turn, updates the file in Obsidian‘s Vault (like 1, but it needs to find the correct line).               |
-| 4   | clicking on a checkbox in query results (same for Reading mode and Live Preview). | As 3                                                                                                                                                                                                                                                | As 3                                                                                                                                                                                             |
-| 5   | via 'Create or edit task' modal Status dropdown                                   | [src/ui/EditTask.svelte](https://github.com/obsidian-tasks-group/obsidian-tasks/blob/main/src/ui/EditTask.svelte) and [src/Commands/CreateOrEdit.ts](https://github.com/obsidian-tasks-group/obsidian-tasks/blob/main/src/Commands/CreateOrEdit.ts) | Not yet implemented: see [#1590](https://github.com/obsidian-tasks-group/obsidian-tasks/issues/1590)                                                                                             |
+| #   | User mechanism                                                                                | Source-code location                                                                                                                                                                                                                                | Toggle behaviour                                                                                                                                                                                                                                                            |
+| --- | --------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| 1   | using the **command** (may be bound to a hotkey).                                             | [src/Commands/ToggleDone.ts](https://github.com/obsidian-tasks-group/obsidian-tasks/blob/main/src/Commands/ToggleDone.ts)                                                                                                                           | toggles the line directly where the cursor is in the file inside Obsidian's vault.                                                                                                                                                                                          |
+| 2   | clicking on a checkbox of an **inline task** in **Live Preview**.                             | [src/LivePreviewExtension.ts](https://github.com/obsidian-tasks-group/obsidian-tasks/blob/main/src/LivePreviewExtension.ts)                                                                                                                         | toggles the line directly where the checkbox is on the "document" of CodeMirror (the library that Obsidian uses to show text on screen).<br>That, in turn, updates the file in Obsidian's Vault.                                                                            |
+| 3   | clicking on a checkbox of an **inline task** in **Reading mode**.                             | uses a checkbox created by `renderTaskLine()` in [src/TaskLineRenderer](https://github.com/obsidian-tasks-group/obsidian-tasks/blob/main/src/TaskLineRenderer.ts).<br>There, the checkbox gets a click event handler.                               | The click event listener of 3. and 4. uses `replaceTaskWithTasks()` in [src/File.ts](https://github.com/obsidian-tasks-group/obsidian-tasks/blob/main/src/File.ts).<br>That, in turn, updates the file in Obsidian‘s Vault (like 1, but it needs to find the correct line). |
+| 4   | clicking on a checkbox in **query results** (same for **Reading mode** and **Live Preview**). | As 3                                                                                                                                                                                                                                                | As 3                                                                                                                                                                                                                                                                        |
+| 5   | via 'Create or edit task' **modal** Status dropdown                                           | [src/ui/EditTask.svelte](https://github.com/obsidian-tasks-group/obsidian-tasks/blob/main/src/ui/EditTask.svelte) and [src/Commands/CreateOrEdit.ts](https://github.com/obsidian-tasks-group/obsidian-tasks/blob/main/src/Commands/CreateOrEdit.ts) | Not yet implemented: see [#1590](https://github.com/obsidian-tasks-group/obsidian-tasks/issues/1590)                                                                                                                                                                        |
 
 Obsidian writes the changes to disk at its own pace.
diff --git a/docs/Advanced/Styling.md b/docs/Advanced/Styling.md
index 50cb2af0..e37f3196 100644
--- a/docs/Advanced/Styling.md
+++ b/docs/Advanced/Styling.md
@@ -33,6 +33,8 @@ The following description relates to a restructuring of the rendered tasks that
 
 The Tasks plugin renders a task in the following structure (this refers to query results, but the Reading View is the same except the top-most containers):
 
+<!-- NEW_TASK_FIELD_EDIT_REQUIRED -->
+
 ```markdown
 - Obsidian code block (div class="block-language-tasks")
   - Results list (ul class="plugin-tasks-query-result") OR Reading View list (ul class="contains-task-list")
@@ -69,6 +71,8 @@ Data attributes were introduced in Tasks 3.0.0.
 Each rendered task component (description, priority, recurrence rule etc) includes a **generic class** that denotes this type of component.
 The generic classes are:
 
+<!-- NEW_TASK_FIELD_EDIT_REQUIRED -->
+
 - `task-description`
 - `task-priority`
 - `task-due`
@@ -83,6 +87,7 @@ In addition to the generic classes, there are [**data attributes**](https://deve
 A **priority data attributes** named `data-task-priority` represents the specific priority of a class. It can be `highest`, `high`, `medium`, `low`, `lowest` or `normal`.
 The `normal` value is special: it is added as a default to a task's upper `task-list-item` even if there is no priority field set for that task.
 
+<!-- NEW_TASK_FIELD_EDIT_REQUIRED -->
 A **date attribute** represents a due, created, start, scheduled or done date in a format relative to the current date.
 The date attributes are `data-task-due`, `data-task-created`, `data-task-start`, `data-task-scheduled` and `data-task-done` and are populated with a relative expression that denotes the number of days this field represents compared to today:
 
diff --git a/docs/Getting Started/Dates.md b/docs/Getting Started/Dates.md
index f18e003c..84187749 100644
--- a/docs/Getting Started/Dates.md
+++ b/docs/Getting Started/Dates.md
@@ -20,6 +20,8 @@ You can find out more in [[Create or edit Task|‘Create or edit Task’ Modal]]
 > [!info]
 > If you prefer to type, it is now very easy to add emojis and other information for you tasks using [[Auto-Suggest|Intelligent Auto-Suggest]].
 
+<!-- NEW_TASK_FIELD_EDIT_REQUIRED -->
+
 ---
 
 ### Due date
diff --git a/docs/How To/Find tasks with invalid data.md b/docs/How To/Find tasks with invalid data.md
index 15c05363..7f32d581 100644
--- a/docs/How To/Find tasks with invalid data.md
+++ b/docs/How To/Find tasks with invalid data.md
@@ -28,6 +28,8 @@ Tasks sees as an un-dated task with the text description:
 
 The following tasks block lists any tasks that have emoji in the description, which usually means data that has not been interpreted by Tasks. This is typically because there is some text other than emoji signifiers and tags at the end of the line.
 
+<!-- NEW_TASK_FIELD_EDIT_REQUIRED -->
+
 ````text
 ```tasks
 # These description instructions need to be all on one line:
@@ -56,6 +58,8 @@ This task has an illegal due date:
 
 The following tasks block lists any tasks with invalid dates, meaning data that has potentially not been interpreted by Tasks.
 
+<!-- NEW_QUERY_INSTRUCTION_EDIT_REQUIRED if the new query is for a new date field -->
+
 ````text
 ```tasks
 (done date is invalid) OR (due date is invalid) OR (scheduled date is invalid) OR (start date is invalid)
diff --git a/docs/Queries/About Queries.md b/docs/Queries/About Queries.md
index 136c547a..45f54e7b 100644
--- a/docs/Queries/About Queries.md
+++ b/docs/Queries/About Queries.md
@@ -22,6 +22,25 @@ In Live Preview and Reading modes, this will list *all* tasks from your vault, r
 
 This is probably not what you want.
 Therefore, Tasks allows you to set query options to filter the tasks that you want to show.
+*All the queries are case insensitive.*
+
+For instance, you can show only the tasks (from anywhere in the vault) that are due today:
+
+    ## Due today
+    ```tasks
+    due today
+    not done
+    ```
+
+You can create as many task queries as you like, and you can also wrap them into [callouts](https://help.obsidian.md/Editing+and+formatting/Callouts) if you want to style them differently:
+
+    > [!check] Due today
+    > ```tasks
+    > due today
+    > not done
+    > ```
+
+In the following sections we will explain all the various options that are available for querying tasks.
 
 ## Tasks Query options
 
diff --git a/docs/Queries/Filters.md b/docs/Queries/Filters.md
index 94bf6be6..d5c927f7 100644
--- a/docs/Queries/Filters.md
+++ b/docs/Queries/Filters.md
@@ -6,6 +6,8 @@ publish: true
 
 <span class="related-pages">#feature/filters</span>
 
+<!-- NEW_QUERY_INSTRUCTION_EDIT_REQUIRED -->
+
 ## Contents
 
 This page is long. Here are some links to the main sections:
@@ -1122,7 +1124,7 @@ Note that the path includes the `.md` extension.
 - `path (includes|does not include) <path>`
   - Matches case-insensitive (disregards capitalization).
   - Use `{{query.file.path}}` or `{{query.file.pathWithoutExtension}}` as a placeholder for the path of the file containing the current query.
-    - For example, `path equals {{query.file.path}}`
+    - For example, `path includes {{query.file.path}}`
     - Useful reading: [[Query Properties]] and [[Placeholders]]
 - `path (regex matches|regex does not match) /<JavaScript-style Regex>/`
   - Does regular expression match (case-sensitive by default).
diff --git a/docs/Queries/Grouping.md b/docs/Queries/Grouping.md
index 33a128e3..6d1cbd70 100644
--- a/docs/Queries/Grouping.md
+++ b/docs/Queries/Grouping.md
@@ -6,6 +6,8 @@ publish: true
 
 <span class="related-pages">#feature/grouping</span>
 
+<!-- NEW_QUERY_INSTRUCTION_EDIT_REQUIRED -->
+
 ## Contents
 
 This page is long. Here are some links to the main sections:
diff --git a/docs/Queries/Layout.md b/docs/Queries/Layout.md
index 0c84ea0a..c4942e59 100644
--- a/docs/Queries/Layout.md
+++ b/docs/Queries/Layout.md
@@ -11,6 +11,8 @@ together with the name of the element.
 
 The following elements exist:
 
+<!-- NEW_QUERY_INSTRUCTION_EDIT_REQUIRED -->
+
 - `edit button`
 - `backlink`
 - `urgency`
diff --git a/docs/Queries/Sorting.md b/docs/Queries/Sorting.md
index dbc432c1..99651f23 100644
--- a/docs/Queries/Sorting.md
+++ b/docs/Queries/Sorting.md
@@ -4,6 +4,8 @@ publish: true
 
 # Sorting
 
+<!-- NEW_QUERY_INSTRUCTION_EDIT_REQUIRED -->
+
 ## Contents
 
 This page is long. Here are some links to the main sections:
diff --git a/docs/Quick Reference.md b/docs/Quick Reference.md
index 3e8b04e9..d42a1502 100644
--- a/docs/Quick Reference.md
+++ b/docs/Quick Reference.md
@@ -8,6 +8,8 @@ aliases:
 
 This table summarizes the filters and other options available inside a `tasks` block.
 
+<!-- NEW_QUERY_INSTRUCTION_EDIT_REQUIRED -->
+
 | [[Filters]]                                                                                                                                                                                                                                                                                                                                           | [[Sorting\|Sort]]                           | [[Grouping\|Group]]    | [[Layout\|Display]]    | [[About Scripting\|Scripting]]                                                                                                 |
 | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------- | ---------------------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
 | **[[Filters#Filters for Dates in Tasks\|Status]]**                                                                                                                                                                                                                                                                                                    |                                             |                        |                        |                                                                                                                                |
diff --git a/docs/Reference/Task Formats/Dataview Format.md b/docs/Reference/Task Formats/Dataview Format.md
index d9beb81f..fc2c8ed8 100644
--- a/docs/Reference/Task Formats/Dataview Format.md
+++ b/docs/Reference/Task Formats/Dataview Format.md
@@ -83,6 +83,8 @@ These samples demonstrate all the fields supported by the Tasks plugin's parsing
 >
 > Tasks also reads dataview fields in parentheses: `(...)`.
 
+<!-- NEW_TASK_FIELD_EDIT_REQUIRED -->
+
 ### Dataview Format for Dates
 
 These names agree with the same fields in [dataview's documentation](https://blacksmithgu.github.io/obsidian-dataview/annotation/metadata-tasks/#field-shorthands).
diff --git a/docs/Reference/Task Formats/Tasks Emoji Format.md b/docs/Reference/Task Formats/Tasks Emoji Format.md
index b599b277..adc5a146 100644
--- a/docs/Reference/Task Formats/Tasks Emoji Format.md
+++ b/docs/Reference/Task Formats/Tasks Emoji Format.md
@@ -8,6 +8,8 @@ publish: true
 
 These samples demonstrate all the fields supported by the Tasks plugin's parsing of its own emoji signifiers.
 
+<!-- NEW_TASK_FIELD_EDIT_REQUIRED -->
+
 ## Tasks Emoji Format for Dates
 
 ```markdown
diff --git a/docs/Scripting/Task Properties.md b/docs/Scripting/Task Properties.md
index e0f8e18f..248035ea 100644
--- a/docs/Scripting/Task Properties.md
+++ b/docs/Scripting/Task Properties.md
@@ -18,6 +18,8 @@ In a growing number of locations, Tasks allows programmatic/scripting access to
 
 This page documents all the available pieces of information in Tasks that you can access.
 
+<!-- NEW_TASK_FIELD_EDIT_REQUIRED - Add a note in the relevant bullet list, to record when the new field was added. (No need to add a new table row manually here.) -->
+
 ## Values for Task Statuses
 
 For more information, including adding your own customised statuses, see [[Statuses]].
diff --git a/manifest.json b/manifest.json
index 5eb5db2d..175d95b1 100644
--- a/manifest.json
+++ b/manifest.json
@@ -1,7 +1,7 @@
 {
   "id": "obsidian-tasks-plugin",
   "name": "Tasks",
-  "version": "5.1.0",
+  "version": "5.1.1",
   "minAppVersion": "1.1.1",
   "description": "Task management for Obsidian",
   "author": "Martin Schenck and Clare Macrae",
diff --git a/package.json b/package.json
index 4947b7ae..9e5f3b3e 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "obsidian-tasks",
-  "version": "5.1.0",
+  "version": "5.1.1",
   "description": "Task management for the Obsidian knowledge base",
   "main": "main.js",
   "engines": {
@@ -34,7 +34,7 @@
     "@testing-library/svelte": "^3.2.2",
     "@tsconfig/svelte": "^3.0.0",
     "@types/jest": "^29.5.8",
-    "@types/mustache": "^4.2.2",
+    "@types/mustache": "^4.2.5",
     "@typescript-eslint/eslint-plugin": "^5.59.8",
     "@typescript-eslint/parser": "^5.59.8",
     "approvals": "^6.2.4",
diff --git a/src/Commands/CreateOrEditTaskParser.ts b/src/Commands/CreateOrEditTaskParser.ts
index d56e513f..cbddd90b 100644
--- a/src/Commands/CreateOrEditTaskParser.ts
+++ b/src/Commands/CreateOrEditTaskParser.ts
@@ -74,6 +74,7 @@ export const taskFromLine = ({ line, path }: { line: string; path: string }): Ta
         console.error('Tasks: Cannot create task on line:', line);
 
         return new Task({
+            // NEW_TASK_FIELD_EDIT_REQUIRED
             status: Status.TODO,
             description: '',
             // We don't need the location fields except file to edit here in the editor.
@@ -109,6 +110,7 @@ export const taskFromLine = ({ line, path }: { line: string; path: string }): Ta
     }
 
     return new Task({
+        // NEW_TASK_FIELD_EDIT_REQUIRED
         status,
         description,
         // We don't need the location fields except file to edit here in the editor.
diff --git a/src/InlineRenderer.ts b/src/InlineRenderer.ts
index 6405293f..75941898 100644
--- a/src/InlineRenderer.ts
+++ b/src/InlineRenderer.ts
@@ -2,7 +2,8 @@ import type { MarkdownPostProcessorContext, Plugin } from 'obsidian';
 import { MarkdownRenderChild } from 'obsidian';
 import { GlobalFilter } from './Config/GlobalFilter';
 import { Task } from './Task';
-import { taskToLi } from './TaskLineRenderer';
+import { LayoutOptions } from './TaskLayout';
+import { TaskLineRenderer } from './TaskLineRenderer';
 import { TaskLocation } from './TaskLocation';
 
 export class InlineRenderer {
@@ -86,25 +87,26 @@ export class InlineRenderer {
             }
         }
 
+        const taskLineRenderer = new TaskLineRenderer({
+            obsidianComponent: childComponent,
+            parentUlElement: element,
+            layoutOptions: new LayoutOptions(),
+        });
+
         // The section index is the nth task within this section.
         for (let sectionIndex = 0; sectionIndex < renderedElements.length; sectionIndex++) {
             const task = fileTasks[sectionIndex];
-            const renderedElement = renderedElements[sectionIndex];
 
+            const renderedElement = renderedElements[sectionIndex];
             if (task === undefined || renderedElement === undefined) {
                 // Assuming match of tasks in file and render preview.
                 // If there is a mis-match in the numbers, we still process
                 // what we can.
                 continue;
             }
-
             const dataLine: string = renderedElement.getAttr('data-line') ?? '0';
-            const listIndex: number = Number.parseInt(dataLine, 10);
-            const taskElement = await taskToLi(task, {
-                parentUlElement: element,
-                listIndex,
-                obsidianComponent: childComponent,
-            });
+            const taskIndex: number = Number.parseInt(dataLine, 10);
+            const taskElement = await taskLineRenderer.renderTaskLine(task, taskIndex);
 
             // If the rendered element contains a sub-list or sub-div (e.g. the
             // folding arrow), we need to keep it.
diff --git a/src/Query/Filter/DateField.ts b/src/Query/Filter/DateField.ts
index e6d976a0..15077d17 100644
--- a/src/Query/Filter/DateField.ts
+++ b/src/Query/Filter/DateField.ts
@@ -38,6 +38,7 @@ export abstract class DateField extends Field {
     }
 
     public canCreateFilterForLine(line: string): boolean {
+        line = line?.toLowerCase();
         if (this.filterInstructions.canCreateFilterForLine(line)) {
             return true;
         }
@@ -54,7 +55,7 @@ export abstract class DateField extends Field {
             return FilterOrErrorMessage.fromError(line, errorText);
         }
 
-        const filterResult = this.filterInstructions.createFilterOrErrorMessage(line);
+        const filterResult = this.filterInstructions.createFilterOrErrorMessage(line.toLowerCase());
         if (filterResult.filter !== undefined) {
             return filterResult;
         }
@@ -68,7 +69,7 @@ export abstract class DateField extends Field {
         }
 
         const keywordAndDateString = fieldNameKeywordDate[1]; // The whole line except the field name
-        const fieldKeyword = fieldNameKeywordDate[2]; // 'on', 'in', 'before', 'after', 'on|in or before|after' or undefined
+        const fieldKeyword = fieldNameKeywordDate[2]?.toLowerCase(); // 'on', 'in', 'before', 'after', 'on|in or before|after' or undefined
         const fieldDateString = fieldNameKeywordDate[3]; // The remainder of the instruction
 
         // Try interpreting everything after the keyword as a date range:
@@ -148,6 +149,7 @@ export abstract class DateField extends Field {
     protected filterRegExp(): RegExp {
         return new RegExp(
             `^${this.fieldNameForFilterInstruction()} (((?:on|in) or before|before|(?:on|in) or after|after|on|in)? ?(.*))`,
+            'i',
         );
     }
 
diff --git a/src/Query/Filter/Field.ts b/src/Query/Filter/Field.ts
index dc51e1c0..c03f2d90 100644
--- a/src/Query/Filter/Field.ts
+++ b/src/Query/Filter/Field.ts
@@ -172,7 +172,7 @@ export abstract class Field {
             throw Error(`sorterRegExp() unimplemented for ${this.fieldNameSingular()}`);
         }
 
-        return new RegExp(`^sort by ${this.fieldNameSingularEscaped()}( reverse)?`);
+        return new RegExp(`^sort by ${this.fieldNameSingularEscaped()}( reverse)?`, 'i');
     }
 
     /**
@@ -269,7 +269,7 @@ export abstract class Field {
         }
 
         // The $ at end is required to distinguish between group by status and status.name
-        return new RegExp(`^group by ${this.fieldNameSingularEscaped()}( reverse)?$`);
+        return new RegExp(`^group by ${this.fieldNameSingularEscaped()}( reverse)?$`, 'i');
     }
 
     /**
diff --git a/src/Query/Filter/FilterInstructionsBasedField.ts b/src/Query/Filter/FilterInstructionsBasedField.ts
index e8069cfc..52ceba59 100644
--- a/src/Query/Filter/FilterInstructionsBasedField.ts
+++ b/src/Query/Filter/FilterInstructionsBasedField.ts
@@ -12,11 +12,11 @@ export abstract class FilterInstructionsBasedField extends Field {
     protected readonly _filters = new FilterInstructions();
 
     public canCreateFilterForLine(line: string): boolean {
-        return this._filters.canCreateFilterForLine(line);
+        return this._filters.canCreateFilterForLine(line.toLowerCase());
     }
 
     public createFilterOrErrorMessage(line: string): FilterOrErrorMessage {
-        return this._filters.createFilterOrErrorMessage(line);
+        return this._filters.createFilterOrErrorMessage(line.toLowerCase());
     }
 
     protected filterRegExp(): RegExp | null {
diff --git a/src/Query/Filter/FunctionField.ts b/src/Query/Filter/FunctionField.ts
index 827e1fd9..6fde564a 100644
--- a/src/Query/Filter/FunctionField.ts
+++ b/src/Query/Filter/FunctionField.ts
@@ -37,7 +37,7 @@ export class FunctionField extends Field {
     }
 
     protected filterRegExp(): RegExp | null {
-        return new RegExp(`^filter by ${this.fieldNameSingularEscaped()} (.*)`);
+        return new RegExp(`^filter by ${this.fieldNameSingularEscaped()} (.*)`, 'i');
     }
 
     // -----------------------------------------------------------------------------------------------------------------
@@ -59,7 +59,7 @@ export class FunctionField extends Field {
     }
 
     protected grouperRegExp(): RegExp {
-        return new RegExp(`^group by ${this.fieldNameSingularEscaped()}( reverse)? (.*)`);
+        return new RegExp(`^group by ${this.fieldNameSingularEscaped()}( reverse)? (.*)`, 'i');
     }
 
     /**
diff --git a/src/Query/Filter/MultiTextField.ts b/src/Query/Filter/MultiTextField.ts
index ef6a0d70..ce5fe474 100644
--- a/src/Query/Filter/MultiTextField.ts
+++ b/src/Query/Filter/MultiTextField.ts
@@ -72,6 +72,6 @@ export abstract class MultiTextField extends TextField {
             throw Error(`grouperRegExp() unimplemented for ${this.fieldNameSingular()}`);
         }
 
-        return new RegExp(`^group by ${this.fieldNamePlural()}( reverse)?$`);
+        return new RegExp(`^group by ${this.fieldNamePlural()}( reverse)?$`, 'i');
     }
 }
diff --git a/src/Query/Filter/PriorityField.ts b/src/Query/Filter/PriorityField.ts
index b6a299b2..6b71c3f7 100644
--- a/src/Query/Filter/PriorityField.ts
+++ b/src/Query/Filter/PriorityField.ts
@@ -12,7 +12,7 @@ export class PriorityField extends Field {
     //  (leading-white-space-in-outer-capture-group(values-to-use-are-in-inner-capture-group))
     // The capture groups are numbered in the order of their opening brackets, from left to right.
     private static readonly priorityRegexp =
-        /^priority(\s+is)?(\s+(above|below|not))?(\s+(lowest|low|none|medium|high|highest))$/;
+        /^priority(\s+is)?(\s+(above|below|not))?(\s+(lowest|low|none|medium|high|highest))$/i;
 
     createFilterOrErrorMessage(line: string): FilterOrErrorMessage {
         const priorityMatch = Field.getMatch(this.filterRegExp(), line);
@@ -20,7 +20,7 @@ export class PriorityField extends Field {
             const filterPriorityString = priorityMatch[5];
             let filterPriority: Priority | null = null;
 
-            switch (filterPriorityString) {
+            switch (filterPriorityString.toLowerCase()) {
                 case 'lowest':
                     filterPriority = Priority.Lowest;
                     break;
@@ -47,7 +47,7 @@ export class PriorityField extends Field {
 
             let explanation = line;
             let filter;
-            switch (priorityMatch[3]) {
+            switch (priorityMatch[3]?.toLowerCase()) {
                 case 'above':
                     filter = (task: Task) => task.priority.localeCompare(filterPriority!) < 0;
                     break;
diff --git a/src/Query/Filter/StatusTypeField.ts b/src/Query/Filter/StatusTypeField.ts
index 51908410..2447bde0 100644
--- a/src/Query/Filter/StatusTypeField.ts
+++ b/src/Query/Filter/StatusTypeField.ts
@@ -18,7 +18,7 @@ export class StatusTypeField extends Field {
     public canCreateFilterForLine(line: string): boolean {
         // Use a relaxed regexp, just checking field name and not the contents,
         // so that we can parse the line later and give meaningful errors if user uses invalid values.
-        const relaxedRegExp = new RegExp(`^(?:${this.fieldNameSingularEscaped()})`);
+        const relaxedRegExp = new RegExp(`^(?:${this.fieldNameSingularEscaped()})`, 'i');
         return Field.lineMatchesFilter(relaxedRegExp, line);
     }
 
@@ -29,7 +29,8 @@ export class StatusTypeField extends Field {
             return this.helpMessage(line);
         }
 
-        const [_, filterOperator, statusTypeAsString] = match;
+        const filterOperator = match[1].toLowerCase();
+        const statusTypeAsString = match[2];
 
         const statusTypeElement = StatusType[statusTypeAsString.toUpperCase() as keyof typeof StatusType];
         if (!statusTypeElement) {
@@ -57,7 +58,7 @@ export class StatusTypeField extends Field {
     }
 
     protected filterRegExp(): RegExp | null {
-        return new RegExp(`^(?:${this.fieldNameSingularEscaped()}) (is|is not) ([^ ]+)$`);
+        return new RegExp(`^(?:${this.fieldNameSingularEscaped()}) (is|is not) ([^ ]+)$`, 'i');
     }
 
     private helpMessage(line: string): FilterOrErrorMessage {
diff --git a/src/Query/Filter/TagsField.ts b/src/Query/Filter/TagsField.ts
index 941f0438..279af888 100644
--- a/src/Query/Filter/TagsField.ts
+++ b/src/Query/Filter/TagsField.ts
@@ -27,7 +27,7 @@ export class TagsField extends MultiTextField {
     }
 
     public createFilterOrErrorMessage(line: string): FilterOrErrorMessage {
-        const filterResult = this.filterInstructions.createFilterOrErrorMessage(line);
+        const filterResult = this.filterInstructions.createFilterOrErrorMessage(line.toLowerCase());
         if (filterResult.filter !== undefined) {
             return filterResult;
         }
@@ -36,7 +36,7 @@ export class TagsField extends MultiTextField {
     }
 
     public canCreateFilterForLine(line: string): boolean {
-        if (this.filterInstructions.canCreateFilterForLine(line)) {
+        if (this.filterInstructions.canCreateFilterForLine(line.toLowerCase())) {
             return true;
         }
 
@@ -83,7 +83,7 @@ export class TagsField extends MultiTextField {
      * `match[2]` will be either the tag number or undefined.
      */
     protected sorterRegExp(): RegExp {
-        return /^sort by tag( reverse)?[\s]*(\d+)?/;
+        return /^sort by tag( reverse)?[\s]*(\d+)?/i;
     }
 
     /**
diff --git a/src/Query/Filter/TextField.ts b/src/Query/Filter/TextField.ts
index a9de5576..2c6b1276 100644
--- a/src/Query/Filter/TextField.ts
+++ b/src/Query/Filter/TextField.ts
@@ -26,7 +26,9 @@ export abstract class TextField extends Field {
 
         // Construct an IStringMatcher for this filter, or return
         // if the inputs are invalid.
-        const [_, filterOperator, filterValue] = match;
+        const filterOperator = match[1].toLowerCase();
+        const filterValue = match[2];
+
         let matcher: IStringMatcher | null = null;
         if (filterOperator.includes('include')) {
             matcher = new SubstringMatcher(filterValue);
@@ -76,7 +78,7 @@ export abstract class TextField extends Field {
     }
 
     protected filterRegExp(): RegExp {
-        return new RegExp(`^(?:${this.fieldPattern()}) (${this.filterOperatorPattern()}) (.*)`);
+        return new RegExp(`^(?:${this.fieldPattern()}) (${this.filterOperatorPattern()}) (.*)`, 'i');
     }
 
     public abstract fieldName(): string;
diff --git a/src/Query/FilterParser.ts b/src/Query/FilterParser.ts
index a49140d6..ca26b0eb 100644
--- a/src/Query/FilterParser.ts
+++ b/src/Query/FilterParser.ts
@@ -34,6 +34,7 @@ import { BacklinkField } from './Filter/BacklinkField';
 // When adding new fields keep this order in mind, putting fields that are more specific before fields that
 // may contain them, and keep BooleanField last.
 export const fieldCreators: EndsWith<BooleanField> = [
+    // NEW_QUERY_INSTRUCTION_EDIT_REQUIRED
     () => new StatusNameField(), // status.name is before status, to avoid ambiguity
     () => new StatusTypeField(), // status.type is before status, to avoid ambiguity
     () => new StatusField(),
@@ -75,7 +76,7 @@ export function parseSorter(sorterString: string): Sorter | null {
     // New style parsing, using sorting which is done by the Field classes.
 
     // Optimisation: Check whether line begins with 'sort by'
-    const sortByRegexp = /^sort by /;
+    const sortByRegexp = /^sort by /i;
     if (sorterString.match(sortByRegexp) === null) {
         return null;
     }
@@ -95,7 +96,7 @@ export function parseGrouper(line: string): Grouper | null {
     // New style parsing, using grouping which is done by the Field classes.
 
     // Optimisation: Check whether line begins with 'group by'
-    const groupByRegexp = /^group by /;
+    const groupByRegexp = /^group by /i;
     if (line.match(groupByRegexp) === null) {
         return null;
     }
diff --git a/src/Query/Query.ts b/src/Query/Query.ts
index d486e69d..9d519476 100644
--- a/src/Query/Query.ts
+++ b/src/Query/Query.ts
@@ -31,16 +31,16 @@ export class Query implements IQuery {
     private _ignoreGlobalQuery: boolean = false;
 
     private readonly hideOptionsRegexp =
-        /^(hide|show) (task count|backlink|priority|created date|start date|scheduled date|done date|due date|recurrence rule|edit button|urgency|tags)/;
-    private readonly shortModeRegexp = /^short/;
-    private readonly explainQueryRegexp = /^explain/;
-    private readonly ignoreGlobalQueryRegexp = /^ignore global query/;
+        /^(hide|show) (task count|backlink|priority|created date|start date|scheduled date|done date|due date|recurrence rule|edit button|urgency|tags)/i;
+    private readonly shortModeRegexp = /^short/i;
+    private readonly explainQueryRegexp = /^explain/i;
+    private readonly ignoreGlobalQueryRegexp = /^ignore global query/i;
 
     logger = logging.getLogger('tasks.Query');
     // Used internally to uniquely log each query execution in the console.
     private _queryId: string = '';
 
-    private readonly limitRegexp = /^limit (groups )?(to )?(\d+)( tasks?)?/;
+    private readonly limitRegexp = /^limit (groups )?(to )?(\d+)( tasks?)?/i;
 
     private readonly commentRegexp = /^#.*/;
 
@@ -285,8 +285,8 @@ Problem line: "${line}"`;
     private parseHideOptions(line: string): void {
         const hideOptionsMatch = line.match(this.hideOptionsRegexp);
         if (hideOptionsMatch !== null) {
-            const hide = hideOptionsMatch[1] === 'hide';
-            const option = hideOptionsMatch[2];
+            const hide = hideOptionsMatch[1].toLowerCase() === 'hide';
+            const option = hideOptionsMatch[2].toLowerCase();
 
             switch (option) {
                 case 'task count':
diff --git a/src/QueryRenderer.ts b/src/QueryRenderer.ts
index 5f41bb38..ed2bec8d 100644
--- a/src/QueryRenderer.ts
+++ b/src/QueryRenderer.ts
@@ -1,21 +1,21 @@
-import { App, Keymap, MarkdownRenderChild, MarkdownRenderer, Plugin, TFile } from 'obsidian';
 import type { EventRef, MarkdownPostProcessorContext } from 'obsidian';
+import { App, Keymap, MarkdownRenderChild, MarkdownRenderer, Plugin, TFile } from 'obsidian';
+import { State } from './Cache';
 import { GlobalFilter } from './Config/GlobalFilter';
 import { GlobalQuery } from './Config/GlobalQuery';
+import { DateFallback } from './DateFallback';
+import { getTaskLineAndFile, replaceTaskWithTasks } from './File';
 
 import type { IQuery } from './IQuery';
-import { State } from './Cache';
-import { getTaskLineAndFile, replaceTaskWithTasks } from './File';
-import type { GroupDisplayHeading } from './Query/GroupDisplayHeading';
-import { taskToLi } from './TaskLineRenderer';
-import { TaskModal } from './TaskModal';
-import type { TasksEvents } from './TasksEvents';
-import type { Task } from './Task';
-import { DateFallback } from './DateFallback';
-import { TaskLayout } from './TaskLayout';
 import { explainResults, getQueryForQueryRenderer } from './lib/QueryRendererHelper';
+import type { GroupDisplayHeading } from './Query/GroupDisplayHeading';
 import type { QueryResult } from './Query/QueryResult';
 import type { TaskGroups } from './Query/TaskGroups';
+import type { Task } from './Task';
+import { TaskLayout } from './TaskLayout';
+import { TaskLineRenderer } from './TaskLineRenderer';
+import { TaskModal } from './TaskModal';
+import type { TasksEvents } from './TasksEvents';
 
 export class QueryRenderer {
     private readonly app: App;
@@ -217,17 +217,16 @@ class QueryRenderChild extends MarkdownRenderChild {
         taskList.addClasses(layout.taskListHiddenClasses);
         const groupingAttribute = this.getGroupingAttribute();
         if (groupingAttribute && groupingAttribute.length > 0) taskList.dataset.taskGroupBy = groupingAttribute;
-        for (const [i, task] of tasks.entries()) {
-            const isFilenameUnique = this.isFilenameUnique({ task });
 
-            const listItem = await taskToLi(task, {
-                parentUlElement: taskList,
-                listIndex: i,
-                layoutOptions: this.query.layoutOptions,
-                isFilenameUnique,
-                taskLayout: layout,
-                obsidianComponent: this,
-            });
+        const taskLineRenderer = new TaskLineRenderer({
+            obsidianComponent: this,
+            parentUlElement: taskList,
+            layoutOptions: this.query.layoutOptions,
+        });
+
+        for (const [taskIndex, task] of tasks.entries()) {
+            const isFilenameUnique = this.isFilenameUnique({ task });
+            const listItem = await taskLineRenderer.renderTaskLine(task, taskIndex, isFilenameUnique);
 
             // Remove all footnotes. They don't re-appear in another document.
             const footnotes = listItem.querySelectorAll('[data-footnote-id]');
@@ -353,12 +352,11 @@ class QueryRenderChild extends MarkdownRenderChild {
             if (result) {
                 const [line, file] = result;
                 const leaf = this.app.workspace.getLeaf(Keymap.isModEvent(ev));
-                // This opens the file with the required line highlighted.
-                // It works for Edit and Reading mode, however, for some reason (maybe an Obsidian bug),
-                // when used in Reading mode, switching the result to Edit does not sync the scroll.
-                // A patch suggested over Discord to use leaf.setEphemeralState({scroll: line}) does not seem
-                // to make a difference.
-                // The issue is tracked here: https://github.com/obsidian-tasks-group/obsidian-tasks/issues/1879
+                // When the corresponding task has been found,
+                // suppress the default behavior of the mouse click event
+                // (which would interfere e.g. if the query is rendered inside a callout).
+                ev.preventDefault();
+                // Instead of the default behavior, open the file with the required line highlighted.
                 await leaf.openFile(file, { eState: { line: line } });
             }
         });
@@ -374,6 +372,7 @@ class QueryRenderChild extends MarkdownRenderChild {
                 if (result) {
                     const [line, file] = result;
                     const leaf = this.app.workspace.getLeaf('tab');
+                    ev.preventDefault();
                     await leaf.openFile(file, { eState: { line: line } });
                 }
             }
diff --git a/src/Suggestor/Suggestor.ts b/src/Suggestor/Suggestor.ts
index 006ca84e..af17ef40 100644
--- a/src/Suggestor/Suggestor.ts
+++ b/src/Suggestor/Suggestor.ts
@@ -17,6 +17,7 @@ export function makeDefaultSuggestionBuilder(
     symbols: DefaultTaskSerializerSymbols,
     maxGenericSuggestions: number /** See {@link DEFAULT_MAX_GENERIC_SUGGESTIONS} */,
 ): SuggestionBuilder {
+    // NEW_TASK_FIELD_EDIT_REQUIRED
     const datePrefixRegex = [symbols.startDateSymbol, symbols.scheduledDateSymbol, symbols.dueDateSymbol].join('|');
     /*
      * Return a list of suggestions, either generic or more fine-grained to the words at the cursor.
@@ -68,6 +69,7 @@ function addTaskPropertySuggestions(
 
     const genericSuggestions: SuggestInfo[] = [];
 
+    // NEW_TASK_FIELD_EDIT_REQUIRED
     if (!line.includes(symbols.dueDateSymbol))
         genericSuggestions.push({
             displayText: `${symbols.dueDateSymbol} due date`,
diff --git a/src/Task.ts b/src/Task.ts
index c8b7e428..02b7d718 100644
--- a/src/Task.ts
+++ b/src/Task.ts
@@ -119,6 +119,7 @@ interface TaskComponents {
  * @class Task
  */
 export class Task {
+    // NEW_TASK_FIELD_EDIT_REQUIRED
     public readonly status: Status;
     public readonly description: string;
     public readonly indentation: string;
@@ -152,6 +153,7 @@ export class Task {
     private _urgency: number | null = null;
 
     constructor({
+        // NEW_TASK_FIELD_EDIT_REQUIRED
         status,
         description,
         taskLocation,
@@ -169,6 +171,7 @@ export class Task {
         originalMarkdown,
         scheduledDateIsInferred,
     }: {
+        // NEW_TASK_FIELD_EDIT_REQUIRED
         status: Status;
         description: string;
         taskLocation: TaskLocation;
@@ -186,6 +189,7 @@ export class Task {
         originalMarkdown: string;
         scheduledDateIsInferred: boolean;
     }) {
+        // NEW_TASK_FIELD_EDIT_REQUIRED
         this.status = status;
         this.description = description;
         this.indentation = indentation;
@@ -700,6 +704,8 @@ export class Task {
      * @param other
      */
     public identicalTo(other: Task) {
+        // NEW_TASK_FIELD_EDIT_REQUIRED
+
         // Based on ideas from koala. AquaCat and javalent in Discord:
         // https://discord.com/channels/686053708261228577/840286264964022302/996735200388186182
         // and later.
diff --git a/src/TaskFieldRenderer.ts b/src/TaskFieldRenderer.ts
new file mode 100644
index 00000000..4d34e48c
--- /dev/null
+++ b/src/TaskFieldRenderer.ts
@@ -0,0 +1,155 @@
+import type { Moment } from 'moment';
+import { PriorityTools } from './lib/PriorityTools';
+import type { Task } from './Task';
+import type { TaskLayoutComponent } from './TaskLayout';
+
+export class TaskFieldRenderer {
+    private readonly data = taskFieldHTMLData;
+
+    /**
+     * Adds data attribute to an {@link element} for a component. For example,
+     * a `<span>` describing a task with medium priority and done yesterday will have
+     * `data-task-priority="medium" data-task-due="past-1d"` in its data attributes (One data attribute per component).
+     *
+     * If no data was found for a component in a task, data attribute won't be added.
+     *
+     * For detailed calculation see {@link TaskFieldHTMLData.addDataAttribute}.
+     *
+     * @param element the HTML element to add the data attribute to.
+     * @param task the task from which the for the data attribute shall be taken.
+     * @param component the component of the task for which the data attribute has to be added.
+     */
+    public addDataAttribute(element: HTMLElement, task: Task, component: TaskLayoutComponent) {
+        this.data[component].addDataAttribute(element, task, component);
+    }
+
+    /**
+     * @returns the component's CSS class describing what this component is (priority, due date etc.).
+     * @param component of the task.
+     */
+    public className(component: TaskLayoutComponent) {
+        return this.data[component].className;
+    }
+}
+
+type AttributeValueCalculator = (component: TaskLayoutComponent, task: Task) => string;
+
+export class TaskFieldHTMLData {
+    public readonly className: string;
+    private readonly attributeName: string;
+    private readonly attributeValueCalculator: AttributeValueCalculator;
+
+    public static noAttributeName = '';
+    public static noAttributeValueCalculator: AttributeValueCalculator = () => {
+        return '';
+    };
+    public static dateAttributeCalculator = (component: TaskLayoutComponent, task: Task) => {
+        /**
+         * Translate a relative date to a CSS class: 'today', 'future-1d' (for tomorrow), 'past-1d' (for yesterday)
+         * etc.
+         * A cutoff (in days) is defined in MAX_DAY_VALUE_RANGE, from beyond that a generic 'far' postfix will be added.
+         * (the cutoff exists because we don't want to flood the DOM with potentially hundreds of unique classes.)
+         */
+        const MAX_DAY_VALUE_RANGE = 7;
+        const DAY_VALUE_OVER_RANGE_POSTFIX = 'far';
+
+        function dateToAttribute(date: Moment) {
+            const today = window.moment().startOf('day');
+            let result = '';
+            const diffDays = today.diff(date, 'days');
+            if (isNaN(diffDays)) return null;
+            if (diffDays === 0) return 'today';
+            else if (diffDays > 0) result += 'past-';
+            else if (diffDays < 0) result += 'future-';
+            if (Math.abs(diffDays) <= MAX_DAY_VALUE_RANGE) {
+                result += Math.abs(diffDays).toString() + 'd';
+            } else {
+                result += DAY_VALUE_OVER_RANGE_POSTFIX;
+            }
+            return result;
+        }
+
+        const date = task[component];
+
+        if (date instanceof window.moment) {
+            const attributeValue = dateToAttribute(date);
+            if (attributeValue) {
+                return attributeValue;
+            }
+        }
+        return '';
+    };
+
+    /**
+     * @param className CSS class that describes what the component is, e.g. a due date or a priority.
+     *
+     * @param attributeName if the component needs data attribute (`data-key="value"`) this is the key.
+     * Otherwise, set this to {@link TaskFieldHTMLData.noAttributeName}.
+     *
+     * @param attributeValueCalculator And this is the value calculator.
+     * Set to {@link TaskFieldHTMLData.noAttributeValueCalculator} if the component has no data attribute.
+     *
+     * There is a relation between {@link attributeName} and {@link attributeValueCalculator}.
+     * For a component to have the data attribute, both need to be set to values other than
+     * {@link TaskFieldHTMLData.noAttributeName} and {@link TaskFieldHTMLData.noAttributeValueCalculator} respectively.
+     * This means that having an empty data attribute (`data-key=""`) is not supported.
+     */
+    constructor(className: string, attributeName: string, attributeValueCalculator: AttributeValueCalculator) {
+        // If className is empty, `span.classList.add(...componentClass);` will fail in runtime.
+        if (className === '') {
+            throw Error('Developer note: CSS class cannot be an empty string, please specify one.');
+        }
+        this.className = className;
+        this.attributeName = attributeName;
+        this.attributeValueCalculator = attributeValueCalculator;
+    }
+
+    /**
+     * Shall be called only by {@link TaskFieldRenderer}. Use that class if you need to add a data attribute.
+     *
+     * Adds the data attribute, associated to with a task's component to an HTML element.
+     * For example, a task with medium priority and done yesterday will have
+     * `data-task-priority="medium" data-task-due="past-1d" ` in its data attributes (One data attribute per component).
+     *
+     * Calculation of the value is done with {@link TaskFieldHTMLData.attributeValueCalculator}.
+     *
+     * @param element the HTML element to add the data attribute to.
+     * @param task the task from which the data shall be taken.
+     * @param component the component of the task for which the data attribute has to be added.
+     */
+    public addDataAttribute(element: HTMLElement, task: Task, component: TaskLayoutComponent) {
+        if (this.attributeName !== TaskFieldHTMLData.noAttributeName) {
+            element.dataset[this.attributeName] = this.attributeValueCalculator(component, task);
+        }
+    }
+}
+
+const taskFieldHTMLData: { [c in TaskLayoutComponent]: TaskFieldHTMLData } = {
+    // NEW_TASK_FIELD_EDIT_REQUIRED
+    createdDate: new TaskFieldHTMLData('task-created', 'taskCreated', TaskFieldHTMLData.dateAttributeCalculator),
+    dueDate: new TaskFieldHTMLData('task-due', 'taskDue', TaskFieldHTMLData.dateAttributeCalculator),
+    startDate: new TaskFieldHTMLData('task-start', 'taskStart', TaskFieldHTMLData.dateAttributeCalculator),
+    scheduledDate: new TaskFieldHTMLData('task-scheduled', 'taskScheduled', TaskFieldHTMLData.dateAttributeCalculator),
+    doneDate: new TaskFieldHTMLData('task-done', 'taskDone', TaskFieldHTMLData.dateAttributeCalculator),
+
+    description: new TaskFieldHTMLData(
+        'task-description',
+        TaskFieldHTMLData.noAttributeName,
+        TaskFieldHTMLData.noAttributeValueCalculator,
+    ),
+    recurrenceRule: new TaskFieldHTMLData(
+        'task-recurring',
+        TaskFieldHTMLData.noAttributeName,
+        TaskFieldHTMLData.noAttributeValueCalculator,
+    ),
+
+    priority: new TaskFieldHTMLData('task-priority', 'taskPriority', (_component, task) => {
+        return PriorityTools.priorityNameUsingNormal(task.priority).toLocaleLowerCase();
+    }),
+
+    blockLink: new TaskFieldHTMLData(
+        'task-block-link',
+        TaskFieldHTMLData.noAttributeName,
+        TaskFieldHTMLData.noAttributeValueCalculator,
+    ),
+};
diff --git a/src/TaskLayout.ts b/src/TaskLayout.ts
index 06c8b5e1..322ce396 100644
--- a/src/TaskLayout.ts
+++ b/src/TaskLayout.ts
@@ -20,6 +20,7 @@ export class LayoutOptions {
 }
 
 export type TaskLayoutComponent =
+    // NEW_TASK_FIELD_EDIT_REQUIRED
     | 'description'
     | 'priority'
     | 'recurrenceRule'
@@ -37,6 +38,7 @@ export type TaskLayoutComponent =
  */
 export class TaskLayout {
     public defaultLayout: TaskLayoutComponent[] = [
+        // NEW_TASK_FIELD_EDIT_REQUIRED
         'description',
         'priority',
         'recurrenceRule',
@@ -66,6 +68,7 @@ export class TaskLayout {
         // Remove components from the layout according to the task options. These represent the existing task options,
         // so some components (e.g. the description) are not here because there are no layout options to remove them.
         const componentsToHideAndGenerateClasses: [boolean, TaskLayoutComponent][] = [
+            // NEW_TASK_FIELD_EDIT_REQUIRED
             [this.options.hidePriority, 'priority'],
             [this.options.hideRecurrenceRule, 'recurrenceRule'],
             [this.options.hideCreatedDate, 'createdDate'],
diff --git a/src/TaskLineRenderer.ts b/src/TaskLineRenderer.ts
index 0e863b94..7b379e45 100644
--- a/src/TaskLineRenderer.ts
+++ b/src/TaskLineRenderer.ts
@@ -3,100 +3,13 @@ import { Component, MarkdownRenderer } from 'obsidian';
 import { GlobalFilter } from './Config/GlobalFilter';
 import { TASK_FORMATS, getSettings } from './Config/Settings';
 import { replaceTaskWithTasks } from './File';
-import { PriorityTools } from './lib/PriorityTools';
 import type { Task } from './Task';
 import * as taskModule from './Task';
+import { TaskFieldRenderer } from './TaskFieldRenderer';
 import type { LayoutOptions, TaskLayoutComponent } from './TaskLayout';
 import { TaskLayout } from './TaskLayout';
 
-export type TaskLineRenderDetails = {
-    parentUlElement: HTMLElement;
-    /** The nth item in this list (including non-tasks). */
-    listIndex: number;
-    obsidianComponent: Component | null;
-    layoutOptions?: LayoutOptions;
-    isFilenameUnique?: boolean;
-    taskLayout?: TaskLayout;
-};
-
-type AttributeValueCalculator = (component: TaskLayoutComponent, task: Task) => string;
-
-export class FieldLayoutDetail {
-    className: string;
-    attributeName: string;
-    attributeValueCalculator: AttributeValueCalculator;
-
-    public static noAttributeName = '';
-    public static noAttributeValueCalculator: AttributeValueCalculator = () => {
-        return '';
-    };
-
-    /**
-     * @param className CSS class of the component.
-     * @param attributeName if the component needs data attribute (`data-key="value"`) this is the key.
-     * Otherwise, set this to {@link FieldLayoutDetail.noAttributeName}.
-     * @param attributeValueCalculator And this is the value.
-     * Set to {@link FieldLayoutDetail.noAttributeValueCalculator} if shall be empty.
-     *
-     * There is a relation between {@link attributeName} and {@link attributeValueCalculator}.
-     * For a component to have the data attribute, both need to be set to values other than
-     * {@link FieldLayoutDetail.noAttributeName} and {@link FieldLayoutDetail.noAttributeValueCalculator} respectively.
-     * This means that having an empty data attribute (`data-key=""`) is not supported.
-     */
-    constructor(className: string, attributeName: string, attributeValueCalculator: AttributeValueCalculator) {
-        // If className is empty, `span.classList.add(...componentClass);` will fail in runtime.
-        if (className === '') {
-            throw Error('Developer note: CSS class cannot be an empty string, please specify one.');
-        }
-        this.className = className;
-        this.attributeName = attributeName;
-        this.attributeValueCalculator = attributeValueCalculator;
-    }
-}
-
-const dateDataAttributeCalculator: AttributeValueCalculator = (component: TaskLayoutComponent, task: Task) => {
-    const date = task[component];
-    if (date instanceof window.moment) {
-        const attributeValue = dateToAttribute(date);
-        if (attributeValue) {
-            return attributeValue;
-        }
-    }
-
-    return '';
-};
-
-export const FieldLayouts: { [c in TaskLayoutComponent]: FieldLayoutDetail } = {
-    createdDate: new FieldLayoutDetail('task-created', 'taskCreated', dateDataAttributeCalculator),
-    dueDate: new FieldLayoutDetail('task-due', 'taskDue', dateDataAttributeCalculator),
-    startDate: new FieldLayoutDetail('task-start', 'taskStart', dateDataAttributeCalculator),
-    scheduledDate: new FieldLayoutDetail('task-scheduled', 'taskScheduled', dateDataAttributeCalculator),
-    doneDate: new FieldLayoutDetail('task-done', 'taskDone', dateDataAttributeCalculator),
-
-    description: new FieldLayoutDetail(
-        'task-description',
-        FieldLayoutDetail.noAttributeName,
-        FieldLayoutDetail.noAttributeValueCalculator,
-    ),
-    recurrenceRule: new FieldLayoutDetail(
-        'task-recurring',
-        FieldLayoutDetail.noAttributeName,
-        FieldLayoutDetail.noAttributeValueCalculator,
-    ),
-
-    priority: new FieldLayoutDetail('task-priority', 'taskPriority', (_component, task) => {
-        return PriorityTools.priorityNameUsingNormal(task.priority).toLocaleLowerCase();
-    }),
-
-    blockLink: new FieldLayoutDetail(
-        'task-block-link',
-        FieldLayoutDetail.noAttributeName,
-        FieldLayoutDetail.noAttributeValueCalculator,
-    ),
-};
-
-const MAX_DAY_VALUE_RANGE = 7;
-const DAY_VALUE_OVER_RANGE_POSTFIX = 'far';
+const fieldRenderer = new TaskFieldRenderer();
 
 /**
  * The function used to render a Markdown task line into an existing HTML element.
@@ -108,358 +21,308 @@ export type TextRenderer = (
     obsidianComponent: Component | null, // null is allowed here only for tests
 ) => Promise<void>;
 
-/**
- * Renders a given Task object into an HTML List Item (LI) element, using the given renderDetails
- * configuration and a supplied TextRenderer (typically the Obsidian Markdown renderer, but for testing
- * purposes it can be a simpler one).
- * The element includes the task and its various components (description, priority, block link etc), the
- * checkbox on the left with its event handling of completing the task, and the button for editing the task.
- */
-export async function renderTaskLine(
-    task: Task,
-    renderDetails: TaskLineRenderDetails,
-    textRenderer: TextRenderer,
-): Promise<HTMLLIElement> {
-    const li: HTMLLIElement = document.createElement('li');
-    renderDetails.parentUlElement.appendChild(li);
-
-    li.classList.add('task-list-item', 'plugin-tasks-list-item');
-
-    // Maintenance note:
-    //  We don't use the Obsidian convenience function li.createEl() here, because we don't have it available
-    //  when running tests, and we want the tests to be able to create the full div and span structure,
-    //  so had to convert all of these to the equivalent but more elaborate document.createElement() and
-    //  appendChild() calls.
-    const textSpan = document.createElement('span');
-    li.appendChild(textSpan);
-    textSpan.classList.add('tasks-list-text');
-    const attributes = await taskToHtml(task, renderDetails, textSpan, textRenderer);
-    for (const key in attributes) li.dataset[key] = attributes[key];
-
-    // NOTE: this area is mentioned in `CONTRIBUTING.md` under "How does Tasks handle status changes". When
-    // moving the code, remember to update that reference too.
-    const checkbox = document.createElement('input');
-    li.appendChild(checkbox);
-    checkbox.classList.add('task-list-item-checkbox');
-    checkbox.type = 'checkbox';
-    if (task.status.symbol !== ' ') {
-        checkbox.checked = true;
-        li.classList.add('is-checked');
+export class TaskLineRenderer {
+    private readonly textRenderer: TextRenderer;
+    private readonly obsidianComponent: Component | null;
+    private readonly parentUlElement: HTMLElement;
+    private readonly layoutOptions: LayoutOptions;
+
+    private static async obsidianMarkdownRenderer(
+        text: string,
+        element: HTMLSpanElement,
+        path: string,
+        obsidianComponent: Component | null,
+    ) {
+        if (!obsidianComponent) throw new Error('Must call the Obsidian renderer with an Obsidian Component object');
+        await MarkdownRenderer.renderMarkdown(text, element, path, obsidianComponent);
     }
 
-    checkbox.addEventListener('click', (event: MouseEvent) => {
-        event.preventDefault();
-        // It is required to stop propagation so that obsidian won't write the file with the
-        // checkbox (un)checked. Obsidian would write after us and overwrite our change.
-        event.stopPropagation();
-
-        // Should be re-rendered as enabled after update in file.
-        checkbox.disabled = true;
-        const toggledTasks = task.toggleWithRecurrenceInUsersOrder();
-        replaceTaskWithTasks({
-            originalTask: task,
-            newTasks: toggledTasks,
-        });
-    });
+    /**
+     * Builds a renderer for tasks with various options.
+     *
+     * @param textRenderer The optional renderer to be used. Skip this parameter for Obsidian rendering.
+     * For test purposes mock renderers shall be used.
+     *
+     * @param obsidianComponent One of the parameters needed by `MarkdownRenderer.renderMarkdown()` Obsidian API,
+     * that is called by the Obsidian renderer. Set this to null in test code.
+     *
+     * @param parentUlElement HTML element where the task shall be rendered.
+     *
+     * @param layoutOptions See {@link LayoutOptions}.
+     */
+    constructor({
+        textRenderer = TaskLineRenderer.obsidianMarkdownRenderer,
+        obsidianComponent,
+        parentUlElement,
+        layoutOptions,
+    }: {
+        textRenderer?: TextRenderer;
+        obsidianComponent: Component | null;
+        parentUlElement: HTMLElement;
+        layoutOptions: LayoutOptions;
+    }) {
+        this.textRenderer = textRenderer;
+        this.obsidianComponent = obsidianComponent;
+        this.parentUlElement = parentUlElement;
+        this.layoutOptions = layoutOptions;
+    }
 
-    li.prepend(checkbox);
+    /**
+     * Renders a given Task object into an HTML List Item (LI) element.
+     *
+     * The element includes the task and its various components (description, priority, block link etc.), the
+     * checkbox on the left with its event handling of completing the task, and the button for editing the task.
+     *
+     * @returns an HTML rendered List Item element (LI) for a task.
+     * @note Output is based on the {@link DefaultTaskSerializer}'s format, with default (emoji) symbols
+     * @param task The task to be rendered.
+     * @param taskIndex Task's index in the list. This affects `data-line` data attributes of the list item.
+     * @param isFilenameUnique Whether the name of the file that contains the task is unique in the vault.
+     *                         If it is undefined, the outcome will be the same as with a unique file name:
+     *                         the file name only. If set to `true`, the full path will be returned.
+     */
+    public async renderTaskLine(task: Task, taskIndex: number, isFilenameUnique?: boolean): Promise<HTMLLIElement> {
+        const li: HTMLLIElement = document.createElement('li');
+        this.parentUlElement.appendChild(li);
+
+        li.classList.add('task-list-item', 'plugin-tasks-list-item');
+
+        // Maintenance note:
+        //  We don't use the Obsidian convenience function li.createEl() here, because we don't have it available
+        //  when running tests, and we want the tests to be able to create the full div and span structure,
+        //  so had to convert all of these to the equivalent but more elaborate document.createElement() and
+        //  appendChild() calls.
+        const textSpan = document.createElement('span');
+        li.appendChild(textSpan);
+        textSpan.classList.add('tasks-list-text');
+        await this.taskToHtml(task, textSpan, li);
+
+        // NOTE: this area is mentioned in `CONTRIBUTING.md` under "How does Tasks handle status changes". When
+        // moving the code, remember to update that reference too.
+        const checkbox = document.createElement('input');
+        li.appendChild(checkbox);
+        checkbox.classList.add('task-list-item-checkbox');
+        checkbox.type = 'checkbox';
+        if (task.status.symbol !== ' ') {
+            checkbox.checked = true;
+            li.classList.add('is-checked');
+        }
 
-    // Set these to be compatible with stock obsidian lists:
-    li.setAttribute('data-task', task.status.symbol.trim()); // Trim to ensure empty attribute for space. Same way as obsidian.
-    li.setAttribute('data-line', renderDetails.listIndex.toString());
-    li.setAttribute('data-task-status-name', task.status.name);
-    li.setAttribute('data-task-status-type', task.status.type);
-    checkbox.setAttribute('data-line', renderDetails.listIndex.toString());
+        checkbox.addEventListener('click', (event: MouseEvent) => {
+            event.preventDefault();
+            // It is required to stop propagation so that obsidian won't write the file with the
+            // checkbox (un)checked. Obsidian would write after us and overwrite our change.
+            event.stopPropagation();
+
+            // Should be re-rendered as enabled after update in file.
+            checkbox.disabled = true;
+            const toggledTasks = task.toggleWithRecurrenceInUsersOrder();
+            replaceTaskWithTasks({
+                originalTask: task,
+                newTasks: toggledTasks,
+            });
+        });
 
-    if (renderDetails.layoutOptions?.shortMode) {
-        addTooltip({ task, element: textSpan, isFilenameUnique: renderDetails.isFilenameUnique });
-    }
+        li.prepend(checkbox);
 
-    return li;
-}
+        // Set these to be compatible with stock obsidian lists:
+        li.setAttribute('data-task', task.status.symbol.trim()); // Trim to ensure empty attribute for space. Same way as obsidian.
+        li.setAttribute('data-line', taskIndex.toString());
+        li.setAttribute('data-task-status-name', task.status.name);
+        li.setAttribute('data-task-status-type', task.status.type);
+        checkbox.setAttribute('data-line', taskIndex.toString());
 
-export type AttributesDictionary = { [key: string]: string };
-
-async function taskToHtml(
-    task: Task,
-    renderDetails: TaskLineRenderDetails,
-    parentElement: HTMLElement,
-    textRenderer: TextRenderer,
-): Promise<AttributesDictionary> {
-    let allAttributes: AttributesDictionary = {};
-    const taskLayout = new TaskLayout(renderDetails.layoutOptions);
-    const emojiSerializer = TASK_FORMATS.tasksPluginEmoji.taskSerializer;
-    // Render and build classes for all the task's visible components
-    for (const component of taskLayout.shownTaskLayoutComponents) {
-        let componentString = emojiSerializer.componentToString(task, taskLayout, component);
-        if (componentString) {
-            if (component === 'description') {
-                componentString = GlobalFilter.getInstance().removeAsWordFromDependingOnSettings(componentString);
-            }
-            // Create the text span that will hold the rendered component
-            const span = document.createElement('span');
-            parentElement.appendChild(span);
-            if (span) {
-                // Inside that text span, we are creating another internal span, that will hold the text itself.
-                // This may seem redundant, and by default it indeed does nothing, but we do it to allow the CSS
-                // to differentiate between the container of the text and the text itself, so it will be possible
-                // to do things like surrouding only the text (rather than its whole placeholder) with a highlight
-                const internalSpan = document.createElement('span');
-                span.appendChild(internalSpan);
-                await renderComponentText(
-                    internalSpan,
-                    componentString,
-                    component,
-                    task,
-                    textRenderer,
-                    renderDetails.obsidianComponent,
-                );
-                addInternalClasses(component, internalSpan);
-
-                // Add the component's CSS class describing what this component is (priority, due date etc.)
-                const componentClass = getTaskComponentClass(component);
-                span.classList.add(...componentClass);
-
-                // Add the component's attribute ('priority-medium', 'due-past-1d' etc.)
-                const componentDataAttribute = getComponentDataAttribute(component, task);
-                for (const key in componentDataAttribute) span.dataset[key] = componentDataAttribute[key];
-                allAttributes = { ...allAttributes, ...componentDataAttribute };
-            }
+        if (this.layoutOptions.shortMode) {
+            this.addTooltip(task, textSpan, isFilenameUnique);
         }
-    }
 
-    // Now build classes for the hidden task components without rendering them
-    for (const component of taskLayout.hiddenTaskLayoutComponents) {
-        const hiddenComponentDataAttribute = getComponentDataAttribute(component, task);
-        allAttributes = { ...allAttributes, ...hiddenComponentDataAttribute };
+        return li;
     }
 
-    // If a task has no priority field set, its priority will not be rendered as part of the loop above and
-    // it will not be set a priority data attribute.
-    // In such a case we want the upper task LI element to mark the task has a 'normal' priority.
-    // So if the priority was not rendered, force it through the pipe of getting the component data for the
-    // priority field.
-    if (allAttributes.taskPriority === undefined) {
-        const priorityDataAttribute = getComponentDataAttribute('priority', task);
-        allAttributes = { ...allAttributes, ...priorityDataAttribute };
-    }
-
-    return allAttributes;
-}
-
-/*
- * Renders the given component into the given HTML span element.
- */
-async function renderComponentText(
-    span: HTMLSpanElement,
-    componentString: string,
-    component: TaskLayoutComponent,
-    task: Task,
-    textRenderer: TextRenderer,
-    obsidianComponent: Component | null,
-) {
-    if (component === 'description') {
-        const { debugSettings } = getSettings();
-        if (debugSettings.showTaskHiddenData) {
-            // Add some debug output to enable hidden information in the task to be inspected.
-            componentString += `<br>🐛 <b>${task.lineNumber}</b> . ${task.sectionStart} . ${task.sectionIndex} . '<code>${task.originalMarkdown}</code>'<br>'<code>${task.path}</code>' > '<code>${task.precedingHeader}</code>'<br>`;
-        }
-        await textRenderer(componentString, span, task.path, obsidianComponent);
-
-        // If the task is a block quote, the block quote wraps the p-tag that contains the content.
-        // In that case, we need to unwrap the p-tag *inside* the surrounding block quote.
-        // Otherwise, we unwrap the p-tag as a direct descendant of the span.
-        const blockQuote = span.querySelector('blockquote');
-        const directParentOfPTag = blockQuote ?? span;
-
-        // Unwrap the p-tag that was created by the MarkdownRenderer:
-        const pElement = directParentOfPTag.querySelector('p');
-        if (pElement !== null) {
-            while (pElement.firstChild) {
-                directParentOfPTag.insertBefore(pElement.firstChild, pElement);
+    private async taskToHtml(task: Task, parentElement: HTMLElement, li: HTMLLIElement): Promise<void> {
+        const taskLayout = new TaskLayout(this.layoutOptions);
+        const emojiSerializer = TASK_FORMATS.tasksPluginEmoji.taskSerializer;
+        // Render and build classes for all the task's visible components
+        for (const component of taskLayout.shownTaskLayoutComponents) {
+            let componentString = emojiSerializer.componentToString(task, taskLayout, component);
+            if (componentString) {
+                if (component === 'description') {
+                    componentString = GlobalFilter.getInstance().removeAsWordFromDependingOnSettings(componentString);
+                }
+                // Create the text span that will hold the rendered component
+                const span = document.createElement('span');
+                parentElement.appendChild(span);
+                if (span) {
+                    // Inside that text span, we are creating another internal span, that will hold the text itself.
+                    // This may seem redundant, and by default it indeed does nothing, but we do it to allow the CSS
+                    // to differentiate between the container of the text and the text itself, so it will be possible
+                    // to do things like surrounding only the text (rather than its whole placeholder) with a highlight
+                    const internalSpan = document.createElement('span');
+                    span.appendChild(internalSpan);
+                    await this.renderComponentText(internalSpan, componentString, component, task);
+                    this.addInternalClasses(component, internalSpan);
+
+                    // Add the component's CSS class describing what this component is (priority, due date etc.)
+                    const componentClass = fieldRenderer.className(component);
+                    span.classList.add(...[componentClass]);
+
+                    // Add the component's attribute ('priority-medium', 'due-past-1d' etc.)
+                    fieldRenderer.addDataAttribute(span, task, component);
+                    fieldRenderer.addDataAttribute(li, task, component);
+                }
             }
-            pElement.remove();
         }
 
-        // Remove an empty trailing p-tag that the MarkdownRenderer appends when there is a block link:
-        span.querySelectorAll('p').forEach((pElement) => {
-            if (!pElement.hasChildNodes()) {
-                pElement.remove();
-            }
-        });
+        // Now build classes for the hidden task components without rendering them
+        for (const component of taskLayout.hiddenTaskLayoutComponents) {
+            fieldRenderer.addDataAttribute(li, task, component);
+        }
 
-        // Remove the footnote that the MarkdownRenderer appends when there is a footnote in the task:
-        span.querySelectorAll('.footnotes').forEach((footnoteElement) => {
-            footnoteElement.remove();
-        });
-    } else {
-        span.innerHTML = componentString;
+        // If a task has no priority field set, its priority will not be rendered as part of the loop above, and
+        // it will not be set a priority data attribute.
+        // In such a case we want the upper task LI element to mark the task has a 'normal' priority.
+        // So if the priority was not rendered, force it through the pipe of getting the component data for the
+        // priority field.
+        if (li.dataset.taskPriority === undefined) {
+            fieldRenderer.addDataAttribute(li, task, 'priority');
+        }
     }
-}
-
-/**
- * The CSS class that describes what the component is, e.g. a due date or a priority, and is a value from FieldLayouts.
- *
- * **Important**: The caller is responsible for ensuring that the Task being rendered does actually have a value for this field,
- * that is, that `task[component]` has a value. Only call this if task has this value.
- */
-function getTaskComponentClass(component: TaskLayoutComponent) {
-    const componentClassContainer: string[] = [];
-
-    const fieldLayoutDetail = FieldLayouts[component];
-    componentClassContainer.push(fieldLayoutDetail.className);
 
-    return componentClassContainer;
-}
+    /*
+     * Renders the given component into the given HTML span element.
+     */
+    private async renderComponentText(
+        span: HTMLSpanElement,
+        componentString: string,
+        component: TaskLayoutComponent,
+        task: Task,
+    ) {
+        if (component === 'description') {
+            const { debugSettings } = getSettings();
+            if (debugSettings.showTaskHiddenData) {
+                // Add some debug output to enable hidden information in the task to be inspected.
+                componentString += `<br>🐛 <b>${task.lineNumber}</b> . ${task.sectionStart} . ${task.sectionIndex} . '<code>${task.originalMarkdown}</code>'<br>'<code>${task.path}</code>' > '<code>${task.precedingHeader}</code>'<br>`;
+            }
+            await this.textRenderer(componentString, span, task.path, this.obsidianComponent);
+
+            // If the task is a block quote, the block quote wraps the p-tag that contains the content.
+            // In that case, we need to unwrap the p-tag *inside* the surrounding block quote.
+            // Otherwise, we unwrap the p-tag as a direct descendant of the span.
+            const blockQuote = span.querySelector('blockquote');
+            const directParentOfPTag = blockQuote ?? span;
+
+            // Unwrap the p-tag that was created by the MarkdownRenderer:
+            const pElement = directParentOfPTag.querySelector('p');
+            if (pElement !== null) {
+                while (pElement.firstChild) {
+                    directParentOfPTag.insertBefore(pElement.firstChild, pElement);
+                }
+                pElement.remove();
+            }
 
-/**
- * The data attribute describes the content of the component, e.g. `data-task-priority="medium"`, `data-task-due="past-1d"` etc.
- */
-function getComponentDataAttribute(component: TaskLayoutComponent, task: Task) {
-    const dataAttribute: AttributesDictionary = {};
-
-    // If a TaskLayoutComponent needs a data attribute in the task's <span>, get the data attribute name (key) &
-    // data attribute value (value). Otherwise, just leave an empty string ('') as the value.
-    // The value is calculated based on FieldLayoutDetail.attributeValueCalculator
-    const fieldLayoutDetail = FieldLayouts[component];
-    const attributeName = fieldLayoutDetail.attributeName;
-    if (attributeName !== FieldLayoutDetail.noAttributeName) {
-        dataAttribute[attributeName] = fieldLayoutDetail.attributeValueCalculator(component, task);
+            // Remove an empty trailing p-tag that the MarkdownRenderer appends when there is a block link:
+            span.querySelectorAll('p').forEach((pElement) => {
+                if (!pElement.hasChildNodes()) {
+                    pElement.remove();
+                }
+            });
+
+            // Remove the footnote that the MarkdownRenderer appends when there is a footnote in the task:
+            span.querySelectorAll('.footnotes').forEach((footnoteElement) => {
+                footnoteElement.remove();
+            });
+        } else {
+            span.innerHTML = componentString;
+        }
     }
+    /*
+     * Adds internal classes for various components (right now just tags actually), meaning that we modify the existing
+     * rendered element to add classes inside it.
+     * In the case of tags, Obsidian renders a Markdown description with <a class="tag"> elements for tags. We want to
+     * enable users to style these, so we modify the rendered Markdown by adding the specific tag classes for these <a>
+     * elements.
+     */
+    private addInternalClasses(component: TaskLayoutComponent, internalSpan: HTMLSpanElement) {
+        /*
+         * Sanitize tag names, so they will be valid attribute values according to the HTML spec:
+         * https://html.spec.whatwg.org/multipage/parsing.html#attribute-value-(double-quoted)-state
+         */
+        function tagToAttributeValue(tag: string) {
+            // eslint-disable-next-line no-control-regex
+            const illegalChars = /["&\x00\r\n]/g;
+            let sanitizedTag = tag.replace(illegalChars, '-');
+            // And if after sanitization the name starts with dashes or underscores, remove them.
+            sanitizedTag = sanitizedTag.replace(/^[-_]+/, '');
+            if (sanitizedTag.length > 0) return sanitizedTag;
+            else return null;
+        }
 
-    return dataAttribute;
-}
-
-/*
- * Adds internal classes for various components (right now just tags actually), meaning that we modify the existing
- * rendered element to add classes inside it.
- * In the case of tags, Obsidian renders a Markdown description with <a class="tag"> elements for tags. We want to
- * enable users to style these, so we modify the rendered Markdown by adding the specific tag classes for these <a>
- * elements.
- */
-function addInternalClasses(component: TaskLayoutComponent, renderedComponent: HTMLSpanElement) {
-    if (component === 'description') {
-        const tags = renderedComponent.getElementsByClassName('tag');
-        for (let i = 0; i < tags.length; i++) {
-            const tagName = tags[i].textContent;
-            if (tagName) {
-                const className = tagToAttributeValue(tagName);
-                const element = tags[i] as HTMLElement;
-                if (className) element.dataset.tagName = className;
+        if (component === 'description') {
+            const tags = internalSpan.getElementsByClassName('tag');
+            for (let i = 0; i < tags.length; i++) {
+                const tagName = tags[i].textContent;
+                if (tagName) {
+                    const className = tagToAttributeValue(tagName);
+                    const element = tags[i] as HTMLElement;
+                    if (className) element.dataset.tagName = className;
+                }
             }
         }
     }
-}
-
-/**
- * Translate a relative date to a CSS class: 'today', 'future-1d' (for tomorrow), 'past-1d' (for yesterday)
- * etc.
- * A cutoff (in days) is defined in MAX_DAY_VALUE_RANGE, from beyond that a generic 'far' postfix will be added.
- * (the cutoff exists because we don't want to flood the DOM with potentially hundreds of unique classes.)
- */
-function dateToAttribute(date: Moment) {
-    const today = window.moment().startOf('day');
-    let result = '';
-    const diffDays = today.diff(date, 'days');
-    if (isNaN(diffDays)) return null;
-    if (diffDays === 0) return 'today';
-    else if (diffDays > 0) result += 'past-';
-    else if (diffDays < 0) result += 'future-';
-    if (Math.abs(diffDays) <= MAX_DAY_VALUE_RANGE) {
-        result += Math.abs(diffDays).toString() + 'd';
-    } else {
-        result += DAY_VALUE_OVER_RANGE_POSTFIX;
-    }
-    return result;
-}
 
-/*
- * Sanitize tag names so they will be valid attribute values according to the HTML spec:
- * https://html.spec.whatwg.org/multipage/parsing.html#attribute-value-(double-quoted)-state
- */
-function tagToAttributeValue(tag: string) {
-    // eslint-disable-next-line no-control-regex
-    const illegalChars = /["&\x00\r\n]/g;
-    let sanitizedTag = tag.replace(illegalChars, '-');
-    // And if after sanitazation the name starts with dashes or underscores, remove them.
-    sanitizedTag = sanitizedTag.replace(/^[-_]+/, '');
-    if (sanitizedTag.length > 0) return sanitizedTag;
-    else return null;
-}
+    private addTooltip(task: Task, element: HTMLSpanElement, isFilenameUnique: boolean | undefined) {
+        const {
+            recurrenceSymbol,
+            startDateSymbol,
+            createdDateSymbol,
+            scheduledDateSymbol,
+            dueDateSymbol,
+            doneDateSymbol,
+        } = TASK_FORMATS.tasksPluginEmoji.taskSerializer.symbols;
+
+        element.addEventListener('mouseenter', () => {
+            function addDateToTooltip(tooltip: HTMLDivElement, date: Moment | null, signifier: string) {
+                if (date) {
+                    const createdDateDiv = tooltip.createDiv();
+                    createdDateDiv.setText(
+                        toTooltipDate({
+                            signifier: signifier,
+                            date: date,
+                        }),
+                    );
+                }
+            }
 
-function addTooltip({
-    task,
-    element,
-    isFilenameUnique,
-}: {
-    task: Task;
-    element: HTMLElement;
-    isFilenameUnique: boolean | undefined;
-}): void {
-    const { recurrenceSymbol, startDateSymbol, createdDateSymbol, scheduledDateSymbol, dueDateSymbol, doneDateSymbol } =
-        TASK_FORMATS.tasksPluginEmoji.taskSerializer.symbols;
-
-    element.addEventListener('mouseenter', () => {
-        const tooltip = element.createDiv();
-        tooltip.addClasses(['tooltip', 'pop-up']);
-
-        if (task.recurrence) {
-            const recurrenceDiv = tooltip.createDiv();
-            recurrenceDiv.setText(`${recurrenceSymbol} ${task.recurrence.toText()}`);
-        }
+            function toTooltipDate({ signifier, date }: { signifier: string; date: Moment }): string {
+                return `${signifier} ${date.format(taskModule.TaskRegularExpressions.dateFormat)} (${date.from(
+                    window.moment().startOf('day'),
+                )})`;
+            }
 
-        addDateToTooltip(tooltip, task.createdDate, createdDateSymbol);
-        addDateToTooltip(tooltip, task.startDate, startDateSymbol);
-        addDateToTooltip(tooltip, task.scheduledDate, scheduledDateSymbol);
-        addDateToTooltip(tooltip, task.dueDate, dueDateSymbol);
-        addDateToTooltip(tooltip, task.doneDate, doneDateSymbol);
+            const tooltip = element.createDiv();
+            tooltip.addClasses(['tooltip', 'pop-up']);
 
-        const linkText = task.getLinkText({ isFilenameUnique });
-        if (linkText) {
-            const backlinkDiv = tooltip.createDiv();
-            backlinkDiv.setText(`🔗 ${linkText}`);
-        }
-
-        element.addEventListener('mouseleave', () => {
-            tooltip.remove();
-        });
-    });
-}
+            // NEW_TASK_FIELD_EDIT_REQUIRED
+            if (task.recurrence) {
+                const recurrenceDiv = tooltip.createDiv();
+                recurrenceDiv.setText(`${recurrenceSymbol} ${task.recurrence.toText()}`);
+            }
 
-function addDateToTooltip(tooltip: HTMLDivElement, date: Moment | null, signifier: string) {
-    if (date) {
-        const createdDateDiv = tooltip.createDiv();
-        createdDateDiv.setText(
-            toTooltipDate({
-                signifier: signifier,
-                date: date,
-            }),
-        );
-    }
-}
+            addDateToTooltip(tooltip, task.createdDate, createdDateSymbol);
+            addDateToTooltip(tooltip, task.startDate, startDateSymbol);
+            addDateToTooltip(tooltip, task.scheduledDate, scheduledDateSymbol);
+            addDateToTooltip(tooltip, task.dueDate, dueDateSymbol);
+            addDateToTooltip(tooltip, task.doneDate, doneDateSymbol);
 
-function toTooltipDate({ signifier, date }: { signifier: string; date: Moment }): string {
-    return `${signifier} ${date.format(taskModule.TaskRegularExpressions.dateFormat)} (${date.from(
-        window.moment().startOf('day'),
-    )})`;
-}
+            const linkText = task.getLinkText({ isFilenameUnique });
+            if (linkText) {
+                const backlinkDiv = tooltip.createDiv();
+                backlinkDiv.setText(`🔗 ${linkText}`);
+            }
 
-/**
- * Create an HTML rendered List Item element (LI) for a task.
- * @note Output is based on the {@link DefaultTaskSerializer}'s format, with default (emoji) symbols
- * @param task
- * @param renderDetails
- */
-export function taskToLi(task: Task, renderDetails: TaskLineRenderDetails): Promise<HTMLLIElement> {
-    async function obsidianMarkdownRenderer(
-        text: string,
-        element: HTMLSpanElement,
-        path: string,
-        obsidianComponent: Component | null,
-    ) {
-        if (!obsidianComponent) throw new Error('Must call the Obsidian renderer with an Obsidian Component object');
-        await MarkdownRenderer.renderMarkdown(text, element, path, obsidianComponent);
+            element.addEventListener('mouseleave', () => {
+                tooltip.remove();
+            });
+        });
     }
-
-    return renderTaskLine(task, renderDetails, obsidianMarkdownRenderer);
 }
diff --git a/src/TaskSerializer/DataviewTaskSerializer.ts b/src/TaskSerializer/DataviewTaskSerializer.ts
index 47058f1d..6a95bd6c 100644
--- a/src/TaskSerializer/DataviewTaskSerializer.ts
+++ b/src/TaskSerializer/DataviewTaskSerializer.ts
@@ -59,6 +59,7 @@ function toInlineFieldRegex(innerFieldRegex: RegExp): RegExp {
  *   [Dataview]{@link https://github.com/blacksmithgu/obsidian-dataview}
  */
 export const DATAVIEW_SYMBOLS = {
+    // NEW_TASK_FIELD_EDIT_REQUIRED
     prioritySymbols: {
         Highest: 'priority:: highest',
         High: 'priority:: high',
diff --git a/src/TaskSerializer/DefaultTaskSerializer.ts b/src/TaskSerializer/DefaultTaskSerializer.ts
index 024899c7..d66f51bb 100644
--- a/src/TaskSerializer/DefaultTaskSerializer.ts
+++ b/src/TaskSerializer/DefaultTaskSerializer.ts
@@ -12,6 +12,7 @@ import type { TaskDetails, TaskSerializer } from '.';
  * @interface DefaultTaskSerializerSymbols
  */
 export interface DefaultTaskSerializerSymbols {
+    // NEW_TASK_FIELD_EDIT_REQUIRED
     readonly prioritySymbols: {
         Highest: string;
         High: string;
@@ -42,6 +43,7 @@ export interface DefaultTaskSerializerSymbols {
  * Uses emojis to concisely convey meaning
  */
 export const DEFAULT_SYMBOLS: DefaultTaskSerializerSymbols = {
+    // NEW_TASK_FIELD_EDIT_REQUIRED
     prioritySymbols: {
         Highest: '🔺',
         High: '⏫',
@@ -92,6 +94,7 @@ export class DefaultTaskSerializer implements TaskSerializer {
      */
     public componentToString(task: Task, layout: TaskLayout, component: TaskLayoutComponent) {
         const {
+            // NEW_TASK_FIELD_EDIT_REQUIRED
             prioritySymbols,
             startDateSymbol,
             createdDateSymbol,
@@ -102,6 +105,7 @@ export class DefaultTaskSerializer implements TaskSerializer {
         } = this.symbols;
 
         switch (component) {
+            // NEW_TASK_FIELD_EDIT_REQUIRED
             case 'description':
                 return task.description;
             case 'priority': {
@@ -195,6 +199,7 @@ export class DefaultTaskSerializer implements TaskSerializer {
         // Keep matching and removing special strings from the end of the
         // description in any order. The loop should only run once if the
         // strings are in the expected order after the description.
+        // NEW_TASK_FIELD_EDIT_REQUIRED
         let matched: boolean;
         let priority: Priority = Priority.None;
         let startDate: Moment | null = null;
@@ -213,6 +218,7 @@ export class DefaultTaskSerializer implements TaskSerializer {
         const maxRuns = 20;
         let runs = 0;
         do {
+            // NEW_TASK_FIELD_EDIT_REQUIRED
             matched = false;
             const priorityMatch = line.match(TaskFormatRegularExpressions.priorityRegex);
             if (priorityMatch !== null) {
@@ -295,6 +301,7 @@ export class DefaultTaskSerializer implements TaskSerializer {
         // to actually have the description 'Do something #tag1 #tag2'
         if (trailingTags.length > 0) line += ' ' + trailingTags;
 
+        // NEW_TASK_FIELD_EDIT_REQUIRED
         return {
             description: line,
             priority,
diff --git a/src/TaskSerializer/index.ts b/src/TaskSerializer/index.ts
index 85d68201..ee2b347a 100644
--- a/src/TaskSerializer/index.ts
+++ b/src/TaskSerializer/index.ts
@@ -11,6 +11,7 @@ type Writeable<T> = { -readonly [P in keyof T]: T[P] };
 export type TaskDetails = Writeable<
     Pick<
         Task,
+        // NEW_TASK_FIELD_EDIT_REQUIRED
         | 'description'
         | 'priority'
         | 'startDate'
diff --git a/src/ui/EditTask.svelte b/src/ui/EditTask.svelte
index b473bfe1..f04b0c03 100644
--- a/src/ui/EditTask.svelte
+++ b/src/ui/EditTask.svelte
@@ -15,6 +15,7 @@
     export let statusOptions: Status[];
 
     const {
+        // NEW_TASK_FIELD_EDIT_REQUIRED
         prioritySymbols,
         recurrenceSymbol,
         startDateSymbol,
@@ -24,6 +25,7 @@
 
     let descriptionInput: HTMLTextAreaElement;
     let editableTask: {
+        // NEW_TASK_FIELD_EDIT_REQUIRED
         description: string;
         status: Status;
         priority: 'none' | 'lowest' | 'low' | 'medium' | 'high' | 'highest';
@@ -184,6 +186,7 @@
     $: formIsValid = isDueDateValid && isRecurrenceValid && isScheduledDateValid && isStartDateValid && isDescriptionValid;
     $: isDescriptionValid = editableTask.description.trim() !== '';
 
+    // NEW_TASK_FIELD_EDIT_REQUIRED
     $: {
         editableTask.startDate = doAutocomplete(editableTask.startDate);
         parsedStartDate = parseTypedDateForDisplayUsingFutureDate('start', editableTask.startDate);
@@ -255,6 +258,7 @@
         }
 
         editableTask = {
+            // NEW_TASK_FIELD_EDIT_REQUIRED
             description,
             status: task.status,
             priority,
@@ -300,6 +304,7 @@
     }
 
     const _onSubmit = () => {
+        // NEW_TASK_FIELD_EDIT_REQUIRED
         let description = editableTask.description.trim();
         if (addGlobalFilterOnSave) {
             description = GlobalFilter.getInstance().prependTo(description);
@@ -343,6 +348,7 @@
         }
 
         const updatedTask = new Task({
+            // NEW_TASK_FIELD_EDIT_REQUIRED
             ...task,
             description,
             status: editableTask.status,
@@ -364,6 +370,8 @@
 
 <div class="tasks-modal">
     <form on:submit|preventDefault={_onSubmit} class:with-accesskeys="{withAccessKeys}">
+        <!-- NEW_TASK_FIELD_EDIT_REQUIRED -->
+
         <!-- --------------------------------------------------------------------------- -->
         <!--  Description  -->
         <!-- --------------------------------------------------------------------------- -->
diff --git a/versions.json b/versions.json
index 1ec90bd9..977c84a2 100644
--- a/versions.json
+++ b/versions.json
@@ -82,5 +82,6 @@
   "4.8.1": "1.1.1",
   "4.9.0": "1.1.1",
   "5.0.0": "1.1.1",
-  "5.1.0": "1.1.1"
+  "5.1.0": "1.1.1",
+  "5.1.1": "1.1.1"
 }
diff --git a/yarn.lock b/yarn.lock
index 302ce9ea..51b381dd 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2,6 +2,11 @@
 # yarn lockfile v1
 
 
+"@aashutoshrathi/word-wrap@^1.2.3":
+  version "1.2.6"
+  resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf"
+  integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==
+
 "@adobe/css-tools@^4.0.1":
   version "4.3.1"
   resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.3.1.tgz#abfccb8ca78075a2b6187345c26243c1a0842f28"
@@ -22,14 +27,7 @@
   dependencies:
     "@babel/highlight" "^7.18.6"
 
-"@babel/code-frame@^7.12.13", "@babel/code-frame@^7.14.5", "@babel/code-frame@^7.15.8":
-  version "7.15.8"
-  resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.15.8.tgz#45990c47adadb00c03677baa89221f7cc23d2503"
-  integrity sha512-2IAnmn8zbvC/jKYhq5Ki9I+DwjlrtMPUCH/CpHvqI4dNnlwHwsxoIhlc8WcYY5LSYknXQtAlFYuHfqAFCvQ4Wg==
-  dependencies:
-    "@babel/highlight" "^7.14.5"
-
-"@babel/code-frame@^7.22.13":
+"@babel/code-frame@^7.12.13", "@babel/code-frame@^7.22.13":
   version "7.22.13"
   resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e"
   integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==
@@ -37,6 +35,13 @@
     "@babel/highlight" "^7.22.13"
     chalk "^2.4.2"
 
+"@babel/code-frame@^7.14.5", "@babel/code-frame@^7.15.8":
+  version "7.15.8"
+  resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.15.8.tgz#45990c47adadb00c03677baa89221f7cc23d2503"
+  integrity sha512-2IAnmn8zbvC/jKYhq5Ki9I+DwjlrtMPUCH/CpHvqI4dNnlwHwsxoIhlc8WcYY5LSYknXQtAlFYuHfqAFCvQ4Wg==
+  dependencies:
+    "@babel/highlight" "^7.14.5"
+
 "@babel/compat-data@^7.15.0":
   version "7.15.0"
   resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.15.0.tgz#2dbaf8b85334796cafbb0f5793a90a2fc010b176"
@@ -278,16 +283,16 @@
   resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f"
   integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==
 
-"@babel/helper-validator-identifier@^7.14.5", "@babel/helper-validator-identifier@^7.18.6":
-  version "7.18.6"
-  resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz#9c97e30d31b2b8c72a1d08984f2ca9b574d7a076"
-  integrity sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==
-
 "@babel/helper-validator-identifier@^7.14.9", "@babel/helper-validator-identifier@^7.15.7":
   version "7.15.7"
   resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz#220df993bfe904a4a6b02ab4f3385a5ebf6e2389"
   integrity sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==
 
+"@babel/helper-validator-identifier@^7.18.6":
+  version "7.18.6"
+  resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz#9c97e30d31b2b8c72a1d08984f2ca9b574d7a076"
+  integrity sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==
+
 "@babel/helper-validator-identifier@^7.22.20":
   version "7.22.20"
   resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0"
@@ -321,13 +326,13 @@
     "@babel/traverse" "^7.18.6"
     "@babel/types" "^7.18.6"
 
-"@babel/highlight@^7.14.5":
-  version "7.14.5"
-  resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.5.tgz#6861a52f03966405001f6aa534a01a24d99e8cd9"
-  integrity sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==
+"@babel/highlight@^7.14.5", "@babel/highlight@^7.22.13":
+  version "7.22.20"
+  resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.20.tgz#4ca92b71d80554b01427815e06f2df965b9c1f54"
+  integrity sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==
   dependencies:
-    "@babel/helper-validator-identifier" "^7.14.5"
-    chalk "^2.0.0"
+    "@babel/helper-validator-identifier" "^7.22.20"
+    chalk "^2.4.2"
     js-tokens "^4.0.0"
 
 "@babel/highlight@^7.18.6":
@@ -339,15 +344,6 @@
     chalk "^2.0.0"
     js-tokens "^4.0.0"
 
-"@babel/highlight@^7.22.13":
-  version "7.22.20"
-  resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.20.tgz#4ca92b71d80554b01427815e06f2df965b9c1f54"
-  integrity sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==
-  dependencies:
-    "@babel/helper-validator-identifier" "^7.22.20"
-    chalk "^2.4.2"
-    js-tokens "^4.0.0"
-
 "@babel/parser@^7.0.0", "@babel/parser@^7.21.4":
   version "7.22.16"
   resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.16.tgz#180aead7f247305cce6551bea2720934e2fa2c95"
@@ -466,13 +462,20 @@
   dependencies:
     "@babel/helper-plugin-utils" "^7.14.5"
 
-"@babel/runtime@^7.12.5", "@babel/runtime@^7.9.2":
+"@babel/runtime@^7.12.5":
   version "7.21.0"
   resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.0.tgz#5b55c9d394e5fcf304909a8b00c07dc217b56673"
   integrity sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==
   dependencies:
     regenerator-runtime "^0.13.11"
 
+"@babel/runtime@^7.9.2":
+  version "7.23.2"
+  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.2.tgz#062b0ac103261d68a966c4c7baf2ae3e62ec3885"
+  integrity sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==
+  dependencies:
+    regenerator-runtime "^0.14.0"
+
 "@babel/template@^7.15.4", "@babel/template@^7.3.3":
   version "7.15.4"
   resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.15.4.tgz#51898d35dcf3faa670c4ee6afcfd517ee139f194"
@@ -585,19 +588,19 @@
   dependencies:
     eslint-visitor-keys "^3.3.0"
 
-"@eslint-community/regexpp@^4.4.0":
-  version "4.5.0"
-  resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.5.0.tgz#f6f729b02feee2c749f57e334b7a1b5f40a81724"
-  integrity sha512-vITaYzIcNmjn5tF5uxcZ/ft7/RXGrMUIS9HalWckEOF6ESiwXKoMzAQf2UW0aVd6rnOeExTJVd5hmWXucBKGXQ==
+"@eslint-community/regexpp@^4.4.0", "@eslint-community/regexpp@^4.6.1":
+  version "4.10.0"
+  resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.10.0.tgz#548f6de556857c8bb73bbee70c35dc82a2e74d63"
+  integrity sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==
 
-"@eslint/eslintrc@^2.0.3":
-  version "2.0.3"
-  resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.0.3.tgz#4910db5505f4d503f27774bf356e3704818a0331"
-  integrity sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==
+"@eslint/eslintrc@^2.1.3":
+  version "2.1.3"
+  resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.3.tgz#797470a75fe0fbd5a53350ee715e85e87baff22d"
+  integrity sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==
   dependencies:
     ajv "^6.12.4"
     debug "^4.3.2"
-    espree "^9.5.2"
+    espree "^9.6.0"
     globals "^13.19.0"
     ignore "^5.2.0"
     import-fresh "^3.2.1"
@@ -605,22 +608,22 @@
     minimatch "^3.1.2"
     strip-json-comments "^3.1.1"
 
-"@eslint/js@8.41.0":
-  version "8.41.0"
-  resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.41.0.tgz#080321c3b68253522f7646b55b577dd99d2950b3"
-  integrity sha512-LxcyMGxwmTh2lY9FwHPGWOHmYFCZvbrFCBZL4FzSSsxsRPuhrYUg/49/0KDfW8tnIEaEHtfmn6+NPN+1DqaNmA==
+"@eslint/js@8.54.0":
+  version "8.54.0"
+  resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.54.0.tgz#4fab9a2ff7860082c304f750e94acd644cf984cf"
+  integrity sha512-ut5V+D+fOoWPgGGNj83GGjnntO39xDy6DWxO0wb7Jp3DcMX0TfIqdzHF85VTQkerdyGmuuMD9AKAo5KiNlf/AQ==
 
 "@evilmartians/lefthook@^1.5.2":
   version "1.5.2"
   resolved "https://registry.yarnpkg.com/@evilmartians/lefthook/-/lefthook-1.5.2.tgz#9a10711368f6386be7e633ad70bf2bca27e5a022"
   integrity sha512-CI2TYP0KFGxd3iXDj/4JcFxN+aMWxC5raRRQjJzXATPok5IVC41+SAG1O05Ec1AQJGN4b6iU1bxqtopbsKgWpg==
 
-"@humanwhocodes/config-array@^0.11.8":
-  version "0.11.8"
-  resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.8.tgz#03595ac2075a4dc0f191cc2131de14fbd7d410b9"
-  integrity sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==
+"@humanwhocodes/config-array@^0.11.13":
+  version "0.11.13"
+  resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.13.tgz#075dc9684f40a531d9b26b0822153c1e832ee297"
+  integrity sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==
   dependencies:
-    "@humanwhocodes/object-schema" "^1.2.1"
+    "@humanwhocodes/object-schema" "^2.0.1"
     debug "^4.1.1"
     minimatch "^3.0.5"
 
@@ -629,10 +632,10 @@
   resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c"
   integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==
 
-"@humanwhocodes/object-schema@^1.2.1":
-  version "1.2.1"
-  resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
-  integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
+"@humanwhocodes/object-schema@^2.0.1":
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz#e5211452df060fa8522b55c7b3c0c4d1981cb044"
+  integrity sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==
 
 "@istanbuljs/load-nyc-config@^1.0.0":
   version "1.1.0"
@@ -696,16 +699,6 @@
     slash "^3.0.0"
     strip-ansi "^6.0.0"
 
-"@jest/environment@^29.5.0":
-  version "29.5.0"
-  resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.5.0.tgz#9152d56317c1fdb1af389c46640ba74ef0bb4c65"
-  integrity sha512-5FXw2+wD29YU1d4I2htpRX7jYnAyTRjP2CsXQdo9SAM8g3ifxWPSV0HnClSn71xwctr0U3oZIIH+dtbfmnbXVQ==
-  dependencies:
-    "@jest/fake-timers" "^29.5.0"
-    "@jest/types" "^29.5.0"
-    "@types/node" "*"
-    jest-mock "^29.5.0"
-
 "@jest/environment@^29.7.0":
   version "29.7.0"
   resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.7.0.tgz#24d61f54ff1f786f3cd4073b4b94416383baf2a7"
@@ -731,18 +724,6 @@
     expect "^29.7.0"
     jest-snapshot "^29.7.0"
 
-"@jest/fake-timers@^29.5.0":
-  version "29.5.0"
-  resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.5.0.tgz#d4d09ec3286b3d90c60bdcd66ed28d35f1b4dc2c"
-  integrity sha512-9ARvuAAQcBwDAqOnglWq2zwNIRUDtk/SCkp/ToGEhFv5r86K21l+VEs0qNTaXtyiY0lEePl3kylijSYJQqdbDg==
-  dependencies:
-    "@jest/types" "^29.5.0"
-    "@sinonjs/fake-timers" "^10.0.2"
-    "@types/node" "*"
-    jest-message-util "^29.5.0"
-    jest-mock "^29.5.0"
-    jest-util "^29.5.0"
-
 "@jest/fake-timers@^29.7.0":
   version "29.7.0"
   resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.7.0.tgz#fd91bf1fffb16d7d0d24a426ab1a47a49881a565"
@@ -943,9 +924,9 @@
     type-detect "4.0.8"
 
 "@sinonjs/fake-timers@^10.0.2":
-  version "10.2.0"
-  resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.2.0.tgz#b3e322a34c5f26e3184e7f6115695f299c1b1194"
-  integrity sha512-OPwQlEdg40HAj5KNF8WW6q2KG4Z+cBCZb3m4ninfTZKaBmbIJodviQsDBoYMPHkOyJJMHnOJo5j2+LKDOhOACg==
+  version "10.3.0"
+  resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz#55fdff1ecab9f354019129daf4df0dd4d923ea66"
+  integrity sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==
   dependencies:
     "@sinonjs/commons" "^3.0.0"
 
@@ -964,9 +945,9 @@
     pretty-format "^27.0.2"
 
 "@testing-library/jest-dom@^5.16.5":
-  version "5.16.5"
-  resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.16.5.tgz#3912846af19a29b2dbf32a6ae9c31ef52580074e"
-  integrity sha512-N5ixQ2qKpi5OLYfwQmUb/5mSV9LneAcaUfp32pn4yCnpb8r/Yz0pXFPck21dIicKmi+ta5WRAknkZCfA8refMA==
+  version "5.17.0"
+  resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.17.0.tgz#5e97c8f9a15ccf4656da00fecab505728de81e0c"
+  integrity sha512-ynmNeT7asXyH3aSVv4vvX4Rb+0qjOhdNHnO/3vuZNqPmhDpV/+rCSGwQ7bLcmU2cJ4dvoheIO85LQj0IbJHEtg==
   dependencies:
     "@adobe/css-tools" "^4.0.1"
     "@babel/runtime" "^7.9.2"
@@ -1052,22 +1033,27 @@
   dependencies:
     "@types/node" "*"
 
-"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1":
+"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0":
+  version "2.0.6"
+  resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7"
+  integrity sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==
+
+"@types/istanbul-lib-coverage@^2.0.1":
   version "2.0.3"
   resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762"
   integrity sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==
 
 "@types/istanbul-lib-report@*":
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686"
-  integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==
+  version "3.0.3"
+  resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz#53047614ae72e19fc0401d872de3ae2b4ce350bf"
+  integrity sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==
   dependencies:
     "@types/istanbul-lib-coverage" "*"
 
 "@types/istanbul-reports@^3.0.0":
-  version "3.0.1"
-  resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz#9153fe98bba2bd565a63add9436d6f0d7f8468ff"
-  integrity sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==
+  version "3.0.4"
+  resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz#0f03e3d2f670fbdac586e34b433783070cc16f54"
+  integrity sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==
   dependencies:
     "@types/istanbul-lib-report" "*"
 
@@ -1089,39 +1075,41 @@
     parse5 "^7.0.0"
 
 "@types/json-schema@^7.0.9":
-  version "7.0.11"
-  resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3"
-  integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==
+  version "7.0.15"
+  resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
+  integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==
 
 "@types/json5@^0.0.29":
   version "0.0.29"
   resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
   integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==
 
-"@types/mustache@^4.2.2":
-  version "4.2.2"
-  resolved "https://registry.yarnpkg.com/@types/mustache/-/mustache-4.2.2.tgz#825bf5c214c3ab84d0b23fef2c8eb898f3ff8717"
-  integrity sha512-MUSpfpW0yZbTgjekDbH0shMYBUD+X/uJJJMm9LXN1d5yjl5lCY1vN/eWKD6D1tOtjA6206K0zcIPnUaFMurdNA==
+"@types/mustache@^4.2.5":
+  version "4.2.5"
+  resolved "https://registry.yarnpkg.com/@types/mustache/-/mustache-4.2.5.tgz#9129f0d6857f976e00e171bbb3460e4b702f84ef"
+  integrity sha512-PLwiVvTBg59tGFL/8VpcGvqOu3L4OuveNvPi0EYbWchRdEVP++yRUXJPFl+CApKEq13017/4Nf7aQ5lTtHUNsA==
 
 "@types/node@*":
-  version "18.0.3"
-  resolved "https://registry.yarnpkg.com/@types/node/-/node-18.0.3.tgz#463fc47f13ec0688a33aec75d078a0541a447199"
-  integrity sha512-HzNRZtp4eepNitP+BD6k2L6DROIDG4Q0fm4x+dwfsr6LGmROENnok75VGw40628xf+iR24WeMFcHuuBDUAzzsQ==
+  version "20.9.2"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-20.9.2.tgz#002815c8e87fe0c9369121c78b52e800fadc0ac6"
+  integrity sha512-WHZXKFCEyIUJzAwh3NyyTHYSR35SevJ6mZ1nWwJafKtiQbqRTIKSRcw3Ma3acqgsent3RRDqeVwpHntMk+9irg==
+  dependencies:
+    undici-types "~5.26.4"
 
 "@types/pug@^2.0.6":
-  version "2.0.6"
-  resolved "https://registry.yarnpkg.com/@types/pug/-/pug-2.0.6.tgz#f830323c88172e66826d0bde413498b61054b5a6"
-  integrity sha512-SnHmG9wN1UVmagJOnyo/qkk0Z7gejYxOYYmaAwr5u2yFYfsupN3sg10kyzN8Hep/2zbHxCnsumxOoRIRMBwKCg==
+  version "2.0.9"
+  resolved "https://registry.yarnpkg.com/@types/pug/-/pug-2.0.9.tgz#97e765bb8eb0b90380524583f1f10838f29822f9"
+  integrity sha512-Yg4LkgFYvn1faISbDNWmcAC1XoDT8IoMUFspp5mnagKk+UvD2N0IWt5A7GRdMubsNWqgCLmrkf8rXkzNqb4szA==
 
 "@types/semver@^7.3.12":
-  version "7.3.13"
-  resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.13.tgz#da4bfd73f49bd541d28920ab0e2bf0ee80f71c91"
-  integrity sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==
+  version "7.5.5"
+  resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.5.tgz#deed5ab7019756c9c90ea86139106b0346223f35"
+  integrity sha512-+d+WYC1BxJ6yVOgUgzK8gWvp5qF8ssV5r4nsDcZWKRWcDQLQ619tvWAxJQYGgBrO1MnLJC7a5GtiYsAoQ47dJg==
 
 "@types/stack-utils@^2.0.0":
-  version "2.0.1"
-  resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c"
-  integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8"
+  integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==
 
 "@types/tern@*":
   version "0.23.4"
@@ -1131,70 +1119,70 @@
     "@types/estree" "*"
 
 "@types/testing-library__jest-dom@^5.9.1":
-  version "5.14.5"
-  resolved "https://registry.yarnpkg.com/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.5.tgz#d113709c90b3c75fdb127ec338dad7d5f86c974f"
-  integrity sha512-SBwbxYoyPIvxHbeHxTZX2Pe/74F/tX2/D3mMvzabdeJ25bBojfW0TyB8BHrbq/9zaaKICJZjLP+8r6AeZMFCuQ==
+  version "5.14.9"
+  resolved "https://registry.yarnpkg.com/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.9.tgz#0fb1e6a0278d87b6737db55af5967570b67cb466"
+  integrity sha512-FSYhIjFlfOpGSRyVoMBMuS3ws5ehFQODymf3vlI7U1K8c7PHwWwFY7VREfmsuzHSOnoKs/9/Y983ayOs7eRzqw==
   dependencies:
     "@types/jest" "*"
 
 "@types/tough-cookie@*":
-  version "4.0.2"
-  resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.2.tgz#6286b4c7228d58ab7866d19716f3696e03a09397"
-  integrity sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==
+  version "4.0.5"
+  resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.5.tgz#cb6e2a691b70cb177c6e3ae9c1d2e8b2ea8cd304"
+  integrity sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==
 
 "@types/yargs-parser@*":
-  version "20.2.1"
-  resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.1.tgz#3b9ce2489919d9e4fea439b76916abc34b2df129"
-  integrity sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==
+  version "21.0.3"
+  resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15"
+  integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==
 
 "@types/yargs@^17.0.8":
-  version "17.0.10"
-  resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.10.tgz#591522fce85d8739bca7b8bb90d048e4478d186a"
-  integrity sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==
+  version "17.0.31"
+  resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.31.tgz#8fd0089803fd55d8a285895a18b88cb71a99683c"
+  integrity sha512-bocYSx4DI8TmdlvxqGpVNXOgCNR1Jj0gNPhhAY+iz1rgKDAaYrAYdFYnhDV1IFuiuVc9HkOwyDcFxaTElF3/wg==
   dependencies:
     "@types/yargs-parser" "*"
 
 "@typescript-eslint/eslint-plugin@^5.59.8":
-  version "5.59.8"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.8.tgz#1e7a3e5318ece22251dfbc5c9c6feeb4793cc509"
-  integrity sha512-JDMOmhXteJ4WVKOiHXGCoB96ADWg9q7efPWHRViT/f09bA8XOMLAVHHju3l0MkZnG1izaWXYmgvQcUjTRcpShQ==
+  version "5.62.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz#aeef0328d172b9e37d9bab6dbc13b87ed88977db"
+  integrity sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==
   dependencies:
     "@eslint-community/regexpp" "^4.4.0"
-    "@typescript-eslint/scope-manager" "5.59.8"
-    "@typescript-eslint/type-utils" "5.59.8"
-    "@typescript-eslint/utils" "5.59.8"
+    "@typescript-eslint/scope-manager" "5.62.0"
+    "@typescript-eslint/type-utils" "5.62.0"
+    "@typescript-eslint/utils" "5.62.0"
     debug "^4.3.4"
-    grapheme-splitter "^1.0.4"
+    graphemer "^1.4.0"
     ignore "^5.2.0"
     natural-compare-lite "^1.4.0"
     semver "^7.3.7"
     tsutils "^3.21.0"
 
 "@typescript-eslint/parser@^5.59.8":
-  version "5.59.8"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.59.8.tgz#60cbb00671d86cf746044ab797900b1448188567"
-  integrity sha512-AnR19RjJcpjoeGojmwZtCwBX/RidqDZtzcbG3xHrmz0aHHoOcbWnpDllenRDmDvsV0RQ6+tbb09/kyc+UT9Orw==
+  version "5.62.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.62.0.tgz#1b63d082d849a2fcae8a569248fbe2ee1b8a56c7"
+  integrity sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==
   dependencies:
-    "@typescript-eslint/scope-manager" "5.59.8"
-    "@typescript-eslint/types" "5.59.8"
-    "@typescript-eslint/typescript-estree" "5.59.8"
+    "@typescript-eslint/scope-manager" "5.62.0"
+    "@typescript-eslint/types" "5.62.0"
+    "@typescript-eslint/typescript-estree" "5.62.0"
     debug "^4.3.4"
 
-"@typescript-eslint/scope-manager@5.59.8":
-  version "5.59.8"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.59.8.tgz#ff4ad4fec6433647b817c4a7d4b4165d18ea2fa8"
-  integrity sha512-/w08ndCYI8gxGf+9zKf1vtx/16y8MHrZs5/tnjHhMLNSixuNcJavSX4wAiPf4aS5x41Es9YPCn44MIe4cxIlig==
+"@typescript-eslint/scope-manager@5.62.0":
+  version "5.62.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz#d9457ccc6a0b8d6b37d0eb252a23022478c5460c"
+  integrity sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==
   dependencies:
-    "@typescript-eslint/types" "5.59.8"
-    "@typescript-eslint/visitor-keys" "5.59.8"
+    "@typescript-eslint/types" "5.62.0"
+    "@typescript-eslint/visitor-keys" "5.62.0"
 
-"@typescript-eslint/type-utils@5.59.8":
-  version "5.59.8"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.59.8.tgz#aa6c029a9d7706d26bbd25eb4666398781df6ea2"
-  integrity sha512-+5M518uEIHFBy3FnyqZUF3BMP+AXnYn4oyH8RF012+e7/msMY98FhGL5SrN29NQ9xDgvqCgYnsOiKp1VjZ/fpA==
+"@typescript-eslint/type-utils@5.62.0":
+  version "5.62.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz#286f0389c41681376cdad96b309cedd17d70346a"
+  integrity sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==
   dependencies:
-    "@typescript-eslint/typescript-estree" "5.59.8"
-    "@typescript-eslint/utils" "5.59.8"
+    "@typescript-eslint/typescript-estree" "5.62.0"
+    "@typescript-eslint/utils" "5.62.0"
     debug "^4.3.4"
     tsutils "^3.21.0"
 
@@ -1203,23 +1191,18 @@
   resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.33.0.tgz#a1e59036a3b53ae8430ceebf2a919dc7f9af6d72"
   integrity sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ==
 
-"@typescript-eslint/types@5.59.8":
-  version "5.59.8"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.59.8.tgz#212e54414733618f5d0fd50b2da2717f630aebf8"
-  integrity sha512-+uWuOhBTj/L6awoWIg0BlWy0u9TyFpCHrAuQ5bNfxDaZ1Ppb3mx6tUigc74LHcbHpOHuOTOJrBoAnhdHdaea1w==
-
 "@typescript-eslint/types@5.62.0":
   version "5.62.0"
   resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f"
   integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==
 
-"@typescript-eslint/typescript-estree@5.59.8":
-  version "5.59.8"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.8.tgz#801a7b1766481629481b3b0878148bd7a1f345d7"
-  integrity sha512-Jy/lPSDJGNow14vYu6IrW790p7HIf/SOV1Bb6lZ7NUkLc2iB2Z9elESmsaUtLw8kVqogSbtLH9tut5GCX1RLDg==
+"@typescript-eslint/typescript-estree@5.62.0", "@typescript-eslint/typescript-estree@^5.55.0":
+  version "5.62.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz#7d17794b77fabcac615d6a48fb143330d962eb9b"
+  integrity sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==
   dependencies:
-    "@typescript-eslint/types" "5.59.8"
-    "@typescript-eslint/visitor-keys" "5.59.8"
+    "@typescript-eslint/types" "5.62.0"
+    "@typescript-eslint/visitor-keys" "5.62.0"
     debug "^4.3.4"
     globby "^11.1.0"
     is-glob "^4.0.3"
@@ -1239,30 +1222,17 @@
     semver "^7.3.5"
     tsutils "^3.21.0"
 
-"@typescript-eslint/typescript-estree@^5.55.0":
+"@typescript-eslint/utils@5.62.0":
   version "5.62.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz#7d17794b77fabcac615d6a48fb143330d962eb9b"
-  integrity sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==
-  dependencies:
-    "@typescript-eslint/types" "5.62.0"
-    "@typescript-eslint/visitor-keys" "5.62.0"
-    debug "^4.3.4"
-    globby "^11.1.0"
-    is-glob "^4.0.3"
-    semver "^7.3.7"
-    tsutils "^3.21.0"
-
-"@typescript-eslint/utils@5.59.8":
-  version "5.59.8"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.59.8.tgz#34d129f35a2134c67fdaf024941e8f96050dca2b"
-  integrity sha512-Tr65630KysnNn9f9G7ROF3w1b5/7f6QVCJ+WK9nhIocWmx9F+TmCAcglF26Vm7z8KCTwoKcNEBZrhlklla3CKg==
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.62.0.tgz#141e809c71636e4a75daa39faed2fb5f4b10df86"
+  integrity sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==
   dependencies:
     "@eslint-community/eslint-utils" "^4.2.0"
     "@types/json-schema" "^7.0.9"
     "@types/semver" "^7.3.12"
-    "@typescript-eslint/scope-manager" "5.59.8"
-    "@typescript-eslint/types" "5.59.8"
-    "@typescript-eslint/typescript-estree" "5.59.8"
+    "@typescript-eslint/scope-manager" "5.62.0"
+    "@typescript-eslint/types" "5.62.0"
+    "@typescript-eslint/typescript-estree" "5.62.0"
     eslint-scope "^5.1.1"
     semver "^7.3.7"
 
@@ -1274,14 +1244,6 @@
     "@typescript-eslint/types" "4.33.0"
     eslint-visitor-keys "^2.0.0"
 
-"@typescript-eslint/visitor-keys@5.59.8":
-  version "5.59.8"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.8.tgz#aa6a7ef862add919401470c09e1609392ef3cc40"
-  integrity sha512-pJhi2ms0x0xgloT7xYabil3SGGlojNNKjK/q6dB3Ey0uJLMjK2UDGJvHieiyJVW/7C3KI+Z4Q3pEHkm4ejA+xQ==
-  dependencies:
-    "@typescript-eslint/types" "5.59.8"
-    eslint-visitor-keys "^3.3.0"
-
 "@typescript-eslint/visitor-keys@5.62.0":
   version "5.62.0"
   resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz#2174011917ce582875954ffe2f6912d5931e353e"
@@ -1290,6 +1252,11 @@
     "@typescript-eslint/types" "5.62.0"
     eslint-visitor-keys "^3.3.0"
 
+"@ungap/structured-clone@^1.2.0":
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406"
+  integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==
+
 abab@^2.0.6:
   version "2.0.6"
   resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291"
@@ -1309,19 +1276,14 @@ acorn-jsx@^5.3.2:
   integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
 
 acorn-walk@^8.0.2:
-  version "8.2.0"
-  resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1"
-  integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==
-
-acorn@^8.1.0:
-  version "8.8.1"
-  resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.1.tgz#0a3f9cbecc4ec3bea6f0a80b66ae8dd2da250b73"
-  integrity sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==
+  version "8.3.0"
+  resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.0.tgz#2097665af50fd0cf7a2dfccd2b9368964e66540f"
+  integrity sha512-FS7hV565M5l1R08MXqo8odwMTB02C2UqzB17RVgu9EyuYFBqJZ3/ZY97sQD5FewVu1UyDFc1yztUDrAwT0EypA==
 
-acorn@^8.8.0:
-  version "8.8.0"
-  resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8"
-  integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==
+acorn@^8.1.0, acorn@^8.8.1, acorn@^8.9.0:
+  version "8.11.2"
+  resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.2.tgz#ca0d78b51895be5390a5903c5b3bdcdaf78ae40b"
+  integrity sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==
 
 agent-base@6:
   version "6.0.2"
@@ -1330,7 +1292,7 @@ agent-base@6:
   dependencies:
     debug "4"
 
-ajv@^6.10.0, ajv@^6.12.4:
+ajv@^6.12.4:
   version "6.12.6"
   resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
   integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
@@ -1388,7 +1350,7 @@ any-promise@^1.1.0:
   resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f"
   integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==
 
-anymatch@^3.0.3, anymatch@~3.1.2:
+anymatch@^3.0.3:
   version "3.1.2"
   resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716"
   integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==
@@ -1396,6 +1358,14 @@ anymatch@^3.0.3, anymatch@~3.1.2:
     normalize-path "^3.0.0"
     picomatch "^2.0.4"
 
+anymatch@~3.1.2:
+  version "3.1.3"
+  resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e"
+  integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==
+  dependencies:
+    normalize-path "^3.0.0"
+    picomatch "^2.0.4"
+
 app-module-path@^2.2.0:
   version "2.2.0"
   resolved "https://registry.yarnpkg.com/app-module-path/-/app-module-path-2.2.0.tgz#641aa55dfb7d6a6f0a8141c4b9c0aa50b6c24dd5"
@@ -1437,11 +1407,11 @@ argparse@^2.0.1:
   integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
 
 aria-query@^5.0.0:
-  version "5.1.3"
-  resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.1.3.tgz#19db27cd101152773631396f7a95a3b58c22c35e"
-  integrity sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==
+  version "5.3.0"
+  resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.0.tgz#650c569e41ad90b51b3d7df5e5eed1c7549c103e"
+  integrity sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==
   dependencies:
-    deep-equal "^2.0.5"
+    dequal "^2.0.3"
 
 array-buffer-byte-length@^1.0.0:
   version "1.0.0"
@@ -1451,15 +1421,15 @@ array-buffer-byte-length@^1.0.0:
     call-bind "^1.0.2"
     is-array-buffer "^3.0.1"
 
-array-includes@^3.1.6:
-  version "3.1.6"
-  resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.6.tgz#9e9e720e194f198266ba9e18c29e6a9b0e4b225f"
-  integrity sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==
+array-includes@^3.1.7:
+  version "3.1.7"
+  resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.7.tgz#8cd2e01b26f7a3086cbc87271593fe921c62abda"
+  integrity sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==
   dependencies:
     call-bind "^1.0.2"
-    define-properties "^1.1.4"
-    es-abstract "^1.20.4"
-    get-intrinsic "^1.1.3"
+    define-properties "^1.2.0"
+    es-abstract "^1.22.1"
+    get-intrinsic "^1.2.1"
     is-string "^1.0.7"
 
 array-union@^2.1.0:
@@ -1467,26 +1437,50 @@ array-union@^2.1.0:
   resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d"
   integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==
 
-array.prototype.flat@^1.3.1:
-  version "1.3.1"
-  resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz#ffc6576a7ca3efc2f46a143b9d1dda9b4b3cf5e2"
-  integrity sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==
+array.prototype.findlastindex@^1.2.3:
+  version "1.2.3"
+  resolved "https://registry.yarnpkg.com/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz#b37598438f97b579166940814e2c0493a4f50207"
+  integrity sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA==
   dependencies:
     call-bind "^1.0.2"
-    define-properties "^1.1.4"
-    es-abstract "^1.20.4"
+    define-properties "^1.2.0"
+    es-abstract "^1.22.1"
     es-shim-unscopables "^1.0.0"
+    get-intrinsic "^1.2.1"
 
-array.prototype.flatmap@^1.3.1:
-  version "1.3.1"
-  resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz#1aae7903c2100433cb8261cd4ed310aab5c4a183"
-  integrity sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==
+array.prototype.flat@^1.3.2:
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz#1476217df8cff17d72ee8f3ba06738db5b387d18"
+  integrity sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==
   dependencies:
     call-bind "^1.0.2"
-    define-properties "^1.1.4"
-    es-abstract "^1.20.4"
+    define-properties "^1.2.0"
+    es-abstract "^1.22.1"
+    es-shim-unscopables "^1.0.0"
+
+array.prototype.flatmap@^1.3.2:
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz#c9a7c6831db8e719d6ce639190146c24bbd3e527"
+  integrity sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==
+  dependencies:
+    call-bind "^1.0.2"
+    define-properties "^1.2.0"
+    es-abstract "^1.22.1"
     es-shim-unscopables "^1.0.0"
 
+arraybuffer.prototype.slice@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz#98bd561953e3e74bb34938e77647179dfe6e9f12"
+  integrity sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==
+  dependencies:
+    array-buffer-byte-length "^1.0.0"
+    call-bind "^1.0.2"
+    define-properties "^1.2.0"
+    es-abstract "^1.22.1"
+    get-intrinsic "^1.2.1"
+    is-array-buffer "^3.0.2"
+    is-shared-array-buffer "^1.0.2"
+
 ast-module-types@^2.7.1:
   version "2.7.1"
   resolved "https://registry.yarnpkg.com/ast-module-types/-/ast-module-types-2.7.1.tgz#3f7989ef8dfa1fdb82dfe0ab02bdfc7c77a57dd3"
@@ -1688,13 +1682,14 @@ builtin-modules@^3.3.0:
   resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6"
   integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==
 
-call-bind@^1.0.0, call-bind@^1.0.2:
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c"
-  integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==
+call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.4, call-bind@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.5.tgz#6fa2b7845ce0ea49bf4d8b9ef64727a2c2e2e513"
+  integrity sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==
   dependencies:
-    function-bind "^1.1.1"
-    get-intrinsic "^1.0.2"
+    function-bind "^1.1.2"
+    get-intrinsic "^1.2.1"
+    set-function-length "^1.1.1"
 
 callsite@^1.0.0:
   version "1.0.0"
@@ -1787,9 +1782,9 @@ chrono-node@2.3.9:
     dayjs "^1.10.0"
 
 ci-info@^3.2.0:
-  version "3.2.0"
-  resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.2.0.tgz#2876cb948a498797b5236f0095bc057d0dca38b6"
-  integrity sha512-dVqRX7fLUm8J6FgHJ418XuIgDLZDkYcDFTeL6TA2gt5WlIZUQrrH6EZrNClwT/H0FateUsZkGIOPRrLbP+PR9A==
+  version "3.9.0"
+  resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4"
+  integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==
 
 cjs-module-lexer@^1.0.0:
   version "1.2.2"
@@ -1981,45 +1976,22 @@ debug@^3.2.7:
   dependencies:
     ms "^2.1.1"
 
-decimal.js@^10.4.1:
-  version "10.4.2"
-  resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.2.tgz#0341651d1d997d86065a2ce3a441fbd0d8e8b98e"
-  integrity sha512-ic1yEvwT6GuvaYwBLLY6/aFFgjZdySKTE8en/fkU3QICTmRtgtSlFn0u0BXN06InZwtfCelR7j8LRiDI/02iGA==
+decimal.js@^10.4.2:
+  version "10.4.3"
+  resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23"
+  integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==
 
 dedent@^1.0.0:
   version "1.5.1"
   resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.5.1.tgz#4f3fc94c8b711e9bb2800d185cd6ad20f2a90aff"
   integrity sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==
 
-deep-equal@^2.0.5:
-  version "2.2.0"
-  resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.2.0.tgz#5caeace9c781028b9ff459f33b779346637c43e6"
-  integrity sha512-RdpzE0Hv4lhowpIUKKMJfeH6C1pXdtT1/it80ubgWqwI3qpuxUBpC1S4hnHg+zjnuOoDkzUtUCEEkG+XG5l3Mw==
-  dependencies:
-    call-bind "^1.0.2"
-    es-get-iterator "^1.1.2"
-    get-intrinsic "^1.1.3"
-    is-arguments "^1.1.1"
-    is-array-buffer "^3.0.1"
-    is-date-object "^1.0.5"
-    is-regex "^1.1.4"
-    is-shared-array-buffer "^1.0.2"
-    isarray "^2.0.5"
-    object-is "^1.1.5"
-    object-keys "^1.1.1"
-    object.assign "^4.1.4"
-    regexp.prototype.flags "^1.4.3"
-    side-channel "^1.0.4"
-    which-boxed-primitive "^1.0.2"
-    which-collection "^1.0.1"
-    which-typed-array "^1.1.9"
-
 deep-extend@^0.6.0:
   version "0.6.0"
   resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac"
   integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==
 
-deep-is@^0.1.3, deep-is@~0.1.3:
+deep-is@^0.1.3:
   version "0.1.4"
   resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
   integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==
@@ -2036,11 +2008,21 @@ defaults@^1.0.3:
   dependencies:
     clone "^1.0.2"
 
-define-properties@^1.1.3, define-properties@^1.1.4:
-  version "1.1.4"
-  resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1"
-  integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==
+define-data-property@^1.0.1, define-data-property@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.1.tgz#c35f7cd0ab09883480d12ac5cb213715587800b3"
+  integrity sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==
   dependencies:
+    get-intrinsic "^1.2.1"
+    gopd "^1.0.1"
+    has-property-descriptors "^1.0.0"
+
+define-properties@^1.1.3, define-properties@^1.1.4, define-properties@^1.2.0:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c"
+  integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==
+  dependencies:
+    define-data-property "^1.0.1"
     has-property-descriptors "^1.0.0"
     object-keys "^1.1.1"
 
@@ -2060,6 +2042,11 @@ dependency-tree@^9.0.0:
     precinct "^9.0.0"
     typescript "^4.0.0"
 
+dequal@^2.0.3:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be"
+  integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==
+
 detect-indent@^6.1.0:
   version "6.1.0"
   resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6"
@@ -2292,9 +2279,9 @@ enhanced-resolve@^5.8.3:
     tapable "^2.2.0"
 
 entities@^4.4.0:
-  version "4.4.0"
-  resolved "https://registry.yarnpkg.com/entities/-/entities-4.4.0.tgz#97bdaba170339446495e653cfd2db78962900174"
-  integrity sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==
+  version "4.5.0"
+  resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48"
+  integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==
 
 entities@~3.0.1:
   version "3.0.1"
@@ -2308,54 +2295,26 @@ error-ex@^1.3.1:
   dependencies:
     is-arrayish "^0.2.1"
 
-es-abstract@^1.19.0, es-abstract@^1.19.5:
-  version "1.20.1"
-  resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.1.tgz#027292cd6ef44bd12b1913b828116f54787d1814"
-  integrity sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==
-  dependencies:
-    call-bind "^1.0.2"
-    es-to-primitive "^1.2.1"
-    function-bind "^1.1.1"
-    function.prototype.name "^1.1.5"
-    get-intrinsic "^1.1.1"
-    get-symbol-description "^1.0.0"
-    has "^1.0.3"
-    has-property-descriptors "^1.0.0"
-    has-symbols "^1.0.3"
-    internal-slot "^1.0.3"
-    is-callable "^1.2.4"
-    is-negative-zero "^2.0.2"
-    is-regex "^1.1.4"
-    is-shared-array-buffer "^1.0.2"
-    is-string "^1.0.7"
-    is-weakref "^1.0.2"
-    object-inspect "^1.12.0"
-    object-keys "^1.1.1"
-    object.assign "^4.1.2"
-    regexp.prototype.flags "^1.4.3"
-    string.prototype.trimend "^1.0.5"
-    string.prototype.trimstart "^1.0.5"
-    unbox-primitive "^1.0.2"
-
-es-abstract@^1.20.4:
-  version "1.21.2"
-  resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.21.2.tgz#a56b9695322c8a185dc25975aa3b8ec31d0e7eff"
-  integrity sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==
+es-abstract@^1.22.1:
+  version "1.22.3"
+  resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.22.3.tgz#48e79f5573198de6dee3589195727f4f74bc4f32"
+  integrity sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==
   dependencies:
     array-buffer-byte-length "^1.0.0"
+    arraybuffer.prototype.slice "^1.0.2"
     available-typed-arrays "^1.0.5"
-    call-bind "^1.0.2"
+    call-bind "^1.0.5"
     es-set-tostringtag "^2.0.1"
     es-to-primitive "^1.2.1"
-    function.prototype.name "^1.1.5"
-    get-intrinsic "^1.2.0"
+    function.prototype.name "^1.1.6"
+    get-intrinsic "^1.2.2"
     get-symbol-description "^1.0.0"
     globalthis "^1.0.3"
     gopd "^1.0.1"
-    has "^1.0.3"
     has-property-descriptors "^1.0.0"
     has-proto "^1.0.1"
     has-symbols "^1.0.3"
+    hasown "^2.0.0"
     internal-slot "^1.0.5"
     is-array-buffer "^3.0.2"
     is-callable "^1.2.7"
@@ -2363,50 +2322,39 @@ es-abstract@^1.20.4:
     is-regex "^1.1.4"
     is-shared-array-buffer "^1.0.2"
     is-string "^1.0.7"
-    is-typed-array "^1.1.10"
+    is-typed-array "^1.1.12"
     is-weakref "^1.0.2"
-    object-inspect "^1.12.3"
+    object-inspect "^1.13.1"
     object-keys "^1.1.1"
     object.assign "^4.1.4"
-    regexp.prototype.flags "^1.4.3"
+    regexp.prototype.flags "^1.5.1"
+    safe-array-concat "^1.0.1"
     safe-regex-test "^1.0.0"
-    string.prototype.trim "^1.2.7"
-    string.prototype.trimend "^1.0.6"
-    string.prototype.trimstart "^1.0.6"
+    string.prototype.trim "^1.2.8"
+    string.prototype.trimend "^1.0.7"
+    string.prototype.trimstart "^1.0.7"
+    typed-array-buffer "^1.0.0"
+    typed-array-byte-length "^1.0.0"
+    typed-array-byte-offset "^1.0.0"
     typed-array-length "^1.0.4"
     unbox-primitive "^1.0.2"
-    which-typed-array "^1.1.9"
-
-es-get-iterator@^1.1.2:
-  version "1.1.3"
-  resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.3.tgz#3ef87523c5d464d41084b2c3c9c214f1199763d6"
-  integrity sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==
-  dependencies:
-    call-bind "^1.0.2"
-    get-intrinsic "^1.1.3"
-    has-symbols "^1.0.3"
-    is-arguments "^1.1.1"
-    is-map "^2.0.2"
-    is-set "^2.0.2"
-    is-string "^1.0.7"
-    isarray "^2.0.5"
-    stop-iteration-iterator "^1.0.0"
+    which-typed-array "^1.1.13"
 
 es-set-tostringtag@^2.0.1:
-  version "2.0.1"
-  resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz#338d502f6f674301d710b80c8592de8a15f09cd8"
-  integrity sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz#11f7cc9f63376930a5f20be4915834f4bc74f9c9"
+  integrity sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==
   dependencies:
-    get-intrinsic "^1.1.3"
-    has "^1.0.3"
+    get-intrinsic "^1.2.2"
     has-tostringtag "^1.0.0"
+    hasown "^2.0.0"
 
 es-shim-unscopables@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241"
-  integrity sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz#1f6942e71ecc7835ed1c8a83006d8771a63a3763"
+  integrity sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==
   dependencies:
-    has "^1.0.3"
+    hasown "^2.0.0"
 
 es-to-primitive@^1.2.1:
   version "1.2.1"
@@ -2577,32 +2525,31 @@ escape-string-regexp@^4.0.0:
   integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
 
 escodegen@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.0.0.tgz#5e32b12833e8aa8fa35e1bf0befa89380484c7dd"
-  integrity sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.1.0.tgz#ba93bbb7a43986d29d6041f99f5262da773e2e17"
+  integrity sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==
   dependencies:
     esprima "^4.0.1"
     estraverse "^5.2.0"
     esutils "^2.0.2"
-    optionator "^0.8.1"
   optionalDependencies:
     source-map "~0.6.1"
 
 eslint-config-prettier@^8.8.0:
-  version "8.8.0"
-  resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz#bfda738d412adc917fd7b038857110efe98c9348"
-  integrity sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==
+  version "8.10.0"
+  resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz#3a06a662130807e2502fc3ff8b4143d8a0658e11"
+  integrity sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==
 
-eslint-import-resolver-node@^0.3.7:
-  version "0.3.7"
-  resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz#83b375187d412324a1963d84fa664377a23eb4d7"
-  integrity sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==
+eslint-import-resolver-node@^0.3.9:
+  version "0.3.9"
+  resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz#d4eaac52b8a2e7c3cd1903eb00f7e053356118ac"
+  integrity sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==
   dependencies:
     debug "^3.2.7"
-    is-core-module "^2.11.0"
-    resolve "^1.22.1"
+    is-core-module "^2.13.0"
+    resolve "^1.22.4"
 
-eslint-module-utils@^2.7.4:
+eslint-module-utils@^2.8.0:
   version "2.8.0"
   resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz#e439fee65fc33f6bba630ff621efc38ec0375c49"
   integrity sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==
@@ -2610,25 +2557,27 @@ eslint-module-utils@^2.7.4:
     debug "^3.2.7"
 
 eslint-plugin-import@^2.27.5:
-  version "2.27.5"
-  resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz#876a6d03f52608a3e5bb439c2550588e51dd6c65"
-  integrity sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==
-  dependencies:
-    array-includes "^3.1.6"
-    array.prototype.flat "^1.3.1"
-    array.prototype.flatmap "^1.3.1"
+  version "2.29.0"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.29.0.tgz#8133232e4329ee344f2f612885ac3073b0b7e155"
+  integrity sha512-QPOO5NO6Odv5lpoTkddtutccQjysJuFxoPS7fAHO+9m9udNHvTCPSAMW9zGAYj8lAIdr40I8yPCdUYrncXtrwg==
+  dependencies:
+    array-includes "^3.1.7"
+    array.prototype.findlastindex "^1.2.3"
+    array.prototype.flat "^1.3.2"
+    array.prototype.flatmap "^1.3.2"
     debug "^3.2.7"
     doctrine "^2.1.0"
-    eslint-import-resolver-node "^0.3.7"
-    eslint-module-utils "^2.7.4"
-    has "^1.0.3"
-    is-core-module "^2.11.0"
+    eslint-import-resolver-node "^0.3.9"
+    eslint-module-utils "^2.8.0"
+    hasown "^2.0.0"
+    is-core-module "^2.13.1"
     is-glob "^4.0.3"
     minimatch "^3.1.2"
-    object.values "^1.1.6"
-    resolve "^1.22.1"
-    semver "^6.3.0"
-    tsconfig-paths "^3.14.1"
+    object.fromentries "^2.0.7"
+    object.groupby "^1.0.1"
+    object.values "^1.1.7"
+    semver "^6.3.1"
+    tsconfig-paths "^3.14.2"
 
 eslint-plugin-prettier@^4.2.1:
   version "4.2.1"
@@ -2645,10 +2594,10 @@ eslint-scope@^5.1.1:
     esrecurse "^4.3.0"
     estraverse "^4.1.1"
 
-eslint-scope@^7.2.0:
-  version "7.2.0"
-  resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.0.tgz#f21ebdafda02352f103634b96dd47d9f81ca117b"
-  integrity sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==
+eslint-scope@^7.2.2:
+  version "7.2.2"
+  resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f"
+  integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==
   dependencies:
     esrecurse "^4.3.0"
     estraverse "^5.2.0"
@@ -2658,37 +2607,33 @@ eslint-visitor-keys@^2.0.0:
   resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303"
   integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==
 
-eslint-visitor-keys@^3.3.0:
-  version "3.3.0"
-  resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826"
-  integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==
-
-eslint-visitor-keys@^3.4.1:
-  version "3.4.1"
-  resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz#c22c48f48942d08ca824cc526211ae400478a994"
-  integrity sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==
+eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3:
+  version "3.4.3"
+  resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800"
+  integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==
 
 eslint@^8.41.0:
-  version "8.41.0"
-  resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.41.0.tgz#3062ca73363b4714b16dbc1e60f035e6134b6f1c"
-  integrity sha512-WQDQpzGBOP5IrXPo4Hc0814r4/v2rrIsB0rhT7jtunIalgg6gYXWhRMOejVO8yH21T/FGaxjmFjBMNqcIlmH1Q==
+  version "8.54.0"
+  resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.54.0.tgz#588e0dd4388af91a2e8fa37ea64924074c783537"
+  integrity sha512-NY0DfAkM8BIZDVl6PgSa1ttZbx3xHgJzSNJKYcQglem6CppHyMhRIQkBVSSMaSRnLhig3jsDbEzOjwCVt4AmmA==
   dependencies:
     "@eslint-community/eslint-utils" "^4.2.0"
-    "@eslint-community/regexpp" "^4.4.0"
-    "@eslint/eslintrc" "^2.0.3"
-    "@eslint/js" "8.41.0"
-    "@humanwhocodes/config-array" "^0.11.8"
+    "@eslint-community/regexpp" "^4.6.1"
+    "@eslint/eslintrc" "^2.1.3"
+    "@eslint/js" "8.54.0"
+    "@humanwhocodes/config-array" "^0.11.13"
     "@humanwhocodes/module-importer" "^1.0.1"
     "@nodelib/fs.walk" "^1.2.8"
-    ajv "^6.10.0"
+    "@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.0"
-    eslint-visitor-keys "^3.4.1"
-    espree "^9.5.2"
+    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"
@@ -2698,7 +2643,6 @@ eslint@^8.41.0:
     globals "^13.19.0"
     graphemer "^1.4.0"
     ignore "^5.2.0"
-    import-fresh "^3.0.0"
     imurmurhash "^0.1.4"
     is-glob "^4.0.0"
     is-path-inside "^3.0.3"
@@ -2708,17 +2652,16 @@ eslint@^8.41.0:
     lodash.merge "^4.6.2"
     minimatch "^3.1.2"
     natural-compare "^1.4.0"
-    optionator "^0.9.1"
+    optionator "^0.9.3"
     strip-ansi "^6.0.1"
-    strip-json-comments "^3.1.0"
     text-table "^0.2.0"
 
-espree@^9.5.2:
-  version "9.5.2"
-  resolved "https://registry.yarnpkg.com/espree/-/espree-9.5.2.tgz#e994e7dc33a082a7a82dceaf12883a829353215b"
-  integrity sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==
+espree@^9.6.0, espree@^9.6.1:
+  version "9.6.1"
+  resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f"
+  integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==
   dependencies:
-    acorn "^8.8.0"
+    acorn "^8.9.0"
     acorn-jsx "^5.3.2"
     eslint-visitor-keys "^3.4.1"
 
@@ -2815,7 +2758,7 @@ fast-diff@^1.1.2:
   resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03"
   integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==
 
-fast-glob@^3.2.11, fast-glob@^3.2.7, fast-glob@^3.2.9:
+fast-glob@^3.2.11:
   version "3.2.11"
   resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9"
   integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==
@@ -2826,20 +2769,31 @@ fast-glob@^3.2.11, fast-glob@^3.2.7, fast-glob@^3.2.9:
     merge2 "^1.3.0"
     micromatch "^4.0.4"
 
+fast-glob@^3.2.7, fast-glob@^3.2.9:
+  version "3.3.2"
+  resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129"
+  integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==
+  dependencies:
+    "@nodelib/fs.stat" "^2.0.2"
+    "@nodelib/fs.walk" "^1.2.3"
+    glob-parent "^5.1.2"
+    merge2 "^1.3.0"
+    micromatch "^4.0.4"
+
 fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
   integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
 
-fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6:
+fast-levenshtein@^2.0.6:
   version "2.0.6"
   resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
   integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
 
 fastq@^1.6.0:
-  version "1.13.0"
-  resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c"
-  integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==
+  version "1.15.0"
+  resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a"
+  integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==
   dependencies:
     reusify "^1.0.4"
 
@@ -2905,17 +2859,18 @@ find-up@^5.0.0:
     path-exists "^4.0.0"
 
 flat-cache@^3.0.4:
-  version "3.0.4"
-  resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11"
-  integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee"
+  integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==
   dependencies:
-    flatted "^3.1.0"
+    flatted "^3.2.9"
+    keyv "^4.5.3"
     rimraf "^3.0.2"
 
-flatted@^3.1.0:
-  version "3.2.6"
-  resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.6.tgz#022e9218c637f9f3fc9c35ab9c9193f05add60b2"
-  integrity sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==
+flatted@^3.2.9:
+  version "3.2.9"
+  resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.9.tgz#7eb4c67ca1ba34232ca9d2d93e9886e611ad7daf"
+  integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==
 
 flatten@^1.0.2:
   version "1.0.3"
@@ -2948,27 +2903,32 @@ fs.realpath@^1.0.0:
   resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
   integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
 
-fsevents@^2.3.2, fsevents@~2.3.2:
+fsevents@^2.3.2:
   version "2.3.2"
   resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
   integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
 
-function-bind@^1.1.1:
-  version "1.1.1"
-  resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
-  integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
+fsevents@~2.3.2:
+  version "2.3.3"
+  resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
+  integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
+
+function-bind@^1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c"
+  integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
 
-function.prototype.name@^1.1.5:
-  version "1.1.5"
-  resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621"
-  integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==
+function.prototype.name@^1.1.6:
+  version "1.1.6"
+  resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.6.tgz#cdf315b7d90ee77a4c6ee216c3c3362da07533fd"
+  integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==
   dependencies:
     call-bind "^1.0.2"
-    define-properties "^1.1.3"
-    es-abstract "^1.19.0"
-    functions-have-names "^1.2.2"
+    define-properties "^1.2.0"
+    es-abstract "^1.22.1"
+    functions-have-names "^1.2.3"
 
-functions-have-names@^1.2.2:
+functions-have-names@^1.2.3:
   version "1.2.3"
   resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834"
   integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==
@@ -2999,23 +2959,15 @@ get-caller-file@^2.0.5:
   resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
   integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
 
-get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1:
-  version "1.1.2"
-  resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.2.tgz#336975123e05ad0b7ba41f152ee4aadbea6cf598"
-  integrity sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==
-  dependencies:
-    function-bind "^1.1.1"
-    has "^1.0.3"
-    has-symbols "^1.0.3"
-
-get-intrinsic@^1.1.3, get-intrinsic@^1.2.0:
-  version "1.2.0"
-  resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.0.tgz#7ad1dc0535f3a2904bba075772763e5051f6d05f"
-  integrity sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==
+get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2:
+  version "1.2.2"
+  resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.2.tgz#281b7622971123e1ef4b3c90fd7539306da93f3b"
+  integrity sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==
   dependencies:
-    function-bind "^1.1.1"
-    has "^1.0.3"
+    function-bind "^1.1.2"
+    has-proto "^1.0.1"
     has-symbols "^1.0.3"
+    hasown "^2.0.0"
 
 get-own-enumerable-property-symbols@^3.0.0:
   version "3.0.2"
@@ -3095,9 +3047,9 @@ globals@^11.1.0:
   integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
 
 globals@^13.19.0:
-  version "13.20.0"
-  resolved "https://registry.yarnpkg.com/globals/-/globals-13.20.0.tgz#ea276a1e508ffd4f1612888f9d1bad1e2717bf82"
-  integrity sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==
+  version "13.23.0"
+  resolved "https://registry.yarnpkg.com/globals/-/globals-13.23.0.tgz#ef31673c926a0976e1f61dab4dca57e0c0a8af02"
+  integrity sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==
   dependencies:
     type-fest "^0.20.2"
 
@@ -3145,21 +3097,11 @@ gopd@^1.0.1:
   dependencies:
     get-intrinsic "^1.1.3"
 
-graceful-fs@^4.1.3, graceful-fs@^4.2.9:
-  version "4.2.10"
-  resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c"
-  integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==
-
-graceful-fs@^4.2.4:
+graceful-fs@^4.1.3, graceful-fs@^4.2.4, graceful-fs@^4.2.9:
   version "4.2.11"
   resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
   integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
 
-grapheme-splitter@^1.0.4:
-  version "1.0.4"
-  resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e"
-  integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==
-
 graphemer@^1.4.0:
   version "1.4.0"
   resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6"
@@ -3181,18 +3123,18 @@ has-flag@^4.0.0:
   integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
 
 has-property-descriptors@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861"
-  integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz#52ba30b6c5ec87fd89fa574bc1c39125c6f65340"
+  integrity sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==
   dependencies:
-    get-intrinsic "^1.1.1"
+    get-intrinsic "^1.2.2"
 
 has-proto@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0"
   integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==
 
-has-symbols@^1.0.1, has-symbols@^1.0.2, has-symbols@^1.0.3:
+has-symbols@^1.0.2, has-symbols@^1.0.3:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8"
   integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==
@@ -3205,11 +3147,16 @@ has-tostringtag@^1.0.0:
     has-symbols "^1.0.2"
 
 has@^1.0.3:
-  version "1.0.3"
-  resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
-  integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/has/-/has-1.0.4.tgz#2eb2860e000011dae4f1406a86fe80e530fb2ec6"
+  integrity sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==
+
+hasown@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.0.tgz#f4c513d454a57b7c7e1650778de226b11700546c"
+  integrity sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==
   dependencies:
-    function-bind "^1.1.1"
+    function-bind "^1.1.2"
 
 html-encoding-sniffer@^3.0.0:
   version "3.0.0"
@@ -3258,11 +3205,11 @@ ieee754@^1.1.13:
   integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
 
 ignore@^5.2.0:
-  version "5.2.0"
-  resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a"
-  integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==
+  version "5.3.0"
+  resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.0.tgz#67418ae40d34d6999c95ff56016759c718c82f78"
+  integrity sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==
 
-import-fresh@^3.0.0, import-fresh@^3.2.1:
+import-fresh@^3.2.1:
   version "3.3.0"
   resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
   integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==
@@ -3311,22 +3258,13 @@ ini@~1.3.0:
   resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
   integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
 
-internal-slot@^1.0.3:
-  version "1.0.3"
-  resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c"
-  integrity sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==
+internal-slot@^1.0.5:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.6.tgz#37e756098c4911c5e912b8edbf71ed3aa116f930"
+  integrity sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==
   dependencies:
-    get-intrinsic "^1.1.0"
-    has "^1.0.3"
-    side-channel "^1.0.4"
-
-internal-slot@^1.0.4, internal-slot@^1.0.5:
-  version "1.0.5"
-  resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986"
-  integrity sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==
-  dependencies:
-    get-intrinsic "^1.2.0"
-    has "^1.0.3"
+    get-intrinsic "^1.2.2"
+    hasown "^2.0.0"
     side-channel "^1.0.4"
 
 interpret@^1.0.0:
@@ -3334,14 +3272,6 @@ interpret@^1.0.0:
   resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e"
   integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==
 
-is-arguments@^1.1.1:
-  version "1.1.1"
-  resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b"
-  integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==
-  dependencies:
-    call-bind "^1.0.2"
-    has-tostringtag "^1.0.0"
-
 is-array-buffer@^3.0.1, is-array-buffer@^3.0.2:
   version "3.0.2"
   resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.2.tgz#f2653ced8412081638ecb0ebbd0c41c6e0aecbbe"
@@ -3378,23 +3308,11 @@ is-boolean-object@^1.1.0:
     call-bind "^1.0.2"
     has-tostringtag "^1.0.0"
 
-is-callable@^1.1.3, is-callable@^1.2.7:
+is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7:
   version "1.2.7"
   resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055"
   integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==
 
-is-callable@^1.1.4, is-callable@^1.2.4:
-  version "1.2.4"
-  resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945"
-  integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==
-
-is-core-module@^2.11.0:
-  version "2.12.1"
-  resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.12.1.tgz#0c0b6885b6f80011c71541ce15c8d66cf5a4f9fd"
-  integrity sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==
-  dependencies:
-    has "^1.0.3"
-
 is-core-module@^2.13.0:
   version "2.13.0"
   resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.0.tgz#bb52aa6e2cbd49a30c2ba68c42bf3435ba6072db"
@@ -3402,6 +3320,13 @@ is-core-module@^2.13.0:
   dependencies:
     has "^1.0.3"
 
+is-core-module@^2.13.1:
+  version "2.13.1"
+  resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384"
+  integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==
+  dependencies:
+    hasown "^2.0.0"
+
 is-core-module@^2.9.0:
   version "2.9.0"
   resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69"
@@ -3409,7 +3334,7 @@ is-core-module@^2.9.0:
   dependencies:
     has "^1.0.3"
 
-is-date-object@^1.0.1, is-date-object@^1.0.5:
+is-date-object@^1.0.1:
   version "1.0.5"
   resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f"
   integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==
@@ -3443,11 +3368,6 @@ is-interactive@^1.0.0:
   resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e"
   integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==
 
-is-map@^2.0.1, is-map@^2.0.2:
-  version "2.0.2"
-  resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.2.tgz#00922db8c9bf73e81b7a335827bc2a43f2b91127"
-  integrity sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==
-
 is-negative-zero@^2.0.2:
   version "2.0.2"
   resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150"
@@ -3498,11 +3418,6 @@ is-relative-path@^1.0.2:
   resolved "https://registry.yarnpkg.com/is-relative-path/-/is-relative-path-1.0.2.tgz#091b46a0d67c1ed0fe85f1f8cfdde006bb251d46"
   integrity sha512-i1h+y50g+0hRbBD+dbnInl3JlJ702aar58snAeX+MxBAPvzXGej7sYoPMhlnykabt0ZzCJNBEyzMlekuQZN7fA==
 
-is-set@^2.0.1, is-set@^2.0.2:
-  version "2.0.2"
-  resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.2.tgz#90755fa4c2562dc1c5d4024760d6119b94ca18ec"
-  integrity sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==
-
 is-shared-array-buffer@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79"
@@ -3529,16 +3444,12 @@ is-symbol@^1.0.2, is-symbol@^1.0.3:
   dependencies:
     has-symbols "^1.0.2"
 
-is-typed-array@^1.1.10, is-typed-array@^1.1.9:
-  version "1.1.10"
-  resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.10.tgz#36a5b5cb4189b575d1a3e4b08536bfb485801e3f"
-  integrity sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==
+is-typed-array@^1.1.10, is-typed-array@^1.1.12, is-typed-array@^1.1.9:
+  version "1.1.12"
+  resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.12.tgz#d0bab5686ef4a76f7a73097b95470ab199c57d4a"
+  integrity sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==
   dependencies:
-    available-typed-arrays "^1.0.5"
-    call-bind "^1.0.2"
-    for-each "^0.3.3"
-    gopd "^1.0.1"
-    has-tostringtag "^1.0.0"
+    which-typed-array "^1.1.11"
 
 is-unicode-supported@^0.1.0:
   version "0.1.0"
@@ -3555,11 +3466,6 @@ is-url@^1.2.4:
   resolved "https://registry.yarnpkg.com/is-url/-/is-url-1.2.4.tgz#04a4df46d28c4cff3d73d01ff06abeb318a1aa52"
   integrity sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==
 
-is-weakmap@^2.0.1:
-  version "2.0.1"
-  resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.1.tgz#5008b59bdc43b698201d18f62b37b2ca243e8cf2"
-  integrity sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==
-
 is-weakref@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2"
@@ -3567,14 +3473,6 @@ is-weakref@^1.0.2:
   dependencies:
     call-bind "^1.0.2"
 
-is-weakset@^2.0.1:
-  version "2.0.2"
-  resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.2.tgz#4569d67a747a1ce5a994dfd4ef6dcea76e7c0a1d"
-  integrity sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==
-  dependencies:
-    call-bind "^1.0.2"
-    get-intrinsic "^1.1.1"
-
 isarray@^2.0.5:
   version "2.0.5"
   resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723"
@@ -3747,17 +3645,17 @@ jest-each@^29.7.0:
     pretty-format "^29.7.0"
 
 jest-environment-jsdom@^29.5.0:
-  version "29.5.0"
-  resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-29.5.0.tgz#cfe86ebaf1453f3297b5ff3470fbe94739c960cb"
-  integrity sha512-/KG8yEK4aN8ak56yFVdqFDzKNHgF4BAymCx2LbPNPsUshUlfAl0eX402Xm1pt+eoG9SLZEUVifqXtX8SK74KCw==
+  version "29.7.0"
+  resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz#d206fa3551933c3fd519e5dfdb58a0f5139a837f"
+  integrity sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==
   dependencies:
-    "@jest/environment" "^29.5.0"
-    "@jest/fake-timers" "^29.5.0"
-    "@jest/types" "^29.5.0"
+    "@jest/environment" "^29.7.0"
+    "@jest/fake-timers" "^29.7.0"
+    "@jest/types" "^29.6.3"
     "@types/jsdom" "^20.0.0"
     "@types/node" "*"
-    jest-mock "^29.5.0"
-    jest-util "^29.5.0"
+    jest-mock "^29.7.0"
+    jest-util "^29.7.0"
     jsdom "^20.0.0"
 
 jest-environment-node@^29.7.0:
@@ -3814,21 +3712,6 @@ jest-matcher-utils@^29.7.0:
     jest-get-type "^29.6.3"
     pretty-format "^29.7.0"
 
-jest-message-util@^29.5.0:
-  version "29.5.0"
-  resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.5.0.tgz#1f776cac3aca332ab8dd2e3b41625435085c900e"
-  integrity sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA==
-  dependencies:
-    "@babel/code-frame" "^7.12.13"
-    "@jest/types" "^29.5.0"
-    "@types/stack-utils" "^2.0.0"
-    chalk "^4.0.0"
-    graceful-fs "^4.2.9"
-    micromatch "^4.0.4"
-    pretty-format "^29.5.0"
-    slash "^3.0.0"
-    stack-utils "^2.0.3"
-
 jest-message-util@^29.7.0:
   version "29.7.0"
   resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3"
@@ -3844,15 +3727,6 @@ jest-message-util@^29.7.0:
     slash "^3.0.0"
     stack-utils "^2.0.3"
 
-jest-mock@^29.5.0:
-  version "29.5.0"
-  resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.5.0.tgz#26e2172bcc71d8b0195081ff1f146ac7e1518aed"
-  integrity sha512-GqOzvdWDE4fAV2bWQLQCkujxYWL7RxjCnj71b5VhDAGOevB3qj3Ovg26A5NI84ZpODxyzaozXLOh2NCgkbvyaw==
-  dependencies:
-    "@jest/types" "^29.5.0"
-    "@types/node" "*"
-    jest-util "^29.5.0"
-
 jest-mock@^29.7.0:
   version "29.7.0"
   resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.7.0.tgz#4e836cf60e99c6fcfabe9f99d017f3fdd50a6347"
@@ -3976,7 +3850,7 @@ jest-snapshot@^29.7.0:
     pretty-format "^29.7.0"
     semver "^7.5.3"
 
-jest-util@^29.0.0, jest-util@^29.5.0:
+jest-util@^29.0.0:
   version "29.5.0"
   resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.5.0.tgz#24a4d3d92fc39ce90425311b23c27a6e0ef16b8f"
   integrity sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ==
@@ -4067,17 +3941,17 @@ js-yaml@^4.1.0:
     argparse "^2.0.1"
 
 jsdom@^20.0.0:
-  version "20.0.2"
-  resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-20.0.2.tgz#65ccbed81d5e877c433f353c58bb91ff374127db"
-  integrity sha512-AHWa+QO/cgRg4N+DsmHg1Y7xnz+8KU3EflM0LVDTdmrYOc1WWTSkOjtpUveQH+1Bqd5rtcVnb/DuxV/UjDO4rA==
+  version "20.0.3"
+  resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-20.0.3.tgz#886a41ba1d4726f67a8858028c99489fed6ad4db"
+  integrity sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==
   dependencies:
     abab "^2.0.6"
-    acorn "^8.8.0"
+    acorn "^8.8.1"
     acorn-globals "^7.0.0"
     cssom "^0.5.0"
     cssstyle "^2.3.0"
     data-urls "^3.0.2"
-    decimal.js "^10.4.1"
+    decimal.js "^10.4.2"
     domexception "^4.0.0"
     escodegen "^2.0.0"
     form-data "^4.0.0"
@@ -4090,12 +3964,12 @@ jsdom@^20.0.0:
     saxes "^6.0.0"
     symbol-tree "^3.2.4"
     tough-cookie "^4.1.2"
-    w3c-xmlserializer "^3.0.0"
+    w3c-xmlserializer "^4.0.0"
     webidl-conversions "^7.0.0"
     whatwg-encoding "^2.0.0"
     whatwg-mimetype "^3.0.0"
     whatwg-url "^11.0.0"
-    ws "^8.9.0"
+    ws "^8.11.0"
     xml-name-validator "^4.0.0"
 
 jsesc@^2.5.1:
@@ -4103,6 +3977,11 @@ jsesc@^2.5.1:
   resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4"
   integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==
 
+json-buffer@3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13"
+  integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==
+
 json-parse-even-better-errors@^2.3.0:
   version "2.3.1"
   resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d"
@@ -4118,7 +3997,7 @@ json-stable-stringify-without-jsonify@^1.0.1:
   resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
   integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==
 
-json5@^1.0.1, json5@^1.0.2:
+json5@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593"
   integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==
@@ -4130,6 +4009,13 @@ json5@^2.1.2, json5@^2.2.1, json5@^2.2.3:
   resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
   integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
 
+keyv@^4.5.3:
+  version "4.5.4"
+  resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93"
+  integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==
+  dependencies:
+    json-buffer "3.0.1"
+
 kleur@^3.0.3:
   version "3.0.3"
   resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
@@ -4148,14 +4034,6 @@ levn@^0.4.1:
     prelude-ls "^1.2.1"
     type-check "~0.4.0"
 
-levn@~0.3.0:
-  version "0.3.0"
-  resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee"
-  integrity sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==
-  dependencies:
-    prelude-ls "~1.1.2"
-    type-check "~0.3.2"
-
 lines-and-columns@^1.1.6:
   version "1.2.4"
   resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632"
@@ -4393,12 +4271,7 @@ minimatch@^5.0.1:
   dependencies:
     brace-expansion "^2.0.1"
 
-minimist@^1.2.0, minimist@^1.2.6:
-  version "1.2.6"
-  resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
-  integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
-
-minimist@^1.2.5:
+minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6:
   version "1.2.8"
   resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
   integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
@@ -4541,43 +4414,20 @@ npm-run-path@^4.0.1:
     path-key "^3.0.0"
 
 nwsapi@^2.2.2:
-  version "2.2.2"
-  resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.2.tgz#e5418863e7905df67d51ec95938d67bf801f0bb0"
-  integrity sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw==
-
-object-inspect@^1.12.0, object-inspect@^1.9.0:
-  version "1.12.2"
-  resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea"
-  integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==
+  version "2.2.7"
+  resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.7.tgz#738e0707d3128cb750dddcfe90e4610482df0f30"
+  integrity sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==
 
-object-inspect@^1.12.3:
-  version "1.12.3"
-  resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9"
-  integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==
-
-object-is@^1.1.5:
-  version "1.1.5"
-  resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac"
-  integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==
-  dependencies:
-    call-bind "^1.0.2"
-    define-properties "^1.1.3"
+object-inspect@^1.13.1, object-inspect@^1.9.0:
+  version "1.13.1"
+  resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2"
+  integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==
 
 object-keys@^1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
   integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
 
-object.assign@^4.1.2:
-  version "4.1.2"
-  resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940"
-  integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==
-  dependencies:
-    call-bind "^1.0.0"
-    define-properties "^1.1.3"
-    has-symbols "^1.0.1"
-    object-keys "^1.1.1"
-
 object.assign@^4.1.4:
   version "4.1.4"
   resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f"
@@ -4588,14 +4438,33 @@ object.assign@^4.1.4:
     has-symbols "^1.0.3"
     object-keys "^1.1.1"
 
-object.values@^1.1.6:
-  version "1.1.6"
-  resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.6.tgz#4abbaa71eba47d63589d402856f908243eea9b1d"
-  integrity sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==
+object.fromentries@^2.0.7:
+  version "2.0.7"
+  resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.7.tgz#71e95f441e9a0ea6baf682ecaaf37fa2a8d7e616"
+  integrity sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==
   dependencies:
     call-bind "^1.0.2"
-    define-properties "^1.1.4"
-    es-abstract "^1.20.4"
+    define-properties "^1.2.0"
+    es-abstract "^1.22.1"
+
+object.groupby@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/object.groupby/-/object.groupby-1.0.1.tgz#d41d9f3c8d6c778d9cbac86b4ee9f5af103152ee"
+  integrity sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ==
+  dependencies:
+    call-bind "^1.0.2"
+    define-properties "^1.2.0"
+    es-abstract "^1.22.1"
+    get-intrinsic "^1.2.1"
+
+object.values@^1.1.7:
+  version "1.1.7"
+  resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.7.tgz#617ed13272e7e1071b43973aa1655d9291b8442a"
+  integrity sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==
+  dependencies:
+    call-bind "^1.0.2"
+    define-properties "^1.2.0"
+    es-abstract "^1.22.1"
 
 obsidian@^1.1.1:
   version "1.2.5"
@@ -4619,29 +4488,17 @@ onetime@^5.1.0, onetime@^5.1.2:
   dependencies:
     mimic-fn "^2.1.0"
 
-optionator@^0.8.1:
-  version "0.8.3"
-  resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495"
-  integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==
-  dependencies:
-    deep-is "~0.1.3"
-    fast-levenshtein "~2.0.6"
-    levn "~0.3.0"
-    prelude-ls "~1.1.2"
-    type-check "~0.3.2"
-    word-wrap "~1.2.3"
-
-optionator@^0.9.1:
-  version "0.9.1"
-  resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499"
-  integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==
+optionator@^0.9.3:
+  version "0.9.3"
+  resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64"
+  integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==
   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"
-    word-wrap "^1.2.3"
 
 ora@^5.4.1:
   version "5.4.1"
@@ -4714,9 +4571,9 @@ parse-ms@^2.1.0:
   integrity sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==
 
 parse5@^7.0.0, parse5@^7.1.1:
-  version "7.1.1"
-  resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.1.1.tgz#4649f940ccfb95d8754f37f73078ea20afe0c746"
-  integrity sha512-kwpuwzB+px5WUg9pyK0IcK/shltJN5/OVhQagxhCQNtT9Y9QRZqNY2e1cmbu/paRh5LMnz/oVTVLBpjFmMZhSg==
+  version "7.1.2"
+  resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.1.2.tgz#0736bebbfd77793823240a23b7fc5e010b7f8e32"
+  integrity sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==
   dependencies:
     entities "^4.4.0"
 
@@ -4848,11 +4705,6 @@ prelude-ls@^1.2.1:
   resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
   integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
 
-prelude-ls@~1.1.2:
-  version "1.1.2"
-  resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
-  integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==
-
 prettier-linter-helpers@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b"
@@ -4879,7 +4731,7 @@ pretty-format@^27.0.2:
     ansi-styles "^5.0.0"
     react-is "^17.0.1"
 
-pretty-format@^29.0.0, pretty-format@^29.5.0, pretty-format@^29.7.0:
+pretty-format@^29.0.0, pretty-format@^29.7.0:
   version "29.7.0"
   resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812"
   integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==
@@ -4909,9 +4761,9 @@ psl@^1.1.33:
   integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==
 
 punycode@^2.1.0, punycode@^2.1.1:
-  version "2.1.1"
-  resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
-  integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
+  version "2.3.1"
+  resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5"
+  integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==
 
 pure-rand@^6.0.0:
   version "6.0.2"
@@ -4996,14 +4848,19 @@ regenerator-runtime@^0.13.11:
   resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9"
   integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==
 
-regexp.prototype.flags@^1.4.3:
-  version "1.4.3"
-  resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac"
-  integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==
+regenerator-runtime@^0.14.0:
+  version "0.14.0"
+  resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz#5e19d68eb12d486f797e15a3c6a918f7cec5eb45"
+  integrity sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==
+
+regexp.prototype.flags@^1.5.1:
+  version "1.5.1"
+  resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz#90ce989138db209f81492edd734183ce99f9677e"
+  integrity sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==
   dependencies:
     call-bind "^1.0.2"
-    define-properties "^1.1.3"
-    functions-have-names "^1.2.2"
+    define-properties "^1.2.0"
+    set-function-name "^2.0.0"
 
 require-directory@^2.1.1:
   version "2.1.1"
@@ -5073,12 +4930,12 @@ resolve@^1.21.0:
     path-parse "^1.0.7"
     supports-preserve-symlinks-flag "^1.0.0"
 
-resolve@^1.22.1:
-  version "1.22.2"
-  resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.2.tgz#0ed0943d4e301867955766c9f3e1ae6d01c6845f"
-  integrity sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==
+resolve@^1.22.4:
+  version "1.22.8"
+  resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d"
+  integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==
   dependencies:
-    is-core-module "^2.11.0"
+    is-core-module "^2.13.0"
     path-parse "^1.0.7"
     supports-preserve-symlinks-flag "^1.0.0"
 
@@ -5137,6 +4994,16 @@ sade@^1.7.4:
   dependencies:
     mri "^1.1.0"
 
+safe-array-concat@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.0.1.tgz#91686a63ce3adbea14d61b14c99572a8ff84754c"
+  integrity sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==
+  dependencies:
+    call-bind "^1.0.2"
+    get-intrinsic "^1.2.1"
+    has-symbols "^1.0.3"
+    isarray "^2.0.5"
+
 safe-buffer@~5.1.1:
   version "5.1.2"
   resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
@@ -5185,7 +5052,7 @@ saxes@^6.0.0:
   dependencies:
     xmlchars "^2.2.0"
 
-semver@^6.0.0, semver@^6.3.0:
+semver@^6.0.0, semver@^6.3.0, semver@^6.3.1:
   version "6.3.1"
   resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
   integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
@@ -5197,6 +5064,25 @@ semver@^7.3.5, semver@^7.3.7, semver@^7.5.3, semver@^7.5.4:
   dependencies:
     lru-cache "^6.0.0"
 
+set-function-length@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.1.1.tgz#4bc39fafb0307224a33e106a7d35ca1218d659ed"
+  integrity sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==
+  dependencies:
+    define-data-property "^1.1.1"
+    get-intrinsic "^1.2.1"
+    gopd "^1.0.1"
+    has-property-descriptors "^1.0.0"
+
+set-function-name@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.1.tgz#12ce38b7954310b9f61faa12701620a0c882793a"
+  integrity sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==
+  dependencies:
+    define-data-property "^1.0.1"
+    functions-have-names "^1.2.3"
+    has-property-descriptors "^1.0.0"
+
 shebang-command@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
@@ -5298,19 +5184,12 @@ sprintf-js@~1.0.2:
   integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==
 
 stack-utils@^2.0.3:
-  version "2.0.5"
-  resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.5.tgz#d25265fca995154659dbbfba3b49254778d2fdd5"
-  integrity sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==
+  version "2.0.6"
+  resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f"
+  integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==
   dependencies:
     escape-string-regexp "^2.0.0"
 
-stop-iteration-iterator@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz#6a60be0b4ee757d1ed5254858ec66b10c49285e4"
-  integrity sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==
-  dependencies:
-    internal-slot "^1.0.4"
-
 stream-combiner@^0.2.2:
   version "0.2.2"
   resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.2.2.tgz#aec8cbac177b56b6f4fa479ced8c1912cee52858"
@@ -5343,50 +5222,32 @@ string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
     is-fullwidth-code-point "^3.0.0"
     strip-ansi "^6.0.1"
 
-string.prototype.trim@^1.2.7:
-  version "1.2.7"
-  resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz#a68352740859f6893f14ce3ef1bb3037f7a90533"
-  integrity sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==
-  dependencies:
-    call-bind "^1.0.2"
-    define-properties "^1.1.4"
-    es-abstract "^1.20.4"
-
-string.prototype.trimend@^1.0.5:
-  version "1.0.5"
-  resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz#914a65baaab25fbdd4ee291ca7dde57e869cb8d0"
-  integrity sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==
-  dependencies:
-    call-bind "^1.0.2"
-    define-properties "^1.1.4"
-    es-abstract "^1.19.5"
-
-string.prototype.trimend@^1.0.6:
-  version "1.0.6"
-  resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz#c4a27fa026d979d79c04f17397f250a462944533"
-  integrity sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==
+string.prototype.trim@^1.2.8:
+  version "1.2.8"
+  resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz#f9ac6f8af4bd55ddfa8895e6aea92a96395393bd"
+  integrity sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==
   dependencies:
     call-bind "^1.0.2"
-    define-properties "^1.1.4"
-    es-abstract "^1.20.4"
+    define-properties "^1.2.0"
+    es-abstract "^1.22.1"
 
-string.prototype.trimstart@^1.0.5:
-  version "1.0.5"
-  resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz#5466d93ba58cfa2134839f81d7f42437e8c01fef"
-  integrity sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==
+string.prototype.trimend@^1.0.7:
+  version "1.0.7"
+  resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz#1bb3afc5008661d73e2dc015cd4853732d6c471e"
+  integrity sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==
   dependencies:
     call-bind "^1.0.2"
-    define-properties "^1.1.4"
-    es-abstract "^1.19.5"
+    define-properties "^1.2.0"
+    es-abstract "^1.22.1"
 
-string.prototype.trimstart@^1.0.6:
-  version "1.0.6"
-  resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz#e90ab66aa8e4007d92ef591bbf3cd422c56bdcf4"
-  integrity sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==
+string.prototype.trimstart@^1.0.7:
+  version "1.0.7"
+  resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz#d4cdb44b83a4737ffbac2d406e405d43d0184298"
+  integrity sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==
   dependencies:
     call-bind "^1.0.2"
-    define-properties "^1.1.4"
-    es-abstract "^1.20.4"
+    define-properties "^1.2.0"
+    es-abstract "^1.22.1"
 
 string_decoder@^1.1.1:
   version "1.3.0"
@@ -5438,7 +5299,7 @@ strip-json-comments@5.0.0:
   resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-5.0.0.tgz#ec101b766476a703031bc607e3c712569de2aa06"
   integrity sha512-V1LGY4UUo0jgwC+ELQ2BNWfPa17TIuwBLg+j1AA/9RPzKINl1lhxVEu2r+ZTTO8aetIsUzE5Qj6LMSBkoGYKKw==
 
-strip-json-comments@^3.1.0, strip-json-comments@^3.1.1:
+strip-json-comments@^3.1.1:
   version "3.1.1"
   resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
   integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
@@ -5496,9 +5357,9 @@ supports-preserve-symlinks-flag@^1.0.0:
   integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
 
 svelte-check@^3.4.6:
-  version "3.4.6"
-  resolved "https://registry.yarnpkg.com/svelte-check/-/svelte-check-3.4.6.tgz#d43de724ad89d1198c96770e9d23965d3379ad44"
-  integrity sha512-OBlY8866Zh1zHQTkBMPS6psPi7o2umTUyj6JWm4SacnIHXpWFm658pG32m3dKvKFL49V4ntAkfFHKo4ztH07og==
+  version "3.6.0"
+  resolved "https://registry.yarnpkg.com/svelte-check/-/svelte-check-3.6.0.tgz#d501813dc83648c1b89cf9953de385762843731e"
+  integrity sha512-8VfqhfuRJ1sKW+o8isH2kPi0RhjXH1nNsIbCFGyoUHG+ZxVxHYRKcb+S8eaL/1tyj3VGvWYx3Y5+oCUsJgnzcw==
   dependencies:
     "@jridgewell/trace-mapping" "^0.3.17"
     chokidar "^3.4.1"
@@ -5506,7 +5367,7 @@ svelte-check@^3.4.6:
     import-fresh "^3.2.1"
     picocolors "^1.0.0"
     sade "^1.7.4"
-    svelte-preprocess "^5.0.4"
+    svelte-preprocess "^5.1.0"
     typescript "^5.0.3"
 
 svelte-jester@^2.3.2:
@@ -5514,10 +5375,10 @@ svelte-jester@^2.3.2:
   resolved "https://registry.yarnpkg.com/svelte-jester/-/svelte-jester-2.3.2.tgz#9eb818da30807bbcc940b6130d15b2c34408d64f"
   integrity sha512-JtxSz4FWAaCRBXbPsh4LcDs4Ua7zdXgLC0TZvT1R56hRV0dymmNP+abw67DTPF7sQPyNxWsOKd0Sl7Q8SnP8kg==
 
-svelte-preprocess@^5.0.4:
-  version "5.0.4"
-  resolved "https://registry.yarnpkg.com/svelte-preprocess/-/svelte-preprocess-5.0.4.tgz#2123898e079a074f7f4ef1799e10e037f5bcc55b"
-  integrity sha512-ABia2QegosxOGsVlsSBJvoWeXy1wUKSfF7SWJdTjLAbx/Y3SrVevvvbFNQqrSJw89+lNSsM58SipmZJ5SRi5iw==
+svelte-preprocess@^5.0.4, svelte-preprocess@^5.1.0:
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/svelte-preprocess/-/svelte-preprocess-5.1.0.tgz#824a1b072da118cba46ad2e5e88a568083acad4b"
+  integrity sha512-EkErPiDzHAc0k2MF5m6vBNmRUh338h2myhinUw/xaqsLs7/ZvsgREiLGj03VrSzbY/TB5ZXgBOsKraFee5yceA==
   dependencies:
     "@types/pug" "^2.0.6"
     detect-indent "^6.1.0"
@@ -5526,9 +5387,9 @@ svelte-preprocess@^5.0.4:
     strip-indent "^3.0.0"
 
 svelte@^3.59.1:
-  version "3.59.1"
-  resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.59.1.tgz#3de3d56b9165748f32f3131589b8d183cabe7449"
-  integrity sha512-pKj8fEBmqf6mq3/NfrB9SLtcJcUvjYSWyePlfCqN9gujLB25RitWK8PvFzlwim6hD/We35KbPlRteuA6rnPGcQ==
+  version "3.59.2"
+  resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.59.2.tgz#a137b28e025a181292b2ae2e3dca90bf8ec73aec"
+  integrity sha512-vzSyuGr3eEoAtT/A6bmajosJZIUWySzY2CzB3w2pgPvnkUjGqlDnsNnA0PMO+mMAhuyMul6C2uuZzY6ELSkzyA==
 
 symbol-tree@^3.2.4:
   version "3.2.4"
@@ -5620,7 +5481,7 @@ ts-jest@^29.0.3, ts-jest@^29.1.1:
     semver "^7.5.3"
     yargs-parser "^21.0.1"
 
-tsconfig-paths@^3.10.1:
+tsconfig-paths@^3.10.1, tsconfig-paths@^3.14.2:
   version "3.14.2"
   resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz#6e32f1f79412decd261f92d633a9dc1cfa99f088"
   integrity sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==
@@ -5630,26 +5491,21 @@ tsconfig-paths@^3.10.1:
     minimist "^1.2.6"
     strip-bom "^3.0.0"
 
-tsconfig-paths@^3.14.1:
-  version "3.14.1"
-  resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a"
-  integrity sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==
-  dependencies:
-    "@types/json5" "^0.0.29"
-    json5 "^1.0.1"
-    minimist "^1.2.6"
-    strip-bom "^3.0.0"
-
 tslib@^1.8.1:
   version "1.14.1"
   resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
   integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
 
-tslib@^2.4.0, tslib@^2.5.2:
+tslib@^2.4.0:
   version "2.5.2"
   resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.2.tgz#1b6f07185c881557b0ffa84b111a0106989e8338"
   integrity sha512-5svOrSA2w3iGFDs1HibEVBGbDrAY82bFQ3HZ3ixB+88nsbsWQoKqDRb5UBYAUPEzbBn6dAp5gRNXglySbx1MlA==
 
+tslib@^2.5.2:
+  version "2.6.2"
+  resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"
+  integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==
+
 tsutils@^3.21.0:
   version "3.21.0"
   resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623"
@@ -5664,13 +5520,6 @@ type-check@^0.4.0, type-check@~0.4.0:
   dependencies:
     prelude-ls "^1.2.1"
 
-type-check@~0.3.2:
-  version "0.3.2"
-  resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72"
-  integrity sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==
-  dependencies:
-    prelude-ls "~1.1.2"
-
 type-detect@4.0.8:
   version "4.0.8"
   resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
@@ -5691,6 +5540,36 @@ type-fest@^1.0.2:
   resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-1.4.0.tgz#e9fb813fe3bf1744ec359d55d1affefa76f14be1"
   integrity sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==
 
+typed-array-buffer@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz#18de3e7ed7974b0a729d3feecb94338d1472cd60"
+  integrity sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==
+  dependencies:
+    call-bind "^1.0.2"
+    get-intrinsic "^1.2.1"
+    is-typed-array "^1.1.10"
+
+typed-array-byte-length@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz#d787a24a995711611fb2b87a4052799517b230d0"
+  integrity sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==
+  dependencies:
+    call-bind "^1.0.2"
+    for-each "^0.3.3"
+    has-proto "^1.0.1"
+    is-typed-array "^1.1.10"
+
+typed-array-byte-offset@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz#cbbe89b51fdef9cd6aaf07ad4707340abbc4ea0b"
+  integrity sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==
+  dependencies:
+    available-typed-arrays "^1.0.5"
+    call-bind "^1.0.2"
+    for-each "^0.3.3"
+    has-proto "^1.0.1"
+    is-typed-array "^1.1.10"
+
 typed-array-length@^1.0.4:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb"
@@ -5711,9 +5590,9 @@ typescript@^4.0.0, typescript@^4.9.5:
   integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==
 
 typescript@^5.0.3, typescript@^5.0.4:
-  version "5.1.6"
-  resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.6.tgz#02f8ac202b6dad2c0dd5e0913745b47a37998274"
-  integrity sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==
+  version "5.2.2"
+  resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.2.2.tgz#5ebb5e5a5b75f085f22bc3f8460fba308310fa78"
+  integrity sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==
 
 uc.micro@^1.0.1, uc.micro@^1.0.5:
   version "1.0.6"
@@ -5730,6 +5609,11 @@ unbox-primitive@^1.0.2:
     has-symbols "^1.0.3"
     which-boxed-primitive "^1.0.2"
 
+undici-types@~5.26.4:
+  version "5.26.5"
+  resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617"
+  integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==
+
 uniq@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff"
@@ -5782,10 +5666,10 @@ w3c-keyname@^2.2.4:
   resolved "https://registry.yarnpkg.com/w3c-keyname/-/w3c-keyname-2.2.4.tgz#4ade6916f6290224cdbd1db8ac49eab03d0eef6b"
   integrity sha512-tOhfEwEzFLJzf6d1ZPkYfGj+FWhIpBux9ppoP3rlclw3Z0BZv3N7b7030Z1kYth+6rDuAsXUFr+d0VE6Ed1ikw==
 
-w3c-xmlserializer@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-3.0.0.tgz#06cdc3eefb7e4d0b20a560a5a3aeb0d2d9a65923"
-  integrity sha512-3WFqGEgSXIyGhOmAFtlicJNMjEps8b1MG31NCA0/vOF9+nKMUW1ckhi9cnNHmf88Rzw5V+dwIwsm2C7X8k9aQg==
+w3c-xmlserializer@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz#aebdc84920d806222936e3cdce408e32488a3073"
+  integrity sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==
   dependencies:
     xml-name-validator "^4.0.0"
 
@@ -5844,27 +5728,16 @@ which-boxed-primitive@^1.0.2:
     is-string "^1.0.5"
     is-symbol "^1.0.3"
 
-which-collection@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.1.tgz#70eab71ebbbd2aefaf32f917082fc62cdcb70906"
-  integrity sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==
-  dependencies:
-    is-map "^2.0.1"
-    is-set "^2.0.1"
-    is-weakmap "^2.0.1"
-    is-weakset "^2.0.1"
-
-which-typed-array@^1.1.9:
-  version "1.1.9"
-  resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.9.tgz#307cf898025848cf995e795e8423c7f337efbde6"
-  integrity sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==
+which-typed-array@^1.1.11, which-typed-array@^1.1.13:
+  version "1.1.13"
+  resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.13.tgz#870cd5be06ddb616f504e7b039c4c24898184d36"
+  integrity sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==
   dependencies:
     available-typed-arrays "^1.0.5"
-    call-bind "^1.0.2"
+    call-bind "^1.0.4"
     for-each "^0.3.3"
     gopd "^1.0.1"
     has-tostringtag "^1.0.0"
-    is-typed-array "^1.1.10"
 
 which@^2.0.1:
   version "2.0.2"
@@ -5873,11 +5746,6 @@ which@^2.0.1:
   dependencies:
     isexe "^2.0.0"
 
-word-wrap@^1.2.3, word-wrap@~1.2.3:
-  version "1.2.5"
-  resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34"
-  integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==
-
 wrap-ansi@^7.0.0:
   version "7.0.0"
   resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
@@ -5900,10 +5768,10 @@ write-file-atomic@^4.0.2:
     imurmurhash "^0.1.4"
     signal-exit "^3.0.7"
 
-ws@^8.9.0:
-  version "8.11.0"
-  resolved "https://registry.yarnpkg.com/ws/-/ws-8.11.0.tgz#6a0d36b8edfd9f96d8b25683db2f8d7de6e8e143"
-  integrity sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==
+ws@^8.11.0:
+  version "8.14.2"
+  resolved "https://registry.yarnpkg.com/ws/-/ws-8.14.2.tgz#6c249a806eb2db7a20d26d51e7709eab7b2e6c7f"
+  integrity sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==
 
 xml-name-validator@^4.0.0:
   version "4.0.0"


Test patch:
diff --git a/tests/Commands/CreateOrEditTaskParser.test.ts b/tests/Commands/CreateOrEditTaskParser.test.ts
index 332bdf57..262e834b 100644
--- a/tests/Commands/CreateOrEditTaskParser.test.ts
+++ b/tests/Commands/CreateOrEditTaskParser.test.ts
@@ -88,6 +88,7 @@ describe('CreateOrEditTaskParser - task recognition', () => {
         expect(task.toFileLineString()).toStrictEqual(taskLine);
         expect(task.path).toStrictEqual('a/b/c.md');
 
+        // NEW_TASK_FIELD_EDIT_REQUIRED
         expect(task.priority).toStrictEqual(Priority.Lowest);
         expect(task.recurrenceRule).toStrictEqual('every 2 days');
         expect(task.createdDate).toEqualMoment(moment('2022-03-10'));
diff --git a/tests/CustomMatchers/CustomMatchersForTaskSerializer.ts b/tests/CustomMatchers/CustomMatchersForTaskSerializer.ts
index 4f715ed6..12b86b85 100644
--- a/tests/CustomMatchers/CustomMatchersForTaskSerializer.ts
+++ b/tests/CustomMatchers/CustomMatchersForTaskSerializer.ts
@@ -35,6 +35,7 @@ function isTaskDetails(val: any): val is TaskDetails {
     }
 
     const dates: ReadonlyArray<keyof TaskDetails> = [
+        // NEW_TASK_FIELD_EDIT_REQUIRED
         'startDate',
         'createdDate',
         'scheduledDate',
@@ -73,6 +74,7 @@ type SummarizedTaskDetails = { [K in keyof TaskDetails]: AsString<TaskDetails[K]
 function summarizeTaskDetails(t: TaskDetails | null): SummarizedTaskDetails | null {
     if (t === null) return null;
     return {
+        // NEW_TASK_FIELD_EDIT_REQUIRED
         ...t,
         startDate: t.startDate?.format(TaskRegularExpressions.dateFormat) ?? null,
         createdDate: t.createdDate?.format(TaskRegularExpressions.dateFormat) ?? null,
@@ -94,6 +96,7 @@ function summarizeTaskDetails(t: TaskDetails | null): SummarizedTaskDetails | nu
  */
 function tryBuildTaskDetails(t: object): TaskDetails | null {
     const toReturn = {
+        // NEW_TASK_FIELD_EDIT_REQUIRED
         description: '',
         priority: Priority.None,
         startDate: null,
diff --git a/tests/Query.test.ts b/tests/Query.test.ts
index 0188ef6d..a70f8d77 100644
--- a/tests/Query.test.ts
+++ b/tests/Query.test.ts
@@ -41,6 +41,7 @@ function sortInstructionLines(filters: ReadonlyArray<string>) {
 describe('Query parsing', () => {
     // In alphabetical order, please
     const filters: ReadonlyArray<string> = [
+        // NEW_QUERY_INSTRUCTION_EDIT_REQUIRED
         '(due this week) AND (description includes Hello World)',
         'created after 2021-12-27',
         'created before 2021-12-27',
@@ -90,7 +91,6 @@ describe('Query parsing', () => {
         'is recurring',
         'no created date',
         'no due date',
-        'no due date',
         'no happens date',
         'no scheduled date',
         'no start date',
@@ -152,11 +152,17 @@ describe('Query parsing', () => {
         test.concurrent.each<string>(filters)('recognises %j', (filter) => {
             // Arrange
             const query = new Query(filter);
+            const queryUpperCase = new Query(filter.toUpperCase());
 
             // Assert
             expect(query.error).toBeUndefined();
             expect(query.filters.length).toEqual(1);
             expect(query.filters[0]).toBeDefined();
+
+            // Assert Uppercase
+            expect(queryUpperCase.error).toBeUndefined();
+            expect(queryUpperCase.filters.length).toEqual(1);
+            expect(queryUpperCase.filters[0]).toBeDefined();
         });
 
         it('sample lines really are in alphabetical order', () => {
@@ -216,6 +222,7 @@ describe('Query parsing', () => {
     describe('should recognise every sort instruction', () => {
         // In alphabetical order, please
         const filters: ReadonlyArray<string> = [
+            // NEW_QUERY_INSTRUCTION_EDIT_REQUIRED
             'sort by created',
             'sort by created reverse',
             'sort by description',
@@ -256,11 +263,17 @@ describe('Query parsing', () => {
         test.concurrent.each<string>(filters)('recognises %j', (filter) => {
             // Arrange
             const query = new Query(filter);
+            const queryUpperCase = new Query(filter.toUpperCase());
 
             // Assert
             expect(query.error).toBeUndefined();
             expect(query.sorting.length).toEqual(1);
             expect(query.sorting[0]).toBeDefined();
+
+            // Assert Uppercase
+            expect(queryUpperCase.error).toBeUndefined();
+            expect(queryUpperCase.sorting.length).toEqual(1);
+            expect(queryUpperCase.sorting[0]).toBeDefined();
         });
 
         it('sample lines really are in alphabetical order', () => {
@@ -291,6 +304,7 @@ describe('Query parsing', () => {
     describe('should recognise every group instruction', () => {
         // In alphabetical order, please
         const filters: ReadonlyArray<string> = [
+            // NEW_QUERY_INSTRUCTION_EDIT_REQUIRED
             'group by backlink',
             'group by backlink reverse',
             'group by created',
@@ -338,11 +352,17 @@ describe('Query parsing', () => {
         test.concurrent.each<string>(filters)('recognises %j', (filter) => {
             // Arrange
             const query = new Query(filter);
+            const queryUpperCase = new Query(filter.toUpperCase());
 
             // Assert
             expect(query.error).toBeUndefined();
             expect(query.grouping.length).toEqual(1);
             expect(query.grouping[0]).toBeDefined();
+
+            // Assert
+            expect(queryUpperCase.error).toBeUndefined();
+            expect(queryUpperCase.grouping.length).toEqual(1);
+            expect(queryUpperCase.grouping[0]).toBeDefined();
         });
 
         it('sample lines really are in alphabetical order', () => {
@@ -410,9 +430,11 @@ describe('Query parsing', () => {
         test.concurrent.each<string>(filters)('recognises %j', (filter) => {
             // Arrange
             const query = new Query(filter);
+            const queryUpperCase = new Query(filter.toUpperCase());
 
             // Assert
             expect(query.error).toBeUndefined();
+            expect(queryUpperCase.error).toBeUndefined();
         });
 
         it('sample lines really are in alphabetical order', () => {
@@ -423,8 +445,8 @@ describe('Query parsing', () => {
     describe('should recognize boolean queries', () => {
         const filters: ReadonlyArray<string> = [
             '# Comment lines are ignored',
-            '(description includes wibble) OR (has due date)',
-            '(has due date) OR ((has start date) AND (due after 2021-12-27))',
+            '(DESCRIPTION INCLUDES wibble) OR (has due date)',
+            '(has due date) OR ((HAS START DATE) AND (due after 2021-12-27))',
             '(is not recurring) XOR ((path includes ab/c) OR (happens before 2021-12-27))',
             String.raw`(description includes line 1) OR \
 (description includes line 1 continued\
@@ -441,13 +463,21 @@ describe('Query parsing', () => {
 
     it('should parse ambiguous sort by queries correctly', () => {
         expect(new Query('sort by status').sorting[0].property).toEqual('status');
+        expect(new Query('SORT BY STATUS').sorting[0].property).toEqual('status');
+
         expect(new Query('sort by status.name').sorting[0].property).toEqual('status.name');
+        expect(new Query('SORT BY STATUS.NAME').sorting[0].property).toEqual('status.name');
     });
 
     it('should parse ambiguous group by queries correctly', () => {
         expect(new Query('group by status').grouping[0].property).toEqual('status');
+        expect(new Query('GROUP BY STATUS').grouping[0].property).toEqual('status');
+
         expect(new Query('group by status.name').grouping[0].property).toEqual('status.name');
+        expect(new Query('GROUP BY STATUS.NAME').grouping[0].property).toEqual('status.name');
+
         expect(new Query('group by status.type').grouping[0].property).toEqual('status.type');
+        expect(new Query('GROUP BY STATUS.TYPE').grouping[0].property).toEqual('status.type');
     });
 
     describe('should include instruction in parsing error messages', () => {
@@ -493,26 +523,38 @@ Problem line: "${source}"`,
 
         it('for invalid sort by', () => {
             const source = 'sort by nonsense';
+            const sourceUpperCase = source.toUpperCase();
             expect(getQueryError(source)).toEqual(`do not understand query
 Problem line: "${source}"`);
+            expect(getQueryError(sourceUpperCase)).toEqual(`do not understand query
+Problem line: "${sourceUpperCase}"`);
         });
 
         it('for invalid group by', () => {
             const source = 'group by nonsense';
+            const sourceUpperCase = source.toUpperCase();
             expect(getQueryError(source)).toEqual(`do not understand query
 Problem line: "${source}"`);
+            expect(getQueryError(sourceUpperCase)).toEqual(`do not understand query
+Problem line: "${sourceUpperCase}"`);
         });
 
         it('for invalid hide', () => {
             const source = 'hide nonsense';
+            const sourceUpperCase = source.toUpperCase();
             expect(getQueryError(source)).toEqual(`do not understand query
 Problem line: "${source}"`);
+            expect(getQueryError(sourceUpperCase)).toEqual(`do not understand query
+Problem line: "${sourceUpperCase}"`);
         });
 
         it('for unknown instruction', () => {
             const source = 'spaghetti';
+            const sourceUpperCase = source.toUpperCase();
             expect(getQueryError(source)).toEqual(`do not understand query
 Problem line: "${source}"`);
+            expect(getQueryError(sourceUpperCase)).toEqual(`do not understand query
+Problem line: "${sourceUpperCase}"`);
         });
     });
 
@@ -754,6 +796,11 @@ describe('Query', () => {
             ],
         ])('should support filtering %s', (_, { tasks: allTaskLines, filters, expectedResult }) => {
             shouldSupportFiltering(filters, allTaskLines, expectedResult);
+            shouldSupportFiltering(
+                filters.map((filter) => filter.toUpperCase()),
+                allTaskLines,
+                expectedResult,
+            );
         });
     });
 
@@ -885,6 +932,7 @@ describe('Query', () => {
 
                 // Act, Assert
                 shouldSupportFiltering([happensFilter], [line], expectedResult);
+                shouldSupportFiltering([happensFilter.toUpperCase()], [line], expectedResult);
             },
         );
     });
@@ -1029,6 +1077,11 @@ describe('Query', () => {
             ],
         ])('should support boolean filtering %s', (_, { tasks: allTaskLines, filters, expectedResult }) => {
             shouldSupportFiltering(filters, allTaskLines, expectedResult);
+            shouldSupportFiltering(
+                filters.map((filter) => filter.toUpperCase()),
+                allTaskLines,
+                expectedResult,
+            );
         });
     });
 
@@ -1243,35 +1296,54 @@ At most 8 tasks per group (if any "group by" options are supplied).
             // Arrange
             const source = 'group by path';
             const query = new Query(source);
+            const queryUpper = new Query(source.toUpperCase());
 
             // Assert
             expect(query.error).toBeUndefined();
+            expect(queryUpper.error).toBeUndefined();
+
             expect(query.grouping.length).toEqual(1);
+            expect(queryUpper.grouping.length).toEqual(1);
         });
 
         it('should work with a custom group that uses query information', () => {
             // Arrange
             const source = 'group by function query.file.path';
+            const sourceUpper = 'GROUP BY FUNCTION query.file.path';
+
             const query = new Query(source, 'hello.md');
+            const queryUpper = new Query(sourceUpper, 'hello.md');
 
             // Act
             const results = query.applyQueryToTasks([new TaskBuilder().build()]);
+            const resultsUpper = queryUpper.applyQueryToTasks([new TaskBuilder().build()]);
 
             // Assert
             const groups = results.taskGroups;
+            const groupsUpper = resultsUpper.taskGroups;
+
             expect(groups.groups.length).toEqual(1);
+            expect(groupsUpper.groups.length).toEqual(1);
+
             expect(groups.groups[0].groups).toEqual(['hello.md']);
+            expect(groupsUpper.groups[0].groups).toEqual(['hello.md']);
         });
 
         it('should log meaningful error for supported group type', () => {
             // Arrange
             const source = 'group by xxxx';
+            const sourceUpper = source.toUpperCase();
+
             const query = new Query(source);
+            const queryUpperr = new Query(sourceUpper);
 
             // Assert
             // Check that the error message contains the actual problem line
             expect(query.error).toContain(source);
+            expect(queryUpperr.error).toContain(sourceUpper);
+
             expect(query.grouping.length).toEqual(0);
+            expect(queryUpperr.grouping.length).toEqual(0);
         });
 
         it('should apply limit correctly, after sorting tasks', () => {
@@ -1286,7 +1358,9 @@ At most 8 tasks per group (if any "group by" options are supplied).
                 # Apply a limit, to test which tasks make it to
                 limit 2
                 `;
+            const sourceUpper = source.toUpperCase();
             const query = new Query(source);
+            const queryUpper = new Query(sourceUpper);
 
             const tasksAsMarkdown = `
 - [x] Task 1 - should not appear in output
@@ -1301,18 +1375,26 @@ At most 8 tasks per group (if any "group by" options are supplied).
 
             // Act
             const queryResult = query.applyQueryToTasks(tasks);
+            const queryUpperResult = queryUpper.applyQueryToTasks(tasks);
 
             // Assert
             expect(queryResult.groups.length).toEqual(1);
+            expect(queryUpperResult.groups.length).toEqual(1);
+
             const soleTaskGroup = queryResult.groups[0];
+            const soleTaskGroupUpper = queryUpperResult.groups[0];
             const expectedTasks = `
 - [ ] Task 3 - will be sorted to 1st place, so should pass limit
 - [ ] Task 4 - will be sorted to 2nd place, so should pass limit
 `;
             expect('\n' + soleTaskGroup.tasksAsStringOfLines()).toStrictEqual(expectedTasks);
+            expect('\n' + soleTaskGroupUpper.tasksAsStringOfLines()).toStrictEqual(expectedTasks);
 
             expect(queryResult.taskGroups.totalTasksCount()).toEqual(2);
+            expect(queryUpperResult.taskGroups.totalTasksCount()).toEqual(2);
+
             expect(queryResult.totalTasksCountBeforeLimit).toEqual(6);
+            expect(queryUpperResult.totalTasksCountBeforeLimit).toEqual(6);
         });
 
         it('should apply group limit correctly, after sorting tasks', () => {
@@ -1327,7 +1409,9 @@ At most 8 tasks per group (if any "group by" options are supplied).
                 # Apply a limit, to test which tasks make it to
                 limit groups 3
                 `;
+            const sourceUpper = source.toUpperCase();
             const query = new Query(source);
+            const queryUpper = new Query(sourceUpper);
 
             const tasksAsMarkdown = `
 - [x] Task 2 - will be in the first group and sorted after next one
@@ -1342,24 +1426,44 @@ At most 8 tasks per group (if any "group by" options are supplied).
 
             // Act
             const queryResult = query.applyQueryToTasks(tasks);
+            const queryUpperResult = queryUpper.applyQueryToTasks(tasks);
 
             // Assert
             expect(queryResult.groups.length).toEqual(2);
+            expect(queryUpperResult.groups.length).toEqual(2);
+
             expect(queryResult.totalTasksCount).toEqual(5);
+            expect(queryUpperResult.totalTasksCount).toEqual(5);
+
             expect(queryResult.groups[0].tasksAsStringOfLines()).toMatchInlineSnapshot(`
                 "- [x] Task 1 - will be in the first group
                 - [x] Task 2 - will be in the first group and sorted after next one
                 "
             `);
+            expect(queryUpperResult.groups[0].tasksAsStringOfLines()).toMatchInlineSnapshot(`
+                "- [x] Task 1 - will be in the first group
+                - [x] Task 2 - will be in the first group and sorted after next one
+                "
+            `);
+
             expect(queryResult.groups[1].tasksAsStringOfLines()).toMatchInlineSnapshot(`
                 "- [ ] Task 3 - will be sorted to 1st place in the second group and pass the limit
                 - [ ] Task 4 - will be sorted to 2nd place in the second group and pass the limit
                 - [ ] Task 5 - will be sorted to 3nd place in the second group and pass the limit
                 "
             `);
+            expect(queryUpperResult.groups[1].tasksAsStringOfLines()).toMatchInlineSnapshot(`
+                "- [ ] Task 3 - will be sorted to 1st place in the second group and pass the limit
+                - [ ] Task 4 - will be sorted to 2nd place in the second group and pass the limit
+                - [ ] Task 5 - will be sorted to 3nd place in the second group and pass the limit
+                "
+            `);
 
             expect(queryResult.taskGroups.totalTasksCount()).toEqual(5);
+            expect(queryUpperResult.taskGroups.totalTasksCount()).toEqual(5);
+
             expect(queryResult.totalTasksCountBeforeLimit).toEqual(6);
+            expect(queryUpperResult.totalTasksCountBeforeLimit).toEqual(6);
         });
     });
 
@@ -1368,15 +1472,20 @@ At most 8 tasks per group (if any "group by" options are supplied).
             // Arrange
             const source = 'filter by function wibble';
             const query = new Query(source);
+            const queryUpper = new Query(source.toUpperCase());
             const task = TaskBuilder.createFullyPopulatedTask();
 
             // Act
             const queryResult = query.applyQueryToTasks([task]);
+            const queryResultUpper = queryUpper.applyQueryToTasks([task]);
 
             // Assert
             expect(queryResult.searchErrorMessage).toEqual(
                 'Error: Search failed.\nThe error message was:\n    "ReferenceError: wibble is not defined"',
             );
+            expect(queryResultUpper.searchErrorMessage).toEqual(
+                'Error: Search failed.\nThe error message was:\n    "ReferenceError: WIBBLE is not defined"',
+            );
         });
     });
 
@@ -1399,6 +1508,7 @@ At most 8 tasks per group (if any "group by" options are supplied).
   (description includes line 1 continued\
 with \ backslash)`;
             const query = new Query(source);
+            const queryUpperCase = new Query(source);
 
             const expectedDisplayText = String.raw`(description includes line 1) OR (description includes line 1 continued with \ backslash) =>
   OR (At least one of):
@@ -1406,6 +1516,7 @@ with \ backslash)`;
     description includes line 1 continued with \ backslash
 `;
             expect(query.explainQuery()).toEqual(expectedDisplayText);
+            expect(queryUpperCase.explainQuery()).toEqual(expectedDisplayText);
         });
     });
 });
diff --git a/tests/Scripting/ScriptingReference/CustomFiltering/CustomFilteringExamples.test.ts b/tests/Scripting/ScriptingReference/CustomFiltering/CustomFilteringExamples.test.ts
index c39a444e..40c40407 100644
--- a/tests/Scripting/ScriptingReference/CustomFiltering/CustomFilteringExamples.test.ts
+++ b/tests/Scripting/ScriptingReference/CustomFiltering/CustomFilteringExamples.test.ts
@@ -24,6 +24,8 @@ afterEach(() => {
     jest.useRealTimers();
 });
 
+// NEW_QUERY_INSTRUCTION_EDIT_REQUIRED
+
 describe('dates', () => {
     const testData: CustomPropertyDocsTestData[] = [
         // ---------------------------------------------------------------------------------
diff --git a/tests/Scripting/ScriptingReference/CustomGrouping/CustomGroupingExamples.test.ts b/tests/Scripting/ScriptingReference/CustomGrouping/CustomGroupingExamples.test.ts
index 66c61c02..a2b0c91b 100644
--- a/tests/Scripting/ScriptingReference/CustomGrouping/CustomGroupingExamples.test.ts
+++ b/tests/Scripting/ScriptingReference/CustomGrouping/CustomGroupingExamples.test.ts
@@ -24,6 +24,8 @@ afterEach(() => {
     jest.useRealTimers();
 });
 
+// NEW_QUERY_INSTRUCTION_EDIT_REQUIRED
+
 describe('dates', () => {
     const testData: CustomPropertyDocsTestData[] = [
         // ---------------------------------------------------------------------------------
diff --git a/tests/Scripting/TaskProperties.test.ts b/tests/Scripting/TaskProperties.test.ts
index caa7a038..e52a7537 100644
--- a/tests/Scripting/TaskProperties.test.ts
+++ b/tests/Scripting/TaskProperties.test.ts
@@ -44,6 +44,8 @@ describe('task', () => {
         jest.useRealTimers();
     });
 
+    // NEW_TASK_FIELD_EDIT_REQUIRED
+
     it('status', () => {
         verifyFieldDataForReferenceDocs([
             'task.isDone',
diff --git a/tests/Suggestor/Suggestor.test.ts b/tests/Suggestor/Suggestor.test.ts
index 7db6732e..5027e1ca 100644
--- a/tests/Suggestor/Suggestor.test.ts
+++ b/tests/Suggestor/Suggestor.test.ts
@@ -48,6 +48,8 @@ function cursorPosition(line: string): [lineWithoutCursor: string, cursorIndex:
 
 const MAX_GENERIC_SUGGESTIONS_FOR_TESTS = 50;
 
+// NEW_TASK_FIELD_EDIT_REQUIRED
+
 describe.each([
     { name: 'emoji', symbols: DEFAULT_SYMBOLS },
     { name: 'dataview', symbols: DATAVIEW_SYMBOLS },
diff --git a/tests/Task.test.ts b/tests/Task.test.ts
index 04f45e89..860fdb68 100644
--- a/tests/Task.test.ts
+++ b/tests/Task.test.ts
@@ -1255,6 +1255,8 @@ describe('order of recurring tasks', () => {
 });
 
 describe('identicalTo', () => {
+    // NEW_TASK_FIELD_EDIT_REQUIRED
+
     it('should check status', () => {
         const lhs = new TaskBuilder().status(Status.makeTodo());
         expect(lhs).toBeIdenticalTo(new TaskBuilder().status(Status.TODO));
diff --git a/tests/TaskFieldRenderer.test.ts b/tests/TaskFieldRenderer.test.ts
new file mode 100644
index 00000000..f376e998
--- /dev/null
+++ b/tests/TaskFieldRenderer.test.ts
@@ -0,0 +1,70 @@
+/**
+ * @jest-environment jsdom
+ */
+import moment from 'moment';
+import { TaskFieldHTMLData, TaskFieldRenderer } from '../src/TaskFieldRenderer';
+import { TaskBuilder } from './TestingTools/TaskBuilder';
+
+window.moment = moment;
+
+const fieldRenderer = new TaskFieldRenderer();
+
+describe('Field Layouts Container tests', () => {
+    beforeEach(() => {
+        jest.useFakeTimers();
+        jest.setSystemTime(new Date('2023-11-19'));
+    });
+
+    afterEach(() => {
+        jest.useRealTimers();
+    });
+
+    it('should add a data attribute for an existing component (date)', () => {
+        const task = new TaskBuilder().dueDate('2023-11-20').build();
+        const span = document.createElement('span');
+
+        fieldRenderer.addDataAttribute(span, task, 'dueDate');
+
+        expect(Object.keys(span.dataset).length).toEqual(1);
+        expect(span.dataset['taskDue']).toEqual('future-1d');
+    });
+
+    it('should add a data attribute for an existing component (not date)', () => {
+        const task = TaskBuilder.createFullyPopulatedTask();
+        const span = document.createElement('span');
+
+        fieldRenderer.addDataAttribute(span, task, 'priority');
+
+        expect(Object.keys(span.dataset).length).toEqual(1);
+        expect(span.dataset['taskPriority']).toEqual('medium');
+    });
+    it('should not add any data attributes for a missing component', () => {
+        const task = new TaskBuilder().build();
+        const span = document.createElement('span');
+
+        fieldRenderer.addDataAttribute(span, task, 'recurrenceRule');
+
+        expect(Object.keys(span.dataset).length).toEqual(0);
+    });
+});
+
+describe('Field Layout Detail tests', () => {
+    it('should supply a class name', () => {
+        const fieldLayoutDetail = new TaskFieldHTMLData('stuff', 'taskAttribute', () => {
+            return '';
+        });
+        expect(fieldLayoutDetail.className).toEqual('stuff');
+    });
+
+    it('should add a data attribute for an HTML element', () => {
+        const fieldLayoutDetail = new TaskFieldHTMLData('dataAttributeTest', 'aKey', () => {
+            return 'aValue';
+        });
+        const span = document.createElement('span');
+
+        fieldLayoutDetail.addDataAttribute(span, new TaskBuilder().build(), 'description');
+
+        expect(Object.keys(span.dataset).length).toEqual(1);
+        expect(span.dataset['aKey']).toEqual('aValue');
+    });
+});
diff --git a/tests/TaskLineRenderer.test.ts b/tests/TaskLineRenderer.test.ts
index 6ca54413..dac39fb6 100644
--- a/tests/TaskLineRenderer.test.ts
+++ b/tests/TaskLineRenderer.test.ts
@@ -8,9 +8,10 @@ import { resetSettings, updateSettings } from '../src/Config/Settings';
 import { DateParser } from '../src/Query/DateParser';
 import type { Task } from '../src/Task';
 import { TaskRegularExpressions } from '../src/Task';
+import { TaskFieldRenderer } from '../src/TaskFieldRenderer';
 import { LayoutOptions } from '../src/TaskLayout';
-import type { AttributesDictionary, TextRenderer } from '../src/TaskLineRenderer';
-import { FieldLayoutDetail, FieldLayouts, renderTaskLine } from '../src/TaskLineRenderer';
+import type { TextRenderer } from '../src/TaskLineRenderer';
+import { TaskLineRenderer } from '../src/TaskLineRenderer';
 import { fromLine } from './TestHelpers';
 import { verifyWithFileExtension } from './TestingTools/ApprovalTestHelpers';
 import { TaskBuilder } from './TestingTools/TaskBuilder';
@@ -18,6 +19,10 @@ import { TaskBuilder } from './TestingTools/TaskBuilder';
 jest.mock('obsidian');
 window.moment = moment;
 
+type AttributesDictionary = { [key: string]: string };
+
+const fieldRenderer = new TaskFieldRenderer();
+
 /**
  * Creates a dummy 'parent element' to host a task render, renders a task inside it,
  * and returns it for inspection.
@@ -29,23 +34,19 @@ async function createMockParentAndRender(task: Task, layoutOptions?: LayoutOptio
         mockTextRenderer = async (text: string, element: HTMLSpanElement, _path: string) => {
             element.innerText = text;
         };
-    await renderTaskLine(
-        task,
-        {
-            parentUlElement: parentElement,
-            listIndex: 0,
-            layoutOptions: layoutOptions,
-            obsidianComponent: null,
-        },
-        mockTextRenderer,
-    );
+    const taskLineRenderer = new TaskLineRenderer({
+        textRenderer: mockTextRenderer,
+        obsidianComponent: null,
+        parentUlElement: parentElement,
+        layoutOptions: layoutOptions ?? new LayoutOptions(),
+    });
+    await taskLineRenderer.renderTaskLine(task, 0);
     return parentElement;
 }
 
 function getTextSpan(parentElement: HTMLElement) {
     const li = parentElement.children[0];
-    const textSpan = li.children[1] as HTMLSpanElement;
-    return textSpan;
+    return li.children[1] as HTMLSpanElement;
 }
 
 function getDescriptionText(parentElement: HTMLElement) {
@@ -60,7 +61,7 @@ function getOtherLayoutComponents(parentElement: HTMLElement): string[] {
     const textSpan = getTextSpan(parentElement);
     const components: string[] = [];
     for (const childSpan of Array.from(textSpan.children)) {
-        if (childSpan.classList.contains(FieldLayouts.description.className)) continue;
+        if (childSpan.classList.contains(fieldRenderer.className('description'))) continue;
         if (childSpan?.textContent) components.push(childSpan.textContent);
     }
     return components;
@@ -353,19 +354,19 @@ describe('task line rendering', () => {
         await testComponentClasses(
             '- [ ] Full task ⏫ 📅 2022-07-02 ⏳ 2022-07-03 🛫 2022-07-04 🔁 every day',
             {},
-            FieldLayouts.priority.className,
+            fieldRenderer.className('priority'),
             { taskPriority: 'high' },
         );
         await testComponentClasses(
             '- [ ] Full task 🔼 📅 2022-07-02 ⏳ 2022-07-03 🛫 2022-07-04 🔁 every day',
             {},
-            FieldLayouts.priority.className,
+            fieldRenderer.className('priority'),
             { taskPriority: 'medium' },
         );
         await testComponentClasses(
             '- [ ] Full task 🔽 📅 2022-07-02 ⏳ 2022-07-03 🛫 2022-07-04 🔁 every day',
             {},
-            FieldLayouts.priority.className,
+            fieldRenderer.className('priority'),
             { taskPriority: 'low' },
         );
     });
@@ -374,174 +375,174 @@ describe('task line rendering', () => {
         await testComponentClasses(
             '- [ ] Full task ⏫ 📅 2022-07-02 ⏳ 2022-07-03 🛫 2022-07-04 🔁 every day',
             {},
-            FieldLayouts.recurrenceRule.className,
+            fieldRenderer.className('recurrenceRule'),
             {},
         );
     });
 
     it('adds a correct "today" CSS class to dates', async () => {
         const today = DateParser.parseDate('today').format(TaskRegularExpressions.dateFormat);
-        await testComponentClasses(`- [ ] Full task ⏫ ➕ ${today}`, {}, FieldLayouts.createdDate.className, {
+        await testComponentClasses(`- [ ] Full task ⏫ ➕ ${today}`, {}, fieldRenderer.className('createdDate'), {
             taskCreated: 'today',
         });
-        await testComponentClasses(`- [ ] Full task ⏫ 📅 ${today}`, {}, FieldLayouts.dueDate.className, {
+        await testComponentClasses(`- [ ] Full task ⏫ 📅 ${today}`, {}, fieldRenderer.className('dueDate'), {
             taskDue: 'today',
         });
-        await testComponentClasses(`- [ ] Full task ⏫ ⏳ ${today}`, {}, FieldLayouts.scheduledDate.className, {
+        await testComponentClasses(`- [ ] Full task ⏫ ⏳ ${today}`, {}, fieldRenderer.className('scheduledDate'), {
             taskScheduled: 'today',
         });
-        await testComponentClasses(`- [ ] Full task ⏫ 🛫 ${today}`, {}, FieldLayouts.startDate.className, {
+        await testComponentClasses(`- [ ] Full task ⏫ 🛫 ${today}`, {}, fieldRenderer.className('startDate'), {
             taskStart: 'today',
         });
-        await testComponentClasses(`- [x] Done task ✅ ${today}`, {}, FieldLayouts.doneDate.className, {
+        await testComponentClasses(`- [x] Done task ✅ ${today}`, {}, fieldRenderer.className('doneDate'), {
             taskDone: 'today',
         });
     });
 
     it('adds a correct "future-1d" CSS class to dates', async () => {
         const future = DateParser.parseDate('tomorrow').format(TaskRegularExpressions.dateFormat);
-        await testComponentClasses(`- [ ] Full task ⏫ ➕ ${future}`, {}, FieldLayouts.createdDate.className, {
+        await testComponentClasses(`- [ ] Full task ⏫ ➕ ${future}`, {}, fieldRenderer.className('createdDate'), {
             taskCreated: 'future-1d',
         });
-        await testComponentClasses(`- [ ] Full task ⏫ 📅 ${future}`, {}, FieldLayouts.dueDate.className, {
+        await testComponentClasses(`- [ ] Full task ⏫ 📅 ${future}`, {}, fieldRenderer.className('dueDate'), {
             taskDue: 'future-1d',
         });
-        await testComponentClasses(`- [ ] Full task ⏫ ⏳ ${future}`, {}, FieldLayouts.scheduledDate.className, {
+        await testComponentClasses(`- [ ] Full task ⏫ ⏳ ${future}`, {}, fieldRenderer.className('scheduledDate'), {
             taskScheduled: 'future-1d',
         });
-        await testComponentClasses(`- [ ] Full task ⏫ 🛫 ${future}`, {}, FieldLayouts.startDate.className, {
+        await testComponentClasses(`- [ ] Full task ⏫ 🛫 ${future}`, {}, fieldRenderer.className('startDate'), {
             taskStart: 'future-1d',
         });
-        await testComponentClasses(`- [x] Done task ✅ ${future}`, {}, FieldLayouts.doneDate.className, {
+        await testComponentClasses(`- [x] Done task ✅ ${future}`, {}, fieldRenderer.className('doneDate'), {
             taskDone: 'future-1d',
         });
     });
 
     it('adds a correct "future-7d" CSS class to dates', async () => {
         const future = DateParser.parseDate('in 7 days').format(TaskRegularExpressions.dateFormat);
-        await testComponentClasses(`- [ ] Full task ⏫ ➕ ${future}`, {}, FieldLayouts.createdDate.className, {
+        await testComponentClasses(`- [ ] Full task ⏫ ➕ ${future}`, {}, fieldRenderer.className('createdDate'), {
             taskCreated: 'future-7d',
         });
-        await testComponentClasses(`- [ ] Full task ⏫ 📅 ${future}`, {}, FieldLayouts.dueDate.className, {
+        await testComponentClasses(`- [ ] Full task ⏫ 📅 ${future}`, {}, fieldRenderer.className('dueDate'), {
             taskDue: 'future-7d',
         });
-        await testComponentClasses(`- [ ] Full task ⏫ ⏳ ${future}`, {}, FieldLayouts.scheduledDate.className, {
+        await testComponentClasses(`- [ ] Full task ⏫ ⏳ ${future}`, {}, fieldRenderer.className('scheduledDate'), {
             taskScheduled: 'future-7d',
         });
-        await testComponentClasses(`- [ ] Full task ⏫ 🛫 ${future}`, {}, FieldLayouts.startDate.className, {
+        await testComponentClasses(`- [ ] Full task ⏫ 🛫 ${future}`, {}, fieldRenderer.className('startDate'), {
             taskStart: 'future-7d',
         });
-        await testComponentClasses(`- [x] Done task ✅ ${future}`, {}, FieldLayouts.doneDate.className, {
+        await testComponentClasses(`- [x] Done task ✅ ${future}`, {}, fieldRenderer.className('doneDate'), {
             taskDone: 'future-7d',
         });
     });
 
     it('adds a correct "past-1d" CSS class to dates', async () => {
         const past = DateParser.parseDate('yesterday').format(TaskRegularExpressions.dateFormat);
-        await testComponentClasses(`- [ ] Full task ⏫ ➕ ${past}`, {}, FieldLayouts.createdDate.className, {
+        await testComponentClasses(`- [ ] Full task ⏫ ➕ ${past}`, {}, fieldRenderer.className('createdDate'), {
             taskCreated: 'past-1d',
         });
-        await testComponentClasses(`- [ ] Full task ⏫ 📅 ${past}`, {}, FieldLayouts.dueDate.className, {
+        await testComponentClasses(`- [ ] Full task ⏫ 📅 ${past}`, {}, fieldRenderer.className('dueDate'), {
             taskDue: 'past-1d',
         });
-        await testComponentClasses(`- [ ] Full task ⏫ ⏳ ${past}`, {}, FieldLayouts.scheduledDate.className, {
+        await testComponentClasses(`- [ ] Full task ⏫ ⏳ ${past}`, {}, fieldRenderer.className('scheduledDate'), {
             taskScheduled: 'past-1d',
         });
-        await testComponentClasses(`- [ ] Full task ⏫ 🛫 ${past}`, {}, FieldLayouts.startDate.className, {
+        await testComponentClasses(`- [ ] Full task ⏫ 🛫 ${past}`, {}, fieldRenderer.className('startDate'), {
             taskStart: 'past-1d',
         });
-        await testComponentClasses(`- [x] Done task ✅ ${past}`, {}, FieldLayouts.doneDate.className, {
+        await testComponentClasses(`- [x] Done task ✅ ${past}`, {}, fieldRenderer.className('doneDate'), {
             taskDone: 'past-1d',
         });
     });
 
     it('adds a correct "past-7d" CSS class to dates', async () => {
         const past = DateParser.parseDate('7 days ago').format(TaskRegularExpressions.dateFormat);
-        await testComponentClasses(`- [ ] Full task ⏫ ➕ ${past}`, {}, FieldLayouts.createdDate.className, {
+        await testComponentClasses(`- [ ] Full task ⏫ ➕ ${past}`, {}, fieldRenderer.className('createdDate'), {
             taskCreated: 'past-7d',
         });
-        await testComponentClasses(`- [ ] Full task ⏫ 📅 ${past}`, {}, FieldLayouts.dueDate.className, {
+        await testComponentClasses(`- [ ] Full task ⏫ 📅 ${past}`, {}, fieldRenderer.className('dueDate'), {
             taskDue: 'past-7d',
         });
-        await testComponentClasses(`- [ ] Full task ⏫ ⏳ ${past}`, {}, FieldLayouts.scheduledDate.className, {
+        await testComponentClasses(`- [ ] Full task ⏫ ⏳ ${past}`, {}, fieldRenderer.className('scheduledDate'), {
             taskScheduled: 'past-7d',
         });
-        await testComponentClasses(`- [ ] Full task ⏫ 🛫 ${past}`, {}, FieldLayouts.startDate.className, {
+        await testComponentClasses(`- [ ] Full task ⏫ 🛫 ${past}`, {}, fieldRenderer.className('startDate'), {
             taskStart: 'past-7d',
         });
-        await testComponentClasses(`- [x] Done task ✅ ${past}`, {}, FieldLayouts.doneDate.className, {
+        await testComponentClasses(`- [x] Done task ✅ ${past}`, {}, fieldRenderer.className('doneDate'), {
             taskDone: 'past-7d',
         });
     });
 
     it('adds the classes "...future-far" and "...past-far" to dates that are further than 7 days', async () => {
         const future = DateParser.parseDate('in 8 days').format(TaskRegularExpressions.dateFormat);
-        await testComponentClasses(`- [ ] Full task ⏫ ➕ ${future}`, {}, FieldLayouts.createdDate.className, {
+        await testComponentClasses(`- [ ] Full task ⏫ ➕ ${future}`, {}, fieldRenderer.className('createdDate'), {
             taskCreated: 'future-far',
         });
-        await testComponentClasses(`- [ ] Full task ⏫ 📅 ${future}`, {}, FieldLayouts.dueDate.className, {
+        await testComponentClasses(`- [ ] Full task ⏫ 📅 ${future}`, {}, fieldRenderer.className('dueDate'), {
             taskDue: 'future-far',
         });
-        await testComponentClasses(`- [ ] Full task ⏫ ⏳ ${future}`, {}, FieldLayouts.scheduledDate.className, {
+        await testComponentClasses(`- [ ] Full task ⏫ ⏳ ${future}`, {}, fieldRenderer.className('scheduledDate'), {
             taskScheduled: 'future-far',
         });
-        await testComponentClasses(`- [ ] Full task ⏫ 🛫 ${future}`, {}, FieldLayouts.startDate.className, {
+        await testComponentClasses(`- [ ] Full task ⏫ 🛫 ${future}`, {}, fieldRenderer.className('startDate'), {
             taskStart: 'future-far',
         });
-        await testComponentClasses(`- [x] Done task ✅ ${future}`, {}, FieldLayouts.doneDate.className, {
+        await testComponentClasses(`- [x] Done task ✅ ${future}`, {}, fieldRenderer.className('doneDate'), {
             taskDone: 'future-far',
         });
         const past = DateParser.parseDate('8 days ago').format(TaskRegularExpressions.dateFormat);
-        await testComponentClasses(`- [ ] Full task ⏫ ➕ ${past}`, {}, FieldLayouts.createdDate.className, {
+        await testComponentClasses(`- [ ] Full task ⏫ ➕ ${past}`, {}, fieldRenderer.className('createdDate'), {
             taskCreated: 'past-far',
         });
-        await testComponentClasses(`- [ ] Full task ⏫ 📅 ${past}`, {}, FieldLayouts.dueDate.className, {
+        await testComponentClasses(`- [ ] Full task ⏫ 📅 ${past}`, {}, fieldRenderer.className('dueDate'), {
             taskDue: 'past-far',
         });
-        await testComponentClasses(`- [ ] Full task ⏫ ⏳ ${past}`, {}, FieldLayouts.scheduledDate.className, {
+        await testComponentClasses(`- [ ] Full task ⏫ ⏳ ${past}`, {}, fieldRenderer.className('scheduledDate'), {
             taskScheduled: 'past-far',
         });
-        await testComponentClasses(`- [ ] Full task ⏫ 🛫 ${past}`, {}, FieldLayouts.startDate.className, {
+        await testComponentClasses(`- [ ] Full task ⏫ 🛫 ${past}`, {}, fieldRenderer.className('startDate'), {
             taskStart: 'past-far',
         });
-        await testComponentClasses(`- [x] Done task ✅ ${past}`, {}, FieldLayouts.doneDate.className, {
+        await testComponentClasses(`- [x] Done task ✅ ${past}`, {}, fieldRenderer.className('doneDate'), {
             taskDone: 'past-far',
         });
     });
 
     it('does not add specific classes to invalid dates', async () => {
-        await testComponentClasses('- [ ] Full task ⏫ 📅 2023-02-29', {}, FieldLayouts.dueDate.className, {});
+        await testComponentClasses('- [ ] Full task ⏫ 📅 2023-02-29', {}, fieldRenderer.className('dueDate'), {});
     });
 
     it('does not render hidden components but sets their specific classes to the upper li element', async () => {
         await testHiddenComponentClasses(
             '- [ ] Full task ⏫ 📅 2022-07-02 ⏳ 2022-07-03 🛫 2022-07-04 🔁 every day',
             { hidePriority: true },
-            FieldLayouts.priority.className,
+            fieldRenderer.className('priority'),
             { taskPriority: 'high' },
         );
         await testHiddenComponentClasses(
             '- [ ] Full task ⏫ 📅 2022-07-02 ⏳ 2022-07-03 ➕ 2022-07-04 🔁 every day',
             { hideCreatedDate: true },
-            FieldLayouts.createdDate.className,
+            fieldRenderer.className('createdDate'),
             { taskCreated: 'past-far' },
         );
         await testHiddenComponentClasses(
             '- [ ] Full task ⏫ 📅 2022-07-02 ⏳ 2022-07-03 🛫 2022-07-04 🔁 every day',
             { hideDueDate: true },
-            FieldLayouts.dueDate.className,
+            fieldRenderer.className('dueDate'),
             { taskDue: 'past-far' },
         );
         await testHiddenComponentClasses(
             '- [ ] Full task ⏫ 📅 2022-07-02 ⏳ 2022-07-03 🛫 2022-07-04 🔁 every day',
             { hideScheduledDate: true },
-            FieldLayouts.scheduledDate.className,
+            fieldRenderer.className('scheduledDate'),
             { taskScheduled: 'past-far' },
         );
         await testHiddenComponentClasses(
             '- [ ] Full task ⏫ 📅 2022-07-02 ⏳ 2022-07-03 🛫 2022-07-04 🔁 every day',
             { hideStartDate: true },
-            FieldLayouts.startDate.className,
+            fieldRenderer.className('startDate'),
             { taskStart: 'past-far' },
         );
     });
@@ -678,21 +679,3 @@ ${task.toFileLineString()}
         await renderAndVerifyHTML(minimalTask, layoutOptionsShortMode());
     });
 });
-
-describe('Field Layout Detail tests', () => {
-    it('should supply a class name and a data attribute name', () => {
-        const fieldLayoutDetail = new FieldLayoutDetail('stuff', 'taskAttribute', () => {
-            return '';
-        });
-        expect(fieldLayoutDetail.className).toEqual('stuff');
-        expect(fieldLayoutDetail.attributeName).toEqual('taskAttribute');
-    });
-
-    it('should calculate data attribute value', () => {
-        const fieldLayoutDetail = new FieldLayoutDetail('foo', 'bar', () => {
-            return 'someValue';
-        });
-        const attributeValue = fieldLayoutDetail.attributeValueCalculator('createdDate', new TaskBuilder().build());
-        expect(attributeValue).toEqual('someValue');
-    });
-});
diff --git a/tests/TaskSerializer/DataviewTaskSerializer.test.ts b/tests/TaskSerializer/DataviewTaskSerializer.test.ts
index 16ecb526..8caead91 100644
--- a/tests/TaskSerializer/DataviewTaskSerializer.test.ts
+++ b/tests/TaskSerializer/DataviewTaskSerializer.test.ts
@@ -12,6 +12,8 @@ import type { TaskDetails } from '../../src/TaskSerializer';
 jest.mock('obsidian');
 window.moment = moment;
 
+// NEW_TASK_FIELD_EDIT_REQUIRED
+
 describe('DataviewTaskSerializer', () => {
     const taskSerializer = new DataviewTaskSerializer();
     const serialize = taskSerializer.serialize.bind(taskSerializer);
diff --git a/tests/TaskSerializer/DefaultTaskSerializer.test.ts b/tests/TaskSerializer/DefaultTaskSerializer.test.ts
index ecc20209..2edacae7 100644
--- a/tests/TaskSerializer/DefaultTaskSerializer.test.ts
+++ b/tests/TaskSerializer/DefaultTaskSerializer.test.ts
@@ -19,6 +19,8 @@ type DefaultTaskSerializeSymbolMap = readonly {
 // A map that facilitates parameterizing the tests over symbols
 const symbolMap: DefaultTaskSerializeSymbolMap = [{ taskFormat: 'tasksPluginEmoji', symbols: DEFAULT_SYMBOLS }];
 
+// NEW_TASK_FIELD_EDIT_REQUIRED
+
 describe.each(symbolMap)("DefaultTaskSerializer with '$taskFormat' symbols", ({ symbols }) => {
     const taskSerializer = new DefaultTaskSerializer(symbols);
     const serialize = taskSerializer.serialize.bind(taskSerializer);
diff --git a/tests/TaskSerializer/TaskSerializer.test.ts b/tests/TaskSerializer/TaskSerializer.test.ts
index 1febd320..2b3560ea 100644
--- a/tests/TaskSerializer/TaskSerializer.test.ts
+++ b/tests/TaskSerializer/TaskSerializer.test.ts
@@ -59,6 +59,7 @@ describe('TaskSerializer Example', () => {
             }
             const description = descriptionParts.join(' ');
             return {
+                // NEW_TASK_FIELD_EDIT_REQUIRED
                 description,
                 tags: Task.extractHashtags(description),
                 dueDate,
diff --git a/tests/TestingTools/TaskBuilder.ts b/tests/TestingTools/TaskBuilder.ts
index 8b239ed2..754d9bde 100644
--- a/tests/TestingTools/TaskBuilder.ts
+++ b/tests/TestingTools/TaskBuilder.ts
@@ -20,6 +20,7 @@ import { TaskLocation } from '../../src/TaskLocation';
  *            Create a new TaskBuilder object to start from a clean state,
  */
 export class TaskBuilder {
+    // NEW_TASK_FIELD_EDIT_REQUIRED
     private _status: Status = Status.makeTodo();
     private _description: string = 'my description';
     private _path: string = '';
@@ -63,6 +64,7 @@ export class TaskBuilder {
             description += ' ' + this._tags.join(' ');
         }
         const task = new Task({
+            // NEW_TASK_FIELD_EDIT_REQUIRED
             status: this._status,
             description: description,
             taskLocation: new TaskLocation(
@@ -98,6 +100,7 @@ export class TaskBuilder {
      */
     public static createFullyPopulatedTask(): Task {
         const taskBuilder = new TaskBuilder()
+            // NEW_TASK_FIELD_EDIT_REQUIRED
             .indentation('  ')
             .description('Do exercises')
             .tags(['#todo', '#health'])