diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
new file mode 100644
index 0000000..e6d16ca
--- /dev/null
+++ b/.devcontainer/Dockerfile
@@ -0,0 +1,7 @@
+FROM --platform=linux/amd64 mcr.microsoft.com/vscode/devcontainers/python:3
+
+WORKDIR /workspace
+
+COPY requirements*.txt .
+
+RUN python -m pip install --no-cache-dir --upgrade --requirement requirements.ci.txt
\ No newline at end of file
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
new file mode 100644
index 0000000..348f93e
--- /dev/null
+++ b/.devcontainer/devcontainer.json
@@ -0,0 +1,52 @@
+{
+ "hostRequirements": {
+ "cpus": 4,
+ "memory": "16gb",
+ "storage": "32gb"
+ },
+ "build": {
+ "dockerfile": "./Dockerfile",
+ "context": ".."
+ },
+ "postAttachCommand": "python -m mkdocs serve --clean --dev-addr 127.0.0.1:8080",
+ "forwardPorts": [
+ 8080
+ ],
+ "containerEnv": {
+ "TETRIS_APP_HOST": "127.0.0.1",
+ "TETRIS_APP_PORT": "8080",
+ "TETRIS_APP_PATH": "github-devsecops-fundamentals"
+ },
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ "GitHub.vscode-pull-request-github",
+ "ms-azuretools.vscode-docker",
+ "github.vscode-github-actions",
+ "ms-python.python",
+ "esbenp.prettier-vscode",
+ "ms-python.flake8",
+ "ms-python.black-formatter",
+ "ms-python.mypy-type-checker",
+ "ms-python.pylint",
+ "ms-python.vscode-pylance",
+ "ms-vsliveshare.vsliveshare",
+ "ms-playwright.playwright",
+ "GitHub.copilot-chat",
+ "GitHub.copilot",
+ "pomdtr.excalidraw-editor"
+ ]
+ }
+ },
+ "features": {
+ "ghcr.io/devcontainers/features/git:1": {
+ "ppa": true,
+ "version": "latest"
+ }
+ },
+ "portsAttributes": {
+ "8080": {
+ "label": "Site"
+ }
+ }
+}
\ No newline at end of file
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..66cc66b
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,5 @@
+*
+!docs/*
+!mkdocs.yml
+!requirements*.txt
+!.devcontainer/*
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 0000000..bbcbbe7
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,20 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+**Is your feature request related to a problem? Please describe.**
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+
+**Describe the solution you'd like**
+A clear and concise description of what you want to happen.
+
+**Describe alternatives you've considered**
+A clear and concise description of any alternative solutions or features you've considered.
+
+**Additional context**
+Add any other context or screenshots about the feature request here.
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..78ab862
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,14 @@
+version: 2
+updates:
+ - package-ecosystem: "pip"
+ directory: "/"
+ schedule:
+ interval: "daily"
+ - package-ecosystem: "github-actions"
+ directory: "/"
+ schedule:
+ interval: "daily"
+ - package-ecosystem: "npm"
+ directory: "/"
+ schedule:
+ interval: "daily"
diff --git a/.github/dependency-review-config.yml b/.github/dependency-review-config.yml
new file mode 100644
index 0000000..d053e5f
--- /dev/null
+++ b/.github/dependency-review-config.yml
@@ -0,0 +1,13 @@
+fail-on-severity: moderate
+
+comment-summary-in-pr: always
+
+allow-licenses:
+ - MIT
+ - GPL-3.0
+ - BSD-3-Clause
+
+fail-on-scopes:
+ - development
+ - runtime
+ - unknown
diff --git a/.github/release.yml b/.github/release.yml
new file mode 100644
index 0000000..8dc79cd
--- /dev/null
+++ b/.github/release.yml
@@ -0,0 +1,18 @@
+changelog:
+ exclude:
+ labels:
+ - ignore-for-release
+ authors:
+ - octocat
+ categories:
+ - title: Breaking Changes š
+ labels:
+ - Semver-Major
+ - breaking-change
+ - title: Exciting New Features š
+ labels:
+ - Semver-Minor
+ - enhancement
+ - title: Other Changes
+ labels:
+ - "*"
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..0a9020b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,7 @@
+.DS_Store
+site
+node_modules/
+/test-results/
+/playwright-report/
+/playwright/.cache/
+.vscode/settings.json
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..b06c827
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,94 @@
+
+# Code of Conduct
+
+The GitHub Universe 2023 Discussion Forum is intended to be a place for questions, feedback and chat related to sessions at the virtual GitHub Universe 2023 event. This is a civilized place for connecting with other attendees, and Hubbers from across the world taking part in the event. By participating in this community, you are agreeing to the same [Terms of Service](https://help.github.com/articles/github-terms-of-service) that apply to GitHub.com, as well as the GitHub Universe 2023 Discussion Forum specific Code of Conduct.
+
+With this Code of Conduct, we hope to help you understand how best to collaborate in Discussions, what you can expect from moderators, and what type of actions or content may result in temporary or permanent suspension from this project. We will investigate any abuse reports and may moderate public content within the discussion that we determine to be in violation of either the GitHub Terms of Service or this Code of Conduct.
+
+GitHub users worldwide bring wildly different perspectives, ideas, and experiences, and range from people who created their first "Hello World" project last week to the most well-known software developers in the world. We are committed to making GitHub Universe a welcoming environment for all the different voices and perspectives here, while maintaining a space where people are free to express themselves.
+
+
+### Pledge
+
+In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to make participation in the GitHub Universe Discussions a harassment-free experience for everyone, regardless of age, body size, ability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
+
+
+### Standards
+
+Treat the GitHub Universe Discussions with respect. The following are not hard and fast rules, merely aids to the human judgment of our Community. Use these guidelines to keep this a clean, well-lighted place for civilized public discourse.
+
+
+#### _Best Practices for Building a Strong Community_
+
+
+
+* Be respectful and considerate.
+ * Be welcoming and open-minded. Other GitHub members may not have the same experience level or background as you, but that doesn't mean they don't have good ideas to contribute. We encourage you to be welcoming to new members and those just getting started.
+ * Respect each other. Nothing sabotages healthy conversation like rudeness. Be civil and professional, and donāt post anything that a reasonable person would consider offensive, abusive, or hate speech. Donāt harass or grief anyone. Treat each other with dignity and consideration in all interactions. \
+You may wish to respond to something by disagreeing with it. Thatās fine. But remember to criticize ideas, not people. Avoid name-calling, ad hominem attacks, responding to a postās tone instead of its actual content, and knee-jerk contradiction. Instead, provide reasoned counter-arguments that improve the conversation.
+ * Communicate with empathy. Disagreements or differences of opinion are a fact of life. Being part of a community means interacting with people from a variety of backgrounds and perspectives, many of which may not be your own. If you disagree with someone, try to understand and share their feelings before you address them. This will promote a respectful and friendly atmosphere where people feel comfortable asking questions, participating in discussions, and making contributions.
+* Contribute in a positive and constructive way.
+ * Improve the discussion. Help us make this a great place for discussion by always working to improve the discussion in some way, however small. If you are not sure your post adds to the conversation, think over what you want to say and try again later. \
+The topics discussed here matter to us, and we want you to act as if they matter to you, too. Be respectful of the topics and the people discussing them, even if you disagree with some of what is being said.
+ * Be clear and stay on topic. Communicating with strangers on the Internet can be awkward. It's hard to convey or read tone, and sarcasm is frequently misunderstood. Try to use clear language, and think about how it will be received by the other person. \
+This applies to sharing links, as well. Any links shared in the discussions should be shared with the intent of providing relevant and appropriate information. Links should not be posted to simply drive traffic or attention to a site. Links should always be accompanied by a full explanation of the content and purpose of the link. Posting links, especially unsolicited ones, without relevant and valuable context can come across as advertising or serving even more malicious purposes.
+ * Share mindfully. Don't share sensitive information. This includes your own email address. We don't allow the sharing of such information in this discussion forum, as it can create security and privacy risks for the poster, as well as other users.
+ * Keep it tidy. Make the effort to put things in the right place, so that we can spend more time discussing and less time cleaning up. So:
+ * Donāt cross-post the same thing in multiple topics.
+ * Donāt post no-content replies.
+ * Donāt divert a topic by changing it midstream.
+ * Rather than posting ā+1ā or āAgreedā, use the Reaction emoji button.
+* Be trustworthy.
+ * Always be honest. Donāt knowingly share incorrect information or intentionally mislead other GitHub members. If you donāt know the answer to someoneās question but still want to help, you can try helping them research or find resources instead. GitHub staff will also be active in the discussions, so if youāre unsure of an answer, itās likely a moderator will be able to help.
+
+#### _What is not Allowed_
+
+* Threats of violence. You may not threaten violence towards others or use the site to organize, promote, or incite acts of real-world violence or terrorism. Think carefully about the words you use, the images you post, and even the software you write, and how they may be interpreted by others. Even if you mean something as a joke, it might not be received that way. If you think that someone else might interpret the content you post as a threat, or as promoting violence or terrorism, stop. Don't post it. In extraordinary cases, we may report threats of violence to law enforcement if we think there may be a genuine risk of physical harm or a threat to public safety.
+* Hate speech and discrimination. While it is not forbidden to broach topics such as age, body size, ability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation, we do not tolerate speech that attacks a person or group of people on the basis of who they are. Just realize that talking about these or other sensitive topics can make others feel unwelcome, or perhaps even unsafe, if approached in an aggressive or insulting manner. We expect our community members to be respectful when discussing sensitive topics.
+* Bullying and harassment. We do not tolerate bullying or harassment. This means any habitual badgering or intimidation targeted at a specific person or group of people. In general, if your actions are unwanted and you continue to engage in them, there's a good chance you are headed into bullying or harassment territory.
+* Impersonation. You may not impersonate another person by copying their avatar, posting content under their email address, intentionally using a deceptively similar username or otherwise posing as someone else. Impersonation is a form of harassment.
+* Doxxing and invasion of privacy. Don't post other people's personal information, such as phone numbers, private email addresses, physical addresses, credit card numbers, Social Security/National Identity numbers, or passwords. Depending on the context, such as in the case of intimidation or harassment, we may consider other information, such as photos or videos that were taken or distributed without the subject's consent, to be an invasion of privacy, especially when such material presents a safety risk to the subject.
+* Prurient/Sexually explicit content. Basically, don't post pornography. This does not mean that all nudity or sexual content is prohibited. We recognize that sexuality is a part of life and non-pornographic sexual content may be a part of your project, or may be presented for educational or artistic purposes. If you have any questions or concerns about something you want to post, [feel free to reach out and ask](https://support.github.com/contact) beforehand.
+* Spam. Respect the GitHub Universe Discussions. Donāt post advertisements, link to spammy websites, or otherwise vandalize the community. This community is meant for GitHub Universe participants to talk about the sessions, to provide feedback, as questions, learn, and share ideas with one another - not for advertising or other spam-like content. Content that we deem spammy will be removed.
+* Copyrighted or illegal content. Only post your own stuff. You are responsible for what you post. If you post something you didnāt create yourself, you must have the right to post it. You may not post illegal content, including content illegal under copyright and trademark laws, links to illegal content, or methods for circumventing the law.
+* Active malware or exploits. Being part of this community includes not taking advantage of other members of the community. We do not allow anyone to use our platform for exploit delivery (e.g. Using the community as a means to deliver malicious executables) or as attack infrastructure (e.g. Organizing denial of service attacks or managing command and control servers). Note, however, that we do not prohibit the posting of source code which could be used to develop malware or exploits, as the publication and distribution of such source code has educational value and provides a net benefit to the security community.
+* Anyone under the age of 13. If you're a child under the age of 13, you may not have an account on GitHub. GitHub does not knowingly collect information from or direct any of our content specifically to children under 13. If we learn or have reason to suspect that you are a user who is under the age of 13, we will unfortunately have to close both your GitHub.com account. We don't want to discourage you from learning to code, but those are the rules. Please see our [Terms of Service](https://help.github.com/articles/github-terms-of-service) for information about account termination.
+* Other conduct which could reasonably be considered inappropriate in a professional setting. The GitHub Universe Discussions is a professional space and should be treated as such.
+
+### Enforcement
+
+
+#### _What GitHub Universe Discussions members Can Do_
+
+* If you see a problem, report it. Moderators have special authority; they are responsible for this community. But so are you. With your help, moderators can be community facilitators, not just janitors or police. \
+When you see bad behavior, donāt reply. It encourages the bad behavior by acknowledging it, consumes your energy, and wastes everyoneās time. Just report it by copying a direct link to the reply in question and emailing it to events@github.com
+
+#### Our Responsibilities
+
+
+There are a variety of actions that we may take in response to inappropriate behavior or content. It usually depends on the exact circumstances of a particular case. We recognize that sometimes people may say or do inappropriate things for any number of reasons. Perhaps they did not realize how their words would be perceived. Or maybe they just let their emotions get the best of them. Of course, sometimes, there are folks who just want to spam or cause trouble.
+
+Each case requires a different approach, and we try to tailor our response to meet the needs of the situation. We'll review each situation on a case-by-case basis. In each case, we will have a diverse team investigate the content and surrounding facts and respond as appropriate, using this Code of Conduct to guide our decision.
+
+Actions we may take in response to a flag or abuse report include, but are not limited to:
+
+
+
+* Content Removal
+* Content Blocking
+* GitHub Account Suspension
+* GitHub Account Termination
+
+### Contacting GitHub Staff
+
+
+If, for any reason, you want to contact GitHub Staff, the Community Managers, Administrators, or Moderators of this forum privately, you can send an email to events@github.com.
+
+Let's work together to keep the discussion a place where people feel safe to participate by being respectful of them and their time.
+
+
+### Legal Notices
+
+Yes, legalese is boring, but we must protect ourselves ā and by extension, you and your data ā against unfriendly folks. We have a [Terms of Service](https://help.github.com/articles/github-terms-of-service/) and [Privacy Statement](https://help.github.com/articles/github-privacy-statement/) describing your (and our) behavior and rights related to content, privacy, and laws. To use this service, you must agree to abide by our [Terms of Service](https://help.github.com/articles/github-terms-of-service/) and the [Privacy Statement](https://help.github.com/articles/github-privacy-statement/).
+
+This Code of Conduct does not modify our [Terms of Service](https://help.github.com/articles/github-terms-of-service/) and is not intended to be a complete list. GitHub retains full discretion under the [Terms of Service](https://help.github.com/articles/github-terms-of-service/) to remove any content or terminate any accounts for activity that is "unlawful, offensive, threatening, libelous, defamatory, pornographic, obscene or otherwise objectionable or violates any party's intellectual property or these Terms of Service." This Code of Conduct describes when we will exercise that discretion.
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..c6f75f6
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,11 @@
+FROM python:3.12.0-alpine
+
+WORKDIR /app
+
+COPY . .
+
+RUN pip install -r requirements.txt
+
+EXPOSE 8080
+
+CMD ["mkdocs", "serve", "--dev-addr=127.0.0.1:8080"]
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..d61f0a1
--- /dev/null
+++ b/README.md
@@ -0,0 +1,15 @@
+
+
+
GitHub DevSecOps Fundamentals
+
+@igwejk ā¢ @mouismail
+
+
+ Prerequisites ā¢
+ Resources ā¢
+ Learning Objectives
+
+
+
+Goto :eyes: workshop site
+
diff --git a/docs/.tetris.game/LICENSE b/docs/.tetris.game/LICENSE
new file mode 100644
index 0000000..b831a05
--- /dev/null
+++ b/docs/.tetris.game/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016 Jake Gordon and contributors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/docs/.tetris.game/README.txt b/docs/.tetris.game/README.txt
new file mode 100644
index 0000000..7a8bdd6
--- /dev/null
+++ b/docs/.tetris.game/README.txt
@@ -0,0 +1,26 @@
+Javascript Tetris
+=================
+
+An HTML5 Tetris Game
+
+ * [play the game](http://codeincomplete.com/projects/tetris/)
+ * read a [blog article](http://codeincomplete.com/posts/2011/10/10/javascript_tetris/)
+ * view the [source](https://github.com/jakesgordon/javascript-tetris)
+
+>> _*SUPPORTED BROWSERS*: Chrome, Firefox, Safari, Opera and IE9+_
+
+FUTURE
+======
+
+ * menu
+ * animation and fx
+ * levels
+ * high scores
+ * touch support
+ * music and sound fx
+
+
+License
+=======
+
+[MIT](http://en.wikipedia.org/wiki/MIT_License) license.
diff --git a/docs/.tetris.game/app.js b/docs/.tetris.game/app.js
new file mode 100644
index 0000000..eba1481
--- /dev/null
+++ b/docs/.tetris.game/app.js
@@ -0,0 +1,559 @@
+//-------------------------------------------------------------------------
+// base helper methods
+//-------------------------------------------------------------------------
+
+function get(id) {
+ return document.getElementById(id);
+}
+function hide(id) {
+ get(id).style.visibility = "hidden";
+}
+function show(id) {
+ get(id).style.visibility = null;
+}
+function html(id, html) {
+ get(id).innerHTML = html;
+}
+
+function timestamp() {
+ return new Date().getTime();
+}
+function random(min, max) {
+ return min + Math.random() * (max - min);
+}
+function randomChoice(choices) {
+ return choices[Math.round(random(0, choices.length - 1))];
+}
+
+if (!window.requestAnimationFrame) {
+ // http://paulirish.com/2011/requestanimationframe-for-smart-animating/
+ window.requestAnimationFrame =
+ window.webkitRequestAnimationFrame ||
+ window.mozRequestAnimationFrame ||
+ window.oRequestAnimationFrame ||
+ window.msRequestAnimationFrame ||
+ function (callback, element) {
+ window.setTimeout(callback, 1000 / 60);
+ };
+}
+
+//-------------------------------------------------------------------------
+// game constants
+//-------------------------------------------------------------------------
+
+var KEY = { ESC: 27, SPACE: 32, LEFT: 37, UP: 38, RIGHT: 39, DOWN: 40 },
+ DIR = { UP: 0, RIGHT: 1, DOWN: 2, LEFT: 3, MIN: 0, MAX: 3 },
+ stats = new Stats(),
+ canvas = get("canvas"),
+ ctx = canvas.getContext("2d"),
+ ucanvas = get("upcoming"),
+ uctx = ucanvas.getContext("2d"),
+ speed = { start: 0.6, decrement: 0.005, min: 0.1 }, // how long before piece drops by 1 row (seconds)
+ nx = 10, // width of tetris court (in blocks)
+ ny = 20, // height of tetris court (in blocks)
+ nu = 5; // width/height of upcoming preview (in blocks)
+
+//-------------------------------------------------------------------------
+// game variables (initialized during reset)
+//-------------------------------------------------------------------------
+
+var dx,
+ dy, // pixel size of a single tetris block
+ blocks, // 2 dimensional array (nx*ny) representing tetris court - either empty block or occupied by a 'piece'
+ actions, // queue of user actions (inputs)
+ playing, // true|false - game is in progress
+ dt, // time since starting this game
+ current, // the current piece
+ next, // the next piece
+ score, // the current score
+ vscore, // the currently displayed score (it catches up to score in small chunks - like a spinning slot machine)
+ rows, // number of completed rows in the current game
+ step; // how long before current piece drops by 1 row
+
+//-------------------------------------------------------------------------
+// tetris pieces
+//
+// blocks: each element represents a rotation of the piece (0, 90, 180, 270)
+// each element is a 16 bit integer where the 16 bits represent
+// a 4x4 set of blocks, e.g. j.blocks[0] = 0x44C0
+//
+// 0100 = 0x4 << 3 = 0x4000
+// 0100 = 0x4 << 2 = 0x0400
+// 1100 = 0xC << 1 = 0x00C0
+// 0000 = 0x0 << 0 = 0x0000
+// ------
+// 0x44C0
+//
+//-------------------------------------------------------------------------
+
+var i = {
+ size: 4,
+ blocks: [0x0f00, 0x2222, 0x00f0, 0x4444],
+ color: "cyan",
+};
+var j = {
+ size: 3,
+ blocks: [0x44c0, 0x8e00, 0x6440, 0x0e20],
+ color: "blue",
+};
+var l = {
+ size: 3,
+ blocks: [0x4460, 0x0e80, 0xc440, 0x2e00],
+ color: "orange",
+};
+var o = {
+ size: 2,
+ blocks: [0xcc00, 0xcc00, 0xcc00, 0xcc00],
+ color: "yellow",
+};
+var s = {
+ size: 3,
+ blocks: [0x06c0, 0x8c40, 0x6c00, 0x4620],
+ color: "green",
+};
+var t = {
+ size: 3,
+ blocks: [0x0e40, 0x4c40, 0x4e00, 0x4640],
+ color: "purple",
+};
+var z = {
+ size: 3,
+ blocks: [0x0c60, 0x4c80, 0xc600, 0x2640],
+ color: "red",
+};
+
+//------------------------------------------------
+// do the bit manipulation and iterate through each
+// occupied block (x,y) for a given piece
+//------------------------------------------------
+function eachblock(type, x, y, dir, fn) {
+ var bit,
+ result,
+ row = 0,
+ col = 0,
+ blocks = type.blocks[dir];
+ for (bit = 0x8000; bit > 0; bit = bit >> 1) {
+ if (blocks & bit) {
+ fn(x + col, y + row);
+ }
+ if (++col === 4) {
+ col = 0;
+ ++row;
+ }
+ }
+}
+
+//-----------------------------------------------------
+// check if a piece can fit into a position in the grid
+//-----------------------------------------------------
+function occupied(type, x, y, dir) {
+ var result = false;
+ eachblock(type, x, y, dir, function (x, y) {
+ if (x < 0 || x >= nx || y < 0 || y >= ny || getBlock(x, y)) result = true;
+ });
+ return result;
+}
+
+function unoccupied(type, x, y, dir) {
+ return !occupied(type, x, y, dir);
+}
+
+//-----------------------------------------
+// start with 4 instances of each piece and
+// pick randomly until the 'bag is empty'
+//-----------------------------------------
+var pieces = [];
+function randomPiece() {
+ if (pieces.length == 0)
+ pieces = [
+ i,
+ i,
+ i,
+ i,
+ j,
+ j,
+ j,
+ j,
+ l,
+ l,
+ l,
+ l,
+ o,
+ o,
+ o,
+ o,
+ s,
+ s,
+ s,
+ s,
+ t,
+ t,
+ t,
+ t,
+ z,
+ z,
+ z,
+ z,
+ ];
+ var type = pieces.splice(random(0, pieces.length - 1), 1)[0];
+ return {
+ type: type,
+ dir: DIR.UP,
+ x: Math.round(random(0, nx - type.size)),
+ y: 0,
+ };
+}
+
+//-------------------------------------------------------------------------
+// GAME LOOP
+//-------------------------------------------------------------------------
+
+function run() {
+ showStats(); // initialize FPS counter
+ addEvents(); // attach keydown and resize events
+
+ var last = (now = timestamp());
+ function frame() {
+ now = timestamp();
+ update(Math.min(1, (now - last) / 1000.0)); // using requestAnimationFrame have to be able to handle large delta's caused when it 'hibernates' in a background or non-visible tab
+ draw();
+ stats.update();
+ last = now;
+ requestAnimationFrame(frame, canvas);
+ }
+
+ resize(); // setup all our sizing information
+ reset(); // reset the per-game variables
+ frame(); // start the first frame
+}
+
+function showStats() {
+ stats.domElement.id = "stats";
+ get("menu").appendChild(stats.domElement);
+}
+
+function addEvents() {
+ document.addEventListener("keydown", keydown, false);
+ window.addEventListener("resize", resize, false);
+}
+
+function resize(event) {
+ canvas.width = canvas.clientWidth; // set canvas logical size equal to its physical size
+ canvas.height = canvas.clientHeight; // (ditto)
+ ucanvas.width = ucanvas.clientWidth;
+ ucanvas.height = ucanvas.clientHeight;
+ dx = canvas.width / nx; // pixel size of a single tetris block
+ dy = canvas.height / ny; // (ditto)
+ invalidate();
+ invalidateNext();
+}
+
+function keydown(ev) {
+ var handled = false;
+ if (playing) {
+ switch (ev.keyCode) {
+ case KEY.LEFT:
+ actions.push(DIR.LEFT);
+ handled = true;
+ break;
+ case KEY.RIGHT:
+ actions.push(DIR.RIGHT);
+ handled = true;
+ break;
+ case KEY.UP:
+ actions.push(DIR.UP);
+ handled = true;
+ break;
+ case KEY.DOWN:
+ actions.push(DIR.DOWN);
+ handled = true;
+ break;
+ case KEY.ESC:
+ lose();
+ handled = true;
+ break;
+ }
+ } else if (ev.keyCode == KEY.SPACE) {
+ play();
+ handled = true;
+ }
+ if (handled) ev.preventDefault(); // prevent arrow keys from scrolling the page (supported in IE9+ and all other browsers)
+}
+
+//-------------------------------------------------------------------------
+// GAME LOGIC
+//-------------------------------------------------------------------------
+
+function play() {
+ hide("start");
+ reset();
+ playing = true;
+}
+function lose() {
+ show("start");
+ setVisualScore();
+ playing = false;
+}
+
+function setVisualScore(n) {
+ vscore = n || score;
+ invalidateScore();
+}
+function setScore(n) {
+ score = n;
+ setVisualScore(n);
+}
+function addScore(n) {
+ score = score + n;
+}
+function clearScore() {
+ setScore(0);
+}
+function clearRows() {
+ setRows(0);
+}
+function setRows(n) {
+ rows = n;
+ step = Math.max(speed.min, speed.start - speed.decrement * rows);
+ invalidateRows();
+}
+function addRows(n) {
+ setRows(rows + n);
+}
+function getBlock(x, y) {
+ return blocks && blocks[x] ? blocks[x][y] : null;
+}
+function setBlock(x, y, type) {
+ blocks[x] = blocks[x] || [];
+ blocks[x][y] = type;
+ invalidate();
+}
+function clearBlocks() {
+ blocks = [];
+ invalidate();
+}
+function clearActions() {
+ actions = [];
+}
+function setCurrentPiece(piece) {
+ current = piece || randomPiece();
+ invalidate();
+}
+function setNextPiece(piece) {
+ next = piece || randomPiece();
+ invalidateNext();
+}
+
+function reset() {
+ dt = 0;
+ clearActions();
+ clearBlocks();
+ clearRows();
+ clearScore();
+ setCurrentPiece(next);
+ setNextPiece();
+}
+
+function update(idt) {
+ if (playing) {
+ if (vscore < score) setVisualScore(vscore + 1);
+ handle(actions.shift());
+ dt = dt + idt;
+ if (dt > step) {
+ dt = dt - step;
+ drop();
+ }
+ }
+}
+
+function handle(action) {
+ switch (action) {
+ case DIR.LEFT:
+ move(DIR.LEFT);
+ break;
+ case DIR.RIGHT:
+ move(DIR.RIGHT);
+ break;
+ case DIR.UP:
+ rotate();
+ break;
+ case DIR.DOWN:
+ drop();
+ break;
+ }
+}
+
+function move(dir) {
+ var x = current.x,
+ y = current.y;
+ switch (dir) {
+ case DIR.RIGHT:
+ x = x + 1;
+ break;
+ case DIR.LEFT:
+ x = x - 1;
+ break;
+ case DIR.DOWN:
+ y = y + 1;
+ break;
+ }
+ if (unoccupied(current.type, x, y, current.dir)) {
+ current.x = x;
+ current.y = y;
+ invalidate();
+ return true;
+ } else {
+ return false;
+ }
+}
+
+function rotate() {
+ var newdir = current.dir == DIR.MAX ? DIR.MIN : current.dir + 1;
+ if (unoccupied(current.type, current.x, current.y, newdir)) {
+ current.dir = newdir;
+ invalidate();
+ }
+}
+
+function drop() {
+ if (!move(DIR.DOWN)) {
+ addScore(10);
+ dropPiece();
+ removeLines();
+ setCurrentPiece(next);
+ setNextPiece(randomPiece());
+ clearActions();
+ if (occupied(current.type, current.x, current.y, current.dir)) {
+ lose();
+ }
+ }
+}
+
+function dropPiece() {
+ eachblock(current.type, current.x, current.y, current.dir, function (x, y) {
+ setBlock(x, y, current.type);
+ });
+}
+
+function removeLines() {
+ var x,
+ y,
+ complete,
+ n = 0;
+ for (y = ny; y > 0; --y) {
+ complete = true;
+ for (x = 0; x < nx; ++x) {
+ if (!getBlock(x, y)) complete = false;
+ }
+ if (complete) {
+ removeLine(y);
+ y = y + 1; // recheck same line
+ n++;
+ }
+ }
+ if (n > 0) {
+ addRows(n);
+ addScore(100 * Math.pow(2, n - 1)); // 1: 100, 2: 200, 3: 400, 4: 800
+ }
+}
+
+function removeLine(n) {
+ var x, y;
+ for (y = n; y >= 0; --y) {
+ for (x = 0; x < nx; ++x) setBlock(x, y, y == 0 ? null : getBlock(x, y - 1));
+ }
+}
+
+//-------------------------------------------------------------------------
+// RENDERING
+//-------------------------------------------------------------------------
+
+var invalid = {};
+
+function invalidate() {
+ invalid.court = true;
+}
+function invalidateNext() {
+ invalid.next = true;
+}
+function invalidateScore() {
+ invalid.score = true;
+}
+function invalidateRows() {
+ invalid.rows = true;
+}
+
+function draw() {
+ ctx.save();
+ ctx.lineWidth = 1;
+ ctx.translate(0.5, 0.5); // for crisp 1px black lines
+ drawCourt();
+ drawNext();
+ drawScore();
+ drawRows();
+ ctx.restore();
+}
+
+function drawCourt() {
+ if (invalid.court) {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ if (playing)
+ drawPiece(ctx, current.type, current.x, current.y, current.dir);
+ var x, y, block;
+ for (y = 0; y < ny; y++) {
+ for (x = 0; x < nx; x++) {
+ if ((block = getBlock(x, y))) drawBlock(ctx, x, y, block.color);
+ }
+ }
+ ctx.strokeRect(0, 0, nx * dx - 1, ny * dy - 1); // court boundary
+ invalid.court = false;
+ }
+}
+
+function drawNext() {
+ if (invalid.next) {
+ var padding = (nu - next.type.size) / 2; // half-arsed attempt at centering next piece display
+ uctx.save();
+ uctx.translate(0.5, 0.5);
+ uctx.clearRect(0, 0, nu * dx, nu * dy);
+ drawPiece(uctx, next.type, padding, padding, next.dir);
+ uctx.strokeStyle = "black";
+ uctx.strokeRect(0, 0, nu * dx - 1, nu * dy - 1);
+ uctx.restore();
+ invalid.next = false;
+ }
+}
+
+function drawScore() {
+ if (invalid.score) {
+ html("score", ("00000" + Math.floor(vscore)).slice(-5));
+ invalid.score = false;
+ }
+}
+
+function drawRows() {
+ if (invalid.rows) {
+ html("rows", rows);
+ invalid.rows = false;
+ }
+}
+
+function drawPiece(ctx, type, x, y, dir) {
+ eachblock(type, x, y, dir, function (x, y) {
+ drawBlock(ctx, x, y, type.color);
+ });
+}
+
+function drawBlock(ctx, x, y, color) {
+ ctx.fillStyle = color;
+ ctx.fillRect(x * dx, y * dy, dx, dy);
+ ctx.strokeRect(x * dx, y * dy, dx, dy);
+}
+
+//-------------------------------------------------------------------------
+// FINALLY, lets run the game
+//-------------------------------------------------------------------------
+
+run();
+
+module.exports = { setScore, addScore };
diff --git a/docs/.tetris.game/app.test.js b/docs/.tetris.game/app.test.js
new file mode 100755
index 0000000..89ccb05
--- /dev/null
+++ b/docs/.tetris.game/app.test.js
@@ -0,0 +1,16 @@
+const { setScore, addScore } = require("./app.js");
+
+describe("setScore", () => {
+ it("should set the score to the given value", () => {
+ setScore(5);
+ expect(score).toBe(5);
+ });
+});
+
+describe("addScore", () => {
+ it("should add the given value to the current score", () => {
+ setScore(5);
+ addScore(3);
+ expect(score).toBe(8);
+ });
+});
diff --git a/docs/.tetris.game/index.html b/docs/.tetris.game/index.html
new file mode 100644
index 0000000..f09137b
--- /dev/null
+++ b/docs/.tetris.game/index.html
@@ -0,0 +1,26 @@
+
+
+
+ Tetris
+
+
+
+
+
+
+
+ Sorry, this example cannot be run because your browser does not support
+ the <canvas> element
+
+
+
+
+
+
+
diff --git a/docs/.tetris.game/stats.js b/docs/.tetris.game/stats.js
new file mode 100644
index 0000000..f538c6a
--- /dev/null
+++ b/docs/.tetris.game/stats.js
@@ -0,0 +1,9 @@
+// stats.js r6 - http://github.com/mrdoob/stats.js
+var Stats=function(){function s(a,g,d){var f,c,e;for(c=0;c<30;c++)for(f=0;f<73;f++)e=(f+c*74)*4,a[e]=a[e+4],a[e+1]=a[e+5],a[e+2]=a[e+6];for(c=0;c<30;c++)e=(73+c*74)*4,cFPS';k.appendChild(d);a=document.createElement("canvas");a.width=74;a.height=30;a.style.display="block";a.style.marginLeft="3px";k.appendChild(a);
+ m=a.getContext("2d");m.fillStyle="rgb("+b.fps.bg.r+","+b.fps.bg.g+","+b.fps.bg.b+")";m.fillRect(0,0,a.width,a.height);y=m.getImageData(0,0,a.width,a.height);f=document.createElement("div");f.style.backgroundColor="rgb("+Math.floor(b.ms.bg.r/2)+","+Math.floor(b.ms.bg.g/2)+","+Math.floor(b.ms.bg.b/2)+")";f.style.padding="2px 0px 3px 0px";f.style.display="none";g.appendChild(f);c=document.createElement("div");c.style.fontFamily="Helvetica, Arial, sans-serif";c.style.textAlign="left";c.style.fontSize=
+ "9px";c.style.color="rgb("+b.ms.fg.r+","+b.ms.fg.g+","+b.ms.fg.b+")";c.style.margin="0px 0px 1px 3px";c.innerHTML='MS ';f.appendChild(c);a=document.createElement("canvas");a.width=74;a.height=30;a.style.display="block";a.style.marginLeft="3px";f.appendChild(a);o=a.getContext("2d");o.fillStyle="rgb("+b.ms.bg.r+","+b.ms.bg.g+","+b.ms.bg.b+")";o.fillRect(0,0,a.width,a.height);B=o.getImageData(0,0,a.width,a.height);try{performance&&performance.memory&&performance.memory.totalJSHeapSize&&
+ (t=3)}catch(G){}h=document.createElement("div");h.style.backgroundColor="rgb("+Math.floor(b.mb.bg.r/2)+","+Math.floor(b.mb.bg.g/2)+","+Math.floor(b.mb.bg.b/2)+")";h.style.padding="2px 0px 3px 0px";h.style.display="none";g.appendChild(h);i=document.createElement("div");i.style.fontFamily="Helvetica, Arial, sans-serif";i.style.textAlign="left";i.style.fontSize="9px";i.style.color="rgb("+b.mb.fg.r+","+b.mb.fg.g+","+b.mb.fg.b+")";i.style.margin="0px 0px 1px 3px";i.innerHTML='MB ';
+ h.appendChild(i);a=document.createElement("canvas");a.width=74;a.height=30;a.style.display="block";a.style.marginLeft="3px";h.appendChild(a);q=a.getContext("2d");q.fillStyle="#301010";q.fillRect(0,0,a.width,a.height);E=q.getImageData(0,0,a.width,a.height);return{domElement:g,update:function(){u++;j=(new Date).getTime();n=j-F;z=Math.min(z,n);A=Math.max(A,n);s(B.data,Math.min(30,30-n/200*30),"ms");c.innerHTML=''+n+" MS ("+z+"-"+A+")";o.putImageData(B,0,0);F=j;if(j>
+ v+1E3){l=Math.round(u*1E3/(j-v));w=Math.min(w,l);x=Math.max(x,l);s(y.data,Math.min(30,30-l/100*30),"fps");d.innerHTML=''+l+" FPS ("+w+"-"+x+")";m.putImageData(y,0,0);if(t==3)p=performance.memory.usedJSHeapSize*9.54E-7,C=Math.min(C,p),D=Math.max(D,p),s(E.data,Math.min(30,30-p/2),"mb"),i.innerHTML=''+Math.round(p)+" MB ("+Math.round(C)+"-"+Math.round(D)+")",q.putImageData(E,0,0);v=j;u=0}}}};
diff --git a/docs/.tetris.game/style.css b/docs/.tetris.game/style.css
new file mode 100644
index 0000000..6012707
--- /dev/null
+++ b/docs/.tetris.game/style.css
@@ -0,0 +1,191 @@
+body {
+ font-family: Helvetica, sans-serif;
+}
+#tetris {
+ margin: 1em auto;
+ padding: 1em;
+ border: 4px solid black;
+ border-radius: 10px;
+ background-color: #f8f8f8;
+}
+#stats {
+ display: inline-block;
+ vertical-align: top;
+}
+#canvas {
+ display: inline-block;
+ vertical-align: top;
+ background: url(texture.jpg);
+ box-shadow: 10px 10px 10px #999;
+ border: 2px solid #333;
+}
+#menu {
+ display: inline-block;
+ vertical-align: top;
+ position: relative;
+}
+#menu p {
+ margin: 0.5em 0;
+ text-align: center;
+}
+#menu p a {
+ text-decoration: none;
+ color: black;
+}
+#menu #go-home-link {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ text-align: left;
+ font-weight: bold;
+ font-size: small;
+}
+#menu #go-home-link a {
+ color: darkslateblue;
+}
+#upcoming {
+ display: block;
+ margin: 0 auto;
+ background-color: #e0e0e0;
+}
+#score {
+ color: red;
+ font-weight: bold;
+ vertical-align: middle;
+}
+#rows {
+ color: blue;
+ font-weight: bold;
+ vertical-align: middle;
+}
+#stats {
+ position: absolute;
+ bottom: 0em;
+ right: 1em;
+}
+@media screen and (min-width: 0px) and (min-height: 0px) {
+ #tetris {
+ font-size: 0.75em;
+ width: 250px;
+ }
+ #menu {
+ width: 100px;
+ height: 200px;
+ }
+ #upcoming {
+ width: 50px;
+ height: 50px;
+ }
+ #canvas {
+ width: 100px;
+ height: 200px;
+ }
+} /* 10px chunks */
+@media screen and (min-width: 400px) and (min-height: 400px) {
+ #tetris {
+ font-size: 1em;
+ width: 350px;
+ }
+ #menu {
+ width: 150px;
+ height: 300px;
+ }
+ #upcoming {
+ width: 75px;
+ height: 75px;
+ }
+ #canvas {
+ width: 150px;
+ height: 300px;
+ }
+} /* 15px chunks */
+@media screen and (min-width: 500px) and (min-height: 500px) {
+ #tetris {
+ font-size: 1.25em;
+ width: 450px;
+ }
+ #menu {
+ width: 200px;
+ height: 400px;
+ }
+ #upcoming {
+ width: 100px;
+ height: 100px;
+ }
+ #canvas {
+ width: 200px;
+ height: 400px;
+ }
+} /* 20px chunks */
+@media screen and (min-width: 600px) and (min-height: 600px) {
+ #tetris {
+ font-size: 1.5em;
+ width: 550px;
+ }
+ #menu {
+ width: 250px;
+ height: 500px;
+ }
+ #upcoming {
+ width: 125px;
+ height: 125px;
+ }
+ #canvas {
+ width: 250px;
+ height: 500px;
+ }
+} /* 25px chunks */
+@media screen and (min-width: 700px) and (min-height: 700px) {
+ #tetris {
+ font-size: 1.75em;
+ width: 650px;
+ }
+ #menu {
+ width: 300px;
+ height: 600px;
+ }
+ #upcoming {
+ width: 150px;
+ height: 150px;
+ }
+ #canvas {
+ width: 300px;
+ height: 600px;
+ }
+} /* 30px chunks */
+@media screen and (min-width: 800px) and (min-height: 800px) {
+ #tetris {
+ font-size: 2em;
+ width: 750px;
+ }
+ #menu {
+ width: 350px;
+ height: 700px;
+ }
+ #upcoming {
+ width: 175px;
+ height: 175px;
+ }
+ #canvas {
+ width: 350px;
+ height: 700px;
+ }
+} /* 35px chunks */
+@media screen and (min-width: 900px) and (min-height: 900px) {
+ #tetris {
+ font-size: 2.25em;
+ width: 850px;
+ }
+ #menu {
+ width: 400px;
+ height: 800px;
+ }
+ #upcoming {
+ width: 200px;
+ height: 200px;
+ }
+ #canvas {
+ width: 400px;
+ height: 800px;
+ }
+} /* 40px chunks */
diff --git a/docs/.tetris.game/texture.jpg b/docs/.tetris.game/texture.jpg
new file mode 100644
index 0000000..c616e59
Binary files /dev/null and b/docs/.tetris.game/texture.jpg differ
diff --git a/docs/assets/img/app-destination-repository-selection.png b/docs/assets/img/app-destination-repository-selection.png
new file mode 100644
index 0000000..d954ee5
Binary files /dev/null and b/docs/assets/img/app-destination-repository-selection.png differ
diff --git a/docs/assets/img/app-home-display.png b/docs/assets/img/app-home-display.png
new file mode 100644
index 0000000..45a925b
Binary files /dev/null and b/docs/assets/img/app-home-display.png differ
diff --git a/docs/assets/img/app-installation-success-notification.png b/docs/assets/img/app-installation-success-notification.png
new file mode 100644
index 0000000..d719372
Binary files /dev/null and b/docs/assets/img/app-installation-success-notification.png differ
diff --git a/docs/assets/img/app-owner-selection.png b/docs/assets/img/app-owner-selection.png
new file mode 100644
index 0000000..2ff7f41
Binary files /dev/null and b/docs/assets/img/app-owner-selection.png differ
diff --git a/docs/assets/img/build-warning.png b/docs/assets/img/build-warning.png
new file mode 100644
index 0000000..eab81f1
Binary files /dev/null and b/docs/assets/img/build-warning.png differ
diff --git a/docs/assets/img/change-port-visibility.png b/docs/assets/img/change-port-visibility.png
new file mode 100644
index 0000000..62dfefc
Binary files /dev/null and b/docs/assets/img/change-port-visibility.png differ
diff --git a/docs/assets/img/codeql-analysis-setup.png b/docs/assets/img/codeql-analysis-setup.png
new file mode 100644
index 0000000..b8c78eb
Binary files /dev/null and b/docs/assets/img/codeql-analysis-setup.png differ
diff --git a/docs/assets/img/codeql-protection-rules-setup.png b/docs/assets/img/codeql-protection-rules-setup.png
new file mode 100644
index 0000000..fddb1ce
Binary files /dev/null and b/docs/assets/img/codeql-protection-rules-setup.png differ
diff --git a/docs/assets/img/completely-passing-checks.png b/docs/assets/img/completely-passing-checks.png
new file mode 100644
index 0000000..ee7fe1c
Binary files /dev/null and b/docs/assets/img/completely-passing-checks.png differ
diff --git a/docs/assets/img/create-a-new-codespaces.png b/docs/assets/img/create-a-new-codespaces.png
new file mode 100644
index 0000000..f3fcdcc
Binary files /dev/null and b/docs/assets/img/create-a-new-codespaces.png differ
diff --git a/docs/assets/img/create-a-new-repository.png b/docs/assets/img/create-a-new-repository.png
new file mode 100644
index 0000000..6093dac
Binary files /dev/null and b/docs/assets/img/create-a-new-repository.png differ
diff --git a/docs/assets/img/create-project.png b/docs/assets/img/create-project.png
new file mode 100644
index 0000000..86f89d7
Binary files /dev/null and b/docs/assets/img/create-project.png differ
diff --git a/docs/assets/img/create-pull-request-from-codespace.png b/docs/assets/img/create-pull-request-from-codespace.png
new file mode 100644
index 0000000..7005167
Binary files /dev/null and b/docs/assets/img/create-pull-request-from-codespace.png differ
diff --git a/docs/assets/img/dependency-review-milestone.png b/docs/assets/img/dependency-review-milestone.png
new file mode 100644
index 0000000..bd88b55
Binary files /dev/null and b/docs/assets/img/dependency-review-milestone.png differ
diff --git a/docs/assets/img/dependency-review-outcome-01.png b/docs/assets/img/dependency-review-outcome-01.png
new file mode 100644
index 0000000..284dc91
Binary files /dev/null and b/docs/assets/img/dependency-review-outcome-01.png differ
diff --git a/docs/assets/img/deployments-list.png b/docs/assets/img/deployments-list.png
new file mode 100644
index 0000000..4609e75
Binary files /dev/null and b/docs/assets/img/deployments-list.png differ
diff --git a/docs/assets/img/deployments-view-on-repo-home.png b/docs/assets/img/deployments-view-on-repo-home.png
new file mode 100644
index 0000000..6c5f06e
Binary files /dev/null and b/docs/assets/img/deployments-view-on-repo-home.png differ
diff --git a/docs/assets/img/develop-and-validate-work.png b/docs/assets/img/develop-and-validate-work.png
new file mode 100644
index 0000000..b9bbece
Binary files /dev/null and b/docs/assets/img/develop-and-validate-work.png differ
diff --git a/docs/assets/img/e2e-github.png b/docs/assets/img/e2e-github.png
new file mode 100644
index 0000000..4e69273
Binary files /dev/null and b/docs/assets/img/e2e-github.png differ
diff --git a/docs/assets/img/example-successful-integration-tetris-game-integration.png b/docs/assets/img/example-successful-integration-tetris-game-integration.png
new file mode 100644
index 0000000..736bfea
Binary files /dev/null and b/docs/assets/img/example-successful-integration-tetris-game-integration.png differ
diff --git a/docs/assets/img/favicon.ico b/docs/assets/img/favicon.ico
new file mode 100644
index 0000000..a59308e
Binary files /dev/null and b/docs/assets/img/favicon.ico differ
diff --git a/docs/assets/img/github-icon-in-sidebar-menu-of-codespace.png b/docs/assets/img/github-icon-in-sidebar-menu-of-codespace.png
new file mode 100644
index 0000000..2a64b47
Binary files /dev/null and b/docs/assets/img/github-icon-in-sidebar-menu-of-codespace.png differ
diff --git a/docs/assets/img/initiate-and-track-goals.png b/docs/assets/img/initiate-and-track-goals.png
new file mode 100644
index 0000000..2db6305
Binary files /dev/null and b/docs/assets/img/initiate-and-track-goals.png differ
diff --git a/docs/assets/img/issue-closed-done.png b/docs/assets/img/issue-closed-done.png
new file mode 100644
index 0000000..dfc7914
Binary files /dev/null and b/docs/assets/img/issue-closed-done.png differ
diff --git a/docs/assets/img/merge-pull-request.png b/docs/assets/img/merge-pull-request.png
new file mode 100644
index 0000000..eb2cab1
Binary files /dev/null and b/docs/assets/img/merge-pull-request.png differ
diff --git a/docs/assets/img/milestone-continuous-deployment.png b/docs/assets/img/milestone-continuous-deployment.png
new file mode 100644
index 0000000..fb289cb
Binary files /dev/null and b/docs/assets/img/milestone-continuous-deployment.png differ
diff --git a/docs/assets/img/milestone-package-and-release-delivery-artifacts.png b/docs/assets/img/milestone-package-and-release-delivery-artifacts.png
new file mode 100644
index 0000000..63d58ec
Binary files /dev/null and b/docs/assets/img/milestone-package-and-release-delivery-artifacts.png differ
diff --git a/docs/assets/img/milestone-versioning.png b/docs/assets/img/milestone-versioning.png
new file mode 100644
index 0000000..1e82482
Binary files /dev/null and b/docs/assets/img/milestone-versioning.png differ
diff --git a/docs/assets/img/milestones-all.png b/docs/assets/img/milestones-all.png
new file mode 100644
index 0000000..4c8f874
Binary files /dev/null and b/docs/assets/img/milestones-all.png differ
diff --git a/docs/assets/img/milestones-start.png b/docs/assets/img/milestones-start.png
new file mode 100644
index 0000000..0e215ea
Binary files /dev/null and b/docs/assets/img/milestones-start.png differ
diff --git a/docs/assets/img/milestones.png b/docs/assets/img/milestones.png
new file mode 100644
index 0000000..a1c74d0
Binary files /dev/null and b/docs/assets/img/milestones.png differ
diff --git a/docs/assets/img/missing-tetris-game.jpeg b/docs/assets/img/missing-tetris-game.jpeg
new file mode 100644
index 0000000..f295600
Binary files /dev/null and b/docs/assets/img/missing-tetris-game.jpeg differ
diff --git a/docs/assets/img/packages.png b/docs/assets/img/packages.png
new file mode 100644
index 0000000..f139340
Binary files /dev/null and b/docs/assets/img/packages.png differ
diff --git a/docs/assets/img/passing-test.png b/docs/assets/img/passing-test.png
new file mode 100644
index 0000000..afb3524
Binary files /dev/null and b/docs/assets/img/passing-test.png differ
diff --git a/docs/assets/img/pull-request-form-in-codespace.png b/docs/assets/img/pull-request-form-in-codespace.png
new file mode 100644
index 0000000..de1a2c4
Binary files /dev/null and b/docs/assets/img/pull-request-form-in-codespace.png differ
diff --git a/docs/assets/img/pull-request-view.png b/docs/assets/img/pull-request-view.png
new file mode 100644
index 0000000..b7943d4
Binary files /dev/null and b/docs/assets/img/pull-request-view.png differ
diff --git a/docs/assets/img/release-discussions.png b/docs/assets/img/release-discussions.png
new file mode 100644
index 0000000..96566e4
Binary files /dev/null and b/docs/assets/img/release-discussions.png differ
diff --git a/docs/assets/img/repository-settings-tab.png b/docs/assets/img/repository-settings-tab.png
new file mode 100644
index 0000000..0f44838
Binary files /dev/null and b/docs/assets/img/repository-settings-tab.png differ
diff --git a/docs/assets/img/restart-codespace.png b/docs/assets/img/restart-codespace.png
new file mode 100644
index 0000000..00e7191
Binary files /dev/null and b/docs/assets/img/restart-codespace.png differ
diff --git a/docs/assets/img/resume-existing-codespace-at-repo-home.png b/docs/assets/img/resume-existing-codespace-at-repo-home.png
new file mode 100644
index 0000000..a05ab38
Binary files /dev/null and b/docs/assets/img/resume-existing-codespace-at-repo-home.png differ
diff --git a/docs/assets/img/select-new-project.png b/docs/assets/img/select-new-project.png
new file mode 100644
index 0000000..19a460c
Binary files /dev/null and b/docs/assets/img/select-new-project.png differ
diff --git a/docs/assets/img/successful-deployment-job.png b/docs/assets/img/successful-deployment-job.png
new file mode 100644
index 0000000..f77376a
Binary files /dev/null and b/docs/assets/img/successful-deployment-job.png differ
diff --git a/docs/assets/img/tetris-game.png b/docs/assets/img/tetris-game.png
new file mode 100644
index 0000000..2c9ae03
Binary files /dev/null and b/docs/assets/img/tetris-game.png differ
diff --git a/docs/assets/javascripts/extra.js b/docs/assets/javascripts/extra.js
new file mode 100644
index 0000000..d240cdd
--- /dev/null
+++ b/docs/assets/javascripts/extra.js
@@ -0,0 +1,26 @@
+// (function facilitateGitHubAppCreation() {
+// const handle = document.getElementById("handle");
+// if (!handle) {
+// return;
+// }
+
+// const manifest = document.getElementById("manifest");
+// if (!manifest) {
+// return;
+// }
+
+// handle.addEventListener("change", (event) => {
+// manifest.value = JSON.stringify({
+// name: `gu2023-release-bot-${handle?.value.trim()}`,
+// description: "My BOT for creating releases",
+// public: false,
+// url: `https://github.com/${handle?.value.trim()}`,
+// setup_url: location.href,
+// redirect_url: location.href,
+// callback_urls: [location.href],
+// default_permissions: {
+// contents: "write",
+// },
+// });
+// });
+// })();
diff --git a/docs/assets/javascripts/katex.js b/docs/assets/javascripts/katex.js
new file mode 100644
index 0000000..0946ce0
--- /dev/null
+++ b/docs/assets/javascripts/katex.js
@@ -0,0 +1,10 @@
+document$.subscribe(({ body }) => {
+ renderMathInElement(body, {
+ delimiters: [
+ { left: "$$", right: "$$", display: true },
+ { left: "$", right: "$", display: false },
+ { left: "\\(", right: "\\)", display: false },
+ { left: "\\[", right: "\\]", display: true },
+ ],
+ });
+});
diff --git a/docs/assets/javascripts/tables.js b/docs/assets/javascripts/tables.js
new file mode 100644
index 0000000..4014300
--- /dev/null
+++ b/docs/assets/javascripts/tables.js
@@ -0,0 +1,6 @@
+document$.subscribe(function () {
+ var tables = document.querySelectorAll("article table:not([class])");
+ tables.forEach(function (table) {
+ new Tablesort(table);
+ });
+});
diff --git a/docs/assets/stylesheets/extra.css b/docs/assets/stylesheets/extra.css
new file mode 100644
index 0000000..bc5548c
--- /dev/null
+++ b/docs/assets/stylesheets/extra.css
@@ -0,0 +1,58 @@
+.gh-repo-settings {
+ background-color: #000;
+ color: #fff;
+ font-weight: bold;
+ padding: 0.25em 0.75em;
+}
+
+.gh-repo-settings-menu {
+ background-color: #111;
+ color: #fff;
+ font-weight: bold;
+ padding: 0.25em 0.75em;
+}
+
+.gh-repo-settings-menu-selected-item {
+ background-color: #333;
+ color: #fff;
+ font-weight: bold;
+ padding: 0.25em 0.75em;
+ border-left: 0.2em solid blue;
+}
+
+.gh-repo-settings-button {
+ background-color: rgb(246, 248, 250);
+ color: #000;
+ padding: 0.25em 0.75em;
+ border-style: solid;
+ border-radius: 0.25em;
+ border-color: rgba(31, 35, 40, 0.15);
+ border-width: 0.1em;
+}
+
+.gh-button-green {
+ background-color: green;
+ color: #fff;
+ font-weight: bold;
+ padding: 0.25em 0.75em;
+ border-radius: 0.25em;
+}
+
+.gh-button-grey {
+ background-color: #eee;
+ color: #000;
+ font-weight: bold;
+ padding: 0.25em 0.75em;
+ border-radius: 0.25em;
+}
+
+.gh-button-green > .gh-button-green-with-dropdown {
+ border-left-style: solid;
+ border-left-color: #070;
+ margin-left: 0.25em;
+ padding-left: 0.25em;
+}
+
+.hidden {
+ display: none;
+}
diff --git a/docs/exercises/01.collaborate/01.md b/docs/exercises/01.collaborate/01.md
new file mode 100644
index 0000000..6098119
--- /dev/null
+++ b/docs/exercises/01.collaborate/01.md
@@ -0,0 +1,72 @@
+# :test_tube: Project Management
+
+
+
+!!! quote ":octicons-milestone-16: Facilitate collaborative tracking of business goal delivery"
+
+ > _As part of the prerequisite steps, you had created a project management board. You will now build on that step and keep stakeholders in the loop through the full course of the development process._
+
+ ![Initiate and track goals](../../assets/img/initiate-and-track-goals.png)
+
+---
+
+## Exercise: Create an `issue` to capture the goal
+
+!!! question "What is GitHub Issues?"
+
+ Use GitHub Issues to track ideas, feedback, tasks, or bugs for work on GitHub.
+
+ When you mention an issue in another issue or pull request, the issue's timeline reflects the cross-reference so that you can keep track of related work. To indicate that work is in progress, you can link an issue to a pull request. When the pull request merges, the linked issue automatically closes.
+
+---
+
+### **:material-numeric-1-circle: Refer back to the project created in the [prerequisite to the workshop](../../prerequisites.md#create-a-project-board-for-the-repository)**
+
+---
+
+### **:material-numeric-2-circle: Create an issue to capture the goal**
+
+1. Identify the **:clipboard: Backlog** column
+2. Click on **+ Add item** at the bottom of the column
+3. Click on the **+** button
+4. Select **:octicons-issue-opened-16: Create new issue** from the menu
+5. Choose the repository you created earlier
+6. Choose **Feature request** (1)
+ { .annotate }
+
+ 1. The issue creation was facilitated with an š [**issue template**](https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/about-issue-and-pull-request-templates#issue-templates).
+
+ ```markdown title=".github/ISSUE_TEMPLATE/feature_request.md"
+ --8<-- ".github/ISSUE_TEMPLATE/feature_request.md"
+ ```
+
+7. Fill the form. Feel free to be creative š with your inputs.
+
+ !!! example "For the title..."
+ You may for instance enter "_**Create a Tetris game to delight our site visitors**_".
+
+ After filling the form in, click Create :material-apple-keyboard-command::material-keyboard-return: to create the issue. (1)
+ { .annotate }
+
+ 1. Things you can do in an issue include but are not limited to
+ - [**Assign**](https://docs.github.com/en/issues/tracking-your-work-with-issues/assigning-issues-and-pull-requests-to-other-github-users) the issue to yourself or other stakeholders.
+ - [**Mention**](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#mentioning-people-and-teams) other stakeholders (people and teams) to notify them for awareness.
+
+---
+
+### **:material-numeric-3-circle: Take note of the issue number**
+
+We will use the issue number to link the the issue to subsequent work later. If you are too curious and cannot wait, see š [Linking a pull request to an issue](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue).
+
+Moreover, having created the issue, we can update the issue with comments to keep collaborating stakeholders in the loop.
+
+---
+
+## š Resources
+
+- [Project planning for developers](https://github.com/features/issues)
+- [Planning and tracking work for your team or project](https://docs.github.com/en/issues/tracking-your-work-with-issues/planning-and-tracking-work-for-your-team-or-project)
+- [Linking a pull request to an issue](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue)
+- [Create diagrams to convey information through charts and graphs](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-diagrams)
+
+---
diff --git a/docs/exercises/01.collaborate/02.md b/docs/exercises/01.collaborate/02.md
new file mode 100644
index 0000000..7260f45
--- /dev/null
+++ b/docs/exercises/01.collaborate/02.md
@@ -0,0 +1,450 @@
+# :test_tube: Implement Features
+
+
+
+!!! quote ":octicons-milestone-16: Enable rapid onboarding to project development"
+
+ > _Our goal is to create a **Tetris Game** to delight our site visitors, without losing time to complex project onboarding process nor complications due to tool-bloat._
+ >
+ > Currently, navigating to the **Tetris Game** page of the workshop site shows that the _feature_ is currently missing. We will address that issue in this exercise.
+ >
+ > ![Tetris Game is missing š](../../assets/img/missing-tetris-game.jpeg)
+
+ ![Develop and validate work](../../assets/img/develop-and-validate-work.png)
+
+---
+
+## Exercise: Integrate the **Tetris Game**
+
+Refer back to the Codespaces development environment created in the [prerequisite to the workshop](../../prerequisites.md#provision-a-new-cloud-development-environment-for-the-workshop).(1)
+{ .annotate }
+
+1. **:octicons-info-16:{ style="color: pink" } Your Codespace may have stopped due to inactivity.** Do not worry, it is quite easy to get back on track...
+
+ - _If the browser tab with your Codespace remains open and you can find it_, click Restart codespace to resume.
+ ![Screen showing "Restart codespace"](../../assets/img/restart-codespace.png)
+ - Otherwise, you can find it at the new repository you had created.
+ ![Drop down showing option to resume Codespace at repository home](../../assets/img/resume-existing-codespace-at-repo-home.png)
+
+---
+
+### **:material-numeric-1-circle: Explore your Codespace**
+
+---
+
+#### Understand Your Codespace
+
+`.devcontainer/devcontainer.json` describes the provisioning of your codespace. Some of the configuration entries are explained below, with annotations next to the respective entry. You can learn more about the [Development Container Specification](https://containers.dev/implementors/spec/) at your convenience.
+
+```json title=".devcontainer/devcontainer.json" linenums="1" hl_lines="11"
+{
+ "hostRequirements": {//(1)!
+ "cpus": 4,
+ "memory": "16gb",
+ "storage": "32gb"
+ },
+ "build": {
+ "dockerfile": "./Dockerfile",//(2)!
+ "context": ".."
+ },
+ "postAttachCommand": "python -m mkdocs serve --clean --dev-addr 127.0.0.1:8080",//(3)!
+ "forwardPorts": [
+ 8080
+ ],
+ "containerEnv": {
+ "TETRIS_APP_HOST": "127.0.0.1",
+ "TETRIS_APP_PORT": "8080",
+ "TETRIS_APP_PATH": "github-devsecops-fundamentals"
+ },
+ ...
+```
+
+1. You can customize the compute resource to be made available for the Codespace environment, to suit your project.
+2. A `Dockerfile` can be used to customize the Codespace container image, for instance you may choose to consistently pre-install dependencies required for the development environment.
+3. Tasks may be configured to be executed at the start of each development session e.g. running a development server.
+
+{==
+
+On the **`TERMINAL`** view at the bottom, notice that a local site server is already running. **This is as a result of the `postAttachCommand` configuration entry in `.devcontainer/devcontainer.json`**.
+
+Looking at the build log in the terminal, you can see a warning like the one in the following example.(1)
+{ .annotate }
+
+1. š” The warning gives a cue to addressing the Tetris Game integration issue. We shall implement the fix in later steps!
+
+![site build warning](../../assets/img/build-warning.png)
+
+:fire: The log `INFO` entry that reads **`INFO - [05:36:30] Watching paths for changes: 'docs', 'mkdocs.yml'`** informs you that thelocal site will rebuild whenever there is a change in the `docs` folder.
+
+==}
+
+---
+
+#### Enable Quick Collaboration Feedback
+
+Switch to the **`PORTS`** view on the bottom panel, notice there is a **`Site (8080)`** entry in the table.
+
+1. Focus your :fontawesome-solid-hand-pointer: mouse pointer on the **`Site (8080)`** to reveal additional contexts.
+1. Click the **:material-file-search-outline: Preview in Editor** icon to browse the local build of the site inside of the editor's browser.(1)
+ { .annotate }
+
+ 1. - :warning: **Only you currently have access to the published site.**
+ - :bulb: You may instead click the **:octicons-globe-16:** icon associated with **`Site (8080)`** entry to view the site in your normal browser.
+
+**Open access to other stakeholders for collaboration.**
+
+1. Right click on the **`Site (8080)`** entry
+1. In the popup menu, set the mouse pointer on the **Port Visibility** item and select the **Public** option.
+ ![Set port visibility to public](../../assets/img/change-port-visibility.png){ style="width: 40em" }
+
+{==
+
+:octicons-goal-16:{ style="color: pink" } Now, other stakeholders you share the site URL with can access the site. **Consequently, they can also assess your ongoing work and can collaborate with timely feedbacks.**
+
+==}
+
+---
+
+### **:material-numeric-2-circle: Implement Automated Checks**
+
+GitHub as a platform, is event-driven. Beyond the ordinary, you can generally automate your actual business processes by modelling activities to produce changes on GitHub, thus triggering execution of certain automated tasks depending on the nature of a change. If you already familiar with the concept of [GitOps](https://www.gitops.tech/), think of how the idea can be extended beyond the scopes of provisioning infrastructure and implementing continuos deployment.
+
+At this fundamentals phase of introduction to DevSecOps, we shall focus our leverage of GitHub's powerful event system to drive {== **continuous integration** ==} checks and tests with GitHub Workflows.
+
+---
+
+#### Learn GitHub Actions
+
+!!! info "What is GitHub Actions?"
+
+ GitHub Actions is a continuous integration and continuous delivery (CI/CD) platform that allows you to automate your build, test, and deployment pipeline. You can create workflows that build and test every pull request to your repository, or deploy merged pull requests to production.
+
+ GitHub Actions goes beyond just DevOps and lets you run workflows when other events happen in your repository. For example, you can run a workflow to automatically add the appropriate labels whenever someone creates a new issue in your repository.
+
+ GitHub provides Linux, Windows, and macOS virtual machines to run your workflows, or you can host your own self-hosted runners in your own data center or cloud infrastructure.
+
+ ~ [Understanding GitHub Actions](https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions)
+
+```yaml title="Anatomy of a GitHub Workflow"
+name: Learn GitHub Actions #(1)!
+
+run-name: ${{ github.actor }} is learning GitHub Actions #(2)!
+
+on: [push] #(3)!
+
+permissions: #(12)!
+ contents: read
+
+env: #(13)!
+ LEARNER_NAME: Igwe
+
+jobs: #(4)!
+
+ check-bats-version: #(5)!
+
+ runs-on: ubuntu-latest #(6)!
+
+ steps: #(7)!
+
+ - uses: actions/checkout@v4 #(8)!
+
+ - uses: actions/setup-node@v3 #(9)!
+ with:
+ node-version: '14'
+
+ - run: npm install -g bats #(10)!
+
+ - run: bats -v #(11)!
+
+ - run: echo "Hey ${LEARNER_NAME}, you're good... You've mastered GitHub Actions\!"
+```
+
+1. `Optional` - The name of the workflow as it will appear in the "Actions" tab of the GitHub repository. If this field is omitted, the name of the workflow file will be used instead.
+2. `Optional` - The name for workflow runs generated from the workflow, which will appear in the list of workflow runs on your repository's "Actions" tab. This example uses an expression with the `github` context to display the username of the actor that triggered the workflow run. For more information, see "[Workflow syntax for GitHub Actions](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#run-name)."
+3. Specifies the trigger for this workflow. This example uses the `push` event, so a workflow run is triggered every time someone pushes a change to the repository or merges a pull request. This is triggered by a push to every branch; for examples of syntax that runs only on pushes to specific branches, paths, or tags, see "[Workflow syntax for GitHub Actions](https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#onpushpull_requestpull_request_targetpathspaths-ignore)" and {== "[Events that trigger workflows](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows)" ==}.
+4. Groups together all the jobs that run in the `learn-github-actions` workflow.
+5. Defines a job named `check-bats-version`. The child keys will define properties of the job.
+6. Configures the job to run on the latest version of an Ubuntu Linux runner. This means that the job will execute on a fresh virtual machine hosted by GitHub. For syntax examples using other runners, see "[Workflow syntax for GitHub Actions](https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idruns-on)"
+7. Groups together all the steps that run in the `check-bats-version` job. Each item nested under this section is a separate action or shell script.
+8. The `uses` keyword specifies that this step will run `v4` of the `actions/checkout` action. This is an action that checks out your repository onto the runner, allowing you to run scripts or other actions against your code (such as build and test tools). You should use the checkout action any time your workflow will use the repository's code.
+
+ !!! info ""
+
+ Actions are the building blocks that power your workflow. A workflow can contain actions created by the community, or you can create your own actions directly within your application's repository.
+
+ To learn more, see:
+
+ - [Finding and customizing actions](https://docs.github.com/en/actions/learn-github-actions/finding-and-customizing-actions)
+ - [About custom actions](https://docs.github.com/en/actions/creating-actions/about-custom-actions)
+
+9. This step uses the `actions/setup-node@v3` action to install the specified version of the Node.js. (This example uses version 14.) This puts both the `node` and `npm` commands in your `PATH`.
+10. The `run` keyword tells the job to execute a command on the runner. In this case, you are using `npm` to install the `bats` software testing package.
+11. Finally, you'll run the `bats` command with a parameter that outputs the software version.
+12. At the start of each workflow job, GitHub automatically creates a unique `GITHUB_TOKEN` secret to use in your workflow. You can use the `GITHUB_TOKEN` to authenticate in the workflow job.
+
+ When you enable GitHub Actions, GitHub installs a GitHub App on your repository. The `GITHUB_TOKEN` secret is a GitHub App installation access token. You can use the installation access token to authenticate on behalf of the GitHub App installed on your repository. The token's permissions are limited to the repository that contains your workflow.
+
+ {==
+
+ **You can modify the permissions for the `GITHUB_TOKEN` in individual workflow files. If the default permissions for the `GITHUB_TOKEN` are restrictive, you may have to elevate the permissions to allow some actions and commands to run successfully. If the default permissions are permissive, you can edit the workflow file to remove some permissions from the `GITHUB_TOKEN`. As a good security practice, you should grant the `GITHUB_TOKEN` the least required access.**
+
+ ==}
+
+ Learn more about the default [Permissions for the `GITHUB_TOKEN`](https://docs.github.com/en/actions/security-guides/automatic-token-authentication).
+
+13. This defines a `map` of variables that are available to the steps of all jobs in the workflow. You can also set variables that are only available to the steps of a single job or to a single step. Learn more at [Defining environment variables for a single workflow](https://docs.github.com/en/actions/learn-github-actions/variables).
+
+---
+
+#### Implement Automated Pull Request Checks
+
+!!! info "What is a Pull Request (also known as PR)?"
+
+ Pull requests let you tell others about changes you've pushed to a branch in a repository on GitHub. Once a pull request is opened, you can discuss and review the potential changes with collaborators and add follow-up commits before your changes are merged into the base branch.
+
+ ~ [About pull requests](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests)
+
+To enable quality checks in the project at high velocity, implement a workflow that standardizes automated checks as follows.
+
+```yaml title=".github/workflows/continuous.integration.yml" linenums="1" hl_lines="3-6 43-45 47-48"
+--8<-- "reference.implementations/workflows/1.1.continuous.integration.yml"
+```
+
+##### Analysis
+
+- **Lines `3 - 6`**
+
+ The workflow will be triggered when a pull request is created targeting the `main` branch. Additional, changes to a pull request will cause the workflow to rerun.
+
+- **Lines `43 - 45`**
+
+ First check: The site is built with the `--strict` option, such that the occurrence of any `WARNING` results in a failed check. As it is, we can expect the automated checks to flag the pull request as non-compliant at this step.
+
+- **Lines `47 - 48`**
+
+ Second check: Provided the build step had been successful, this next step tests the correctness of the feature implementation in accordance to customer-value expectations. So far, we should not get to this point in the workflow run as the **`Build` should have failed because the Tetris game feature implementation has not been completed**.
+
+??? quote ":material-cancel:{ style="color: red;" } `DEPRECATED EXERCISE STEP` Optionally Implement Merge Queue for GitHub **_Enterprise_**"
+
+ !!! info "What is a Merge Queue?"
+
+ A merge queue helps increase velocity by automating pull request merges into a busy branch and ensuring the branch is never broken by incompatible changes.
+
+ The merge queue provides the same benefits as the Require branches to be up to date before merging branch protection, but does not require a pull request author to update their pull request branch and wait for status checks to finish before trying to merge.
+
+ Using a merge queue is particularly useful on branches that have a relatively high number of pull requests merging each day from many different users.
+
+ Once a pull request has passed all required branch protection checks, a user with write access to the repository can add the pull request to the queue. The merge queue will ensure the pull request's changes pass all required status checks when applied to the latest version of the target branch and any pull requests already in the queue.
+
+ š Learn more...
+
+ - [Managing a merge queue](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/configuring-pull-request-merges/managing-a-merge-queue)
+ - [Merging a pull request with a merge queue](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/incorporating-changes-from-a-pull-request/merging-a-pull-request-with-a-merge-queue)
+
+ The previously implemented pull request checks will be executed in isolation. However, to provide assurance of stability on the target branch as different changes are being introduced to it at a fast pace by multiple collaborators, we want to aggregate isolated changes and re-validate them in batches i.e. the Merge Queue.
+
+ !!! question "Is this like a _Daily Build_?"
+
+ You probably recognize this concept as similar to the implementation of a daily/nightly build. **Except you do not have to wait ~ 24 hours or there about to validate your project's quality status.**
+
+ !!! note ":octicons-git-merge-queue-16:{ .lg .middle } **Configure Merge Queue**"
+
+ A Merge Queue is ideal for rolling out continuous integration at scale, enabling larger projects and teams to be optimally productive -- delivering high quality value without compromising product stability.
+
+ {==
+
+ You can reserve execution of expensive checks in a merge queue, to save cost and time.
+
+ ==}
+
+ To effectively leverage Merge Queue, you must first configure your repository to require it for pull requests as follows.
+
+ 1. On GitHub.com, navigate to the main page of the repository you have created.
+ 1. Under your repository name, click :octicons-gear-16: **Settings**. If you cannot see the "Settings" tab, select the dropdown menu, then click **Settings**.
+ ![Repository settings tab](../../assets/img/repository-settings-tab.png)
+ 1. In the "Code and automation" section of the sidebar, click :octicons-git-branch-16: **Branches**.
+ 1. Next to "Branch protection rules", click **Add branch protection rule**.
+ 1. Under "Branch name pattern", type the branch name **`main`** for the branch you want to protect.
+ 1. Select **Require merge queue**.
+ 1. Click Create to complete Merge Queue configuration
+
+ Finally implement the checks in merge queue as follows.
+
+ ```yaml title=".github/workflows/continuous.integration.merge.queue.yml"
+ --8<-- "reference.implementations/workflows/1.3.continuous.integration.yml"
+ ```
+
+---
+
+#### Publish your changes
+
+!!! tip "You can link your changes to an issue"
+
+ Recall the issue you created earlier and its respective issue number, you will use it to link your current changes to the issue.
+
+1. Commit and push the changes.
+
+ ```bash linenums="1"
+ git checkout -b feature/tetris-game
+ git add .
+ git commit -m "$(printf 'Create a tetris game to drive site engagement\n\n- Implement continuous integration\n\n- Resolves #')"
+ git push -u origin feature/tetris-game
+ ```
+
+1. Create a pull request by clicking on the **GitHub** icon in the sidebar menu, and then clicking the **Create Pull Request** icon.
+
+ 1. ![Show GitHub icon in sidebar menu of Codespace](../../assets/img/github-icon-in-sidebar-menu-of-codespace.png){ style="width: 15em" }
+
+ 1. ![Icon for creating a pull request from a Codespace](../../assets/img/create-pull-request-from-codespace.png){ style="width: 25em" }
+
+ Check that the local branch and repository you're merging from, and the remote branch and repository you're merging into, are correct. **You may leave the pre-filled pull request title and description as they are**.
+
+ ![GitHub pull request form in a Codespace](../../assets/img/pull-request-form-in-codespace.png){ style="height: 25em" }
+
+ Click "**Create**", and if prompted then click "**Always**" or "**Publish Branch**".
+
+1. The pull request you created will be listed at `https://github.com//github-devsecops-fundamentals/pulls`.
+
+ {==
+
+ Opening the pull request for exploration you should observe the following
+
+ - **:octicons-comment-discussion-16: Conversation**
+
+ Particularly enables and summarizes discussions/reviews on your changes with other collaborators and stakeholders. And additionally summarizes outcome of checks on the pull request.
+ - **:octicons-git-commit-16: Commits**
+
+ Summarizes all the commits contributing to the pull request.
+
+ - **:octicons-checklist-16: Checks**
+
+ Lists the checks run on the pull request and their respective outcomes.
+
+ - **:octicons-file-diff-16: Files changed**
+
+ Shows a `diff` of files modified and added due to the pull request.
+
+ ![Show pull request view](../../assets/img/pull-request-view.png)
+
+ !!! note
+
+ Observe the 3 executed checks.
+
+ - 1 failure due to error in site build.
+ - 2 code scanning check passes.
+
+ ==}
+
+---
+
+### **:material-numeric-3-circle: Implement Feature Changes**
+
+The `Build` step in the previously implemented automated checks should identify a failure ultimately pointing to incompletion of the expected Tetris game feature. In the current phase, we will take steps to integrate the Tetris game as follows.
+
+1. Rename the **`docs/.tetris.game`** folder to **`docs/tetris.game`**, i.e. simply remove the `.` prefix from the name.
+2. Find and open the file **`mkdocs.yml`** in the File Explorer (it's at the root of the file tree)
+
+ - Confirm that the `Tetris Game` relevant entry points to an index file in the `tetris.game` folder
+
+ ```yaml linenums="150" hl_lines="20"
+ nav:
+ - Home: index.md
+ - Prerequisites: prerequisites.md
+ - Exercises:
+ - Introduction: exercises/index.md
+ - Collaborate:
+ - exercises/01.collaborate/index.md
+ - exercises/01.collaborate/01.md
+ - exercises/01.collaborate/02.md
+ - Secure the Software Supply Chain:
+ - exercises/02.secure.the.software.supply.chain/index.md
+ - exercises/02.secure.the.software.supply.chain/01.md
+ - exercises/02.secure.the.software.supply.chain/02.md
+ - Automate Release:
+ - exercises/03.automate.release/index.md
+ - exercises/03.automate.release/01.md
+ - exercises/03.automate.release/02.md
+ - āØ Recap:
+ - exercises/recap/index.md
+ - Tetris Game: tetris.game/index.html
+ ```
+
+---
+
+#### **Manual Test**
+
+1. Observe the local build in the terminal, there should not be any :warning: `WARNING` in the log.
+1. Manually confirm that the Tetris Game integration was effective.
+ ![Example successful Tetris Game integration](../../assets/img/example-successful-integration-tetris-game-integration.png)
+
+---
+
+#### **Automated Test**
+
+Before publishing our new changes, we will implement and execute a functional test to validate the feature implementation.
+
+1. Open the terminal and install test dependencies.(1)
+ { .annotate }
+
+ 1. š” You can create additional terminal session by clicking :octicons-plus-16: at the top right side of the **`TERMINAL`** panel.
+
+ ```shell
+ # Install Node.js
+ nvm install --lts #(1)!
+ # Install packages required for running the functional UI test
+ npm install
+ # Install the browser(s) against which the functional UI test should run
+ npx playwright install --with-deps chromium #(2)!
+ ```
+
+ 1. :octicons-info-16: A codespace environment can be customized as needed. In this case, we customized the running session by installing the latest version of `Node.js`.
+ 2. :octicons-info-16: This command installs browsers required for the functional test. **Please be patient, it may take 1 to 2 minutes to complete the installation.**
+
+ Then implement the functional test as follows.
+
+ ```javascript title="tests/tetris.spec.js" linenums="1"
+ --8<-- "reference.implementations/tests/tetris.spec.js"
+ ```
+
+ Finally execute the automated test.
+
+ ```shell
+ npx playwright test
+ ```
+
+ When everything has been done right, the test should pass.
+ ![Screenshot of passing tests](../../assets/img/passing-test.png)
+
+1. Commit the changes to the current `feature/tetris-game` branch and publish it for a review.
+
+ !!! tip "You can link your changes to an issue"
+
+ Recall the issue you created earlier and its respective issue number, you will use it to link your current changes to the issue.
+
+ ```bash linenums="1"
+ git add .
+ git commit -m "$(printf 'Create a tetris game to drive site engagement\n\n-Integrate Tetris game implementation\n\n- Resolves #')"
+ git push origin
+ ```
+
+ {==
+
+ Observe that all checks have passed.
+
+ ![Show completely passing checks](../../assets/img/completely-passing-checks.png)
+
+ ==}
+
+---
+
+## š Resources
+
+- [Creating a GitHub Pages site](https://docs.github.com/en/pages/getting-started-with-github-pages/creating-a-github-pages-site)
+- [Introduction to dev containers](https://docs.github.com/en/codespaces/setting-up-your-project-for-codespaces/adding-a-dev-container-configuration/introduction-to-dev-containers)
+- [Workflow syntax for GitHub Actions](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idenv)
+- [Events that trigger workflows](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows)
+- [Finding and customizing actions](https://docs.github.com/en/actions/learn-github-actions/finding-and-customizing-actions)
+- [Code scanning](https://docs.github.com/en/code-security/code-scanning)
+- [Security hardening for GitHub Actions](https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions)
+
+---
diff --git a/docs/exercises/01.collaborate/index.md b/docs/exercises/01.collaborate/index.md
new file mode 100644
index 0000000..4bb7b5a
--- /dev/null
+++ b/docs/exercises/01.collaborate/index.md
@@ -0,0 +1,14 @@
+# :test_tube: Collaborate
+
+!!! quote "Continuous Collaboration"
+
+ Continuous collaboration enables working together across technical and cultural boundaries as a unified team, reducing frictions and increasing the velocity to accomplish common goals.
+
+ ## :octicons-goal-16:{ style="color: pink" } Objectives
+
+ Behaviors that enable addressing the challenge include
+
+ - [x] Facilitate tracking of business goal delivery
+ - [x] Enable rapid onboarding to project development
+ - [x] Provide a standardized validation of work done
+ - [x] Enable collaborative validation of done requirement
diff --git a/docs/exercises/02.secure.the.software.supply.chain/01.md b/docs/exercises/02.secure.the.software.supply.chain/01.md
new file mode 100644
index 0000000..77b6feb
--- /dev/null
+++ b/docs/exercises/02.secure.the.software.supply.chain/01.md
@@ -0,0 +1,100 @@
+# :test_tube: Dependency Review
+
+
+
+!!! quote ":octicons-milestone-16: Add Security Checks"
+
+ > Dependency review helps you understand dependency changes and the security impact of these changes at every pull request. It provides an easily understandable visualization of dependency changes with a rich diff on the "Files Changed" tab of a pull request. Dependency review informs you of:
+
+ > - Which dependencies were added, removed, or updated, along with the release dates.
+ > - How many projects use these components.
+ > - Vulnerability data for these dependencies.
+
+ ![Review dependency](../../assets/img/dependency-review-milestone.png)
+
+---
+
+## Exercise: Automate Enforcement of Dependency Review
+
+---
+
+### **:material-numeric-1-circle: Understand the Dependency Review Configurations**
+
+```yaml title=".github/dependency-review-config.yml"
+fail-on-severity: moderate #(1)!
+
+comment-summary-in-pr: always #(2)!
+
+allow-licenses: #(3)!
+ - MIT
+ - GPL-3.0
+ - BSD-3-Clause
+
+deny-licenses: #(4)!
+ - LGPL-2.0
+ - BSD-2-Clause
+
+allow-ghsas: #(5)!
+ - GHSA-abcd-1234-5679
+ - GHSA-efgh-1234-5679
+
+fail-on-scopes: #(6)!
+ - development
+ - runtime
+ - unknown
+```
+
+1. Possible values: "critical", "high", "moderate", "low"
+2. Post summary as a comment to respective pull request
+3. Only allow the listed licenses (optional). Possible values: Any `spdx_id` value(s) from https://docs.github.com/en/rest/licenses
+4. Block the pull request on these licenses (optional). Possible values: Any `spdx_id` value(s) from https://docs.github.com/en/rest/licenses
+
+ :warning: You can only include one of either of these two options: `allow-licenses` and `deny-licenses`.
+
+5. Skip these GitHub Advisory Database IDs during detection (optional). Possible values: Any valid GitHub Advisory Database ID from https://github.com/advisories
+6. Block pull requests that introduce vulnerabilities in the scopes that match this list (optional). Possible values: "development", "runtime", "unknown"
+
+---
+
+### **:material-numeric-2-circle: Implement Dependency Review Check**
+
+Open the `.github/workflows/continuous.integration.yml` file in the editor and add the {== highlighted ==} content as follows:
+
+```yaml title=".github/workflows/continuous.integration.yml" linenums="1" hl_lines="58-67"
+--8<-- "reference.implementations/workflows/1.2.continuous.integration.yml"
+```
+
+---
+
+### **:material-numeric-2-circle: Commit and publish your changes**
+
+Pushing your changes will apply the check on the existing pull request.
+
+!!! tip "You can link your changes to an issue"
+
+ Recall the issue you created earlier and its respective issue number, you will use it to link your current changes to the issue.
+
+```bash linenums="1"
+git add .
+git commit -m "$(printf 'Create a tetris game to drive site engagement\n\n-Add dependency review as a security check\n\n- Resolves #')"
+git push origin feature/tetris-game
+```
+
+{==
+
+When you navigate to the repository on GitHub and open the existing pull request, you can confirm that the check was executed. As there are no compliance violations currently in the PR, the check is successful.
+
+![Show dependency review outcome](../../assets/img/dependency-review-outcome-01.png)
+
+==}
+
+!!! question "Homework"
+
+ Can you attempt to make the dependency review check to fail?
+
+---
+
+## š Resources
+
+- [Understanding your software supply chain](https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain)
+- [GitHub Advanced Security - Dependency Graph](https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-the-dependency-graph)
diff --git a/docs/exercises/02.secure.the.software.supply.chain/02.md b/docs/exercises/02.secure.the.software.supply.chain/02.md
new file mode 100644
index 0000000..4fac8d2
--- /dev/null
+++ b/docs/exercises/02.secure.the.software.supply.chain/02.md
@@ -0,0 +1,92 @@
+# :test_tube: Versioning
+
+
+
+!!! quote ":octicons-milestone-16: Motivation for Versions"
+
+ > A pull request represents a single aggregation of multiple significant changes (i.e. commits). When collaborating at scale in a large team or project, pull requests targeting the default (production) branch can be further buffered through a merge queue for further validation. In either cases, it is imperative to keep track of the aggregated changes introduced into the default branch which serves as the basic source of production releases.
+ >
+ > Some versions may eventually be released whereas others are not. Moreover, a released version may be rolled back to an earlier release as business decisions evolve.
+
+ ![Show versioning milestone](../../assets/img/milestone-versioning.png)
+
+---
+
+## Exercise: Version Changes Added to the Main Branch
+
+Semantic Versioning, `semver` is a scheme that conveys meaning about the underlying code and what has been modified from one version to the next.
+
+`semver` version `2.0.0` is officially summarized as follows.
+
+> Given a version number MAJOR.MINOR.PATCH, increment the:
+>
+> 1. MAJOR version when you make incompatible API changes
+> 1. MINOR version when you add functionality in a backward compatible manner
+> 1. PATCH version when you make backward compatible bug fixes
+>
+> Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format.
+
+At this fundamentals phase of DevSecOps implementation, we will apply the `semver` scheme and automate patch increment. There are conventions that can be adopted to equally automate increment in `MAJOR` and `MINOR` versions, but for simplicity our intended implementation assumes the understanding that `MAJOR` and `MINOR` version bumps will be manually initiated.
+
+---
+
+### **:material-numeric-1-circle: Implement `PATCH` Versioning**
+
+In the file explorer, create a new workflow `.github/workflows/continuous.versioning.yml` as follows.
+
+```yaml title=".github/workflows/continuous.versioning.yml" linenums="1" hl_lines="4-6 13-17 25-31 33-47 49-60"
+--8<-- "reference.implementations/workflows/2.1.continuous.delivery.yml"
+```
+
+---
+
+#### Analysis
+
+- **Lines `4 - 6`**
+
+ We are introduced to a new event that encapsulates the activity of pushing to particular branches, specifically the `main` branch alone in this implementation.
+
+- **Lines `13 - 17`**
+
+ A GitHub App will be used as the actor for the operations we shall be executing. Hence the `actions/create-github-app-token@v1` action is used here to generate an authorization token for the app.
+
+ !!! question "What is a GitHub App?"
+ GitHub Apps, much _like_ `service accounts` and `bots`, are tools that extend GitHub's functionality. You can build a GitHub App to provide flexibility and reduce friction in your processes, {== without needing to sign in a user or create a service account ==}. GitHub Apps can do things on GitHub like open issues, comment on pull requests, and manage projects. They can also do things outside of GitHub based on events that happen on GitHub. For example, a GitHub App can post on Slack when an issue is opened on GitHub.
+
+ The GitHub App actor will later be used to checkout the repository's source code and to push tags.
+
+- **Lines `25 - 31`**
+
+ The step `Get last version number` computes the last version and writes it to the step output named `last_version`.
+
+- **Lines `33 - 47`**
+
+ The step `Get new version number` uses reads the output from the previous step by the expression `${{ steps.get_last_version.outputs.last_version }}`, and use it to compute the patch increment.
+
+- **Lines `49 - 60`**
+
+ The step `Create tag for the new version` creates a new `git` tag for release. A tag identifies specific points in a repositoryās history as being important. Here, the tag is being used to mark a release point.
+
+ Because a persisted app token exists and will be used in the tag push, we can be confident that the push will effectively trigger other relevant automation. On the other hand, if the default `GITHUB_TOKEN` was used it would not trigger a workflow as {== **GitHub Actions rightly guard against unintentional recursive triggering of workflows** ==}.
+
+---
+
+### **:material-numeric-2-circle: Commit and publish your changes**
+
+!!! tip "You can link your changes to an issue"
+
+ Recall the issue you created earlier and its respective issue number, you will use it to link your current changes to the issue.
+
+```bash linenums="1"
+git add .
+git commit -m "$(printf 'Create a tetris game to drive site engagement\n\n-Implement automatic patch versioning\n\n- Resolves #')"
+git push origin feature/tetris-game
+```
+
+---
+
+## š Resources
+
+- [Semantic Versioning 2.0.0](https://semver.org/)
+
+---
diff --git a/docs/exercises/02.secure.the.software.supply.chain/index.md b/docs/exercises/02.secure.the.software.supply.chain/index.md
new file mode 100644
index 0000000..79d64b8
--- /dev/null
+++ b/docs/exercises/02.secure.the.software.supply.chain/index.md
@@ -0,0 +1,14 @@
+# :test_tube: Secure the Software Supply Chain
+
+!!! quote "Continuous Delivery and Secure Supply Chain"
+
+ Supply chain security is integral to continuous delivery. With the accelerated use of open source, most projects depend on hundreds of open-source dependencies. This poses a security problem: what if the dependencies you're using are vulnerable? You could be putting your users at risk of a supply chain attack. One of the most important things you can do to protect your supply chain is to patch your vulnerable dependencies and replace any malware.
+
+ Building on the progress from previous exercises, we will apply supply chain security and continuously deliver the product by fulfilling the following objectives.
+
+ ## :octicons-goal-16:{ style="color: pink" } Objectives
+
+ - [x] Create Integration **Checks** for the `Tetris` app using GitHub Actions.
+ - [x] Create a Continuous Delivery workflow for the `Tetris` app using GitHub Actions.
+ - [x] Create a new release `tag` for the `Tetris` app using GitHub Actions.
+ - [x] Create a Compliance using CodeQL, Dependency Review, and Dependabot.
diff --git a/docs/exercises/03.automate.release/01.md b/docs/exercises/03.automate.release/01.md
new file mode 100644
index 0000000..fb84906
--- /dev/null
+++ b/docs/exercises/03.automate.release/01.md
@@ -0,0 +1,90 @@
+# :test_tube: Release Version
+
+
+
+!!! quote ":octicons-milestone-16: Release"
+
+ > As streams of changes make it into the _production_ branch and are versioned in batches, an essential part of continuous delivery/deployment is to produce artifacts from versions which can then be deployed to various targets.
+ >
+ > Releases are deployable software iterations you can package and make available for a wider audience to download and use.
+ >
+ > How do we determine when to create a release? Let's see...
+
+ ![Show milestone package delivery artifacts](../../assets/img/milestone-package-and-release-delivery-artifacts.png)
+
+---
+
+## Exercise: Create a Release of Versions
+
+As you can guess already, GitHub provides an event that encapsulates the activity of creating a tag and pushing it to a repository. We will automate addressing such events, so that we can create a release when necessary.
+
+---
+
+### **:material-numeric-1-circle: Implement Release Creation**
+
+In the file explorer, create a new workflow `.github/workflows/continuous.delivery.yml` as follows.
+
+```yaml title=".github/workflows/continuous.delivery.yml" linenums="1" hl_lines="3-6 29-34 39-43 45-70 72-81 83-105"
+--8<-- "reference.implementations/workflows/2.2.continuous.delivery.yml"
+```
+
+---
+
+#### Analysis
+
+- **Lines `3 - 6`**
+
+ The release creation workflow will be triggered by tag pushes and when the pushed tag version is suffixed with the `-release` metadata.
+
+- **Lines `29 - 34`**
+
+ In the two steps included here, the site is built and archived as a release artifact.
+
+- **Lines `39 - 43`**
+
+ A GitHub App will be used as the actor for the operations we shall be executing. Hence the `actions/create-github-app-token@v1` action is used here to generate an authorization token for the app.
+
+ !!! question "What is a GitHub App?"
+ GitHub Apps, much _like_ `service accounts` and `bots`, are tools that extend GitHub's functionality. You can build a GitHub App to provide flexibility and reduce friction in your processes, {== without needing to sign in a user or create a service account ==}. GitHub Apps can do things on GitHub like open issues, comment on pull requests, and manage projects. They can also do things outside of GitHub based on events that happen on GitHub. For example, a GitHub App can post on Slack when an issue is opened on GitHub.
+
+ > _When you use the repository's `GITHUB_TOKEN` to perform tasks, events triggered by the `GITHUB_TOKEN`, with the exception of workflow_dispatch and repository_dispatch, will not create a new workflow run. This prevents you from accidentally creating recursive workflow runs. For example, if a workflow run pushes code using the repository's `GITHUB_TOKEN`, a new workflow will not run even when the repository contains a workflow configured to run when push events occur._
+ >
+ > ~ [Using the GITHUB_TOKEN in a workflow](https://docs.github.com/en/actions/security-guides/automatic-token-authentication#using-the-github_token-in-a-workflow)
+
+ So, we are authorizing operations as a GitHub App to enable the execution of the operation to further trigger other workflow flow runs, possibly.
+
+- **Lines `45 - 70`**
+
+ A `draft` release is created by the respective step, allowing for modification of the release before publishing it. The release references the tag that had triggered the workflow.
+
+- **Lines `72 - 81`**
+
+ This step leverages GitHub's ReST API to adjust the previously created draft release, specifically adding the archive created in the `Archive Site` step to the release as an asset.
+
+- **Lines `83 - 105`**
+
+ Having drafted the release and attached deployable assets to it, the `Publish release` step ultimately publishes the release by flipping the state of it `draft` attribute to `false`.
+
+---
+
+### **:material-numeric-2-circle: Commit and publish your changes**
+
+!!! tip "You can link your changes to an issue"
+
+ Recall the issue you created earlier and its respective issue number, you will use it to link your current changes to the issue.
+
+```bash linenums="1"
+git add .
+git commit -m "$(printf 'Create a tetris game to drive site engagement\n\n-Implement release automation\n\n- Resolves #')"
+git push origin feature/tetris-game
+```
+
+---
+
+## š Resources
+
+- [GitHub Apps documentation](https://docs.github.com/en/apps)
+- [GitHub REST API documentation](https://docs.github.com/en/rest?apiVersion=2022-11-28)
+- [GitHub Packages](https://docs.github.com/en/packages/guides/about-github-container-registry)
+
+---
diff --git a/docs/exercises/03.automate.release/02.md b/docs/exercises/03.automate.release/02.md
new file mode 100644
index 0000000..ef3cc21
--- /dev/null
+++ b/docs/exercises/03.automate.release/02.md
@@ -0,0 +1,137 @@
+# :test_tube: Continuous Deployment
+
+
+
+!!! quote ":octicons-milestone-16: Deliver high quality value at speed"
+
+ > Continuous deployment (CD) is the practice of using automation to publish and deploy software updates.
+ >
+ > To ultimately fulfill our goal on complete development of the feature, we altogether apply the practice of continuous deployment to validate and ship the value to our customers at high velocity.
+
+ ![Show milestone-continuous-deployment](../../assets/img/milestone-continuous-deployment.png)
+
+---
+
+## Exercise: Automate Deployment of Releases
+
+The previous exercise walked us through automating the publication of release. We will continue to build upon that milestone to automate deployment of published releases.
+
+---
+
+### **:material-numeric-1-circle: Implement Deployment Workflow**
+
+In the file explorer, create a new workflow `.github/workflows/continuous.deployment.yml` as follows.
+
+```yaml title=".github/workflows/continuous.deployment.yml" linenums="1" hl_lines="3-6 8-10 25-27 36-40 42-65 67-71 73-77"
+--8<-- "reference.implementations/workflows/3.continuous.deployment.yml"
+```
+
+---
+
+#### Analysis
+
+- **Lines `3 - 6`**
+
+ Deployments will be triggered in the event of a release publication.
+
+- **Lines `8 - 10`**
+
+ Ensure that only a single job or workflow using the same concurrency group will run at a time.
+
+ When a concurrent job or workflow is queued, if another job or workflow using the same concurrency group in the repository is in progress, the queued job or workflow will be pending. Any previously pending job or workflow in the concurrency group will be canceled. If you do not wish to also cancel any currently running job or workflow in the same concurrency group, specify `cancel-in-progress: false`.
+
+- **Lines `25 - 27`**
+
+ This will cause the URL of the deployed site to be displayed where the workflow is executed.
+
+- **Lines `36 - 40`**
+
+ This step enables [GitHub Pages](https://docs.github.com/en/pages/getting-started-with-github-pages/about-github-pages) and extracts various metadata about a site. It can also be used to configure various static site generators we support as starter workflows.
+
+- **Lines `42 - 65`**
+
+ Download assets associated with the release publication that triggered this workflow, and where necessary, adjust permissions for the files in the assets.
+
+- **Lines `67 - 71`**
+
+ Upload the site artifacts to the storage location where it should be ultimately collected by the next step for deployment.
+
+- **Lines `73 - 77`**
+
+ This step deploys the site.
+
+---
+
+### **:material-numeric-2-circle: Commit and publish your changes**
+
+!!! tip "You can link your changes to an issue"
+
+ Recall the issue you created earlier and its respective issue number, you will use it to link your current changes to the issue.
+
+```bash linenums="1"
+git add .
+git commit -m "$(printf 'Create a tetris game to drive site engagement\n\n-Implement continuous deployment\n\n- Resolves #')"
+git push origin feature/tetris-game
+```
+
+---
+
+### **:material-numeric-3-circle: Merge your open pull request**
+
+- On the pull request page, open the drop-down from the Merge pull request :material-menu-down: button to choose the **Rebase and merge** option.
+
+- Then press the Rebase and merge :material-menu-down: button to merge your pull request.
+
+ ![Show pull request merge view](../../assets/img/merge-pull-request.png){ style="width: 25em;" }
+
+- Finally, press Confirm rebase and merge .
+
+When you merge the open pull request, you should make the following observations.
+
+{==
+
+**Collaboration**
+
+- The linked issue in your project board automatically gets closed and moved to the **ā
Done** column of the board.
+
+ ![Show done work item](../../assets/img/issue-closed-done.png){ style="width: 25em;" }
+
+**Automation**
+
+When you head over to the **:octicons-play-16: Actions** tab on your repository, and notice...
+
+- The `Version Changes to the Main Branch` workflow is executed.
+- The `Package Delivery Artifacts & Create Release` workflow is executed.
+
+ Consequently the release is announced in **:octicons-comment-discussion-16: Discussions**.
+ ![Show release discussion](../../assets/img/release-discussions.png){ style="width: 25em;" }
+
+- The `Deploy Release` workflow is executed.
+
+ And on successful completion of the workflow, the public URL to your site is displayed on the job.
+
+ ![Show successful execution of release deployment](../../assets/img/successful-deployment-job.png){ style="width: 25em;" }
+
+**Stakeholder Enablement**
+
+Stakeholders can easily explore deployments of the project.
+
+- Deployments summary on repository home.
+
+ ![Show deployment on repository home](../../assets/img/deployments-view-on-repo-home.png){ style="width: 25em;" }
+
+- Detailed deployment environments list.
+
+ ![Show deployments list](../../assets/img/deployments-list.png){ style="width: 25em;" }
+
+==}
+
+---
+
+## š Resources
+
+- [Using environments for deployment](https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment)
+- [Creating custom deployment protection rules](https://docs.github.com/en/actions/deployment/protecting-deployments/creating-custom-deployment-protection-rules)
+- [Security harden deployments with OpenID Connect](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect)
+
+---
diff --git a/docs/exercises/03.automate.release/index.md b/docs/exercises/03.automate.release/index.md
new file mode 100644
index 0000000..46a948a
--- /dev/null
+++ b/docs/exercises/03.automate.release/index.md
@@ -0,0 +1,12 @@
+# :test_tube: Automate Deployment of Releases
+
+!!! quote "Continuous Deployment"
+
+ Continuous deployment (CD) is the practice of using automation to publish and deploy software updates. As part of the typical CD process, the code is automatically built and tested before deployment.
+
+ Continuous deployment is often coupled with continuous integration which we have seen earlier. Building on the progress from previous exercises, we will apply continuous deployment mindset by fulfilling the following objectives.
+
+ ## :octicons-goal-16:{ style="color: pink" } Objectives
+
+ - [x] Validate releases
+ - [x] Execute production deployments on successful validation of respective releases
diff --git a/docs/exercises/index.md b/docs/exercises/index.md
new file mode 100644
index 0000000..8fc9475
--- /dev/null
+++ b/docs/exercises/index.md
@@ -0,0 +1,47 @@
+# :fontawesome-solid-flask-vial: Introduction
+
+!!! quote ":octicons-milestone-16: Workshop Exercises"
+
+ The workshop exercises provide guidance for implementing DevSecOps at the fundamental level with the GitHub platform. On completion, you would have gained the exposure to enable you seamlessly implement DevSecOps capabilities such as
+
+ - [x] Continuous collaboration
+ - [x] Continuous planning
+ - [x] Continuous integration
+ - [x] Continuous delivery
+ - [x] Continuous quality
+ - [x] Continuous security
+
+ This workshop site also serves as the ***product*** we shall target for development. And During the course of the exercises, you will discover how you can rapidly build and test the site locally, and implement standardized tests to enable automated quality checks on the site.
+
+ ---
+
+ ## **:material-numeric-1-circle: Workshop Scenario**
+
+ You are working in a team that owns **_this_** site, and a new business goal has come into the vision of your organisation. In order to grow engagement with the site...
+
+ > _...the goal is to create a **Tetris Game** to delight our site visitors._
+
+
+ ![Tetris Game](../assets/img/tetris-game.png){ loading=lazy }
+ Outcome: Tetris Game
+
+
+ ---
+
+ ## **:material-numeric-2-circle: Milestones**
+
+ The workshop objectives will be delivered by completion of the following milestones
+
+ ![Milestones](../assets/img/milestones-start.png)
+
+ - [x] Collaborate
+
+ Facilitate onboarding into the site project and enable project development at high velocity for stakeholders of different experience with the project.
+
+ - [x] Secure the software supply chain
+
+ Seamlessly implement application security and secure the software supply chain.
+
+ - [x] Automate Deployment of Releases
+
+ Reliably deliver value to the end-user at high velocity.
diff --git a/docs/exercises/recap/index.md b/docs/exercises/recap/index.md
new file mode 100644
index 0000000..a314360
--- /dev/null
+++ b/docs/exercises/recap/index.md
@@ -0,0 +1,13 @@
+# :test_tube: Review of Workshop Outcomes
+
+![Show all covered milestones](../../assets/img/milestones-all.png)
+
+So far, we have reached the following milestones, learning to
+
+- [x] Track work on GitHub
+- [x] Rapidly onboard onto an existing project
+- [x] Work in a collaborative manner, and enable quick incorporation of feedbacks
+- [x] Efficiently secure the software supply chain, catching vulnerabilities and non-compliant dependencies as they are introduced
+- [x] Automate versioning
+- [x] Automate releases
+- [x] Implement continuous deployment
diff --git a/docs/index.md b/docs/index.md
new file mode 100644
index 0000000..710fe89
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1,31 @@
+# :octicons-mark-github-16: GitHub DevSecOps Fundamentals | Workshop, GitHub Universe 2023
+
+
+
+This self-paced workshop will enable you acquire the capabilities to implement a DevSecOps mindset, leveraging the GitHub platform for a seamless developer experience.
+
+
+![GitHub Platform](assets/img/e2e-github.png){ loading=lazy }
+GitHub Platform
+
+
+!!! success ""
+
+ > ## :octicons-goal-16:{ style="color: pink" } Learning Objectives
+ >
+ > - [x] Collaborate effectively for high-velocity productivity
+ > - [x] Apply preventive security to every aspect of the development process
+ > - [x] Implement a secure software supply chain
+ > - [x] Automate product release and business processes in general
+ > - [x] Remove complications due to tool-bloat
+
+
+ [:material-cursor-default-click: Complete the **:footprints: Prerequisites** checklist!](./prerequisites.md){ .md-button .md-button--primary }
+
+ > ## š Resources
+ >
+ > - [Project planning for developers](https://github.com/features/issues)
+ > - [Start coding instantly with Codespaces](https://github.com/features/codespaces)
+ > - [Automate your workflow from idea to production](https://github.com/features/actions)
+ > - [Secure at every step](https://github.com/features/security)
+ > - [Your packages, at home with their code](https://github.com/features/packages)
diff --git a/docs/prerequisites.md b/docs/prerequisites.md
new file mode 100644
index 0000000..dcce59f
--- /dev/null
+++ b/docs/prerequisites.md
@@ -0,0 +1,186 @@
+# :footprints: Prerequisites
+
+
+
+!!! tip "Prepare before the workshop"
+
+ To ensure a seamless workshop experience, complete the following checklist items in order.
+
+## :material-numeric-1-circle: :fontawesome-brands-github-alt:{ .lg .middle } **Set up a GitHub account**
+
+Login to your GitHub account.
+
+You may create a new GitHub account, if you do not already have one. (1)
+{ .annotate }
+
+1. :bulb: Clicking the button below will open the page on a new tab, for you to create a new GitHub account. Keep your **`@handle`** handy after you have logged in.
+
+[:material-cursor-default-click: Create a new GitHub account](https://github.com/signup){.md-button target="\_blank"}
+
+
+
+---
+
+## :material-numeric-2-circle: :material-source-repository-multiple:{ .lg .middle } **Clone the workshop project to your own account**
+
+1. [:material-cursor-default-click: Create workshop project](https://github.com/new?template_owner=githubuniverseworkshops&template_name=github-devsecops-fundamentals&owner=%40me&name=github-devsecops-fundamentals&description=GitHub+DevSecOps+Fundamentals+%7C+GitHub+Universe+2023+workshop&visibility=public){.md-button target="\_blank"}
+1. In the new tab, most of the prompts will automatically fill in for you and the default settings should be fine. Otherwise...
+
+ - For owner, choose your personal account or.
+ - We recommend creating a public repository. (1)
+ { .annotate }
+
+ 1. :bulb: Public repositories benefit from
+
+ - [Free use of GitHub Actions minutes](https://docs.github.com/en/billing/managing-billing-for-github-actions/about-billing-for-github-actions).
+ - [Free use of GitHub Advanced Security features](https://docs.github.com/en/get-started/learning-about-github/about-github-advanced-security#about-advanced-security-features).
+
+ - Scroll down and click the Create repository button at the bottom of the form.
+
+ ??? info "Expand for sample view..."
+
+ ![Create a new repository from a template](./assets/img/create-a-new-repository.png)
+
+1. After creation of your new repository has begun, wait about 20 seconds, then refresh the page.
+
+
+
+---
+
+## :material-numeric-3-circle: :octicons-feed-discussion-16: **Enable Discussions**
+
+1. On GitHub.com, navigate to the main page of your new repository.
+1. Under your repository name, click :octicons-gear-16: **Settings**.
+ ![Repository settings tab](./assets/img/repository-settings-tab.png)
+1. Scroll down to the "Features" section and click Set up discussions
+
+
+
+---
+
+## :material-numeric-4-circle: :material-security:{ .lg .middle } **Configure base security**
+
+1. Navigate to the :octicons-gear-16: **Settings** tab on your new workshop repository.
+1. Under the "**Security**" section of the menu, select :octicons-codescan-16: **Code security and analysis**. Then configure the settings to match as follows (by clicking Enable where the option is/becomes available).
+
+ !!! note "Further configurations guide..."
+
+ - **Dependabot version updates**
+
+ When enabling, you will be prompted to edit the `.github/dependabot.yml` configuration file. Simply **navigate back** on your browser or click on Cancel changes to leave the prompt, and to continue the base security configuration.
+ - **CodeQL analysis**
+
+ 1. From the Set up , select the Default option.
+ ![Show CodeQL analysis setup](./assets/img/codeql-analysis-setup.png)
+ 1. Leave the default settings in the prompt that follows, and simply click on Enable CodeQL to continue.
+ - **Protection rules**
+
+ Select the configuration as shown below.
+ ![Show CodeQL protection rules](./assets/img/codeql-protection-rules-setup.png)
+
+
+
+---
+
+## :material-numeric-5-circle: :octicons-dependabot-16:{ .lg .middle } **Register a GitHub App**
+
+1. In the upper-right corner of any page on GitHub, click your profile photo.
+1. Navigate to your account **:octicons-gear-16: Settings**.
+1. In the left sidebar, click **:octicons-code-16: Developer settings**.
+1. In the left sidebar, click **:octicons-apps-16: GitHub Apps**
+1. Click **New GitHub App**
+
+ !!! note "Provide the form fields value as follows"
+
+ ```text
+ GitHub App Name..........: -bot
+
+ Homepage URL.............: https://github.com//github-devsecops-fundamentals
+
+ Webhook
+ Active.................: DESELECT
+
+ Repository permissions
+ Actions................: Read and write
+ Administration.........: Read and write
+ Contents...............: Read and write
+ Deployments............: Read and write
+ Discussions............: Read and write
+ Environments...........: Read and write
+ Pages..................: Read and write
+
+ Where can this GitHub App be installed?
+ Any account.................: SELECT
+ ```
+
+1. Generate a private key for the new GitHub App as documented in [**:eyes: here**](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/managing-private-keys-for-github-apps#generating-private-keys){ target="\_blank" }.
+1. Save the GitHub App private key to your new repository's secrets under the name of `APP_PRIVATE_KEY_ACTIONS_ASSISTANT`.
+
+ š [**Creating secrets for a repository**](https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions#creating-secrets-for-a-repository).
+
+1. Save the GitHub App's `appid` to your new repository's variables under the name of `APP_ID_ACTIONS_ASSISTANT`.
+
+ š [**Creating configuration variables for a repository**](https://docs.github.com/en/actions/learn-github-actions/variables#creating-configuration-variables-for-a-repository)
+
+1. Goto `https://github.com/apps/-bot`. Install the app in the repository you created earlier.
+
+ ??? info "Expand for additional guide and sample views..."
+
+ 1. ![Show app home](./assets/img/app-home-display.png){ style="width: 25em;" }
+ 1. ![Show resource owner selection](./assets/img/app-owner-selection.png){ style="width: 25em;" }
+ 1. ![Show selection of repository and install](./assets/img/app-destination-repository-selection.png){ style="width: 25em;" }
+ 1. ![Show notification of successful installation](./assets/img/app-installation-success-notification.png){ style="width: 25em;" }
+
+!!! question "What is the use of a GitHub App?"
+
+ > _When you use the repository's `GITHUB_TOKEN` to perform tasks, events triggered by the `GITHUB_TOKEN`, with the exception of `workflow_dispatch` and `repository_dispatch`, will not create a new workflow run. This prevents you from accidentally creating recursive workflow runs. For example, if a workflow run pushes code using the repository's `GITHUB_TOKEN`, a new workflow will not run even when the repository contains a workflow configured to run when push events occur._
+ >
+ > _If you do want to trigger a workflow from within a workflow run, you can use a GitHub App installation access token or a personal access token instead of `GITHUB_TOKEN` to trigger events that require a token._
+ >
+ > _If you use a GitHub App, you'll need to create a GitHub App and store the app ID and private key as secrets._
+ >
+ > ~ [Triggering a workflow from a workflow](https://docs.github.com/en/actions/using-workflows/triggering-a-workflow#triggering-a-workflow-from-a-workflow)
+
+ During the course of the workshop exercises, you will discover how the GitHub App is used to trigger the automatic deployment of release artifacts.
+
+
+
+---
+
+## :material-numeric-6-circle: :octicons-project-16:{ .lg .middle } **Create a project board for the repository**
+
+1. Open the recently created workshop repository.
+
+2. Navigate to the :octicons-project-template-16: **Projects** tab.
+
+3. Open the Link a project :material-menu-down: {== dropdown menu ==}. Then select the **New Project** option.
+
+4. Click on the New project :material-menu-down: {== button ==} to start the project creation wizard.
+5. Select the :octicons-people-16: **Team backlog** template.
+6. Give a name to the project
+7. Finally click on the Create button.
+
+
+
+---
+
+## :material-numeric-7-circle: :octicons-codespaces-16: **Provision a new cloud development environment for the workshop**
+
+GitHub Codespaces enables you to instantly start coding on the workshop project. Personal accounts on GitHub can benefit from a [free quota of GitHub Codespaces usage](https://docs.github.com/en/billing/managing-billing-for-github-codespaces/about-billing-for-github-codespaces#monthly-included-storage-and-core-hours-for-personal-accounts).
+
+1. Navigate to the :octicons-code-16: **Code** tab.
+
+2. Open the :octicons-code-16: Code :material-menu-down: menu and click on :material-cursor-default-click: Create codespaces on main .
+
+ ??? info "Expand for sample view..."
+ ![Create a new GitHub Codespaces](./assets/img/create-a-new-codespaces.png){ style="height: 40em"}
+
+3. {==
+
+ **Leave the Codespace development environment to initialize and run, we will come back to use it later.**
+
+ ==}
+
+
+
+---
diff --git a/jest.config.js b/jest.config.js
new file mode 100644
index 0000000..774fe41
--- /dev/null
+++ b/jest.config.js
@@ -0,0 +1,198 @@
+/**
+ * For a detailed explanation regarding each configuration property, visit:
+ * https://jestjs.io/docs/configuration
+ */
+
+/** @type {import('jest').Config} */
+const config = {
+ // All imported modules in your tests should be mocked automatically
+ // automock: false,
+
+ // Stop running tests after `n` failures
+ // bail: 0,
+
+ // The directory where Jest should store its cached dependency information
+ // cacheDirectory: "/private/var/folders/lh/mxxx3bm567z9x2d8bvt4s0zm0000gn/T/jest_dx",
+
+ // Automatically clear mock calls, instances, contexts and results before every test
+ clearMocks: true,
+
+ // Indicates whether the coverage information should be collected while executing the test
+ // collectCoverage: false,
+
+ // An array of glob patterns indicating a set of files for which coverage information should be collected
+ // collectCoverageFrom: undefined,
+
+ // The directory where Jest should output its coverage files
+ // coverageDirectory: undefined,
+
+ // An array of regexp pattern strings used to skip coverage collection
+ // coveragePathIgnorePatterns: [
+ // "/node_modules/"
+ // ],
+
+ // Indicates which provider should be used to instrument code for coverage
+ coverageProvider: "v8",
+
+ // A list of reporter names that Jest uses when writing coverage reports
+ // coverageReporters: [
+ // "json",
+ // "text",
+ // "lcov",
+ // "clover"
+ // ],
+
+ // An object that configures minimum threshold enforcement for coverage results
+ // coverageThreshold: undefined,
+
+ // A path to a custom dependency extractor
+ // dependencyExtractor: undefined,
+
+ // Make calling deprecated APIs throw helpful error messages
+ // errorOnDeprecated: false,
+
+ // The default configuration for fake timers
+ // fakeTimers: {
+ // "enableGlobally": false
+ // },
+
+ // Force coverage collection from ignored files using an array of glob patterns
+ // forceCoverageMatch: [],
+
+ // A path to a module which exports an async function that is triggered once before all test suites
+ // globalSetup: undefined,
+
+ // A path to a module which exports an async function that is triggered once after all test suites
+ // globalTeardown: undefined,
+
+ // A set of global variables that need to be available in all test environments
+ // globals: {},
+
+ // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.
+ // maxWorkers: "50%",
+
+ // An array of directory names to be searched recursively up from the requiring module's location
+ // moduleDirectories: [
+ // "node_modules"
+ // ],
+
+ // An array of file extensions your modules use
+ // moduleFileExtensions: [
+ // "js",
+ // "mjs",
+ // "cjs",
+ // "jsx",
+ // "ts",
+ // "tsx",
+ // "json",
+ // "node"
+ // ],
+
+ // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
+ // moduleNameMapper: {},
+
+ // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
+ // modulePathIgnorePatterns: [],
+
+ // Activates notifications for test results
+ // notify: false,
+
+ // An enum that specifies notification mode. Requires { notify: true }
+ // notifyMode: "failure-change",
+
+ // A preset that is used as a base for Jest's configuration
+ // preset: undefined,
+
+ // Run tests from one or more projects
+ // projects: undefined,
+
+ // Use this configuration option to add custom reporters to Jest
+ // reporters: undefined,
+
+ // Automatically reset mock state before every test
+ // resetMocks: false,
+
+ // Reset the module registry before running each individual test
+ // resetModules: false,
+
+ // A path to a custom resolver
+ // resolver: undefined,
+
+ // Automatically restore mock state and implementation before every test
+ // restoreMocks: false,
+
+ // The root directory that Jest should scan for tests and modules within
+ rootDir: "docs/Tetris",
+
+ // A list of paths to directories that Jest should use to search for files in
+ // roots: [
+ // "docs/Tetris"
+ // ],
+
+ // Allows you to use a custom runner instead of Jest's default test runner
+ // runner: "jest-runner",
+
+ // The paths to modules that run some code to configure or set up the testing environment before each test
+ // setupFiles: [],
+
+ // A list of paths to modules that run some code to configure or set up the testing framework before each test
+ // setupFilesAfterEnv: [],
+
+ // The number of seconds after which a test is considered as slow and reported as such in the results.
+ // slowTestThreshold: 5,
+
+ // A list of paths to snapshot serializer modules Jest should use for snapshot testing
+ // snapshotSerializers: [],
+
+ // The test environment that will be used for testing
+ testEnvironment: "jsdom",
+
+ // Options that will be passed to the testEnvironment
+ // testEnvironmentOptions: {},
+
+ // Adds a location field to test results
+ // testLocationInResults: false,
+
+ // The glob patterns Jest uses to detect test files
+ // testMatch: [
+ // "**/__tests__/**/*.[jt]s?(x)",
+ // "**/?(*.)+(spec|test).[tj]s?(x)"
+ // ],
+
+ // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
+ // testPathIgnorePatterns: [
+ // "/node_modules/"
+ // ],
+
+ // The regexp pattern or array of patterns that Jest uses to detect test files
+ // testRegex: [],
+
+ // This option allows the use of a custom results processor
+ // testResultsProcessor: undefined,
+
+ // This option allows use of a custom test runner
+ // testRunner: "jest-circus/runner",
+
+ // A map from regular expressions to paths to transformers
+ // transform: undefined,
+
+ // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
+ // transformIgnorePatterns: [
+ // "/node_modules/",
+ // "\\.pnp\\.[^\\/]+$"
+ // ],
+
+ // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
+ // unmockedModulePathPatterns: undefined,
+
+ // Indicates whether each individual test should be reported during the run
+ // verbose: undefined,
+
+ // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode
+ // watchPathIgnorePatterns: [],
+
+ // Whether to use watchman for file crawling
+ // watchman: true,
+};
+
+module.exports = config;
diff --git a/mkdocs.yml b/mkdocs.yml
new file mode 100644
index 0000000..b12b921
--- /dev/null
+++ b/mkdocs.yml
@@ -0,0 +1,169 @@
+---
+edit_uri: edit/main/docs/
+
+site_url: https://githubuniverseworkshops.github.io/github-devsecops-fundamentals
+site_name: GitHub DevSecOps Fundamentals
+site_author: Igwe Kalu
+site_description: GitHub Universe 2023 | Workshop
+
+remote_name: origin
+remote_branch: gh-pages
+
+repo_url: https://github.com/githubuniverseworkshops/github-devsecops-fundamentals/
+repo_name: GitHub Repository
+
+theme:
+ name: material
+
+ icon:
+ logo: simple/githubactions
+ # edit: material/pencil
+ # view: material/eye
+
+ favicon: assets/img/favicon.ico
+
+ locale: en
+
+ palette:
+ # Palette toggle for automatic mode
+ - toggle:
+ icon: material/brightness-auto
+ name: Switch to dark mode
+ media: "(prefers-color-scheme)"
+
+ # Palette toggle for light mode
+ - scheme: default
+ toggle:
+ icon: material/brightness-7
+ name: Switch to system preference
+ media: "(prefers-color-scheme: light)"
+
+ # Palette toggle for dark mode
+ - scheme: slate
+ toggle:
+ icon: material/brightness-4
+ name: Switch to light mode
+ media: "(prefers-color-scheme: dark)"
+
+ features:
+ - content.action.edit
+ - content.action.view
+ - content.code.copy
+ - content.code.annotate
+ - content.tooltips
+ - navigation.expand
+ - navigation.footer
+ - navigation.indexes
+ - navigation.sections
+ - navigation.tabs
+
+plugins:
+ - search
+
+ - minify:
+ minify_html: true
+
+markdown_extensions:
+ - abbr
+ - admonition
+ - attr_list
+ - md_in_html
+
+ - codehilite:
+ linenums: true
+ guess_lang: false
+
+ - toc:
+ permalink: true
+ separator: "-"
+
+ - def_list
+ - footnotes
+ - meta
+
+ - pymdownx.betterem:
+ smart_enable: all
+
+ - pymdownx.caret
+
+ - pymdownx.critic:
+ mode: view
+
+ - pymdownx.details
+
+ - pymdownx.emoji:
+ emoji_index: !!python/name:material.extensions.emoji.twemoji
+ emoji_generator: !!python/name:material.extensions.emoji.to_svg
+
+ - pymdownx.highlight:
+ anchor_linenums: true
+ line_spans: __span
+ pygments_lang_class: true
+
+ - pymdownx.inlinehilite
+ - pymdownx.keys
+ - pymdownx.magiclink
+ - pymdownx.mark
+ - pymdownx.smartsymbols
+
+ - pymdownx.snippets:
+ check_paths: true
+
+ - pymdownx.superfences:
+ custom_fences:
+ - name: mermaid
+ class: mermaid
+ format: !!python/name:pymdownx.superfences.fence_code_format
+
+ - pymdownx.tabbed:
+ alternate_style: true
+
+ - pymdownx.tasklist:
+ custom_checkbox: true
+ clickable_checkbox: true
+
+ - pymdownx.tilde
+
+extra:
+ # Enable footer
+ generator: true
+copyright: >-
+ Copyright © 2023
+
+ GitHub Expert Services
+
+
+extra_javascript:
+ - assets/javascripts/extra.js
+ # KATEX
+ - assets/javascripts/katex.js
+ - https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.7/katex.min.js
+ - https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.7/contrib/auto-render.min.js
+ # Sortable table
+ - https://unpkg.com/tablesort@5.3.0/dist/tablesort.min.js
+ - assets/javascripts/tables.js
+
+extra_css:
+ - https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.7/katex.min.css
+ - assets/stylesheets/extra.css
+
+nav:
+ - Home: index.md
+ - Prerequisites: prerequisites.md
+ - Exercises:
+ - Introduction: exercises/index.md
+ - Collaborate:
+ - exercises/01.collaborate/index.md
+ - exercises/01.collaborate/01.md
+ - exercises/01.collaborate/02.md
+ - Secure the Software Supply Chain:
+ - exercises/02.secure.the.software.supply.chain/index.md
+ - exercises/02.secure.the.software.supply.chain/01.md
+ - exercises/02.secure.the.software.supply.chain/02.md
+ - Automate Release:
+ - exercises/03.automate.release/index.md
+ - exercises/03.automate.release/01.md
+ - exercises/03.automate.release/02.md
+ - āØ Recap:
+ - exercises/recap/index.md
+ - Tetris Game: tetris.game/index.html
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..ad47d05
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,4235 @@
+{
+ "name": "github-devsecops-fundamentals",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "github-devsecops-fundamentals",
+ "version": "1.0.0",
+ "license": "MIT",
+ "devDependencies": {
+ "@playwright/test": "^1.38.1",
+ "@types/node": "^20.8.10",
+ "jest": "^29.7.0",
+ "jest-environment-jsdom": "^29.7.0"
+ }
+ },
+ "node_modules/@ampproject/remapping": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz",
+ "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.0",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.22.13",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz",
+ "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==",
+ "dev": true,
+ "dependencies": {
+ "@babel/highlight": "^7.22.13",
+ "chalk": "^2.4.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/code-frame/node_modules/ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^1.9.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@babel/code-frame/node_modules/chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@babel/code-frame/node_modules/color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "1.1.3"
+ }
+ },
+ "node_modules/@babel/code-frame/node_modules/color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
+ "dev": true
+ },
+ "node_modules/@babel/code-frame/node_modules/escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/@babel/code-frame/node_modules/has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@babel/code-frame/node_modules/supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@babel/compat-data": {
+ "version": "7.22.20",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.20.tgz",
+ "integrity": "sha512-BQYjKbpXjoXwFW5jGqiizJQQT/aC7pFm9Ok1OWssonuguICi264lbgMzRp2ZMmRSlfkX6DsWDDcsrctK8Rwfiw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.23.0",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.0.tgz",
+ "integrity": "sha512-97z/ju/Jy1rZmDxybphrBuI+jtJjFVoz7Mr9yUQVVVi+DNZE333uFQeMOqcCIy1x3WYBIbWftUSLmbNXNT7qFQ==",
+ "dev": true,
+ "dependencies": {
+ "@ampproject/remapping": "^2.2.0",
+ "@babel/code-frame": "^7.22.13",
+ "@babel/generator": "^7.23.0",
+ "@babel/helper-compilation-targets": "^7.22.15",
+ "@babel/helper-module-transforms": "^7.23.0",
+ "@babel/helpers": "^7.23.0",
+ "@babel/parser": "^7.23.0",
+ "@babel/template": "^7.22.15",
+ "@babel/traverse": "^7.23.0",
+ "@babel/types": "^7.23.0",
+ "convert-source-map": "^2.0.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.3",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/babel"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.23.0",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz",
+ "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.23.0",
+ "@jridgewell/gen-mapping": "^0.3.2",
+ "@jridgewell/trace-mapping": "^0.3.17",
+ "jsesc": "^2.5.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets": {
+ "version": "7.22.15",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz",
+ "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/compat-data": "^7.22.9",
+ "@babel/helper-validator-option": "^7.22.15",
+ "browserslist": "^4.21.9",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-environment-visitor": {
+ "version": "7.22.20",
+ "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz",
+ "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-function-name": {
+ "version": "7.23.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz",
+ "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/template": "^7.22.15",
+ "@babel/types": "^7.23.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-hoist-variables": {
+ "version": "7.22.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz",
+ "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.22.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.22.15",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz",
+ "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.22.15"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.23.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.0.tgz",
+ "integrity": "sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-environment-visitor": "^7.22.20",
+ "@babel/helper-module-imports": "^7.22.15",
+ "@babel/helper-simple-access": "^7.22.5",
+ "@babel/helper-split-export-declaration": "^7.22.6",
+ "@babel/helper-validator-identifier": "^7.22.20"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-plugin-utils": {
+ "version": "7.22.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz",
+ "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-simple-access": {
+ "version": "7.22.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz",
+ "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.22.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-split-export-declaration": {
+ "version": "7.22.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz",
+ "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.22.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.22.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz",
+ "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.22.20",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
+ "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.22.15",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz",
+ "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.23.1",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.1.tgz",
+ "integrity": "sha512-chNpneuK18yW5Oxsr+t553UZzzAs3aZnFm4bxhebsNTeshrC95yA7l5yl7GBAG+JG1rF0F7zzD2EixK9mWSDoA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/template": "^7.22.15",
+ "@babel/traverse": "^7.23.0",
+ "@babel/types": "^7.23.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/highlight": {
+ "version": "7.22.20",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz",
+ "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.22.20",
+ "chalk": "^2.4.2",
+ "js-tokens": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/highlight/node_modules/ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^1.9.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@babel/highlight/node_modules/chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@babel/highlight/node_modules/color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "1.1.3"
+ }
+ },
+ "node_modules/@babel/highlight/node_modules/color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
+ "dev": true
+ },
+ "node_modules/@babel/highlight/node_modules/escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/@babel/highlight/node_modules/has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@babel/highlight/node_modules/supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.23.0",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz",
+ "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==",
+ "dev": true,
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-async-generators": {
+ "version": "7.8.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz",
+ "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-bigint": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz",
+ "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-class-properties": {
+ "version": "7.12.13",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz",
+ "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.12.13"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-import-meta": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz",
+ "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-json-strings": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz",
+ "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-jsx": {
+ "version": "7.22.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz",
+ "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.22.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-logical-assignment-operators": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz",
+ "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz",
+ "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-numeric-separator": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz",
+ "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-object-rest-spread": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz",
+ "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-optional-catch-binding": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz",
+ "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-optional-chaining": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz",
+ "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-top-level-await": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz",
+ "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.14.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-typescript": {
+ "version": "7.22.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz",
+ "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.22.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/template": {
+ "version": "7.22.15",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz",
+ "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.22.13",
+ "@babel/parser": "^7.22.15",
+ "@babel/types": "^7.22.15"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.23.2",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz",
+ "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.22.13",
+ "@babel/generator": "^7.23.0",
+ "@babel/helper-environment-visitor": "^7.22.20",
+ "@babel/helper-function-name": "^7.23.0",
+ "@babel/helper-hoist-variables": "^7.22.5",
+ "@babel/helper-split-export-declaration": "^7.22.6",
+ "@babel/parser": "^7.23.0",
+ "@babel/types": "^7.23.0",
+ "debug": "^4.1.0",
+ "globals": "^11.1.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.23.0",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz",
+ "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.22.5",
+ "@babel/helper-validator-identifier": "^7.22.20",
+ "to-fast-properties": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@bcoe/v8-coverage": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz",
+ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
+ "dev": true
+ },
+ "node_modules/@istanbuljs/load-nyc-config": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
+ "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==",
+ "dev": true,
+ "dependencies": {
+ "camelcase": "^5.3.1",
+ "find-up": "^4.1.0",
+ "get-package-type": "^0.1.0",
+ "js-yaml": "^3.13.1",
+ "resolve-from": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@istanbuljs/schema": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
+ "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@jest/console": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz",
+ "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==",
+ "dev": true,
+ "dependencies": {
+ "@jest/types": "^29.6.3",
+ "@types/node": "*",
+ "chalk": "^4.0.0",
+ "jest-message-util": "^29.7.0",
+ "jest-util": "^29.7.0",
+ "slash": "^3.0.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/@jest/core": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz",
+ "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==",
+ "dev": true,
+ "dependencies": {
+ "@jest/console": "^29.7.0",
+ "@jest/reporters": "^29.7.0",
+ "@jest/test-result": "^29.7.0",
+ "@jest/transform": "^29.7.0",
+ "@jest/types": "^29.6.3",
+ "@types/node": "*",
+ "ansi-escapes": "^4.2.1",
+ "chalk": "^4.0.0",
+ "ci-info": "^3.2.0",
+ "exit": "^0.1.2",
+ "graceful-fs": "^4.2.9",
+ "jest-changed-files": "^29.7.0",
+ "jest-config": "^29.7.0",
+ "jest-haste-map": "^29.7.0",
+ "jest-message-util": "^29.7.0",
+ "jest-regex-util": "^29.6.3",
+ "jest-resolve": "^29.7.0",
+ "jest-resolve-dependencies": "^29.7.0",
+ "jest-runner": "^29.7.0",
+ "jest-runtime": "^29.7.0",
+ "jest-snapshot": "^29.7.0",
+ "jest-util": "^29.7.0",
+ "jest-validate": "^29.7.0",
+ "jest-watcher": "^29.7.0",
+ "micromatch": "^4.0.4",
+ "pretty-format": "^29.7.0",
+ "slash": "^3.0.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ },
+ "peerDependencies": {
+ "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
+ },
+ "peerDependenciesMeta": {
+ "node-notifier": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@jest/environment": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz",
+ "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==",
+ "dev": true,
+ "dependencies": {
+ "@jest/fake-timers": "^29.7.0",
+ "@jest/types": "^29.6.3",
+ "@types/node": "*",
+ "jest-mock": "^29.7.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/@jest/expect": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz",
+ "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==",
+ "dev": true,
+ "dependencies": {
+ "expect": "^29.7.0",
+ "jest-snapshot": "^29.7.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/@jest/expect-utils": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz",
+ "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==",
+ "dev": true,
+ "dependencies": {
+ "jest-get-type": "^29.6.3"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/@jest/fake-timers": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz",
+ "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==",
+ "dev": true,
+ "dependencies": {
+ "@jest/types": "^29.6.3",
+ "@sinonjs/fake-timers": "^10.0.2",
+ "@types/node": "*",
+ "jest-message-util": "^29.7.0",
+ "jest-mock": "^29.7.0",
+ "jest-util": "^29.7.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/@jest/globals": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz",
+ "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==",
+ "dev": true,
+ "dependencies": {
+ "@jest/environment": "^29.7.0",
+ "@jest/expect": "^29.7.0",
+ "@jest/types": "^29.6.3",
+ "jest-mock": "^29.7.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/@jest/reporters": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz",
+ "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==",
+ "dev": true,
+ "dependencies": {
+ "@bcoe/v8-coverage": "^0.2.3",
+ "@jest/console": "^29.7.0",
+ "@jest/test-result": "^29.7.0",
+ "@jest/transform": "^29.7.0",
+ "@jest/types": "^29.6.3",
+ "@jridgewell/trace-mapping": "^0.3.18",
+ "@types/node": "*",
+ "chalk": "^4.0.0",
+ "collect-v8-coverage": "^1.0.0",
+ "exit": "^0.1.2",
+ "glob": "^7.1.3",
+ "graceful-fs": "^4.2.9",
+ "istanbul-lib-coverage": "^3.0.0",
+ "istanbul-lib-instrument": "^6.0.0",
+ "istanbul-lib-report": "^3.0.0",
+ "istanbul-lib-source-maps": "^4.0.0",
+ "istanbul-reports": "^3.1.3",
+ "jest-message-util": "^29.7.0",
+ "jest-util": "^29.7.0",
+ "jest-worker": "^29.7.0",
+ "slash": "^3.0.0",
+ "string-length": "^4.0.1",
+ "strip-ansi": "^6.0.0",
+ "v8-to-istanbul": "^9.0.1"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ },
+ "peerDependencies": {
+ "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
+ },
+ "peerDependenciesMeta": {
+ "node-notifier": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@jest/schemas": {
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz",
+ "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==",
+ "dev": true,
+ "dependencies": {
+ "@sinclair/typebox": "^0.27.8"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/@jest/source-map": {
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz",
+ "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/trace-mapping": "^0.3.18",
+ "callsites": "^3.0.0",
+ "graceful-fs": "^4.2.9"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/@jest/test-result": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz",
+ "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==",
+ "dev": true,
+ "dependencies": {
+ "@jest/console": "^29.7.0",
+ "@jest/types": "^29.6.3",
+ "@types/istanbul-lib-coverage": "^2.0.0",
+ "collect-v8-coverage": "^1.0.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/@jest/test-sequencer": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz",
+ "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==",
+ "dev": true,
+ "dependencies": {
+ "@jest/test-result": "^29.7.0",
+ "graceful-fs": "^4.2.9",
+ "jest-haste-map": "^29.7.0",
+ "slash": "^3.0.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/@jest/transform": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz",
+ "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/core": "^7.11.6",
+ "@jest/types": "^29.6.3",
+ "@jridgewell/trace-mapping": "^0.3.18",
+ "babel-plugin-istanbul": "^6.1.1",
+ "chalk": "^4.0.0",
+ "convert-source-map": "^2.0.0",
+ "fast-json-stable-stringify": "^2.1.0",
+ "graceful-fs": "^4.2.9",
+ "jest-haste-map": "^29.7.0",
+ "jest-regex-util": "^29.6.3",
+ "jest-util": "^29.7.0",
+ "micromatch": "^4.0.4",
+ "pirates": "^4.0.4",
+ "slash": "^3.0.0",
+ "write-file-atomic": "^4.0.2"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/@jest/types": {
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz",
+ "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==",
+ "dev": true,
+ "dependencies": {
+ "@jest/schemas": "^29.6.3",
+ "@types/istanbul-lib-coverage": "^2.0.0",
+ "@types/istanbul-reports": "^3.0.0",
+ "@types/node": "*",
+ "@types/yargs": "^17.0.8",
+ "chalk": "^4.0.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
+ "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/set-array": "^1.0.1",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz",
+ "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/set-array": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
+ "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.4.15",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
+ "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
+ "dev": true
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.19",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz",
+ "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@playwright/test": {
+ "version": "1.38.1",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.38.1.tgz",
+ "integrity": "sha512-NqRp8XMwj3AK+zKLbZShl0r/9wKgzqI/527bkptKXomtuo+dOjU9NdMASQ8DNC9z9zLOMbG53T4eihYr3XR+BQ==",
+ "dev": true,
+ "dependencies": {
+ "playwright": "1.38.1"
+ },
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@sinclair/typebox": {
+ "version": "0.27.8",
+ "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz",
+ "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==",
+ "dev": true
+ },
+ "node_modules/@sinonjs/commons": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz",
+ "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==",
+ "dev": true,
+ "dependencies": {
+ "type-detect": "4.0.8"
+ }
+ },
+ "node_modules/@sinonjs/fake-timers": {
+ "version": "10.3.0",
+ "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz",
+ "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==",
+ "dev": true,
+ "dependencies": {
+ "@sinonjs/commons": "^3.0.0"
+ }
+ },
+ "node_modules/@tootallnate/once": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
+ "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==",
+ "dev": true,
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@types/babel__core": {
+ "version": "7.20.2",
+ "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.2.tgz",
+ "integrity": "sha512-pNpr1T1xLUc2l3xJKuPtsEky3ybxN3m4fJkknfIpTCTfIZCDW57oAg+EfCgIIp2rvCe0Wn++/FfodDS4YXxBwA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/parser": "^7.20.7",
+ "@babel/types": "^7.20.7",
+ "@types/babel__generator": "*",
+ "@types/babel__template": "*",
+ "@types/babel__traverse": "*"
+ }
+ },
+ "node_modules/@types/babel__generator": {
+ "version": "7.6.5",
+ "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.5.tgz",
+ "integrity": "sha512-h9yIuWbJKdOPLJTbmSpPzkF67e659PbQDba7ifWm5BJ8xTv+sDmS7rFmywkWOvXedGTivCdeGSIIX8WLcRTz8w==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__template": {
+ "version": "7.4.2",
+ "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.2.tgz",
+ "integrity": "sha512-/AVzPICMhMOMYoSx9MoKpGDKdBRsIXMNByh1PXSZoa+v6ZoLa8xxtsT/uLQ/NJm0XVAWl/BvId4MlDeXJaeIZQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/parser": "^7.1.0",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__traverse": {
+ "version": "7.20.2",
+ "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.2.tgz",
+ "integrity": "sha512-ojlGK1Hsfce93J0+kn3H5R73elidKUaZonirN33GSmgTUMpzI/MIFfSpF3haANe3G1bEBS9/9/QEqwTzwqFsKw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.20.7"
+ }
+ },
+ "node_modules/@types/graceful-fs": {
+ "version": "4.1.7",
+ "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.7.tgz",
+ "integrity": "sha512-MhzcwU8aUygZroVwL2jeYk6JisJrPl/oov/gsgGCue9mkgl9wjGbzReYQClxiUgFDnib9FuHqTndccKeZKxTRw==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/istanbul-lib-coverage": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz",
+ "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==",
+ "dev": true
+ },
+ "node_modules/@types/istanbul-lib-report": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz",
+ "integrity": "sha512-gPQuzaPR5h/djlAv2apEG1HVOyj1IUs7GpfMZixU0/0KXT3pm64ylHuMUI1/Akh+sq/iikxg6Z2j+fcMDXaaTQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/istanbul-lib-coverage": "*"
+ }
+ },
+ "node_modules/@types/istanbul-reports": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.2.tgz",
+ "integrity": "sha512-kv43F9eb3Lhj+lr/Hn6OcLCs/sSM8bt+fIaP11rCYngfV6NVjzWXJ17owQtDQTL9tQ8WSLUrGsSJ6rJz0F1w1A==",
+ "dev": true,
+ "dependencies": {
+ "@types/istanbul-lib-report": "*"
+ }
+ },
+ "node_modules/@types/jsdom": {
+ "version": "20.0.1",
+ "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz",
+ "integrity": "sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*",
+ "@types/tough-cookie": "*",
+ "parse5": "^7.0.0"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "20.8.10",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.10.tgz",
+ "integrity": "sha512-TlgT8JntpcbmKUFzjhsyhGfP2fsiz1Mv56im6enJ905xG1DAYesxJaeSbGqQmAw8OWPdhyJGhGSQGKRNJ45u9w==",
+ "dev": true,
+ "dependencies": {
+ "undici-types": "~5.26.4"
+ }
+ },
+ "node_modules/@types/stack-utils": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz",
+ "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==",
+ "dev": true
+ },
+ "node_modules/@types/tough-cookie": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.3.tgz",
+ "integrity": "sha512-THo502dA5PzG/sfQH+42Lw3fvmYkceefOspdCwpHRul8ik2Jv1K8I5OZz1AT3/rs46kwgMCe9bSBmDLYkkOMGg==",
+ "dev": true
+ },
+ "node_modules/@types/yargs": {
+ "version": "17.0.26",
+ "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.26.tgz",
+ "integrity": "sha512-Y3vDy2X6zw/ZCumcwLpdhM5L7jmyGpmBCTYMHDLqT2IKVMYRRLdv6ZakA+wxhra6Z/3bwhNbNl9bDGXaFU+6rw==",
+ "dev": true,
+ "dependencies": {
+ "@types/yargs-parser": "*"
+ }
+ },
+ "node_modules/@types/yargs-parser": {
+ "version": "21.0.1",
+ "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.1.tgz",
+ "integrity": "sha512-axdPBuLuEJt0c4yI5OZssC19K2Mq1uKdrfZBzuxLvaztgqUtFYZUNw7lETExPYJR9jdEoIg4mb7RQKRQzOkeGQ==",
+ "dev": true
+ },
+ "node_modules/abab": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
+ "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==",
+ "dev": true
+ },
+ "node_modules/acorn": {
+ "version": "8.10.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz",
+ "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==",
+ "dev": true,
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-globals": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz",
+ "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==",
+ "dev": true,
+ "dependencies": {
+ "acorn": "^8.1.0",
+ "acorn-walk": "^8.0.2"
+ }
+ },
+ "node_modules/acorn-walk": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
+ "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/agent-base": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+ "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+ "dev": true,
+ "dependencies": {
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6.0.0"
+ }
+ },
+ "node_modules/ansi-escapes": {
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
+ "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
+ "dev": true,
+ "dependencies": {
+ "type-fest": "^0.21.3"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/anymatch": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "dev": true,
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
+ "dependencies": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+ "dev": true
+ },
+ "node_modules/babel-jest": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz",
+ "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==",
+ "dev": true,
+ "dependencies": {
+ "@jest/transform": "^29.7.0",
+ "@types/babel__core": "^7.1.14",
+ "babel-plugin-istanbul": "^6.1.1",
+ "babel-preset-jest": "^29.6.3",
+ "chalk": "^4.0.0",
+ "graceful-fs": "^4.2.9",
+ "slash": "^3.0.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.8.0"
+ }
+ },
+ "node_modules/babel-plugin-istanbul": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz",
+ "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@istanbuljs/load-nyc-config": "^1.0.0",
+ "@istanbuljs/schema": "^0.1.2",
+ "istanbul-lib-instrument": "^5.0.4",
+ "test-exclude": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz",
+ "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/core": "^7.12.3",
+ "@babel/parser": "^7.14.7",
+ "@istanbuljs/schema": "^0.1.2",
+ "istanbul-lib-coverage": "^3.2.0",
+ "semver": "^6.3.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/babel-plugin-jest-hoist": {
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz",
+ "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/template": "^7.3.3",
+ "@babel/types": "^7.3.3",
+ "@types/babel__core": "^7.1.14",
+ "@types/babel__traverse": "^7.0.6"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/babel-preset-current-node-syntax": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz",
+ "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/plugin-syntax-async-generators": "^7.8.4",
+ "@babel/plugin-syntax-bigint": "^7.8.3",
+ "@babel/plugin-syntax-class-properties": "^7.8.3",
+ "@babel/plugin-syntax-import-meta": "^7.8.3",
+ "@babel/plugin-syntax-json-strings": "^7.8.3",
+ "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3",
+ "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3",
+ "@babel/plugin-syntax-numeric-separator": "^7.8.3",
+ "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
+ "@babel/plugin-syntax-optional-catch-binding": "^7.8.3",
+ "@babel/plugin-syntax-optional-chaining": "^7.8.3",
+ "@babel/plugin-syntax-top-level-await": "^7.8.3"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/babel-preset-jest": {
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz",
+ "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==",
+ "dev": true,
+ "dependencies": {
+ "babel-plugin-jest-hoist": "^29.6.3",
+ "babel-preset-current-node-syntax": "^1.0.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+ "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "dev": true,
+ "dependencies": {
+ "fill-range": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/browserslist": {
+ "version": "4.22.1",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz",
+ "integrity": "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "caniuse-lite": "^1.0.30001541",
+ "electron-to-chromium": "^1.4.535",
+ "node-releases": "^2.0.13",
+ "update-browserslist-db": "^1.0.13"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/bser": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz",
+ "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==",
+ "dev": true,
+ "dependencies": {
+ "node-int64": "^0.4.0"
+ }
+ },
+ "node_modules/buffer-from": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
+ "dev": true
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001546",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001546.tgz",
+ "integrity": "sha512-zvtSJwuQFpewSyRrI3AsftF6rM0X80mZkChIt1spBGEvRglCrjTniXvinc8JKRoqTwXAgvqTImaN9igfSMtUBw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ]
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/char-regex": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz",
+ "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/ci-info": {
+ "version": "3.9.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz",
+ "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/sibiraj-s"
+ }
+ ],
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cjs-module-lexer": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz",
+ "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==",
+ "dev": true
+ },
+ "node_modules/cliui": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "dev": true,
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/co": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
+ "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==",
+ "dev": true,
+ "engines": {
+ "iojs": ">= 1.0.0",
+ "node": ">= 0.12.0"
+ }
+ },
+ "node_modules/collect-v8-coverage": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz",
+ "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==",
+ "dev": true
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "dev": true,
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true
+ },
+ "node_modules/convert-source-map": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+ "dev": true
+ },
+ "node_modules/create-jest": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz",
+ "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==",
+ "dev": true,
+ "dependencies": {
+ "@jest/types": "^29.6.3",
+ "chalk": "^4.0.0",
+ "exit": "^0.1.2",
+ "graceful-fs": "^4.2.9",
+ "jest-config": "^29.7.0",
+ "jest-util": "^29.7.0",
+ "prompts": "^2.0.1"
+ },
+ "bin": {
+ "create-jest": "bin/create-jest.js"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+ "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+ "dev": true,
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/cssom": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz",
+ "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==",
+ "dev": true
+ },
+ "node_modules/cssstyle": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz",
+ "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==",
+ "dev": true,
+ "dependencies": {
+ "cssom": "~0.3.6"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cssstyle/node_modules/cssom": {
+ "version": "0.3.8",
+ "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz",
+ "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==",
+ "dev": true
+ },
+ "node_modules/data-urls": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz",
+ "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==",
+ "dev": true,
+ "dependencies": {
+ "abab": "^2.0.6",
+ "whatwg-mimetype": "^3.0.0",
+ "whatwg-url": "^11.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "dev": true,
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/decimal.js": {
+ "version": "10.4.3",
+ "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz",
+ "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==",
+ "dev": true
+ },
+ "node_modules/dedent": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz",
+ "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==",
+ "dev": true,
+ "peerDependencies": {
+ "babel-plugin-macros": "^3.1.0"
+ },
+ "peerDependenciesMeta": {
+ "babel-plugin-macros": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/deepmerge": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
+ "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/detect-newline": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz",
+ "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/diff-sequences": {
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz",
+ "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==",
+ "dev": true,
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/domexception": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz",
+ "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==",
+ "dev": true,
+ "dependencies": {
+ "webidl-conversions": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.4.543",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.543.tgz",
+ "integrity": "sha512-t2ZP4AcGE0iKCCQCBx/K2426crYdxD3YU6l0uK2EO3FZH0pbC4pFz/sZm2ruZsND6hQBTcDWWlo/MLpiOdif5g==",
+ "dev": true
+ },
+ "node_modules/emittery": {
+ "version": "0.13.1",
+ "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz",
+ "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/emittery?sponsor=1"
+ }
+ },
+ "node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
+ },
+ "node_modules/entities": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+ "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/error-ex": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+ "dev": true,
+ "dependencies": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
+ "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
+ "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/escodegen": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz",
+ "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==",
+ "dev": true,
+ "dependencies": {
+ "esprima": "^4.0.1",
+ "estraverse": "^5.2.0",
+ "esutils": "^2.0.2"
+ },
+ "bin": {
+ "escodegen": "bin/escodegen.js",
+ "esgenerate": "bin/esgenerate.js"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "optionalDependencies": {
+ "source-map": "~0.6.1"
+ }
+ },
+ "node_modules/esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true,
+ "bin": {
+ "esparse": "bin/esparse.js",
+ "esvalidate": "bin/esvalidate.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/execa": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
+ "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
+ "dev": true,
+ "dependencies": {
+ "cross-spawn": "^7.0.3",
+ "get-stream": "^6.0.0",
+ "human-signals": "^2.1.0",
+ "is-stream": "^2.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^4.0.1",
+ "onetime": "^5.1.2",
+ "signal-exit": "^3.0.3",
+ "strip-final-newline": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/execa?sponsor=1"
+ }
+ },
+ "node_modules/exit": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
+ "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/expect": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz",
+ "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==",
+ "dev": true,
+ "dependencies": {
+ "@jest/expect-utils": "^29.7.0",
+ "jest-get-type": "^29.6.3",
+ "jest-matcher-utils": "^29.7.0",
+ "jest-message-util": "^29.7.0",
+ "jest-util": "^29.7.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true
+ },
+ "node_modules/fb-watchman": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz",
+ "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==",
+ "dev": true,
+ "dependencies": {
+ "bser": "2.1.1"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+ "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "dev": true,
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+ "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+ "dev": true,
+ "dependencies": {
+ "locate-path": "^5.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/form-data": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+ "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+ "dev": true,
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+ "dev": true
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/gensync": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dev": true,
+ "engines": {
+ "node": "6.* || 8.* || >= 10.*"
+ }
+ },
+ "node_modules/get-package-type": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz",
+ "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/get-stream": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
+ "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/globals": {
+ "version": "11.12.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
+ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "dev": true
+ },
+ "node_modules/has": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/has/-/has-1.0.4.tgz",
+ "integrity": "sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/html-encoding-sniffer": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz",
+ "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==",
+ "dev": true,
+ "dependencies": {
+ "whatwg-encoding": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/html-escaper": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
+ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
+ "dev": true
+ },
+ "node_modules/http-proxy-agent": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
+ "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==",
+ "dev": true,
+ "dependencies": {
+ "@tootallnate/once": "2",
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/https-proxy-agent": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
+ "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
+ "dev": true,
+ "dependencies": {
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/human-signals": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
+ "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
+ "dev": true,
+ "engines": {
+ "node": ">=10.17.0"
+ }
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+ "dev": true,
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/import-local": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz",
+ "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==",
+ "dev": true,
+ "dependencies": {
+ "pkg-dir": "^4.2.0",
+ "resolve-cwd": "^3.0.0"
+ },
+ "bin": {
+ "import-local-fixture": "fixtures/cli.js"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "dev": true,
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true
+ },
+ "node_modules/is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
+ "dev": true
+ },
+ "node_modules/is-core-module": {
+ "version": "2.13.0",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz",
+ "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==",
+ "dev": true,
+ "dependencies": {
+ "has": "^1.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-generator-fn": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz",
+ "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/is-potential-custom-element-name": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
+ "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==",
+ "dev": true
+ },
+ "node_modules/is-stream": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
+ "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true
+ },
+ "node_modules/istanbul-lib-coverage": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz",
+ "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/istanbul-lib-instrument": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.1.tgz",
+ "integrity": "sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/core": "^7.12.3",
+ "@babel/parser": "^7.14.7",
+ "@istanbuljs/schema": "^0.1.2",
+ "istanbul-lib-coverage": "^3.2.0",
+ "semver": "^7.5.4"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/istanbul-lib-instrument/node_modules/lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "dev": true,
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/istanbul-lib-instrument/node_modules/semver": {
+ "version": "7.5.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
+ "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
+ "dev": true,
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/istanbul-lib-instrument/node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
+ },
+ "node_modules/istanbul-lib-report": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz",
+ "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==",
+ "dev": true,
+ "dependencies": {
+ "istanbul-lib-coverage": "^3.0.0",
+ "make-dir": "^4.0.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/istanbul-lib-source-maps": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz",
+ "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==",
+ "dev": true,
+ "dependencies": {
+ "debug": "^4.1.1",
+ "istanbul-lib-coverage": "^3.0.0",
+ "source-map": "^0.6.1"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/istanbul-reports": {
+ "version": "3.1.6",
+ "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz",
+ "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==",
+ "dev": true,
+ "dependencies": {
+ "html-escaper": "^2.0.0",
+ "istanbul-lib-report": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/jest": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz",
+ "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==",
+ "dev": true,
+ "dependencies": {
+ "@jest/core": "^29.7.0",
+ "@jest/types": "^29.6.3",
+ "import-local": "^3.0.2",
+ "jest-cli": "^29.7.0"
+ },
+ "bin": {
+ "jest": "bin/jest.js"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ },
+ "peerDependencies": {
+ "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
+ },
+ "peerDependenciesMeta": {
+ "node-notifier": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/jest-changed-files": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz",
+ "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==",
+ "dev": true,
+ "dependencies": {
+ "execa": "^5.0.0",
+ "jest-util": "^29.7.0",
+ "p-limit": "^3.1.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-circus": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz",
+ "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==",
+ "dev": true,
+ "dependencies": {
+ "@jest/environment": "^29.7.0",
+ "@jest/expect": "^29.7.0",
+ "@jest/test-result": "^29.7.0",
+ "@jest/types": "^29.6.3",
+ "@types/node": "*",
+ "chalk": "^4.0.0",
+ "co": "^4.6.0",
+ "dedent": "^1.0.0",
+ "is-generator-fn": "^2.0.0",
+ "jest-each": "^29.7.0",
+ "jest-matcher-utils": "^29.7.0",
+ "jest-message-util": "^29.7.0",
+ "jest-runtime": "^29.7.0",
+ "jest-snapshot": "^29.7.0",
+ "jest-util": "^29.7.0",
+ "p-limit": "^3.1.0",
+ "pretty-format": "^29.7.0",
+ "pure-rand": "^6.0.0",
+ "slash": "^3.0.0",
+ "stack-utils": "^2.0.3"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-cli": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz",
+ "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==",
+ "dev": true,
+ "dependencies": {
+ "@jest/core": "^29.7.0",
+ "@jest/test-result": "^29.7.0",
+ "@jest/types": "^29.6.3",
+ "chalk": "^4.0.0",
+ "create-jest": "^29.7.0",
+ "exit": "^0.1.2",
+ "import-local": "^3.0.2",
+ "jest-config": "^29.7.0",
+ "jest-util": "^29.7.0",
+ "jest-validate": "^29.7.0",
+ "yargs": "^17.3.1"
+ },
+ "bin": {
+ "jest": "bin/jest.js"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ },
+ "peerDependencies": {
+ "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
+ },
+ "peerDependenciesMeta": {
+ "node-notifier": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/jest-config": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz",
+ "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/core": "^7.11.6",
+ "@jest/test-sequencer": "^29.7.0",
+ "@jest/types": "^29.6.3",
+ "babel-jest": "^29.7.0",
+ "chalk": "^4.0.0",
+ "ci-info": "^3.2.0",
+ "deepmerge": "^4.2.2",
+ "glob": "^7.1.3",
+ "graceful-fs": "^4.2.9",
+ "jest-circus": "^29.7.0",
+ "jest-environment-node": "^29.7.0",
+ "jest-get-type": "^29.6.3",
+ "jest-regex-util": "^29.6.3",
+ "jest-resolve": "^29.7.0",
+ "jest-runner": "^29.7.0",
+ "jest-util": "^29.7.0",
+ "jest-validate": "^29.7.0",
+ "micromatch": "^4.0.4",
+ "parse-json": "^5.2.0",
+ "pretty-format": "^29.7.0",
+ "slash": "^3.0.0",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ },
+ "peerDependencies": {
+ "@types/node": "*",
+ "ts-node": ">=9.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "ts-node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/jest-diff": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz",
+ "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==",
+ "dev": true,
+ "dependencies": {
+ "chalk": "^4.0.0",
+ "diff-sequences": "^29.6.3",
+ "jest-get-type": "^29.6.3",
+ "pretty-format": "^29.7.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-docblock": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz",
+ "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==",
+ "dev": true,
+ "dependencies": {
+ "detect-newline": "^3.0.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-each": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz",
+ "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==",
+ "dev": true,
+ "dependencies": {
+ "@jest/types": "^29.6.3",
+ "chalk": "^4.0.0",
+ "jest-get-type": "^29.6.3",
+ "jest-util": "^29.7.0",
+ "pretty-format": "^29.7.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-environment-jsdom": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz",
+ "integrity": "sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==",
+ "dev": true,
+ "dependencies": {
+ "@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.7.0",
+ "jest-util": "^29.7.0",
+ "jsdom": "^20.0.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ },
+ "peerDependencies": {
+ "canvas": "^2.5.0"
+ },
+ "peerDependenciesMeta": {
+ "canvas": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/jest-environment-node": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz",
+ "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==",
+ "dev": true,
+ "dependencies": {
+ "@jest/environment": "^29.7.0",
+ "@jest/fake-timers": "^29.7.0",
+ "@jest/types": "^29.6.3",
+ "@types/node": "*",
+ "jest-mock": "^29.7.0",
+ "jest-util": "^29.7.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-get-type": {
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz",
+ "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==",
+ "dev": true,
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-haste-map": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz",
+ "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==",
+ "dev": true,
+ "dependencies": {
+ "@jest/types": "^29.6.3",
+ "@types/graceful-fs": "^4.1.3",
+ "@types/node": "*",
+ "anymatch": "^3.0.3",
+ "fb-watchman": "^2.0.0",
+ "graceful-fs": "^4.2.9",
+ "jest-regex-util": "^29.6.3",
+ "jest-util": "^29.7.0",
+ "jest-worker": "^29.7.0",
+ "micromatch": "^4.0.4",
+ "walker": "^1.0.8"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "^2.3.2"
+ }
+ },
+ "node_modules/jest-leak-detector": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz",
+ "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==",
+ "dev": true,
+ "dependencies": {
+ "jest-get-type": "^29.6.3",
+ "pretty-format": "^29.7.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-matcher-utils": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz",
+ "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==",
+ "dev": true,
+ "dependencies": {
+ "chalk": "^4.0.0",
+ "jest-diff": "^29.7.0",
+ "jest-get-type": "^29.6.3",
+ "pretty-format": "^29.7.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-message-util": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz",
+ "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.12.13",
+ "@jest/types": "^29.6.3",
+ "@types/stack-utils": "^2.0.0",
+ "chalk": "^4.0.0",
+ "graceful-fs": "^4.2.9",
+ "micromatch": "^4.0.4",
+ "pretty-format": "^29.7.0",
+ "slash": "^3.0.0",
+ "stack-utils": "^2.0.3"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-mock": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz",
+ "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==",
+ "dev": true,
+ "dependencies": {
+ "@jest/types": "^29.6.3",
+ "@types/node": "*",
+ "jest-util": "^29.7.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-pnp-resolver": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz",
+ "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ },
+ "peerDependencies": {
+ "jest-resolve": "*"
+ },
+ "peerDependenciesMeta": {
+ "jest-resolve": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/jest-regex-util": {
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz",
+ "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==",
+ "dev": true,
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-resolve": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz",
+ "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==",
+ "dev": true,
+ "dependencies": {
+ "chalk": "^4.0.0",
+ "graceful-fs": "^4.2.9",
+ "jest-haste-map": "^29.7.0",
+ "jest-pnp-resolver": "^1.2.2",
+ "jest-util": "^29.7.0",
+ "jest-validate": "^29.7.0",
+ "resolve": "^1.20.0",
+ "resolve.exports": "^2.0.0",
+ "slash": "^3.0.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-resolve-dependencies": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz",
+ "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==",
+ "dev": true,
+ "dependencies": {
+ "jest-regex-util": "^29.6.3",
+ "jest-snapshot": "^29.7.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-runner": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz",
+ "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==",
+ "dev": true,
+ "dependencies": {
+ "@jest/console": "^29.7.0",
+ "@jest/environment": "^29.7.0",
+ "@jest/test-result": "^29.7.0",
+ "@jest/transform": "^29.7.0",
+ "@jest/types": "^29.6.3",
+ "@types/node": "*",
+ "chalk": "^4.0.0",
+ "emittery": "^0.13.1",
+ "graceful-fs": "^4.2.9",
+ "jest-docblock": "^29.7.0",
+ "jest-environment-node": "^29.7.0",
+ "jest-haste-map": "^29.7.0",
+ "jest-leak-detector": "^29.7.0",
+ "jest-message-util": "^29.7.0",
+ "jest-resolve": "^29.7.0",
+ "jest-runtime": "^29.7.0",
+ "jest-util": "^29.7.0",
+ "jest-watcher": "^29.7.0",
+ "jest-worker": "^29.7.0",
+ "p-limit": "^3.1.0",
+ "source-map-support": "0.5.13"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-runtime": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz",
+ "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==",
+ "dev": true,
+ "dependencies": {
+ "@jest/environment": "^29.7.0",
+ "@jest/fake-timers": "^29.7.0",
+ "@jest/globals": "^29.7.0",
+ "@jest/source-map": "^29.6.3",
+ "@jest/test-result": "^29.7.0",
+ "@jest/transform": "^29.7.0",
+ "@jest/types": "^29.6.3",
+ "@types/node": "*",
+ "chalk": "^4.0.0",
+ "cjs-module-lexer": "^1.0.0",
+ "collect-v8-coverage": "^1.0.0",
+ "glob": "^7.1.3",
+ "graceful-fs": "^4.2.9",
+ "jest-haste-map": "^29.7.0",
+ "jest-message-util": "^29.7.0",
+ "jest-mock": "^29.7.0",
+ "jest-regex-util": "^29.6.3",
+ "jest-resolve": "^29.7.0",
+ "jest-snapshot": "^29.7.0",
+ "jest-util": "^29.7.0",
+ "slash": "^3.0.0",
+ "strip-bom": "^4.0.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-snapshot": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz",
+ "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/core": "^7.11.6",
+ "@babel/generator": "^7.7.2",
+ "@babel/plugin-syntax-jsx": "^7.7.2",
+ "@babel/plugin-syntax-typescript": "^7.7.2",
+ "@babel/types": "^7.3.3",
+ "@jest/expect-utils": "^29.7.0",
+ "@jest/transform": "^29.7.0",
+ "@jest/types": "^29.6.3",
+ "babel-preset-current-node-syntax": "^1.0.0",
+ "chalk": "^4.0.0",
+ "expect": "^29.7.0",
+ "graceful-fs": "^4.2.9",
+ "jest-diff": "^29.7.0",
+ "jest-get-type": "^29.6.3",
+ "jest-matcher-utils": "^29.7.0",
+ "jest-message-util": "^29.7.0",
+ "jest-util": "^29.7.0",
+ "natural-compare": "^1.4.0",
+ "pretty-format": "^29.7.0",
+ "semver": "^7.5.3"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-snapshot/node_modules/lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "dev": true,
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/jest-snapshot/node_modules/semver": {
+ "version": "7.5.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
+ "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
+ "dev": true,
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/jest-snapshot/node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
+ },
+ "node_modules/jest-util": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz",
+ "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==",
+ "dev": true,
+ "dependencies": {
+ "@jest/types": "^29.6.3",
+ "@types/node": "*",
+ "chalk": "^4.0.0",
+ "ci-info": "^3.2.0",
+ "graceful-fs": "^4.2.9",
+ "picomatch": "^2.2.3"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-validate": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz",
+ "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==",
+ "dev": true,
+ "dependencies": {
+ "@jest/types": "^29.6.3",
+ "camelcase": "^6.2.0",
+ "chalk": "^4.0.0",
+ "jest-get-type": "^29.6.3",
+ "leven": "^3.1.0",
+ "pretty-format": "^29.7.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-validate/node_modules/camelcase": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
+ "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/jest-watcher": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz",
+ "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==",
+ "dev": true,
+ "dependencies": {
+ "@jest/test-result": "^29.7.0",
+ "@jest/types": "^29.6.3",
+ "@types/node": "*",
+ "ansi-escapes": "^4.2.1",
+ "chalk": "^4.0.0",
+ "emittery": "^0.13.1",
+ "jest-util": "^29.7.0",
+ "string-length": "^4.0.1"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-worker": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz",
+ "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*",
+ "jest-util": "^29.7.0",
+ "merge-stream": "^2.0.0",
+ "supports-color": "^8.0.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-worker/node_modules/supports-color": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
+ }
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true
+ },
+ "node_modules/js-yaml": {
+ "version": "3.14.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
+ "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
+ "dev": true,
+ "dependencies": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/jsdom": {
+ "version": "20.0.3",
+ "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz",
+ "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==",
+ "dev": true,
+ "dependencies": {
+ "abab": "^2.0.6",
+ "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.2",
+ "domexception": "^4.0.0",
+ "escodegen": "^2.0.0",
+ "form-data": "^4.0.0",
+ "html-encoding-sniffer": "^3.0.0",
+ "http-proxy-agent": "^5.0.0",
+ "https-proxy-agent": "^5.0.1",
+ "is-potential-custom-element-name": "^1.0.1",
+ "nwsapi": "^2.2.2",
+ "parse5": "^7.1.1",
+ "saxes": "^6.0.0",
+ "symbol-tree": "^3.2.4",
+ "tough-cookie": "^4.1.2",
+ "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.11.0",
+ "xml-name-validator": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "peerDependencies": {
+ "canvas": "^2.5.0"
+ },
+ "peerDependenciesMeta": {
+ "canvas": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/jsesc": {
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
+ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
+ "dev": true,
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/json-parse-even-better-errors": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
+ "dev": true
+ },
+ "node_modules/json5": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "dev": true,
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/kleur": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
+ "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/leven": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
+ "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/lines-and-columns": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
+ "dev": true
+ },
+ "node_modules/locate-path": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+ "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+ "dev": true,
+ "dependencies": {
+ "p-locate": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "dependencies": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "node_modules/make-dir": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz",
+ "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==",
+ "dev": true,
+ "dependencies": {
+ "semver": "^7.5.3"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/make-dir/node_modules/lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "dev": true,
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/make-dir/node_modules/semver": {
+ "version": "7.5.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
+ "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
+ "dev": true,
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/make-dir/node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
+ },
+ "node_modules/makeerror": {
+ "version": "1.0.12",
+ "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz",
+ "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==",
+ "dev": true,
+ "dependencies": {
+ "tmpl": "1.0.5"
+ }
+ },
+ "node_modules/merge-stream": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
+ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
+ "dev": true
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
+ "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
+ "dev": true,
+ "dependencies": {
+ "braces": "^3.0.2",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dev": true,
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mimic-fn": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true
+ },
+ "node_modules/node-int64": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
+ "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==",
+ "dev": true
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.13",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz",
+ "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==",
+ "dev": true
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/npm-run-path": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
+ "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
+ "dev": true,
+ "dependencies": {
+ "path-key": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/nwsapi": {
+ "version": "2.2.7",
+ "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz",
+ "integrity": "sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==",
+ "dev": true
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dev": true,
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/onetime": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+ "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+ "dev": true,
+ "dependencies": {
+ "mimic-fn": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+ "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+ "dev": true,
+ "dependencies": {
+ "p-limit": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/p-locate/node_modules/p-limit": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "dev": true,
+ "dependencies": {
+ "p-try": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-try": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/parse-json": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
+ "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.0.0",
+ "error-ex": "^1.3.1",
+ "json-parse-even-better-errors": "^2.3.0",
+ "lines-and-columns": "^1.1.6"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/parse5": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz",
+ "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==",
+ "dev": true,
+ "dependencies": {
+ "entities": "^4.4.0"
+ },
+ "funding": {
+ "url": "https://github.com/inikulin/parse5?sponsor=1"
+ }
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true
+ },
+ "node_modules/picocolors": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
+ "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
+ "dev": true
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/pirates": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz",
+ "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/pkg-dir": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
+ "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
+ "dev": true,
+ "dependencies": {
+ "find-up": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/playwright": {
+ "version": "1.38.1",
+ "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.38.1.tgz",
+ "integrity": "sha512-oRMSJmZrOu1FP5iu3UrCx8JEFRIMxLDM0c/3o4bpzU5Tz97BypefWf7TuTNPWeCe279TPal5RtPPZ+9lW/Qkow==",
+ "dev": true,
+ "dependencies": {
+ "playwright-core": "1.38.1"
+ },
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=16"
+ },
+ "optionalDependencies": {
+ "fsevents": "2.3.2"
+ }
+ },
+ "node_modules/playwright-core": {
+ "version": "1.38.1",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.38.1.tgz",
+ "integrity": "sha512-tQqNFUKa3OfMf4b2jQ7aGLB8o9bS3bOY0yMEtldtC2+spf8QXG9zvXLTXUeRsoNuxEYMgLYR+NXfAa1rjKRcrg==",
+ "dev": true,
+ "bin": {
+ "playwright-core": "cli.js"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/pretty-format": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
+ "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==",
+ "dev": true,
+ "dependencies": {
+ "@jest/schemas": "^29.6.3",
+ "ansi-styles": "^5.0.0",
+ "react-is": "^18.0.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/pretty-format/node_modules/ansi-styles": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
+ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/prompts": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz",
+ "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==",
+ "dev": true,
+ "dependencies": {
+ "kleur": "^3.0.3",
+ "sisteransi": "^1.0.5"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/psl": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
+ "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==",
+ "dev": true
+ },
+ "node_modules/punycode": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
+ "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/pure-rand": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.4.tgz",
+ "integrity": "sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/dubzzz"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fast-check"
+ }
+ ]
+ },
+ "node_modules/querystringify": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
+ "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
+ "dev": true
+ },
+ "node_modules/react-is": {
+ "version": "18.2.0",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
+ "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==",
+ "dev": true
+ },
+ "node_modules/require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/requires-port": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
+ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
+ "dev": true
+ },
+ "node_modules/resolve": {
+ "version": "1.22.6",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.6.tgz",
+ "integrity": "sha512-njhxM7mV12JfufShqGy3Rz8j11RPdLy4xi15UurGJeoHLfJpVXKdh3ueuOqbYUcDZnffr6X739JBo5LzyahEsw==",
+ "dev": true,
+ "dependencies": {
+ "is-core-module": "^2.13.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/resolve-cwd": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz",
+ "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==",
+ "dev": true,
+ "dependencies": {
+ "resolve-from": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/resolve-from": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
+ "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/resolve.exports": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz",
+ "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "dev": true
+ },
+ "node_modules/saxes": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz",
+ "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==",
+ "dev": true,
+ "dependencies": {
+ "xmlchars": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=v12.22.7"
+ }
+ },
+ "node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/signal-exit": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+ "dev": true
+ },
+ "node_modules/sisteransi": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
+ "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
+ "dev": true
+ },
+ "node_modules/slash": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-support": {
+ "version": "0.5.13",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz",
+ "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==",
+ "dev": true,
+ "dependencies": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ }
+ },
+ "node_modules/sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
+ "dev": true
+ },
+ "node_modules/stack-utils": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz",
+ "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==",
+ "dev": true,
+ "dependencies": {
+ "escape-string-regexp": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/string-length": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz",
+ "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==",
+ "dev": true,
+ "dependencies": {
+ "char-regex": "^1.0.2",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-bom": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz",
+ "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-final-newline": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
+ "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/symbol-tree": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
+ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
+ "dev": true
+ },
+ "node_modules/test-exclude": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz",
+ "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==",
+ "dev": true,
+ "dependencies": {
+ "@istanbuljs/schema": "^0.1.2",
+ "glob": "^7.1.4",
+ "minimatch": "^3.0.4"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/tmpl": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
+ "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==",
+ "dev": true
+ },
+ "node_modules/to-fast-properties": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
+ "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/tough-cookie": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz",
+ "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==",
+ "dev": true,
+ "dependencies": {
+ "psl": "^1.1.33",
+ "punycode": "^2.1.1",
+ "universalify": "^0.2.0",
+ "url-parse": "^1.5.3"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/tr46": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz",
+ "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==",
+ "dev": true,
+ "dependencies": {
+ "punycode": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/type-detect": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
+ "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/type-fest": {
+ "version": "0.21.3",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
+ "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "5.26.5",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
+ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
+ "dev": true
+ },
+ "node_modules/universalify": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
+ "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4.0.0"
+ }
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.0.13",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",
+ "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "escalade": "^3.1.1",
+ "picocolors": "^1.0.0"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
+ "node_modules/url-parse": {
+ "version": "1.5.10",
+ "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
+ "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
+ "dev": true,
+ "dependencies": {
+ "querystringify": "^2.1.1",
+ "requires-port": "^1.0.0"
+ }
+ },
+ "node_modules/v8-to-istanbul": {
+ "version": "9.1.3",
+ "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.3.tgz",
+ "integrity": "sha512-9lDD+EVI2fjFsMWXc6dy5JJzBsVTcQ2fVkfBvncZ6xJWG9wtBhOldG+mHkSL0+V1K/xgZz0JDO5UT5hFwHUghg==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/trace-mapping": "^0.3.12",
+ "@types/istanbul-lib-coverage": "^2.0.1",
+ "convert-source-map": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10.12.0"
+ }
+ },
+ "node_modules/w3c-xmlserializer": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz",
+ "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==",
+ "dev": true,
+ "dependencies": {
+ "xml-name-validator": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/walker": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz",
+ "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==",
+ "dev": true,
+ "dependencies": {
+ "makeerror": "1.0.12"
+ }
+ },
+ "node_modules/webidl-conversions": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
+ "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/whatwg-encoding": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz",
+ "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==",
+ "dev": true,
+ "dependencies": {
+ "iconv-lite": "0.6.3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/whatwg-mimetype": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz",
+ "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/whatwg-url": {
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz",
+ "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==",
+ "dev": true,
+ "dependencies": {
+ "tr46": "^3.0.0",
+ "webidl-conversions": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "dev": true
+ },
+ "node_modules/write-file-atomic": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz",
+ "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==",
+ "dev": true,
+ "dependencies": {
+ "imurmurhash": "^0.1.4",
+ "signal-exit": "^3.0.7"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/ws": {
+ "version": "8.14.2",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz",
+ "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==",
+ "dev": true,
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": ">=5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/xml-name-validator": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz",
+ "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/xmlchars": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
+ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
+ "dev": true
+ },
+ "node_modules/y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true
+ },
+ "node_modules/yargs": {
+ "version": "17.7.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+ "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+ "dev": true,
+ "dependencies": {
+ "cliui": "^8.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.3",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^21.1.1"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/yargs-parser": {
+ "version": "21.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..b453118
--- /dev/null
+++ b/package.json
@@ -0,0 +1,36 @@
+{
+ "name": "github-devsecops-fundamentals",
+ "version": "1.0.0",
+ "description": "GitHub Universe 2023 workshop: GitHub DevSecOps Fundamentals",
+ "main": "index.js",
+ "directories": {
+ "doc": "docs"
+ },
+ "scripts": {
+ "functional-test": "npx playwright test",
+ "unit-test": "jest --config=jest.config.js"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/githubuniverseworkshops/github-devsecops-fundamentals.git"
+ },
+ "keywords": [
+ "GitHub",
+ "DevSecOps",
+ "Fundamentals",
+ "Universe",
+ "Workshop"
+ ],
+ "author": "services@github.com",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/githubuniverseworkshops/github-devsecops-fundamentals/issues"
+ },
+ "homepage": "https://github.com/githubuniverseworkshops/github-devsecops-fundamentals#readme",
+ "devDependencies": {
+ "@playwright/test": "^1.38.1",
+ "@types/node": "^20.8.10",
+ "jest": "^29.7.0",
+ "jest-environment-jsdom": "^29.7.0"
+ }
+}
diff --git a/playwright.config.js b/playwright.config.js
new file mode 100644
index 0000000..be8951c
--- /dev/null
+++ b/playwright.config.js
@@ -0,0 +1,78 @@
+// @ts-check
+const { defineConfig, devices } = require("@playwright/test");
+
+/**
+ * Read environment variables from file.
+ * https://github.com/motdotla/dotenv
+ */
+// require('dotenv').config();
+
+/**
+ * @see https://playwright.dev/docs/test-configuration
+ */
+module.exports = defineConfig({
+ testDir: "./tests",
+ /* Run tests in files in parallel */
+ fullyParallel: true,
+ /* Fail the build on CI if you accidentally left test.only in the source code. */
+ forbidOnly: !!process.env.CI,
+ /* Retry on CI only */
+ retries: process.env.CI ? 2 : 0,
+ /* Opt out of parallel tests on CI. */
+ workers: process.env.CI ? 1 : undefined,
+ /* Reporter to use. See https://playwright.dev/docs/test-reporters */
+ reporter: "html",
+ /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
+ use: {
+ /* Base URL to use in actions like `await page.goto('/')`. */
+ baseURL: `http://${process.env.TETRIS_APP_HOST}:${process.env.TETRIS_APP_PORT}`,
+
+ /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
+ trace: "on-first-retry",
+ },
+
+ /* Configure projects for major browsers */
+ projects: [
+ {
+ name: "chromium",
+ use: { ...devices["Desktop Chrome"] },
+ },
+
+ // {
+ // name: "firefox",
+ // use: { ...devices["Desktop Firefox"] },
+ // },
+
+ // {
+ // name: "webkit",
+ // use: { ...devices["Desktop Safari"] },
+ // },
+
+ /* Test against mobile viewports. */
+ // {
+ // name: 'Mobile Chrome',
+ // use: { ...devices['Pixel 5'] },
+ // },
+ // {
+ // name: 'Mobile Safari',
+ // use: { ...devices['iPhone 12'] },
+ // },
+
+ /* Test against branded browsers. */
+ // {
+ // name: 'Microsoft Edge',
+ // use: { ...devices['Desktop Edge'], channel: 'msedge' },
+ // },
+ // {
+ // name: 'Google Chrome',
+ // use: { ...devices['Desktop Chrome'], channel: 'chrome' },
+ // },
+ ],
+
+ /* Run your local dev server before starting the tests */
+ webServer: {
+ command: `mkdocs serve --dev-addr ${process.env.TETRIS_APP_HOST}:${process.env.TETRIS_APP_PORT}`,
+ url: `http://${process.env.TETRIS_APP_HOST}:${process.env.TETRIS_APP_PORT}`,
+ reuseExistingServer: !process.env.CI,
+ },
+});
diff --git a/reference.implementations/dependabot.yml b/reference.implementations/dependabot.yml
new file mode 100644
index 0000000..78ab862
--- /dev/null
+++ b/reference.implementations/dependabot.yml
@@ -0,0 +1,14 @@
+version: 2
+updates:
+ - package-ecosystem: "pip"
+ directory: "/"
+ schedule:
+ interval: "daily"
+ - package-ecosystem: "github-actions"
+ directory: "/"
+ schedule:
+ interval: "daily"
+ - package-ecosystem: "npm"
+ directory: "/"
+ schedule:
+ interval: "daily"
diff --git a/reference.implementations/dependency-review-config.yml b/reference.implementations/dependency-review-config.yml
new file mode 100644
index 0000000..f8d2ff1
--- /dev/null
+++ b/reference.implementations/dependency-review-config.yml
@@ -0,0 +1,28 @@
+# Possible values: "critical", "high", "moderate", "low"
+fail-on-severity: moderate
+
+# You can only include one of these two options: `allow-licenses` and `deny-licenses`
+# ([String]). Only allow these licenses (optional)
+# Possible values: Any `spdx_id` value(s) from https://docs.github.com/en/rest/licenses
+allow-licenses:
+ - MIT
+ - GPL-3.0
+ - BSD-3-Clause
+# ([String]). Block the pull request on these licenses (optional)
+# Possible values: Any `spdx_id` value(s) from https://docs.github.com/en/rest/licenses
+# deny-licenses:
+# - LGPL-2.0
+# - BSD-2-Clause
+
+# ([String]). Skip these GitHub Advisory Database IDs during detection (optional)
+# Possible values: Any valid GitHub Advisory Database ID from https://github.com/advisories
+# allow-ghsas:
+# - GHSA-abcd-1234-5679
+# - GHSA-efgh-1234-5679
+
+# ([String]). Block pull requests that introduce vulnerabilities in the scopes that match this list (optional)
+# Possible values: "development", "runtime", "unknown"
+fail-on-scopes:
+ - development
+ - runtime
+ - unknown
diff --git a/reference.implementations/release.yml b/reference.implementations/release.yml
new file mode 100644
index 0000000..8dc79cd
--- /dev/null
+++ b/reference.implementations/release.yml
@@ -0,0 +1,18 @@
+changelog:
+ exclude:
+ labels:
+ - ignore-for-release
+ authors:
+ - octocat
+ categories:
+ - title: Breaking Changes š
+ labels:
+ - Semver-Major
+ - breaking-change
+ - title: Exciting New Features š
+ labels:
+ - Semver-Minor
+ - enhancement
+ - title: Other Changes
+ labels:
+ - "*"
diff --git a/reference.implementations/tests/tetris.spec.js b/reference.implementations/tests/tetris.spec.js
new file mode 100644
index 0000000..7617572
--- /dev/null
+++ b/reference.implementations/tests/tetris.spec.js
@@ -0,0 +1,26 @@
+import { test, expect } from "@playwright/test";
+
+test("Tetris Game", async ({ page }) => {
+ const { TETRIS_APP_HOST, TETRIS_APP_PORT, TETRIS_APP_PATH } = process.env;
+
+ // should be similar to http://127.0.0.1:8080/github-devsecops-fundamentals/
+ console.log(
+ `http://${TETRIS_APP_HOST}:${TETRIS_APP_PORT}/${TETRIS_APP_PATH}`
+ );
+ await page.goto(
+ `http://${TETRIS_APP_HOST}:${TETRIS_APP_PORT}/${TETRIS_APP_PATH}`
+ );
+
+ await page.getByRole("link", { name: "Tetris Game" }).click();
+ await expect(page.getByText("score 00000")).toBeVisible();
+ await expect(page.getByText("rows 0")).toBeVisible();
+ await expect(page.locator("#upcoming")).toBeVisible();
+ await expect(
+ page.getByRole("link", { name: "Press Space to Play." })
+ ).toBeVisible();
+
+ await page.getByRole("link", { name: "Press Space to Play." }).click();
+ await expect(page.getByText("score 00000")).not.toBeVisible({
+ timeout: 0.5 * 60 * 1000,
+ });
+});
diff --git a/reference.implementations/workflows/1.1.continuous.integration.yml b/reference.implementations/workflows/1.1.continuous.integration.yml
new file mode 100644
index 0000000..1092d1a
--- /dev/null
+++ b/reference.implementations/workflows/1.1.continuous.integration.yml
@@ -0,0 +1,56 @@
+name: Run Checks on PR
+
+on:
+ pull_request:
+ branches:
+ - main
+
+permissions:
+ actions: write
+ checks: write
+ contents: read
+ security-events: write
+ pull-requests: write
+
+env:
+ CI: true
+ SITE_DIR: site
+ TETRIS_APP_HOST: "127.0.0.1"
+ TETRIS_APP_PORT: "8080"
+ TETRIS_APP_PATH: "github-devsecops-fundamentals"
+
+jobs:
+ quality-checks:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+
+ - uses: actions/setup-python@v4
+ with:
+ python-version: 3.12
+ - uses: actions/setup-node@v3
+ with:
+ node-version: 20
+
+ - name: Install Dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install -r requirements.ci.txt
+ npm ci
+ # Install browsers for running functional tests
+ npx playwright install --with-deps chromium
+
+ - name: Build
+ run: |
+ python -m mkdocs build --clean --strict --verbose --site-dir '${{ env.SITE_DIR }}'
+
+ - name: Functional Test
+ run: npx playwright test
+
+ - name: Upload Functional Test Report
+ uses: actions/upload-artifact@v3
+ if: always()
+ with:
+ name: playwright-report
+ path: playwright-report/
+ retention-days: 30
diff --git a/reference.implementations/workflows/1.2.continuous.integration.yml b/reference.implementations/workflows/1.2.continuous.integration.yml
new file mode 100644
index 0000000..010c7e5
--- /dev/null
+++ b/reference.implementations/workflows/1.2.continuous.integration.yml
@@ -0,0 +1,67 @@
+name: Run Checks on PR
+
+on:
+ pull_request:
+ branches:
+ - main
+
+permissions:
+ actions: write
+ checks: write
+ contents: read
+ security-events: write
+ pull-requests: write
+
+env:
+ CI: true
+ SITE_DIR: site
+ TETRIS_APP_HOST: "127.0.0.1"
+ TETRIS_APP_PORT: "8080"
+ TETRIS_APP_PATH: "github-devsecops-fundamentals"
+
+jobs:
+ quality-checks:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+
+ - uses: actions/setup-python@v4
+ with:
+ python-version: 3.12
+ - uses: actions/setup-node@v3
+ with:
+ node-version: 20
+
+ - name: Install Dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install -r requirements.ci.txt
+ npm ci
+ # Install browsers for running functional tests
+ npx playwright install --with-deps chromium
+
+ - name: Build
+ run: |
+ python -m mkdocs build --clean --strict --verbose --site-dir '${{ env.SITE_DIR }}'
+
+ - name: Functional Test
+ run: npx playwright test
+
+ - name: Upload Functional Test Report
+ uses: actions/upload-artifact@v3
+ if: always()
+ with:
+ name: playwright-report
+ path: playwright-report/
+ retention-days: 30
+
+ security-checks:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+
+ - name: Dependency Review
+ uses: actions/dependency-review-action@v3
+ with:
+ config-file: >-
+ ./.github/dependency-review-config.yml
diff --git a/reference.implementations/workflows/1.3.continuous.integration.yml b/reference.implementations/workflows/1.3.continuous.integration.yml
new file mode 100644
index 0000000..f797fe0
--- /dev/null
+++ b/reference.implementations/workflows/1.3.continuous.integration.yml
@@ -0,0 +1,55 @@
+name: Run Checks on Merge Queue
+
+on:
+ merge_group:
+ types: [checks_requested]
+
+permissions:
+ actions: write
+ checks: write
+ contents: read
+ security-events: write
+ pull-requests: write
+
+env:
+ CI: true
+ SITE_DIR: site
+ TETRIS_APP_HOST: "127.0.0.1"
+ TETRIS_APP_PORT: "8080"
+ TETRIS_APP_PATH: "github-devsecops-fundamentals"
+
+jobs:
+ run-acceptance:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+
+ - uses: actions/setup-python@v4
+ with:
+ python-version: 3.12
+ - uses: actions/setup-node@v3
+ with:
+ node-version: 20
+
+ - name: Install Dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install -r requirements.ci.txt
+ npm ci
+ # Install browsers for running functional tests
+ npx playwright install --with-deps chromium
+
+ - name: Build
+ run: |
+ python -m mkdocs build --clean --strict --verbose --site-dir '${{ env.SITE_DIR }}'
+
+ - name: Functional Test
+ run: npx playwright test
+
+ - name: Upload Functional Test Report
+ uses: actions/upload-artifact@v3
+ if: always()
+ with:
+ name: playwright-report
+ path: playwright-report/
+ retention-days: 30
diff --git a/reference.implementations/workflows/2.1.continuous.delivery.yml b/reference.implementations/workflows/2.1.continuous.delivery.yml
new file mode 100644
index 0000000..36b6ebe
--- /dev/null
+++ b/reference.implementations/workflows/2.1.continuous.delivery.yml
@@ -0,0 +1,60 @@
+name: Version Changes to the Main Branch
+
+on:
+ push:
+ branches:
+ - main
+ workflow_dispatch: {}
+
+jobs:
+ version-main-branch-changes:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/create-github-app-token@v1
+ id: generate-app-token
+ with:
+ app-id: ${{ vars.APP_ID_ACTIONS_ASSISTANT }}
+ private-key: ${{ secrets.APP_PRIVATE_KEY_ACTIONS_ASSISTANT }}
+
+ - name: Checkout
+ uses: actions/checkout@v3
+ with:
+ fetch-depth: 0
+ token: ${{ steps.generate-app-token.outputs.token }}
+
+ - name: Get last version number
+ id: get_last_version
+ run: |
+ # Retrieve the last git tag, as we will only be processing one delivery line.
+ last_version=$(git describe --tags --abbrev=0 2>/dev/null || echo "0.0.0")
+ echo "Last version is $last_version"
+ echo "last_version=$last_version" >> "$GITHUB_OUTPUT"
+
+ - name: Get new version number
+ id: get_new_version
+ run: |
+ major=$(echo $last_version | cut -d. -f1)
+ minor=$(echo $last_version | cut -d. -f2)
+ patch=$(echo $last_version | cut -d. -f3)
+
+ new_patch=$((patch+1))
+
+ new_version="$major.$minor.$new_patch"
+
+ echo "New version is $new_version"
+ echo "new_version=$new_version" >> "$GITHUB_OUTPUT"
+ env:
+ last_version: ${{ steps.get_last_version.outputs.last_version }}
+
+ - name: Create tag for the new version
+ run: |
+ git config --global user.name "${GITHUB_ACTOR}"
+ git config --global user.email "${GITHUB_ACTOR}@users.noreply.github.com"
+
+ if git tag -a "$new_version" -m "Version $new_version"; then
+ git push --follow-tags origin "$new_version"
+ else
+ echo "Failed to create tag $new_version, it probably already exists"
+ fi
+ env:
+ new_version: ${{ steps.get_new_version.outputs.new_version }}-release
diff --git a/reference.implementations/workflows/2.2.continuous.delivery.yml b/reference.implementations/workflows/2.2.continuous.delivery.yml
new file mode 100644
index 0000000..7493057
--- /dev/null
+++ b/reference.implementations/workflows/2.2.continuous.delivery.yml
@@ -0,0 +1,105 @@
+name: Package Delivery Artifacts & Create Release
+
+on:
+ push:
+ tags:
+ - "*-release"
+
+permissions:
+ contents: write
+
+env:
+ CI: true
+ SITE_DIR: site
+
+jobs:
+ package-delivery:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - uses: actions/setup-python@v4
+ with:
+ python-version: 3.12
+ - name: Install Python dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install -r requirements.txt
+ - name: Build Site
+ run: |
+ python -m mkdocs build --clean --strict --verbose --site-dir '${{ env.SITE_DIR }}'
+ - name: Archive Site
+ run: |
+ zip -r ${{ env.SITE_DIR }}.zip ${{ env.SITE_DIR }}
+ - run: |
+ echo "Site directory ${{ env.SITE_DIR }} content:"
+ ls -al ${{ env.SITE_DIR }}
+
+ - uses: actions/create-github-app-token@v1
+ id: generate-app-token
+ with:
+ app-id: ${{ vars.APP_ID_ACTIONS_ASSISTANT }}
+ private-key: ${{ secrets.APP_PRIVATE_KEY_ACTIONS_ASSISTANT }}
+
+ - name: Draft release
+ uses: actions/github-script@v6
+ id: draft-release
+ with:
+ github-token: ${{ steps.generate-app-token.outputs.token }}
+ script: |
+
+ const response = await github.request(
+ 'POST /repos/{owner}/{repo}/releases',
+ {
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ draft: true,
+ tag_name: '${{ github.ref }}',
+ discussion_category_name: 'announcements',
+ generate_release_notes: true,
+ make_latest: 'legacy',
+ headers: {
+ 'X-GitHub-Api-Version': '2022-11-28'
+ }
+ }
+ );
+
+ console.dir(response);
+
+ return response.data.id;
+
+ - name: Upload release asset
+ run: |
+ curl -L \
+ -X POST \
+ -H "Accept: application/vnd.github+json" \
+ -H "Authorization: Bearer ${{ steps.generate-app-token.outputs.token }}" \
+ -H "X-GitHub-Api-Version: 2022-11-28" \
+ -H "Content-Type: application/octet-stream" \
+ "https://uploads.github.com/repos/${{ github.repository }}/releases/${{ steps.draft-release.outputs.result }}/assets?name=${{ env.SITE_DIR }}.zip" \
+ --data-binary "@${{ env.SITE_DIR }}.zip"
+
+ - name: Publish release
+ uses: actions/github-script@v6
+ id: publish-release
+ with:
+ github-token: ${{ steps.generate-app-token.outputs.token }}
+ script: |
+
+ const response = await github.request(
+ 'PATCH /repos/{owner}/{repo}/releases/{release_id}',
+ {
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ release_id: ${{ steps.draft-release.outputs.result }},
+ draft: false,
+ discussion_category_name: 'announcements',
+ make_latest: 'true',
+ headers: {
+ 'X-GitHub-Api-Version': '2022-11-28'
+ }
+ }
+ );
+
+ console.dir(response);
diff --git a/reference.implementations/workflows/2.3.optional.container.image.packaging.yml b/reference.implementations/workflows/2.3.optional.container.image.packaging.yml
new file mode 100644
index 0000000..66de34c
--- /dev/null
+++ b/reference.implementations/workflows/2.3.optional.container.image.packaging.yml
@@ -0,0 +1,28 @@
+name: (Optional) Package Container Image
+
+on:
+ workflow_dispatch: {}
+
+permissions:
+ contents: read
+ packages: write
+
+jobs:
+ build-and-push:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+
+ - name: Login to GitHub Container Registry
+ uses: docker/login-action@v3
+ with:
+ registry: ghcr.io
+ username: ${{ github.actor }}
+ password: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Build and push to GHCR
+ uses: docker/build-push-action@v2
+ with:
+ push: true
+ context: ${{ github.workspace }}
+ tags: ghcr.io/${{ github.repository }}:${{ github.sha }}
diff --git a/reference.implementations/workflows/3.continuous.deployment.yml b/reference.implementations/workflows/3.continuous.deployment.yml
new file mode 100644
index 0000000..19af8ec
--- /dev/null
+++ b/reference.implementations/workflows/3.continuous.deployment.yml
@@ -0,0 +1,77 @@
+name: Deploy Release
+
+on:
+ release:
+ types:
+ - released
+
+concurrency:
+ group: github-pages
+ cancel-in-progress: false
+
+permissions:
+ contents: write
+ deployments: write
+ pages: write
+ id-token: write
+
+env:
+ CI: true
+ SITE_DIR: site
+
+jobs:
+ deploy:
+ runs-on: ubuntu-latest
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+
+ steps:
+ - uses: actions/create-github-app-token@v1
+ id: generate-app-token
+ with:
+ app-id: ${{ vars.APP_ID_ACTIONS_ASSISTANT }}
+ private-key: ${{ secrets.APP_PRIVATE_KEY_ACTIONS_ASSISTANT }}
+
+ - name: Setup Pages
+ uses: actions/configure-pages@v3
+ with:
+ enablement: "true"
+ token: "${{ steps.generate-app-token.outputs.token }}"
+
+ - name: Download release asset
+ id: download-release-asset
+ run: |
+ cat << 'EOF' > event.json
+ ${{ toJson(github.event) }}
+ EOF
+ asset_id=$(cat event.json | jq '.release.assets[] | select(.name == "${{ env.SITE_DIR }}.zip") | .id')
+
+ curl --location --output '${{ env.SITE_DIR }}.zip' \
+ -H "Accept: application/octet-stream" \
+ -H "Authorization: Bearer ${{ steps.generate-app-token.outputs.token }}" \
+ -H "X-GitHub-Api-Version: 2022-11-28" \
+ "https://api.github.com/repos/${{ github.repository }}/releases/assets/${asset_id}"
+
+ unzip -o "${{ env.SITE_DIR }}.zip" -d .
+
+ echo '##### Debug'
+ ls -al "${{ env.SITE_DIR }}"
+
+ - name: Fix site file permissions
+ run: |
+ chmod -c -R +rX "${{ env.SITE_DIR }}/" | while read line; do
+ echo "::warning title=Invalid file permissions automatically fixed::$line"
+ done
+
+ - name: Upload Pages artifact
+ uses: actions/upload-pages-artifact@v2
+ with:
+ path: "${{ env.SITE_DIR }}"
+ retention-days: "2"
+
+ - name: Deploy to GitHub Pages
+ id: deployment
+ uses: actions/deploy-pages@v2
+ with:
+ token: ${{ steps.generate-app-token.outputs.token }}
diff --git a/requirements.ci.txt b/requirements.ci.txt
new file mode 100644
index 0000000..d7c0f31
--- /dev/null
+++ b/requirements.ci.txt
@@ -0,0 +1,4 @@
+-r requirements.txt
+ruff
+pytest
+pytest-cov
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..7a3f2b0
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,8 @@
+# https://squidfunk.github.io/mkdocs-material/getting-started/#with-pip
+mkdocs
+mkdocs-glightbox
+mkdocs-material
+mkdocs-macros-plugin
+mkdocs-minify-plugin
+mkdocs-redirects
+nodeenv