diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 23972e28b99..6ca0c2f81dc 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -23,7 +23,7 @@ on: jobs: analyze: name: Analyze - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 permissions: actions: read contents: read diff --git a/.github/workflows/full-stack-test.yml b/.github/workflows/full-stack-test.yml new file mode 100644 index 00000000000..657d9f52428 --- /dev/null +++ b/.github/workflows/full-stack-test.yml @@ -0,0 +1,43 @@ +name: Test Metaflow with complete Kubernetes stack + +on: + push: + branches: + - master + pull_request: + branches: + - master + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - name: Check out source + uses: actions/checkout@v4 + + - name: Install Metaflow + run: | + python -m pip install --upgrade pip + pip install . kubernetes + + + - name: Bring up the environment + run: | + echo "Starting environment in the background..." + MINIKUBE_CPUS=2 metaflow-dev all-up & + # Give time to spin up. Adjust as needed: + sleep 150 + + - name: Wait & run flow + run: | + # When the environment is up, metaflow-dev shell will wait for readiness + # and then drop into a shell. We feed commands via a heredoc: + cat <> ./stubs/test/mypy_${{ matrix.ver }}.cfg + if [[ "${{ matrix.ver }}" == "3.7" ]]; then + echo "follow_imports = skip" >> ./stubs/test/mypy_${{ matrix.ver }}.cfg + fi + + - name: Run mypy tests + uses: nick-fields/retry@v2 + with: + max_attempts: 2 + timeout_minutes: 3 + retry_on: error + command: cd ./stubs && pytest --mypy-ini-file test/mypy_${{ matrix.ver }}.cfg --mypy-only-local-stub && cd - diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f6790f8b729..c1b993fef60 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -9,14 +9,14 @@ on: workflow_call: permissions: read-all - + jobs: pre-commit: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2.7.0 - uses: actions/setup-python@75f3110429a8c05be0e1bf360334e4cced2b63fa # v2.3.3 - - uses: pre-commit/action@9b88afc9cd57fd75b655d5c71bd38146d07135fe # v2.0.3 + - uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1 Python: name: core / Python ${{ matrix.ver }} on ${{ matrix.os }} @@ -24,8 +24,17 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-20.04, macos-latest] - ver: ['3.5', '3.6', '3.7', '3.8', '3.9', '3.10', '3.11',] + os: [ubuntu-22.04] + ver: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12', '3.13'] + include: + - os: macos-latest + ver: "3.13" + - os: macos-latest + ver: "3.12" + - os: macos-latest + ver: "3.11" + - os: macos-latest + ver: "3.10" steps: - uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2.7.0 @@ -34,6 +43,8 @@ jobs: uses: actions/setup-python@75f3110429a8c05be0e1bf360334e4cced2b63fa # v2.3.3 with: python-version: ${{ matrix.ver }} + env: + PIP_TRUSTED_HOST: "pypi.python.org pypi.org files.pythonhosted.org" - name: Install Python ${{ matrix.ver }} dependencies run: | @@ -49,27 +60,29 @@ jobs: strategy: fail-fast: false matrix: - os: [macos-latest] - ver: ['4.0'] - + os: [ubuntu-22.04] + ver: ['4.4.1'] + steps: - uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2.7.0 - name: Set up ${{ matrix.ver }} uses: r-lib/actions/setup-r@33f03a860e4659235eb60a4d87ebc0b2ea65f722 # v2.4.0 with: r-version: ${{ matrix.ver }} - + - name: Install R ${{ matrix.ver }} system dependencies - if: matrix.os == 'ubuntu-20.04' + if: matrix.os == 'ubuntu-22.04' run: sudo apt-get update; sudo apt-get install -y libcurl4-openssl-dev qpdf libgit2-dev libharfbuzz-dev libfribidi-dev - name: Install R ${{ matrix.ver }} Rlang dependencies run: | - python3 -m pip install . + python3 -m venv path/to/venv + source path/to/venv/bin/activate + python3 -m pip install . Rscript -e 'install.packages("devtools", repos="https://cloud.r-project.org", Ncpus=8)' Rscript -e 'devtools::install_deps("R", dependencies=TRUE, repos="https://cloud.r-project.org", upgrade="default")' R CMD INSTALL R - Rscript -e 'install.packages(c("data.table", "caret", "glmnet", "Matrix", "rjson"), repos="https://cloud.r-project.org", Ncpus=8)' + Rscript -e 'install.packages(c("data.table", "caret", "glmnet", "Matrix", "rjson"), repos="https://cloud.r-project.org", Ncpus=8)' - name: Execute R tests run: | diff --git a/.gitignore b/.gitignore index aca49f0b921..b36301c7c44 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,7 @@ main.js.map # Pycharm .idea +stubs/version.py + +# devtools +.devtools diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 648aaa7bf51..115953d3f36 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,9 +6,12 @@ repos: - id: check-yaml - id: check-json - repo: https://github.com/ambv/black - rev: 22.10.0 + rev: 24.4.2 hooks: - id: black language_version: python3 exclude: "^metaflow/_vendor/" additional_dependencies: ["click<8.1.0"] + args: [-t, py34, -t, py35, -t, py36, -t, py37, -t, py38, -t, py39, -t, py310, -t, py311, -t, py312] + + diff --git a/ADOPTERS.md b/ADOPTERS.md new file mode 100644 index 00000000000..e7701c9aa98 --- /dev/null +++ b/ADOPTERS.md @@ -0,0 +1,72 @@ +# Adopters + +Below is a partial list of organizations using Metaflow in production. If you'd like to be included in this list, please raise a pull request + +- [23andMe](https://www.23andme.com) +- [Adept](https://www.adept.ai) +- [Amazon](https://www.amazon.com) +- [Amazon Prime Video](https://www.primevideo.com) +- [Attentive](https://www.attentive.com) +- [Autodesk](https://www.autodesk.com) +- [Bosch](https://www.bosch.com) +- [Boston Consulting Group](https://www.bcg.com) +- [Carsales](https://www.carsales.com.au) +- [Carta](https://carta.com) +- [Chess.com](https://www.chess.com) +- [CloudKitchens](https://www.cloudkitchens.com) +- [Coveo](https://www.coveo.com) +- [Crexi](https://www.crexi.com) +- [Dell](https://www.dell.com) +- [Deliveroo](https://deliveroo.com) +- [DeliveryHero](https://deliveryhero.com) +- [Disney](https://disney.com) +- [Doordash](https://doordash.com) +- [DraftKings](https://www.draftkings.com) +- [DTN](https://www.dtn.com) +- [DuckDuckGo](https://www.duckduckgo.com) +- [Dyson](https://www.dyson.com) +- [Equilibrium Energy](https://www.equilibriumenergy.com) +- [Forward Financing](https://www.forwardfinancing.com) +- [Fortum](https://www.fortum.com) +- [Genesys](https://www.genesys.com) +- [Goldman Sachs](https://www.goldmansachs.com) +- [Gradle](https://www.gradle.com) +- [GSK](https://www.gsk.com) +- [Intel](https://www.intel.com) +- [Intuitive Surgical](https://www.intuitivesurgical.com) +- [JPMorgan Chase](https://www.jpmorganchase.com) +- [Lightricks](https://www.lightricks.com) +- [Medtronic](https://www.medtronic.com) +- [Merck](https://www.merck.com) +- [Morningstar](https://www.morningstar.com) +- [Mozilla](https://www.mozilla.org) +- [Netflix](https://netflixtechblog.com/open-sourcing-metaflow-a-human-centric-framework-for-data-science-fa72e04a5d9) +- [Nextdoor](https://www.nextdoor.com) +- [Porsche](https://www.porsche.com) +- [Pratilipi](https://www.pratilipi.com) +- [Rad.ai](https://www.rad.ai) +- [Ramp](https://ramp.com) +- [Realtor](https://www.realtor.com) +- [Roku](https://www.roku.com) +- [S&P Global](https://www.spglobal.com) +- [Sainsbury's](https://www.sainsburys.co.uk) +- [Salk Institute](https://www.salk.edu) +- [Sanofi](https://www.sanofi.com) +- [SAP](https://www.sap.com) +- [SEEK](https://www.seek.com.au) +- [Shutterstock](https://www.shutterstock.com) +- [Stanford](https://www.stanford.edu) +- [Thoughtworks](https://www.thoughtworks.com) +- [Too Good To Go](https://www.toogoodtogo.com) +- [Toyota](https://www.toyota.com) +- [Upstart](https://www.upstart.com) +- [Veriff](https://www.veriff.com) +- [Verisk](https://www.verisk.com) +- [Vouch Insurance](https://www.vouchinsurance.com) +- [Wadhwani AI](https://www.wadhwani.ai) +- [Warner Media](https://www.warnermedia.com) +- [Workiva](https://www.workiva.com) +- [Zendesk](https://www.zendesk.com) +- [Zillow](https://www.zillow.com) +- [Zipline](https://www.zipline.com) +- [Zynga](https://www.zynga.com) \ No newline at end of file diff --git a/MANIFEST.in b/MANIFEST.in index fab093f1724..5248fb971d3 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,6 @@ include LICENSE include metaflow/plugins/cards/card_modules/*.html include metaflow/plugins/cards/card_modules/*.js -include metaflow/plugins/cards/card_modules/*.css \ No newline at end of file +include metaflow/plugins/cards/card_modules/*.css +include metaflow/plugins/cards/card_viewer/*.html +recursive-include devtools * \ No newline at end of file diff --git a/README.md b/README.md index 2e3ef6a0c11..b7bb74cdf8d 100644 --- a/README.md +++ b/README.md @@ -2,49 +2,62 @@ # Metaflow -Metaflow is a human-friendly Python/R library that helps scientists and engineers build and manage real-life data science projects. Metaflow was originally developed at Netflix to boost productivity of data scientists who work on a wide variety of projects from classical statistics to state-of-the-art deep learning. +[Metaflow](https://metaflow.org) is a human-centric framework designed to help scientists and engineers **build and manage real-life AI and ML systems**. Serving teams of all sizes and scale, Metaflow streamlines the entire development lifecycleβ€”from rapid prototyping in notebooks to reliable, maintainable production deploymentsβ€”enabling teams to iterate quickly and deliver robust systems efficiently. -For more information, see [Metaflow's website](https://metaflow.org) and [documentation](https://docs.metaflow.org). +Originally developed at [Netflix](https://netflixtechblog.com/open-sourcing-metaflow-a-human-centric-framework-for-data-science-fa72e04a5d9) and now supported by [Outerbounds](https://outerbounds.com), Metaflow is designed to boost the productivity for research and engineering teams working on [a wide variety of projects](https://netflixtechblog.com/supporting-diverse-ml-systems-at-netflix-2d2e6b6d205d), from classical statistics to state-of-the-art deep learning and foundation models. By unifying code, data, and compute at every stage, Metaflow ensures seamless, end-to-end management of real-world AI and ML systems. -## Getting Started +Today, Metaflow powers thousands of AI and ML experiences across a diverse array of companies, large and small, including Amazon, Doordash, Dyson, Goldman Sachs, Ramp, and [many others](ADOPTERS.md). At Netflix alone, Metaflow supports over 3000 AI and ML projects, executes hundreds of millions of data-intensive high-performance compute jobs processing petabytes of data and manages tens of petabytes of models and artifacts for hundreds of users across its AI, ML, data science, and engineering teams. -Getting up and running with Metaflow is easy. +## From prototype to production (and back) -### Python -Install metaflow from [pypi](https://pypi.org/project/metaflow/): +Metaflow provides a simple and friendly pythonic [API](https://docs.metaflow.org) that covers foundational needs of AI and ML systems: + + +1. [Rapid local prototyping](https://docs.metaflow.org/metaflow/basics), [support for notebooks](https://docs.metaflow.org/metaflow/managing-flows/notebook-runs), and built-in support for [experiment tracking, versioning](https://docs.metaflow.org/metaflow/client) and [visualization](https://docs.metaflow.org/metaflow/visualizing-results). +2. [Effortlessly scale horizontally and vertically in your cloud](https://docs.metaflow.org/scaling/remote-tasks/introduction), utilizing both CPUs and GPUs, with [fast data access](https://docs.metaflow.org/scaling/data) for running [massive embarrassingly parallel](https://docs.metaflow.org/metaflow/basics#foreach) as well as [gang-scheduled](https://docs.metaflow.org/scaling/remote-tasks/distributed-computing) compute workloads [reliably](https://docs.metaflow.org/scaling/failures) and [efficiently](https://docs.metaflow.org/scaling/checkpoint/introduction). +3. [Easily manage dependencies](https://docs.metaflow.org/scaling/dependencies) and [deploy with one-click](https://docs.metaflow.org/production/introduction) to highly available production orchestrators with built in support for [reactive orchestration](https://docs.metaflow.org/production/event-triggering). + +For full documentation, check out our [API Reference](https://docs.metaflow.org/api) or see our [Release Notes](https://github.com/Netflix/metaflow/releases) for the latest features and improvements. + + +## Getting started + +Getting up and running is easy. If you don't know where to start, [Metaflow sandbox](https://outerbounds.com/sandbox) will have you running and exploring in seconds. + +### Installing Metaflow + +To install Metaflow in your Python environment from [PyPI](https://pypi.org/project/metaflow/): ```sh pip install metaflow ``` - -and access tutorials by typing: +Alternatively, using [conda-forge](https://anaconda.org/conda-forge/metaflow): ```sh -metaflow tutorials pull +conda install -c conda-forge metaflow ``` -### R +Once installed, a great way to get started is by following our [tutorial](https://docs.metaflow.org/getting-started/tutorials). It walks you through creating and running your first Metaflow flow step by step. -Install Metaflow from [github](https://github.com/Netflix/metaflow/tree/master/R): +For more details on Metaflow’s features and best practices, check out: +- [How Metaflow works](https://docs.metaflow.org/metaflow/basics) +- [Additional resources](https://docs.metaflow.org/introduction/metaflow-resources) -```R -devtools::install_github("Netflix/metaflow", subdir="R") -metaflow::install() -``` +If you need help, don’t hesitate to reach out on our [Slack community](http://slack.outerbounds.co/)! -and access tutorials by typing: -```R -metaflow::pull_tutorials() -``` +### Deploying infrastructure for Metaflow in your cloud + -## Get in Touch -There are several ways to get in touch with us: -* Open an issue at: https://github.com/Netflix/metaflow -* Email us at: help@metaflow.org -* Chat with us on: http://chat.metaflow.org +While you can get started with Metaflow easily on your laptop, the main benefits of Metaflow lie in its ability to [scale out to external compute clusters](https://docs.metaflow.org/scaling/remote-tasks/introduction) +and to [deploy to production-grade workflow orchestrators](https://docs.metaflow.org/production/introduction). To benefit from these features, follow this [guide](https://outerbounds.com/engineering/welcome/) to +configure Metaflow and the infrastructure behind it appropriately. -## Contributing +## Get in touch +We'd love to hear from you. Join our community [Slack workspace](http://slack.outerbounds.co/)! + +## Contributing We welcome contributions to Metaflow. Please see our [contribution guide](https://docs.metaflow.org/introduction/contributing-to-metaflow) for more details. + diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000000..dc4503118f9 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,9 @@ +# Security Policy + +We currently accept reports for vulnerabilities on all published versions of the project. + +## Reporting a Vulnerability + +You can disclose vulnerabilities securely through the [Netflix Bugcrowd](https://bugcrowd.com/netflix) site. When reporting a finding, mention the project name or repository in the title and the report will find its way to the correct people. + +Please note that at the moment, the Metaflow project does not offer a bounty for any disclosure. diff --git a/devtools/Makefile b/devtools/Makefile new file mode 100644 index 00000000000..c30db2a26bd --- /dev/null +++ b/devtools/Makefile @@ -0,0 +1,344 @@ +SHELL := /bin/bash +.SHELLFLAGS := -eu -o pipefail -c + +help: + @echo "Available targets:" + @echo " up - Start the development environment" + @echo " shell - Switch to development environment's shell" + @echo " ui - Open Metaflow UI" + @echo " dashboard - Open Minikube dashboard" + @echo " down - Stop and clean up the environment" + @echo " all-up - Start the development environment with all services" + @echo " help - Show this help message" + +HELM_VERSION := v3.14.0 +MINIKUBE_VERSION := v1.32.0 +TILT_VERSION := v0.33.11 +GUM_VERSION := v0.15.2 + +MKFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST))) +MKFILE_DIR := $(dir $(MKFILE_PATH)) +DEVTOOLS_DIR := $(MKFILE_DIR).devtools +PICK_SERVICES := $(MKFILE_DIR)pick_services.sh +MINIKUBE_DIR := $(DEVTOOLS_DIR)/minikube +MINIKUBE := $(MINIKUBE_DIR)/minikube +HELM_DIR := $(DEVTOOLS_DIR)/helm +TILT_DIR := $(DEVTOOLS_DIR)/tilt +TILT := $(TILT_DIR)/tilt +TILTFILE := $(MKFILE_DIR)/Tiltfile +MAKE_CMD := $(MAKE) -f "$(MKFILE_PATH)" + +MINIKUBE_CPUS ?= 4 +MINIKUBE_MEMORY ?= 6144 +MINIKUBE_DISK_SIZE ?= 20g + +ifeq ($(shell uname), Darwin) + minikube_os = darwin + tilt_os = mac +else + minikube_os = linux + tilt_os = linux +endif + +ifeq ($(shell uname -m), x86_64) + arch = amd64 + tilt_arch = x86_64 +else + arch = arm64 + tilt_arch = arm64 +endif + +# TODO: Move scripts to a folder + +install-helm: + @if ! command -v helm >/dev/null 2>&1; then \ + echo "πŸ“₯ Installing Helm $(HELM_VERSION)..."; \ + mkdir -p "$(HELM_DIR)"; \ + curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 \ + | HELM_INSTALL_VERSION="$(HELM_VERSION)" \ + USE_SUDO="false" \ + PATH="$(HELM_DIR):$$PATH" \ + HELM_INSTALL_DIR="$(HELM_DIR)" \ + bash; \ + chmod +x "$(HELM_DIR)/helm"; \ + echo "βœ… Helm installation complete"; \ + else \ + echo "βœ… Helm is already installed at $$(command -v helm)"; \ + fi + +check-docker: + @if ! command -v docker >/dev/null 2>&1; then \ + echo "❌ Docker is not installed. Please install Docker first: https://docs.docker.com/get-docker/"; \ + exit 1; \ + fi + @echo "πŸ” Checking Docker daemon..." + @if [ "$(shell uname)" = "Darwin" ]; then \ + open -a Docker || (echo "❌ Please start Docker Desktop" && exit 1); \ + else \ + docker info >/dev/null 2>&1 || (echo "❌ Docker daemon is not running." && exit 1); \ + fi + @echo "βœ… Docker is running" + +install-brew: + @if [ "$(shell uname)" = "Darwin" ] && ! command -v brew >/dev/null 2>&1; then \ + echo "πŸ“₯ Installing Homebrew..."; \ + /bin/bash -c "$$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"; \ + echo "βœ… Homebrew installation complete"; \ + fi + +install-curl: + @if ! command -v curl >/dev/null 2>&1; then \ + echo "πŸ“₯ Installing curl..."; \ + if [ "$(shell uname)" = "Darwin" ]; then \ + HOMEBREW_NO_AUTO_UPDATE=1 brew install curl; \ + elif command -v apt-get >/dev/null 2>&1; then \ + sudo apt-get update && sudo apt-get install -y curl; \ + elif command -v yum >/dev/null 2>&1; then \ + sudo yum install -y curl; \ + elif command -v dnf >/dev/null 2>&1; then \ + sudo dnf install -y curl; \ + else \ + echo "❌ Could not install curl. Please install manually."; \ + exit 1; \ + fi; \ + echo "βœ… curl installation complete"; \ + fi + +install-gum: + @echo "πŸ” Checking if gum is installed..." + @if ! command -v gum >/dev/null 2>&1; then \ + echo "πŸ“₯ Installing gum..."; \ + if [ "$(shell uname)" = "Darwin" ]; then \ + HOMEBREW_NO_AUTO_UPDATE=1 brew install gum|| { echo "❌ Failed to install gum via Homebrew"; exit 1; }; \ + elif command -v apt-get >/dev/null 2>&1; then \ + curl -fsSL -o /tmp/gum.deb \ + "https://github.com/charmbracelet/gum/releases/download/$(GUM_VERSION)/gum_$(GUM_VERSION:v%=%)_$(arch).deb"; \ + sudo apt-get update -qq; \ + sudo apt-get install -y /tmp/gum.deb || sudo dpkg -i /tmp/gum.deb; \ + rm -f /tmp/gum.deb; \ + else \ + echo "❌ Could not determine how to install gum for your platform. Please install manually."; \ + exit 1; \ + fi; \ + echo "βœ… gum installation complete"; \ + else \ + echo "βœ… gum is already installed."; \ + fi + +setup-minikube: + @if [ ! -f "$(MINIKUBE)" ]; then \ + echo "πŸ“₯ Installing Minikube $(MINIKUBE_VERSION)"; \ + mkdir -p $(MINIKUBE_DIR); \ + curl -L --fail https://github.com/kubernetes/minikube/releases/download/$(MINIKUBE_VERSION)/minikube-$(minikube_os)-$(arch) -o $(MINIKUBE) || (echo "❌ Failed to download minikube" && exit 1); \ + chmod +x $(MINIKUBE); \ + echo "βœ… Minikube $(MINIKUBE_VERSION) installed successfully"; \ + fi + @echo "πŸ”§ Setting up Minikube $(MINIKUBE_VERSION) cluster..." + @if ! $(MINIKUBE) status >/dev/null 2>&1; then \ + echo "πŸš€ Starting new Minikube $(MINIKUBE_VERSION) cluster..."; \ + $(MINIKUBE) start \ + --cpus $(MINIKUBE_CPUS) \ + --memory $(MINIKUBE_MEMORY) \ + --disk-size $(MINIKUBE_DISK_SIZE) \ + --driver docker \ + || { echo "❌ Failed to start Minikube (check if Docker is running)"; exit 1; }; \ + echo "πŸ”Œ Enabling metrics-server and dashboard (quietly)..."; \ + $(MINIKUBE) addons enable metrics-server >/dev/null 2>&1; \ + $(MINIKUBE) addons enable dashboard >/dev/null 2>&1; \ + else \ + echo "βœ… Minikube $(MINIKUBE_VERSION) cluster is already running"; \ + fi + @echo "πŸŽ‰ Minikube $(MINIKUBE_VERSION) cluster is ready!" + +setup-tilt: + @if [ ! -f "$(TILT)" ]; then \ + echo "πŸ“₯ Installing Tilt $(TILT_VERSION)"; \ + mkdir -p $(TILT_DIR); \ + (curl -L https://github.com/tilt-dev/tilt/releases/download/$(TILT_VERSION)/tilt.$(TILT_VERSION:v%=%).$(tilt_os).$(tilt_arch).tar.gz | tar -xz -C $(TILT_DIR)) && echo "βœ… Tilt $(TILT_VERSION) installed successfully" || (echo "❌ Failed to install Tilt" && exit 1); \ + fi + +tunnel: + $(MINIKUBE) tunnel + +teardown-minikube: + @echo "πŸ›‘ Stopping Minikube $(MINIKUBE_VERSION) cluster..." + -$(MINIKUBE) stop + @echo "πŸ—‘οΈ Deleting Minikube $(MINIKUBE_VERSION) cluster..." + -$(MINIKUBE) delete --all + @echo "🧹 Removing Minikube binary..." + -rm -rf $(MINIKUBE_DIR) + @echo "βœ… Minikube $(MINIKUBE_VERSION) teardown complete" + +dashboard: + @echo "πŸ”— Opening Minikube Dashboard..." + @$(MINIKUBE) dashboard + +# make shell is symlinked to metaflow-dev shell by metaflow +up: install-brew check-docker install-curl install-gum setup-minikube install-helm setup-tilt + @echo "πŸš€ Starting up (may require sudo access)..." + @mkdir -p $(DEVTOOLS_DIR) + @echo '#!/bin/bash' > $(DEVTOOLS_DIR)/start.sh + @echo 'set -e' >> $(DEVTOOLS_DIR)/start.sh + @echo 'trap "exit" INT TERM' >> $(DEVTOOLS_DIR)/start.sh + @echo 'trap "kill 0" EXIT' >> $(DEVTOOLS_DIR)/start.sh + @echo 'eval $$($(MINIKUBE) docker-env)' >> $(DEVTOOLS_DIR)/start.sh + @echo 'if [ -n "$$SERVICES_OVERRIDE" ]; then' >> "$(DEVTOOLS_DIR)/start.sh" + @echo ' echo "🌐 Using user-provided list of services: $$SERVICES_OVERRIDE"' >> "$(DEVTOOLS_DIR)/start.sh" + @echo ' SERVICES="$$SERVICES_OVERRIDE"' >> "$(DEVTOOLS_DIR)/start.sh" + @echo 'else' >> "$(DEVTOOLS_DIR)/start.sh" + @echo ' echo "πŸ“ Selecting services..."' >> "$(DEVTOOLS_DIR)/start.sh" + @echo ' SERVICES=$$($(PICK_SERVICES))' >> "$(DEVTOOLS_DIR)/start.sh" + @echo 'fi' >> "$(DEVTOOLS_DIR)/start.sh" + @echo 'PATH="$(MINIKUBE_DIR):$(TILT_DIR):$$PATH" $(MINIKUBE) tunnel &' >> $(DEVTOOLS_DIR)/start.sh + @echo 'echo -e "πŸš€ Starting Tilt with selected services..."' >> $(DEVTOOLS_DIR)/start.sh + @echo 'echo -e "\033[1;38;5;46m\nπŸ”₯ \033[1;38;5;196mNext Steps:\033[0;38;5;46m Use \033[3mmetaflow-dev shell\033[23m to switch to the development\n environment'\''s shell and start executing your Metaflow flows.\n\033[0m"' >> "$(DEVTOOLS_DIR)/start.sh" + @echo 'PATH="$(HELM_DIR):$(MINIKUBE_DIR):$(TILT_DIR):$$PATH" SERVICES="$$SERVICES" tilt up -f $(TILTFILE)' >> $(DEVTOOLS_DIR)/start.sh + @echo 'wait' >> $(DEVTOOLS_DIR)/start.sh + @chmod +x $(DEVTOOLS_DIR)/start.sh + @$(DEVTOOLS_DIR)/start.sh + +all-up: + @echo "πŸš€ Starting up all services..." + SERVICES_OVERRIDE=all $(MAKE_CMD) up + +down: + @echo "πŸ›‘ Stopping all services..." + @-pkill -f "$(MINIKUBE) tunnel" 2>/dev/null || true + @echo "⏹️ Stopping Tilt..." + @echo "🧹 Cleaning up Minikube..." + $(MAKE_CMD) teardown-minikube + @echo "πŸ—‘οΈ Removing Tilt binary and directory..." + -rm -rf $(TILT_DIR) + @echo "🧹 Removing temporary scripts..." + -rm -rf $(DEVTOOLS_DIR) + @echo "✨ All done!" + +shell: setup-tilt + @echo "⏳ Checking if development environment is up..." + @set -eu; \ + for i in $$(seq 1 90); do \ + if "$(TILT)" get session >/dev/null 2>&1; then \ + found_session=1; \ + break; \ + else \ + sleep 2; \ + fi; \ + done; \ + if [ -z "$${found_session:-}" ]; then \ + echo "❌ Development environment is not up."; \ + echo " Please run 'metaflow-dev up' in another terminal, then re-run 'metaflow-dev shell'."; \ + exit 1; \ + fi + @echo "⏳ Waiting for development environment to be ready..." + @while true; do \ + "$(TILT)" get uiresource generate-configs >/dev/null 2>&1; \ + status=$$?; \ + if [ $$status -eq 0 ]; then \ + if ! "$(TILT)" wait --for=condition=Ready uiresource/generate-configs --timeout=300s; then \ + echo "❌ Timed out waiting for development environment to be ready."; \ + exit 1; \ + fi; \ + break; \ + elif [ $$status -eq 127 ]; then \ + echo "❌ Development environment is not up."; \ + echo " Please run 'metaflow-dev up' in another terminal, then re-run 'metaflow-dev shell'."; \ + exit 1; \ + else \ + sleep 1; \ + fi; \ + done + @echo "πŸ”§ Starting a new shell for development environment..." + @bash -c '\ + if [ -n "$$SHELL" ]; then \ + user_shell="$$SHELL"; \ + else \ + user_shell="$(SHELL)"; \ + fi; \ + echo "πŸ”Ž Using $$user_shell for interactive session."; \ + echo "🐍 If you installed Metaflow in a virtual environment, activate it now."; \ + if [ -f "$(DEVTOOLS_DIR)/aws_config" ]; then \ + env METAFLOW_HOME="$(DEVTOOLS_DIR)" \ + METAFLOW_PROFILE=local \ + AWS_CONFIG_FILE="$(DEVTOOLS_DIR)/aws_config" \ + AWS_SHARED_CREDENTIALS_FILE= \ + "$$user_shell" -i; \ + else \ + env METAFLOW_HOME="$(DEVTOOLS_DIR)" \ + METAFLOW_PROFILE=local \ + "$$user_shell" -i; \ + fi' + + +# @echo '$(MAKE_CMD) create-dev-shell' >> $(DEVTOOLS_DIR)/start.sh +# @echo 'rm -f /tmp/metaflow-devshell-*' >> $(DEVTOOLS_DIR)/start.sh +create-dev-shell: setup-tilt + @bash -c '\ + SHELL_PATH=/tmp/metaflow-dev-shell-$$$$ && \ + echo "#!/bin/bash" > $$SHELL_PATH && \ + echo "set -e" >> $$SHELL_PATH && \ + echo "" >> $$SHELL_PATH && \ + echo "echo \"⏳ Checking if development environment is up...\"" >> $$SHELL_PATH && \ + echo "if ! $(TILT) get session >/dev/null 2>&1; then" >> $$SHELL_PATH && \ + echo " echo \"❌ Development environment is not up.\"" >> $$SHELL_PATH && \ + echo " echo \" Please run '\''make up'\'' in another terminal, then re-run this script.\"" >> $$SHELL_PATH && \ + echo " exit 1" >> $$SHELL_PATH && \ + echo "fi" >> $$SHELL_PATH && \ + echo "" >> $$SHELL_PATH && \ + echo "echo \"⏳ Waiting for development environment to be ready...\"" >> $$SHELL_PATH && \ + echo "if ! $(TILT) wait --for=condition=Ready uiresource/generate-configs --timeout=300s; then" >> $$SHELL_PATH && \ + echo " echo \"❌ Timed out waiting for development environment to be ready.\"" >> $$SHELL_PATH && \ + echo " exit 1" >> $$SHELL_PATH && \ + echo "fi" >> $$SHELL_PATH && \ + echo "" >> $$SHELL_PATH && \ + echo "echo \"πŸ”§ Starting a new shell for development environment...\"" >> $$SHELL_PATH && \ + echo "if [ -n \"\$$SHELL\" ]; then" >> $$SHELL_PATH && \ + echo " user_shell=\"\$$SHELL\"" >> $$SHELL_PATH && \ + echo "else" >> $$SHELL_PATH && \ + echo " user_shell=\"$(SHELL)\"" >> $$SHELL_PATH && \ + echo "fi" >> $$SHELL_PATH && \ + echo "echo \"πŸ”Ž Using \$$user_shell for interactive session.\"" >> $$SHELL_PATH && \ + echo "echo \"🐍 If you installed Metaflow in a virtual environment, activate it now.\"" >> $$SHELL_PATH && \ + echo "if [ -f \"$(DEVTOOLS_DIR)/aws_config\" ]; then" >> $$SHELL_PATH && \ + echo " env METAFLOW_HOME=\"$(DEVTOOLS_DIR)\" \\" >> $$SHELL_PATH && \ + echo " METAFLOW_PROFILE=local \\" >> $$SHELL_PATH && \ + echo " AWS_CONFIG_FILE=\"$(DEVTOOLS_DIR)/aws_config\" \\" >> $$SHELL_PATH && \ + echo " AWS_SHARED_CREDENTIALS_FILE= \\" >> $$SHELL_PATH && \ + echo " \"\$$user_shell\" -i" >> $$SHELL_PATH && \ + echo "else" >> $$SHELL_PATH && \ + echo " env METAFLOW_HOME=\"$(DEVTOOLS_DIR)\" \\" >> $$SHELL_PATH && \ + echo " METAFLOW_PROFILE=local \\" >> $$SHELL_PATH && \ + echo " \"\$$user_shell\" -i" >> $$SHELL_PATH && \ + echo "fi" >> $$SHELL_PATH && \ + chmod +x $$SHELL_PATH && \ + echo "✨ Created $$SHELL_PATH" && \ + echo "πŸ”‘ Execute it from ANY directory to switch to development environment shell!" \ + ' + +ui: setup-tilt + @echo "⏳ Checking if the development environment is up..." + @if ! $(TILT) get session >/dev/null 2>&1; then \ + echo "❌ Development environment is not up."; \ + echo " Please run 'metaflow-dev up' in another terminal, then re-run 'metaflow-dev ui'."; \ + exit 1; \ + fi + @echo "⏳ Waiting for Metaflow UI to be ready..." + @while true; do \ + "$(TILT)" get uiresource metaflow-ui >/dev/null 2>&1; \ + status=$$?; \ + if [ $$status -eq 0 ]; then \ + "$(TILT)" wait --for=condition=Ready uiresource/metaflow-ui; \ + break; \ + elif [ $$status -eq 127 ]; then \ + echo "❌ Development environment is not up."; \ + echo " Please run 'metaflow-dev up' in another terminal, then re-run 'metaflow-dev shell'."; \ + exit 1; \ + else \ + sleep 1; \ + fi; \ + done + @echo "πŸ”— Opening Metaflow UI at http://localhost:3000" + @open http://localhost:3000 + +.PHONY: install-helm setup-minikube setup-tilt teardown-minikube tunnel up down check-docker install-curl install-gum install-brew up down dashboard shell ui all-up help + +.DEFAULT_GOAL := help diff --git a/devtools/Tiltfile b/devtools/Tiltfile new file mode 100644 index 00000000000..620671b0137 --- /dev/null +++ b/devtools/Tiltfile @@ -0,0 +1,651 @@ +# Tilt configuration for running Metaflow on a local Kubernetes stack +# +# Usage: +# Start the development environment: +# $ tilt up +# Stop and clean up: +# $ tilt down + +# TODO: +# 1. move away from temporary images +# 2. introduce kueue and jobsets +# 3. lock versions + +version_settings(constraint='>=0.22.2') +allow_k8s_contexts('minikube') + +components = { + "metadata-service": ["postgresql"], + "ui": ["postgresql", "minio"], + "minio": [], + "postgresql": [], + "argo-workflows": [], + "argo-events": ["argo-workflows"], +} + +services_env = os.getenv("SERVICES", "").strip().lower() + +if services_env: + if services_env == "all": + requested_components = list(components.keys()) + else: + requested_components = services_env.split(",") +else: + requested_components = list(components.keys()) + +metaflow_config = {} +metaflow_config["METAFLOW_KUBERNETES_NAMESPACE"] = "default" + +aws_config = [] + +def write_config_files(): + metaflow_json = encode_json(metaflow_config) + cmd = '''cat > .devtools/config_local.json < .devtools/aws_config <&2 + +gum style "Select services to deploy (press enter to select all):" \ + --foreground "$COLOR" \ + --bold >&2 + +pretty_print() { + local items=("$@") + + if [ "${#items[@]}" -eq 1 ]; then + echo "${items[0]}" + return + fi + + if [ "${#items[@]}" -eq 2 ]; then + echo "${items[0]} and ${items[1]}" + return + fi + + local last_item="${items[-1]}" + unset 'items[-1]' + echo "$(IFS=,; echo "${items[*]}"), and $last_item" +} + +pretty_print() { + local items=("$@") + local length=${#items[@]} + + if [ "$length" -eq 0 ]; then + echo "(none)" + return + fi + + if [ "$length" -eq 1 ]; then + echo "${items[0]}" + return + fi + + if [ "$length" -eq 2 ]; then + echo "${items[0]} and ${items[1]}" + return + fi + + local last_index=$((length - 1)) + local last_item="${items[$last_index]}" + unset 'items[last_index]' + + local joined + IFS="," + joined="${items[*]}" + unset IFS + joined="${joined//,/, }" + + echo "$joined, and $last_item" +} + +SELECTED="$( + gum choose "${SERVICE_OPTIONS[@]}" \ + --no-limit \ + --cursor.foreground="$COLOR" \ + --selected.foreground="$COLOR" +)" + +SELECTED_SERVICES=() +while IFS= read -r line; do + [ -n "$line" ] && SELECTED_SERVICES+=("$line") +done <<< "$SELECTED" + +# If nothing was chosen, default to all +if [ -z "$SELECTED_SERVICES" ]; then + gum style "πŸ™… No services selected. Deploying all..." --foreground "$COLOR" >&2 + SELECTED_SERVICES=("${SERVICE_OPTIONS[@]}") +fi + +PRINTABLE="$(pretty_print "${SELECTED_SERVICES[@]}")" +gum style "βœ… Deploying $PRINTABLE" --foreground "$COLOR" >&2 + +echo "$(IFS=,; echo "${SELECTED_SERVICES[*]}")" \ No newline at end of file diff --git a/docs/cards.md b/docs/cards.md index 5bddab2b729..900384861cc 100644 --- a/docs/cards.md +++ b/docs/cards.md @@ -121,7 +121,7 @@ PATH_TO_CUSTOM_HTML = 'myhtml.html' class CustomCard(MetaflowCard): type = "custom_card" - def __init__(self, options={"no_header": True}, graph=None,components=[]): + def __init__(self, options={"no_header": True}, graph=None, components=[], flow=None, **kwargs): super().__init__() self._no_header = True self._graph = graph @@ -177,7 +177,7 @@ class CustomCard(MetaflowCard): HTML = "{data}" - def __init__(self, options={"no_header": True}, graph=None,components=[]): + def __init__(self, options={"no_header": True}, graph=None, components=[], flow=None, **kwargs): super().__init__() self._no_header = True self._graph = graph @@ -276,7 +276,7 @@ class YCard(MetaflowCard): ALLOW_USER_COMPONENTS = True - def __init__(self, options={}, components=[], graph=None): + def __init__(self, options={}, components=[], graph=None, flow=None, **kwargs): self._components = components def render(self, task): diff --git a/docs/metaflow.svg b/docs/metaflow.svg new file mode 100644 index 00000000000..40c05465188 --- /dev/null +++ b/docs/metaflow.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/docs/multicloud.png b/docs/multicloud.png new file mode 100644 index 00000000000..b63bcfc6636 Binary files /dev/null and b/docs/multicloud.png differ diff --git a/docs/prototype-to-prod.png b/docs/prototype-to-prod.png new file mode 100644 index 00000000000..13bbb39a564 Binary files /dev/null and b/docs/prototype-to-prod.png differ diff --git a/metaflow/R.py b/metaflow/R.py index e24dbf5c2bc..9a758dfe987 100644 --- a/metaflow/R.py +++ b/metaflow/R.py @@ -1,7 +1,9 @@ import os -import imp +import sys +from importlib import util as imp_util, machinery as imp_machinery from tempfile import NamedTemporaryFile +from . import parameters from .util import to_bytes R_FUNCTIONS = {} @@ -70,6 +72,26 @@ def working_dir(): return None +def load_module_from_path(module_name: str, path: str): + """ + Loads a module from a given path + + Parameters + ---------- + module_name: str + Name to assign for the loaded module. Usable for importing after loading. + path: str + path to the file to be loaded + """ + loader = imp_machinery.SourceFileLoader(module_name, path) + spec = imp_util.spec_from_loader(loader.name, loader) + module = imp_util.module_from_spec(spec) + loader.exec_module(module) + # Required in order to be able to import the module by name later. + sys.modules[module_name] = module + return module + + def run( flow_script, r_functions, @@ -100,19 +122,21 @@ def run( full_cmdline[0] = os.path.basename(full_cmdline[0]) with NamedTemporaryFile(prefix="metaflowR.", delete=False) as tmp: tmp.write(to_bytes(flow_script)) - module = imp.load_source("metaflowR", tmp.name) + module = load_module_from_path("metaflowR", tmp.name) flow = module.FLOW(use_cli=False) from . import exception - from . import cli try: - cli.main( - flow, - args=metaflow_args, - handle_exceptions=False, - entrypoint=full_cmdline[: -len(metaflow_args)], - ) + with parameters.flow_context(flow.__class__) as _: + from . import cli + + cli.main( + flow, + args=metaflow_args, + handle_exceptions=False, + entrypoint=full_cmdline[: -len(metaflow_args)], + ) except exception.MetaflowException as e: cli.print_metaflow_exception(e) os.remove(tmp.name) diff --git a/metaflow/__init__.py b/metaflow/__init__.py index 646550a504c..0eba0da3f33 100644 --- a/metaflow/__init__.py +++ b/metaflow/__init__.py @@ -42,14 +42,8 @@ class and related decorators. Metaflow GitHub page. """ -import importlib +import os import sys -import types - -from os import path - -CURRENT_DIRECTORY = path.dirname(path.abspath(__file__)) -INFO_FILE = path.join(path.dirname(CURRENT_DIRECTORY), "INFO") from metaflow.extension_support import ( alias_submodules, @@ -61,7 +55,6 @@ class and related decorators. _ext_debug, ) - # We load the module overrides *first* explicitly. Non overrides can be loaded # in toplevel as well but these can be loaded first if needed. Note that those # modules should be careful not to include anything in Metaflow at their top-level @@ -79,9 +72,14 @@ class and related decorators. ) tl_module = m.module.__dict__.get("toplevel", None) if tl_module is not None: - _tl_modules.append(".".join([EXT_PKG, m.tl_package, "toplevel", tl_module])) + _tl_modules.append( + ( + m.package_name, + ".".join([EXT_PKG, m.tl_package, "toplevel", tl_module]), + ) + ) _ext_debug("Got overrides to load: %s" % _override_modules) - _ext_debug("Got top-level imports: %s" % _tl_modules) + _ext_debug("Got top-level imports: %s" % str(_tl_modules)) except Exception as e: _ext_debug("Error in importing toplevel/overrides: %s" % e) @@ -98,14 +96,21 @@ class and related decorators. from .metaflow_profile import profile # current runtime singleton -from .current import current +from .metaflow_current import current # Flow spec from .flowspec import FlowSpec -from .parameters import Parameter, JSONTypeClass +from .parameters import Parameter, JSONTypeClass, JSONType -JSONType = JSONTypeClass() +from .user_configs.config_parameters import Config, ConfigValue, config_expr +from .user_decorators.user_step_decorator import ( + UserStepDecorator, + StepMutator, + user_step_decorator, + USER_SKIP_STEP, +) +from .user_decorators.user_flow_decorator import FlowMutator # data layer # For historical reasons, we make metaflow.plugins.datatools accessible as @@ -120,11 +125,17 @@ class and related decorators. # Decorators from .decorators import step, _import_plugin_decorators + +# Parsers (for configs) for now +from .plugins import _import_tl_plugins + +_import_tl_plugins(globals()) + # this auto-generates decorator functions from Decorator objects # in the top-level metaflow namespace _import_plugin_decorators(globals()) -# Setting card import for only python 3.4 -if sys.version_info[0] >= 3 and sys.version_info[1] >= 4: +# Setting card import for only python 3.6 +if sys.version_info[0] >= 3 and sys.version_info[1] >= 6: from . import cards # Client @@ -143,9 +154,20 @@ class and related decorators. DataArtifact, ) -__version_addl__ = [] +# Import data class within tuple_util but not introduce new symbols. +from . import tuple_util + +# Runner API +if sys.version_info >= (3, 7): + from .runner.metaflow_runner import Runner + from .runner.nbrun import NBRunner + from .runner.deployer import Deployer + from .runner.deployer import DeployedFlow + from .runner.nbdeploy import NBDeployer + +__ext_tl_modules__ = [] _ext_debug("Loading top-level modules") -for m in _tl_modules: +for pkg_name, m in _tl_modules: extension_module = load_module(m) if extension_module: tl_package = m.split(".")[1] @@ -153,15 +175,7 @@ class and related decorators. lazy_load_aliases( alias_submodules(extension_module, tl_package, None, extra_indent=True) ) - version_info = getattr(extension_module, "__mf_extensions__", "") - if extension_module.__version__: - version_info = "%s(%s)" % (version_info, extension_module.__version__) - __version_addl__.append(version_info) - -if __version_addl__: - __version_addl__ = ";".join(__version_addl__) -else: - __version_addl__ = None + __ext_tl_modules__.append((pkg_name, extension_module)) # Erase all temporary names to avoid leaking things for _n in [ @@ -188,11 +202,6 @@ class and related decorators. pass del globals()["_n"] -import pkg_resources +from .version import metaflow_version as _mf_version -try: - __version__ = pkg_resources.get_distribution("metaflow").version -except: - # this happens on remote environments since the job package - # does not have a version - __version__ = None +__version__ = _mf_version diff --git a/metaflow/_vendor/click/core.py b/metaflow/_vendor/click/core.py index f58bf26d2f9..69604c40c51 100644 --- a/metaflow/_vendor/click/core.py +++ b/metaflow/_vendor/click/core.py @@ -719,7 +719,7 @@ def main( prog_name=None, complete_var=None, standalone_mode=True, - **extra + **extra, ): """This is the way to invoke a script with all the bells and whistles as a command line application. This will always terminate @@ -1101,7 +1101,7 @@ def __init__( subcommand_metavar=None, chain=False, result_callback=None, - **attrs + **attrs, ): Command.__init__(self, name, **attrs) if no_args_is_help is None: @@ -1463,6 +1463,7 @@ class Parameter(object): parameter. The old callback format will still work, but it will raise a warning to give you a chance to migrate the code easier. """ + param_type_name = "parameter" def __init__( @@ -1708,7 +1709,7 @@ def __init__( hidden=False, show_choices=True, show_envvar=False, - **attrs + **attrs, ): default_is_missing = attrs.get("default", _missing) is _missing Parameter.__init__(self, param_decls, type=type, **attrs) diff --git a/metaflow/_vendor/imghdr.LICENSE b/metaflow/_vendor/imghdr.LICENSE new file mode 100644 index 00000000000..c046fc7d756 --- /dev/null +++ b/metaflow/_vendor/imghdr.LICENSE @@ -0,0 +1,48 @@ +Copyright Β© 2001-2023 Python Software Foundation; All Rights Reserved + +This code originally taken from the Python 3.11.3 distribution +and it is therefore now released under the following Python-style +license: + +1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and + the Individual or Organization ("Licensee") accessing and + otherwise using imghdr software in source or binary form and + its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, PSF hereby + grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, + analyze, test, perform and/or display publicly, prepare derivative works, + distribute, and otherwise use imghdr alone or in any derivative + version, provided, however, that PSF's License Agreement and PSF's notice of + copyright, i.e., "Copyright Β© 2001-2023 Python Software Foundation; All Rights + Reserved" are retained in imghdr alone or in any derivative version + prepared by Licensee. + +3. In the event Licensee prepares a derivative work that is based on or + incorporates imghdr or any part thereof, and wants to make the + derivative work available to others as provided herein, then Licensee hereby + agrees to include in any such work a brief summary of the + changes made to imghdr. + +4. PSF is making imghdr available to Licensee on an "AS IS" basis. + PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF + EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR + WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE + USE OF IMGHDR WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. + +5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF IMGHDR + FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF + MODIFYING, DISTRIBUTING, OR OTHERWISE USING IMGHDR, OR ANY DERIVATIVE + THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material breach of + its terms and conditions. + +7. Nothing in this License Agreement shall be deemed to create any relationship + of agency, partnership, or joint venture between PSF and Licensee. This License + Agreement does not grant permission to use PSF trademarks or trade name in a + trademark sense to endorse or promote products or services of Licensee, or any + third party. + +8. By copying, installing or otherwise using imghdr, Licensee agrees + to be bound by the terms and conditions of this License Agreement. diff --git a/metaflow/_vendor/imghdr/__init__.py b/metaflow/_vendor/imghdr/__init__.py new file mode 100644 index 00000000000..33868883470 --- /dev/null +++ b/metaflow/_vendor/imghdr/__init__.py @@ -0,0 +1,180 @@ +"""Recognize image file formats based on their first few bytes.""" + +from os import PathLike +import warnings + +__all__ = ["what"] + + +warnings._deprecated(__name__, remove=(3, 13)) + + +#-------------------------# +# Recognize image headers # +#-------------------------# + +def what(file, h=None): + """Return the type of image contained in a file or byte stream.""" + f = None + try: + if h is None: + if isinstance(file, (str, PathLike)): + f = open(file, 'rb') + h = f.read(32) + else: + location = file.tell() + h = file.read(32) + file.seek(location) + for tf in tests: + res = tf(h, f) + if res: + return res + finally: + if f: f.close() + return None + + +#---------------------------------# +# Subroutines per image file type # +#---------------------------------# + +tests = [] + +def test_jpeg(h, f): + """Test for JPEG data with JFIF or Exif markers; and raw JPEG.""" + if h[6:10] in (b'JFIF', b'Exif'): + return 'jpeg' + elif h[:4] == b'\xff\xd8\xff\xdb': + return 'jpeg' + +tests.append(test_jpeg) + +def test_png(h, f): + """Verify if the image is a PNG.""" + if h.startswith(b'\211PNG\r\n\032\n'): + return 'png' + +tests.append(test_png) + +def test_gif(h, f): + """Verify if the image is a GIF ('87 or '89 variants).""" + if h[:6] in (b'GIF87a', b'GIF89a'): + return 'gif' + +tests.append(test_gif) + +def test_tiff(h, f): + """Verify if the image is a TIFF (can be in Motorola or Intel byte order).""" + if h[:2] in (b'MM', b'II'): + return 'tiff' + +tests.append(test_tiff) + +def test_rgb(h, f): + """test for the SGI image library.""" + if h.startswith(b'\001\332'): + return 'rgb' + +tests.append(test_rgb) + +def test_pbm(h, f): + """Verify if the image is a PBM (portable bitmap).""" + if len(h) >= 3 and \ + h[0] == ord(b'P') and h[1] in b'14' and h[2] in b' \t\n\r': + return 'pbm' + +tests.append(test_pbm) + +def test_pgm(h, f): + """Verify if the image is a PGM (portable graymap).""" + if len(h) >= 3 and \ + h[0] == ord(b'P') and h[1] in b'25' and h[2] in b' \t\n\r': + return 'pgm' + +tests.append(test_pgm) + +def test_ppm(h, f): + """Verify if the image is a PPM (portable pixmap).""" + if len(h) >= 3 and \ + h[0] == ord(b'P') and h[1] in b'36' and h[2] in b' \t\n\r': + return 'ppm' + +tests.append(test_ppm) + +def test_rast(h, f): + """test for the Sun raster file.""" + if h.startswith(b'\x59\xA6\x6A\x95'): + return 'rast' + +tests.append(test_rast) + +def test_xbm(h, f): + """Verify if the image is a X bitmap (X10 or X11).""" + if h.startswith(b'#define '): + return 'xbm' + +tests.append(test_xbm) + +def test_bmp(h, f): + """Verify if the image is a BMP file.""" + if h.startswith(b'BM'): + return 'bmp' + +tests.append(test_bmp) + +def test_webp(h, f): + """Verify if the image is a WebP.""" + if h.startswith(b'RIFF') and h[8:12] == b'WEBP': + return 'webp' + +tests.append(test_webp) + +def test_exr(h, f): + """verify is the image ia a OpenEXR fileOpenEXR.""" + if h.startswith(b'\x76\x2f\x31\x01'): + return 'exr' + +tests.append(test_exr) + +#--------------------# +# Small test program # +#--------------------# + +def test(): + import sys + recursive = 0 + if sys.argv[1:] and sys.argv[1] == '-r': + del sys.argv[1:2] + recursive = 1 + try: + if sys.argv[1:]: + testall(sys.argv[1:], recursive, 1) + else: + testall(['.'], recursive, 1) + except KeyboardInterrupt: + sys.stderr.write('\n[Interrupted]\n') + sys.exit(1) + +def testall(list, recursive, toplevel): + import sys + import os + for filename in list: + if os.path.isdir(filename): + print(filename + '/:', end=' ') + if recursive or toplevel: + print('recursing down:') + import glob + names = glob.glob(os.path.join(glob.escape(filename), '*')) + testall(names, recursive, 0) + else: + print('*** directory (use -r) ***') + else: + print(filename + ':', end=' ') + sys.stdout.flush() + try: + print(what(filename)) + except OSError: + print('*** not found ***') + +if __name__ == '__main__': + test() diff --git a/metaflow/_vendor/v3_5/importlib_metadata.LICENSE b/metaflow/_vendor/importlib_metadata.LICENSE similarity index 100% rename from metaflow/_vendor/v3_5/importlib_metadata.LICENSE rename to metaflow/_vendor/importlib_metadata.LICENSE diff --git a/metaflow/_vendor/importlib_metadata/__init__.py b/metaflow/_vendor/importlib_metadata/__init__.py new file mode 100644 index 00000000000..d6c84fb70e9 --- /dev/null +++ b/metaflow/_vendor/importlib_metadata/__init__.py @@ -0,0 +1,1063 @@ +import os +import re +import abc +import csv +import sys +from metaflow._vendor import zipp +import email +import pathlib +import operator +import textwrap +import warnings +import functools +import itertools +import posixpath +import collections + +from . import _adapters, _meta +from ._collections import FreezableDefaultDict, Pair +from ._compat import ( + NullFinder, + install, + pypy_partial, +) +from ._functools import method_cache, pass_none +from ._itertools import always_iterable, unique_everseen +from ._meta import PackageMetadata, SimplePath + +from contextlib import suppress +from importlib import import_module +from importlib.abc import MetaPathFinder +from itertools import starmap +from typing import List, Mapping, Optional, Union + + +__all__ = [ + 'Distribution', + 'DistributionFinder', + 'PackageMetadata', + 'PackageNotFoundError', + 'distribution', + 'distributions', + 'entry_points', + 'files', + 'metadata', + 'packages_distributions', + 'requires', + 'version', +] + + +class PackageNotFoundError(ModuleNotFoundError): + """The package was not found.""" + + def __str__(self): + return f"No package metadata was found for {self.name}" + + @property + def name(self): + (name,) = self.args + return name + + +class Sectioned: + """ + A simple entry point config parser for performance + + >>> for item in Sectioned.read(Sectioned._sample): + ... print(item) + Pair(name='sec1', value='# comments ignored') + Pair(name='sec1', value='a = 1') + Pair(name='sec1', value='b = 2') + Pair(name='sec2', value='a = 2') + + >>> res = Sectioned.section_pairs(Sectioned._sample) + >>> item = next(res) + >>> item.name + 'sec1' + >>> item.value + Pair(name='a', value='1') + >>> item = next(res) + >>> item.value + Pair(name='b', value='2') + >>> item = next(res) + >>> item.name + 'sec2' + >>> item.value + Pair(name='a', value='2') + >>> list(res) + [] + """ + + _sample = textwrap.dedent( + """ + [sec1] + # comments ignored + a = 1 + b = 2 + + [sec2] + a = 2 + """ + ).lstrip() + + @classmethod + def section_pairs(cls, text): + return ( + section._replace(value=Pair.parse(section.value)) + for section in cls.read(text, filter_=cls.valid) + if section.name is not None + ) + + @staticmethod + def read(text, filter_=None): + lines = filter(filter_, map(str.strip, text.splitlines())) + name = None + for value in lines: + section_match = value.startswith('[') and value.endswith(']') + if section_match: + name = value.strip('[]') + continue + yield Pair(name, value) + + @staticmethod + def valid(line): + return line and not line.startswith('#') + + +class DeprecatedTuple: + """ + Provide subscript item access for backward compatibility. + + >>> recwarn = getfixture('recwarn') + >>> ep = EntryPoint(name='name', value='value', group='group') + >>> ep[:] + ('name', 'value', 'group') + >>> ep[0] + 'name' + >>> len(recwarn) + 1 + """ + + _warn = functools.partial( + warnings.warn, + "EntryPoint tuple interface is deprecated. Access members by name.", + DeprecationWarning, + stacklevel=pypy_partial(2), + ) + + def __getitem__(self, item): + self._warn() + return self._key()[item] + + +class EntryPoint(DeprecatedTuple): + """An entry point as defined by Python packaging conventions. + + See `the packaging docs on entry points + `_ + for more information. + """ + + pattern = re.compile( + r'(?P[\w.]+)\s*' + r'(:\s*(?P[\w.]+))?\s*' + r'(?P\[.*\])?\s*$' + ) + """ + A regular expression describing the syntax for an entry point, + which might look like: + + - module + - package.module + - package.module:attribute + - package.module:object.attribute + - package.module:attr [extra1, extra2] + + Other combinations are possible as well. + + The expression is lenient about whitespace around the ':', + following the attr, and following any extras. + """ + + dist: Optional['Distribution'] = None + + def __init__(self, name, value, group): + vars(self).update(name=name, value=value, group=group) + + def load(self): + """Load the entry point from its definition. If only a module + is indicated by the value, return that module. Otherwise, + return the named object. + """ + match = self.pattern.match(self.value) + module = import_module(match.group('module')) + attrs = filter(None, (match.group('attr') or '').split('.')) + return functools.reduce(getattr, attrs, module) + + @property + def module(self): + match = self.pattern.match(self.value) + return match.group('module') + + @property + def attr(self): + match = self.pattern.match(self.value) + return match.group('attr') + + @property + def extras(self): + match = self.pattern.match(self.value) + return list(re.finditer(r'\w+', match.group('extras') or '')) + + def _for(self, dist): + vars(self).update(dist=dist) + return self + + def __iter__(self): + """ + Supply iter so one may construct dicts of EntryPoints by name. + """ + msg = ( + "Construction of dict of EntryPoints is deprecated in " + "favor of EntryPoints." + ) + warnings.warn(msg, DeprecationWarning) + return iter((self.name, self)) + + def matches(self, **params): + attrs = (getattr(self, param) for param in params) + return all(map(operator.eq, params.values(), attrs)) + + def _key(self): + return self.name, self.value, self.group + + def __lt__(self, other): + return self._key() < other._key() + + def __eq__(self, other): + return self._key() == other._key() + + def __setattr__(self, name, value): + raise AttributeError("EntryPoint objects are immutable.") + + def __repr__(self): + return ( + f'EntryPoint(name={self.name!r}, value={self.value!r}, ' + f'group={self.group!r})' + ) + + def __hash__(self): + return hash(self._key()) + + +class DeprecatedList(list): + """ + Allow an otherwise immutable object to implement mutability + for compatibility. + + >>> recwarn = getfixture('recwarn') + >>> dl = DeprecatedList(range(3)) + >>> dl[0] = 1 + >>> dl.append(3) + >>> del dl[3] + >>> dl.reverse() + >>> dl.sort() + >>> dl.extend([4]) + >>> dl.pop(-1) + 4 + >>> dl.remove(1) + >>> dl += [5] + >>> dl + [6] + [1, 2, 5, 6] + >>> dl + (6,) + [1, 2, 5, 6] + >>> dl.insert(0, 0) + >>> dl + [0, 1, 2, 5] + >>> dl == [0, 1, 2, 5] + True + >>> dl == (0, 1, 2, 5) + True + >>> len(recwarn) + 1 + """ + + _warn = functools.partial( + warnings.warn, + "EntryPoints list interface is deprecated. Cast to list if needed.", + DeprecationWarning, + stacklevel=pypy_partial(2), + ) + + def _wrap_deprecated_method(method_name: str): # type: ignore + def wrapped(self, *args, **kwargs): + self._warn() + return getattr(super(), method_name)(*args, **kwargs) + + return wrapped + + for method_name in [ + '__setitem__', + '__delitem__', + 'append', + 'reverse', + 'extend', + 'pop', + 'remove', + '__iadd__', + 'insert', + 'sort', + ]: + locals()[method_name] = _wrap_deprecated_method(method_name) + + def __add__(self, other): + if not isinstance(other, tuple): + self._warn() + other = tuple(other) + return self.__class__(tuple(self) + other) + + def __eq__(self, other): + if not isinstance(other, tuple): + self._warn() + other = tuple(other) + + return tuple(self).__eq__(other) + + +class EntryPoints(DeprecatedList): + """ + An immutable collection of selectable EntryPoint objects. + """ + + __slots__ = () + + def __getitem__(self, name): # -> EntryPoint: + """ + Get the EntryPoint in self matching name. + """ + if isinstance(name, int): + warnings.warn( + "Accessing entry points by index is deprecated. " + "Cast to tuple if needed.", + DeprecationWarning, + stacklevel=2, + ) + return super().__getitem__(name) + try: + return next(iter(self.select(name=name))) + except StopIteration: + raise KeyError(name) + + def select(self, **params): + """ + Select entry points from self that match the + given parameters (typically group and/or name). + """ + return EntryPoints(ep for ep in self if ep.matches(**params)) + + @property + def names(self): + """ + Return the set of all names of all entry points. + """ + return {ep.name for ep in self} + + @property + def groups(self): + """ + Return the set of all groups of all entry points. + + For coverage while SelectableGroups is present. + >>> EntryPoints().groups + set() + """ + return {ep.group for ep in self} + + @classmethod + def _from_text_for(cls, text, dist): + return cls(ep._for(dist) for ep in cls._from_text(text)) + + @staticmethod + def _from_text(text): + return ( + EntryPoint(name=item.value.name, value=item.value.value, group=item.name) + for item in Sectioned.section_pairs(text or '') + ) + + +class Deprecated: + """ + Compatibility add-in for mapping to indicate that + mapping behavior is deprecated. + + >>> recwarn = getfixture('recwarn') + >>> class DeprecatedDict(Deprecated, dict): pass + >>> dd = DeprecatedDict(foo='bar') + >>> dd.get('baz', None) + >>> dd['foo'] + 'bar' + >>> list(dd) + ['foo'] + >>> list(dd.keys()) + ['foo'] + >>> 'foo' in dd + True + >>> list(dd.values()) + ['bar'] + >>> len(recwarn) + 1 + """ + + _warn = functools.partial( + warnings.warn, + "SelectableGroups dict interface is deprecated. Use select.", + DeprecationWarning, + stacklevel=pypy_partial(2), + ) + + def __getitem__(self, name): + self._warn() + return super().__getitem__(name) + + def get(self, name, default=None): + self._warn() + return super().get(name, default) + + def __iter__(self): + self._warn() + return super().__iter__() + + def __contains__(self, *args): + self._warn() + return super().__contains__(*args) + + def keys(self): + self._warn() + return super().keys() + + def values(self): + self._warn() + return super().values() + + +class SelectableGroups(Deprecated, dict): + """ + A backward- and forward-compatible result from + entry_points that fully implements the dict interface. + """ + + @classmethod + def load(cls, eps): + by_group = operator.attrgetter('group') + ordered = sorted(eps, key=by_group) + grouped = itertools.groupby(ordered, by_group) + return cls((group, EntryPoints(eps)) for group, eps in grouped) + + @property + def _all(self): + """ + Reconstruct a list of all entrypoints from the groups. + """ + groups = super(Deprecated, self).values() + return EntryPoints(itertools.chain.from_iterable(groups)) + + @property + def groups(self): + return self._all.groups + + @property + def names(self): + """ + for coverage: + >>> SelectableGroups().names + set() + """ + return self._all.names + + def select(self, **params): + if not params: + return self + return self._all.select(**params) + + +class PackagePath(pathlib.PurePosixPath): + """A reference to a path in a package""" + + def read_text(self, encoding='utf-8'): + with self.locate().open(encoding=encoding) as stream: + return stream.read() + + def read_binary(self): + with self.locate().open('rb') as stream: + return stream.read() + + def locate(self): + """Return a path-like object for this path""" + return self.dist.locate_file(self) + + +class FileHash: + def __init__(self, spec): + self.mode, _, self.value = spec.partition('=') + + def __repr__(self): + return f'' + + +class Distribution: + """A Python distribution package.""" + + @abc.abstractmethod + def read_text(self, filename): + """Attempt to load metadata file given by the name. + + :param filename: The name of the file in the distribution info. + :return: The text if found, otherwise None. + """ + + @abc.abstractmethod + def locate_file(self, path): + """ + Given a path to a file in this distribution, return a path + to it. + """ + + @classmethod + def from_name(cls, name): + """Return the Distribution for the given package name. + + :param name: The name of the distribution package to search for. + :return: The Distribution instance (or subclass thereof) for the named + package, if found. + :raises PackageNotFoundError: When the named package's distribution + metadata cannot be found. + """ + for resolver in cls._discover_resolvers(): + dists = resolver(DistributionFinder.Context(name=name)) + dist = next(iter(dists), None) + if dist is not None: + return dist + else: + raise PackageNotFoundError(name) + + @classmethod + def discover(cls, **kwargs): + """Return an iterable of Distribution objects for all packages. + + Pass a ``context`` or pass keyword arguments for constructing + a context. + + :context: A ``DistributionFinder.Context`` object. + :return: Iterable of Distribution objects for all packages. + """ + context = kwargs.pop('context', None) + if context and kwargs: + raise ValueError("cannot accept context and kwargs") + context = context or DistributionFinder.Context(**kwargs) + return itertools.chain.from_iterable( + resolver(context) for resolver in cls._discover_resolvers() + ) + + @staticmethod + def at(path): + """Return a Distribution for the indicated metadata path + + :param path: a string or path-like object + :return: a concrete Distribution instance for the path + """ + return PathDistribution(pathlib.Path(path)) + + @staticmethod + def _discover_resolvers(): + """Search the meta_path for resolvers.""" + declared = ( + getattr(finder, 'find_distributions', None) for finder in sys.meta_path + ) + return filter(None, declared) + + @classmethod + def _local(cls, root='.'): + from pep517 import build, meta + + system = build.compat_system(root) + builder = functools.partial( + meta.build, + source_dir=root, + system=system, + ) + return PathDistribution(zipp.Path(meta.build_as_zip(builder))) + + @property + def metadata(self) -> _meta.PackageMetadata: + """Return the parsed metadata for this Distribution. + + The returned object will have keys that name the various bits of + metadata. See PEP 566 for details. + """ + text = ( + self.read_text('METADATA') + or self.read_text('PKG-INFO') + # This last clause is here to support old egg-info files. Its + # effect is to just end up using the PathDistribution's self._path + # (which points to the egg-info file) attribute unchanged. + or self.read_text('') + ) + return _adapters.Message(email.message_from_string(text)) + + @property + def name(self): + """Return the 'Name' metadata for the distribution package.""" + return self.metadata['Name'] + + @property + def _normalized_name(self): + """Return a normalized version of the name.""" + return Prepared.normalize(self.name) + + @property + def version(self): + """Return the 'Version' metadata for the distribution package.""" + return self.metadata['Version'] + + @property + def entry_points(self): + return EntryPoints._from_text_for(self.read_text('entry_points.txt'), self) + + @property + def files(self): + """Files in this distribution. + + :return: List of PackagePath for this distribution or None + + Result is `None` if the metadata file that enumerates files + (i.e. RECORD for dist-info or SOURCES.txt for egg-info) is + missing. + Result may be empty if the metadata exists but is empty. + """ + + def make_file(name, hash=None, size_str=None): + result = PackagePath(name) + result.hash = FileHash(hash) if hash else None + result.size = int(size_str) if size_str else None + result.dist = self + return result + + @pass_none + def make_files(lines): + return list(starmap(make_file, csv.reader(lines))) + + return make_files(self._read_files_distinfo() or self._read_files_egginfo()) + + def _read_files_distinfo(self): + """ + Read the lines of RECORD + """ + text = self.read_text('RECORD') + return text and text.splitlines() + + def _read_files_egginfo(self): + """ + SOURCES.txt might contain literal commas, so wrap each line + in quotes. + """ + text = self.read_text('SOURCES.txt') + return text and map('"{}"'.format, text.splitlines()) + + @property + def requires(self): + """Generated requirements specified for this Distribution""" + reqs = self._read_dist_info_reqs() or self._read_egg_info_reqs() + return reqs and list(reqs) + + def _read_dist_info_reqs(self): + return self.metadata.get_all('Requires-Dist') + + def _read_egg_info_reqs(self): + source = self.read_text('requires.txt') + return source and self._deps_from_requires_text(source) + + @classmethod + def _deps_from_requires_text(cls, source): + return cls._convert_egg_info_reqs_to_simple_reqs(Sectioned.read(source)) + + @staticmethod + def _convert_egg_info_reqs_to_simple_reqs(sections): + """ + Historically, setuptools would solicit and store 'extra' + requirements, including those with environment markers, + in separate sections. More modern tools expect each + dependency to be defined separately, with any relevant + extras and environment markers attached directly to that + requirement. This method converts the former to the + latter. See _test_deps_from_requires_text for an example. + """ + + def make_condition(name): + return name and f'extra == "{name}"' + + def quoted_marker(section): + section = section or '' + extra, sep, markers = section.partition(':') + if extra and markers: + markers = f'({markers})' + conditions = list(filter(None, [markers, make_condition(extra)])) + return '; ' + ' and '.join(conditions) if conditions else '' + + def url_req_space(req): + """ + PEP 508 requires a space between the url_spec and the quoted_marker. + Ref python/importlib_metadata#357. + """ + # '@' is uniquely indicative of a url_req. + return ' ' * ('@' in req) + + for section in sections: + space = url_req_space(section.value) + yield section.value + space + quoted_marker(section.name) + + +class DistributionFinder(MetaPathFinder): + """ + A MetaPathFinder capable of discovering installed distributions. + """ + + class Context: + """ + Keyword arguments presented by the caller to + ``distributions()`` or ``Distribution.discover()`` + to narrow the scope of a search for distributions + in all DistributionFinders. + + Each DistributionFinder may expect any parameters + and should attempt to honor the canonical + parameters defined below when appropriate. + """ + + name = None + """ + Specific name for which a distribution finder should match. + A name of ``None`` matches all distributions. + """ + + def __init__(self, **kwargs): + vars(self).update(kwargs) + + @property + def path(self): + """ + The sequence of directory path that a distribution finder + should search. + + Typically refers to Python installed package paths such as + "site-packages" directories and defaults to ``sys.path``. + """ + return vars(self).get('path', sys.path) + + @abc.abstractmethod + def find_distributions(self, context=Context()): + """ + Find distributions. + + Return an iterable of all Distribution instances capable of + loading the metadata for packages matching the ``context``, + a DistributionFinder.Context instance. + """ + + +class FastPath: + """ + Micro-optimized class for searching a path for + children. + + >>> FastPath('').children() + ['...'] + """ + + @functools.lru_cache() # type: ignore + def __new__(cls, root): + return super().__new__(cls) + + def __init__(self, root): + self.root = str(root) + + def joinpath(self, child): + return pathlib.Path(self.root, child) + + def children(self): + with suppress(Exception): + return os.listdir(self.root or '.') + with suppress(Exception): + return self.zip_children() + return [] + + def zip_children(self): + zip_path = zipp.Path(self.root) + names = zip_path.root.namelist() + self.joinpath = zip_path.joinpath + + return dict.fromkeys(child.split(posixpath.sep, 1)[0] for child in names) + + def search(self, name): + return self.lookup(self.mtime).search(name) + + @property + def mtime(self): + with suppress(OSError): + return os.stat(self.root).st_mtime + self.lookup.cache_clear() + + @method_cache + def lookup(self, mtime): + return Lookup(self) + + +class Lookup: + def __init__(self, path: FastPath): + base = os.path.basename(path.root).lower() + base_is_egg = base.endswith(".egg") + self.infos = FreezableDefaultDict(list) + self.eggs = FreezableDefaultDict(list) + + for child in path.children(): + low = child.lower() + if low.endswith((".dist-info", ".egg-info")): + # rpartition is faster than splitext and suitable for this purpose. + name = low.rpartition(".")[0].partition("-")[0] + normalized = Prepared.normalize(name) + self.infos[normalized].append(path.joinpath(child)) + elif base_is_egg and low == "egg-info": + name = base.rpartition(".")[0].partition("-")[0] + legacy_normalized = Prepared.legacy_normalize(name) + self.eggs[legacy_normalized].append(path.joinpath(child)) + + self.infos.freeze() + self.eggs.freeze() + + def search(self, prepared): + infos = ( + self.infos[prepared.normalized] + if prepared + else itertools.chain.from_iterable(self.infos.values()) + ) + eggs = ( + self.eggs[prepared.legacy_normalized] + if prepared + else itertools.chain.from_iterable(self.eggs.values()) + ) + return itertools.chain(infos, eggs) + + +class Prepared: + """ + A prepared search for metadata on a possibly-named package. + """ + + normalized = None + legacy_normalized = None + + def __init__(self, name): + self.name = name + if name is None: + return + self.normalized = self.normalize(name) + self.legacy_normalized = self.legacy_normalize(name) + + @staticmethod + def normalize(name): + """ + PEP 503 normalization plus dashes as underscores. + """ + return re.sub(r"[-_.]+", "-", name).lower().replace('-', '_') + + @staticmethod + def legacy_normalize(name): + """ + Normalize the package name as found in the convention in + older packaging tools versions and specs. + """ + return name.lower().replace('-', '_') + + def __bool__(self): + return bool(self.name) + + +@install +class MetadataPathFinder(NullFinder, DistributionFinder): + """A degenerate finder for distribution packages on the file system. + + This finder supplies only a find_distributions() method for versions + of Python that do not have a PathFinder find_distributions(). + """ + + def find_distributions(self, context=DistributionFinder.Context()): + """ + Find distributions. + + Return an iterable of all Distribution instances capable of + loading the metadata for packages matching ``context.name`` + (or all names if ``None`` indicated) along the paths in the list + of directories ``context.path``. + """ + found = self._search_paths(context.name, context.path) + return map(PathDistribution, found) + + @classmethod + def _search_paths(cls, name, paths): + """Find metadata directories in paths heuristically.""" + prepared = Prepared(name) + return itertools.chain.from_iterable( + path.search(prepared) for path in map(FastPath, paths) + ) + + def invalidate_caches(cls): + FastPath.__new__.cache_clear() + + +class PathDistribution(Distribution): + def __init__(self, path: SimplePath): + """Construct a distribution. + + :param path: SimplePath indicating the metadata directory. + """ + self._path = path + + def read_text(self, filename): + with suppress( + FileNotFoundError, + IsADirectoryError, + KeyError, + NotADirectoryError, + PermissionError, + ): + return self._path.joinpath(filename).read_text(encoding='utf-8') + + read_text.__doc__ = Distribution.read_text.__doc__ + + def locate_file(self, path): + return self._path.parent / path + + @property + def _normalized_name(self): + """ + Performance optimization: where possible, resolve the + normalized name from the file system path. + """ + stem = os.path.basename(str(self._path)) + return self._name_from_stem(stem) or super()._normalized_name + + def _name_from_stem(self, stem): + name, ext = os.path.splitext(stem) + if ext not in ('.dist-info', '.egg-info'): + return + name, sep, rest = stem.partition('-') + return name + + +def distribution(distribution_name): + """Get the ``Distribution`` instance for the named package. + + :param distribution_name: The name of the distribution package as a string. + :return: A ``Distribution`` instance (or subclass thereof). + """ + return Distribution.from_name(distribution_name) + + +def distributions(**kwargs): + """Get all ``Distribution`` instances in the current environment. + + :return: An iterable of ``Distribution`` instances. + """ + return Distribution.discover(**kwargs) + + +def metadata(distribution_name) -> _meta.PackageMetadata: + """Get the metadata for the named package. + + :param distribution_name: The name of the distribution package to query. + :return: A PackageMetadata containing the parsed metadata. + """ + return Distribution.from_name(distribution_name).metadata + + +def version(distribution_name): + """Get the version string for the named package. + + :param distribution_name: The name of the distribution package to query. + :return: The version string for the package as defined in the package's + "Version" metadata key. + """ + return distribution(distribution_name).version + + +def entry_points(**params) -> Union[EntryPoints, SelectableGroups]: + """Return EntryPoint objects for all installed packages. + + Pass selection parameters (group or name) to filter the + result to entry points matching those properties (see + EntryPoints.select()). + + For compatibility, returns ``SelectableGroups`` object unless + selection parameters are supplied. In the future, this function + will return ``EntryPoints`` instead of ``SelectableGroups`` + even when no selection parameters are supplied. + + For maximum future compatibility, pass selection parameters + or invoke ``.select`` with parameters on the result. + + :return: EntryPoints or SelectableGroups for all installed packages. + """ + norm_name = operator.attrgetter('_normalized_name') + unique = functools.partial(unique_everseen, key=norm_name) + eps = itertools.chain.from_iterable( + dist.entry_points for dist in unique(distributions()) + ) + return SelectableGroups.load(eps).select(**params) + + +def files(distribution_name): + """Return a list of files for the named package. + + :param distribution_name: The name of the distribution package to query. + :return: List of files composing the distribution. + """ + return distribution(distribution_name).files + + +def requires(distribution_name): + """ + Return a list of requirements for the named package. + + :return: An iterator of requirements, suitable for + packaging.requirement.Requirement. + """ + return distribution(distribution_name).requires + + +def packages_distributions() -> Mapping[str, List[str]]: + """ + Return a mapping of top-level packages to their + distributions. + + >>> import collections.abc + >>> pkgs = packages_distributions() + >>> all(isinstance(dist, collections.abc.Sequence) for dist in pkgs.values()) + True + """ + pkg_to_dist = collections.defaultdict(list) + for dist in distributions(): + for pkg in _top_level_declared(dist) or _top_level_inferred(dist): + pkg_to_dist[pkg].append(dist.metadata['Name']) + return dict(pkg_to_dist) + + +def _top_level_declared(dist): + return (dist.read_text('top_level.txt') or '').split() + + +def _top_level_inferred(dist): + return { + f.parts[0] if len(f.parts) > 1 else f.with_suffix('').name + for f in always_iterable(dist.files) + if f.suffix == ".py" + } diff --git a/metaflow/_vendor/importlib_metadata/_adapters.py b/metaflow/_vendor/importlib_metadata/_adapters.py new file mode 100644 index 00000000000..aa460d3eda5 --- /dev/null +++ b/metaflow/_vendor/importlib_metadata/_adapters.py @@ -0,0 +1,68 @@ +import re +import textwrap +import email.message + +from ._text import FoldedCase + + +class Message(email.message.Message): + multiple_use_keys = set( + map( + FoldedCase, + [ + 'Classifier', + 'Obsoletes-Dist', + 'Platform', + 'Project-URL', + 'Provides-Dist', + 'Provides-Extra', + 'Requires-Dist', + 'Requires-External', + 'Supported-Platform', + 'Dynamic', + ], + ) + ) + """ + Keys that may be indicated multiple times per PEP 566. + """ + + def __new__(cls, orig: email.message.Message): + res = super().__new__(cls) + vars(res).update(vars(orig)) + return res + + def __init__(self, *args, **kwargs): + self._headers = self._repair_headers() + + # suppress spurious error from mypy + def __iter__(self): + return super().__iter__() + + def _repair_headers(self): + def redent(value): + "Correct for RFC822 indentation" + if not value or '\n' not in value: + return value + return textwrap.dedent(' ' * 8 + value) + + headers = [(key, redent(value)) for key, value in vars(self)['_headers']] + if self._payload: + headers.append(('Description', self.get_payload())) + return headers + + @property + def json(self): + """ + Convert PackageMetadata to a JSON-compatible format + per PEP 0566. + """ + + def transform(key): + value = self.get_all(key) if key in self.multiple_use_keys else self[key] + if key == 'Keywords': + value = re.split(r'\s+', value) + tk = key.lower().replace('-', '_') + return tk, value + + return dict(map(transform, map(FoldedCase, self))) diff --git a/metaflow/_vendor/importlib_metadata/_collections.py b/metaflow/_vendor/importlib_metadata/_collections.py new file mode 100644 index 00000000000..cf0954e1a30 --- /dev/null +++ b/metaflow/_vendor/importlib_metadata/_collections.py @@ -0,0 +1,30 @@ +import collections + + +# from jaraco.collections 3.3 +class FreezableDefaultDict(collections.defaultdict): + """ + Often it is desirable to prevent the mutation of + a default dict after its initial construction, such + as to prevent mutation during iteration. + + >>> dd = FreezableDefaultDict(list) + >>> dd[0].append('1') + >>> dd.freeze() + >>> dd[1] + [] + >>> len(dd) + 1 + """ + + def __missing__(self, key): + return getattr(self, '_frozen', super().__missing__)(key) + + def freeze(self): + self._frozen = lambda key: self.default_factory() + + +class Pair(collections.namedtuple('Pair', 'name value')): + @classmethod + def parse(cls, text): + return cls(*map(str.strip, text.split("=", 1))) diff --git a/metaflow/_vendor/importlib_metadata/_compat.py b/metaflow/_vendor/importlib_metadata/_compat.py new file mode 100644 index 00000000000..15927dbb753 --- /dev/null +++ b/metaflow/_vendor/importlib_metadata/_compat.py @@ -0,0 +1,71 @@ +import sys +import platform + + +__all__ = ['install', 'NullFinder', 'Protocol'] + + +try: + from typing import Protocol +except ImportError: # pragma: no cover + from metaflow._vendor.typing_extensions import Protocol # type: ignore + + +def install(cls): + """ + Class decorator for installation on sys.meta_path. + + Adds the backport DistributionFinder to sys.meta_path and + attempts to disable the finder functionality of the stdlib + DistributionFinder. + """ + sys.meta_path.append(cls()) + disable_stdlib_finder() + return cls + + +def disable_stdlib_finder(): + """ + Give the backport primacy for discovering path-based distributions + by monkey-patching the stdlib O_O. + + See #91 for more background for rationale on this sketchy + behavior. + """ + + def matches(finder): + return getattr( + finder, '__module__', None + ) == '_frozen_importlib_external' and hasattr(finder, 'find_distributions') + + for finder in filter(matches, sys.meta_path): # pragma: nocover + del finder.find_distributions + + +class NullFinder: + """ + A "Finder" (aka "MetaClassFinder") that never finds any modules, + but may find distributions. + """ + + @staticmethod + def find_spec(*args, **kwargs): + return None + + # In Python 2, the import system requires finders + # to have a find_module() method, but this usage + # is deprecated in Python 3 in favor of find_spec(). + # For the purposes of this finder (i.e. being present + # on sys.meta_path but having no other import + # system functionality), the two methods are identical. + find_module = find_spec + + +def pypy_partial(val): + """ + Adjust for variable stacklevel on partial under PyPy. + + Workaround for #327. + """ + is_pypy = platform.python_implementation() == 'PyPy' + return val + is_pypy diff --git a/metaflow/_vendor/importlib_metadata/_functools.py b/metaflow/_vendor/importlib_metadata/_functools.py new file mode 100644 index 00000000000..71f66bd03cb --- /dev/null +++ b/metaflow/_vendor/importlib_metadata/_functools.py @@ -0,0 +1,104 @@ +import types +import functools + + +# from jaraco.functools 3.3 +def method_cache(method, cache_wrapper=None): + """ + Wrap lru_cache to support storing the cache data in the object instances. + + Abstracts the common paradigm where the method explicitly saves an + underscore-prefixed protected property on first call and returns that + subsequently. + + >>> class MyClass: + ... calls = 0 + ... + ... @method_cache + ... def method(self, value): + ... self.calls += 1 + ... return value + + >>> a = MyClass() + >>> a.method(3) + 3 + >>> for x in range(75): + ... res = a.method(x) + >>> a.calls + 75 + + Note that the apparent behavior will be exactly like that of lru_cache + except that the cache is stored on each instance, so values in one + instance will not flush values from another, and when an instance is + deleted, so are the cached values for that instance. + + >>> b = MyClass() + >>> for x in range(35): + ... res = b.method(x) + >>> b.calls + 35 + >>> a.method(0) + 0 + >>> a.calls + 75 + + Note that if method had been decorated with ``functools.lru_cache()``, + a.calls would have been 76 (due to the cached value of 0 having been + flushed by the 'b' instance). + + Clear the cache with ``.cache_clear()`` + + >>> a.method.cache_clear() + + Same for a method that hasn't yet been called. + + >>> c = MyClass() + >>> c.method.cache_clear() + + Another cache wrapper may be supplied: + + >>> cache = functools.lru_cache(maxsize=2) + >>> MyClass.method2 = method_cache(lambda self: 3, cache_wrapper=cache) + >>> a = MyClass() + >>> a.method2() + 3 + + Caution - do not subsequently wrap the method with another decorator, such + as ``@property``, which changes the semantics of the function. + + See also + http://code.activestate.com/recipes/577452-a-memoize-decorator-for-instance-methods/ + for another implementation and additional justification. + """ + cache_wrapper = cache_wrapper or functools.lru_cache() + + def wrapper(self, *args, **kwargs): + # it's the first call, replace the method with a cached, bound method + bound_method = types.MethodType(method, self) + cached_method = cache_wrapper(bound_method) + setattr(self, method.__name__, cached_method) + return cached_method(*args, **kwargs) + + # Support cache clear even before cache has been created. + wrapper.cache_clear = lambda: None + + return wrapper + + +# From jaraco.functools 3.3 +def pass_none(func): + """ + Wrap func so it's not called if its first param is None + + >>> print_text = pass_none(print) + >>> print_text('text') + text + >>> print_text(None) + """ + + @functools.wraps(func) + def wrapper(param, *args, **kwargs): + if param is not None: + return func(param, *args, **kwargs) + + return wrapper diff --git a/metaflow/_vendor/importlib_metadata/_itertools.py b/metaflow/_vendor/importlib_metadata/_itertools.py new file mode 100644 index 00000000000..d4ca9b9140e --- /dev/null +++ b/metaflow/_vendor/importlib_metadata/_itertools.py @@ -0,0 +1,73 @@ +from itertools import filterfalse + + +def unique_everseen(iterable, key=None): + "List unique elements, preserving order. Remember all elements ever seen." + # unique_everseen('AAAABBBCCDAABBB') --> A B C D + # unique_everseen('ABBCcAD', str.lower) --> A B C D + seen = set() + seen_add = seen.add + if key is None: + for element in filterfalse(seen.__contains__, iterable): + seen_add(element) + yield element + else: + for element in iterable: + k = key(element) + if k not in seen: + seen_add(k) + yield element + + +# copied from more_itertools 8.8 +def always_iterable(obj, base_type=(str, bytes)): + """If *obj* is iterable, return an iterator over its items:: + + >>> obj = (1, 2, 3) + >>> list(always_iterable(obj)) + [1, 2, 3] + + If *obj* is not iterable, return a one-item iterable containing *obj*:: + + >>> obj = 1 + >>> list(always_iterable(obj)) + [1] + + If *obj* is ``None``, return an empty iterable: + + >>> obj = None + >>> list(always_iterable(None)) + [] + + By default, binary and text strings are not considered iterable:: + + >>> obj = 'foo' + >>> list(always_iterable(obj)) + ['foo'] + + If *base_type* is set, objects for which ``isinstance(obj, base_type)`` + returns ``True`` won't be considered iterable. + + >>> obj = {'a': 1} + >>> list(always_iterable(obj)) # Iterate over the dict's keys + ['a'] + >>> list(always_iterable(obj, base_type=dict)) # Treat dicts as a unit + [{'a': 1}] + + Set *base_type* to ``None`` to avoid any special handling and treat objects + Python considers iterable as iterable: + + >>> obj = 'foo' + >>> list(always_iterable(obj, base_type=None)) + ['f', 'o', 'o'] + """ + if obj is None: + return iter(()) + + if (base_type is not None) and isinstance(obj, base_type): + return iter((obj,)) + + try: + return iter(obj) + except TypeError: + return iter((obj,)) diff --git a/metaflow/_vendor/importlib_metadata/_meta.py b/metaflow/_vendor/importlib_metadata/_meta.py new file mode 100644 index 00000000000..37ee43e6ef4 --- /dev/null +++ b/metaflow/_vendor/importlib_metadata/_meta.py @@ -0,0 +1,48 @@ +from ._compat import Protocol +from typing import Any, Dict, Iterator, List, TypeVar, Union + + +_T = TypeVar("_T") + + +class PackageMetadata(Protocol): + def __len__(self) -> int: + ... # pragma: no cover + + def __contains__(self, item: str) -> bool: + ... # pragma: no cover + + def __getitem__(self, key: str) -> str: + ... # pragma: no cover + + def __iter__(self) -> Iterator[str]: + ... # pragma: no cover + + def get_all(self, name: str, failobj: _T = ...) -> Union[List[Any], _T]: + """ + Return all values associated with a possibly multi-valued key. + """ + + @property + def json(self) -> Dict[str, Union[str, List[str]]]: + """ + A JSON-compatible form of the metadata. + """ + + +class SimplePath(Protocol): + """ + A minimal subset of pathlib.Path required by PathDistribution. + """ + + def joinpath(self) -> 'SimplePath': + ... # pragma: no cover + + def __truediv__(self) -> 'SimplePath': + ... # pragma: no cover + + def parent(self) -> 'SimplePath': + ... # pragma: no cover + + def read_text(self) -> str: + ... # pragma: no cover diff --git a/metaflow/_vendor/importlib_metadata/_text.py b/metaflow/_vendor/importlib_metadata/_text.py new file mode 100644 index 00000000000..c88cfbb2349 --- /dev/null +++ b/metaflow/_vendor/importlib_metadata/_text.py @@ -0,0 +1,99 @@ +import re + +from ._functools import method_cache + + +# from jaraco.text 3.5 +class FoldedCase(str): + """ + A case insensitive string class; behaves just like str + except compares equal when the only variation is case. + + >>> s = FoldedCase('hello world') + + >>> s == 'Hello World' + True + + >>> 'Hello World' == s + True + + >>> s != 'Hello World' + False + + >>> s.index('O') + 4 + + >>> s.split('O') + ['hell', ' w', 'rld'] + + >>> sorted(map(FoldedCase, ['GAMMA', 'alpha', 'Beta'])) + ['alpha', 'Beta', 'GAMMA'] + + Sequence membership is straightforward. + + >>> "Hello World" in [s] + True + >>> s in ["Hello World"] + True + + You may test for set inclusion, but candidate and elements + must both be folded. + + >>> FoldedCase("Hello World") in {s} + True + >>> s in {FoldedCase("Hello World")} + True + + String inclusion works as long as the FoldedCase object + is on the right. + + >>> "hello" in FoldedCase("Hello World") + True + + But not if the FoldedCase object is on the left: + + >>> FoldedCase('hello') in 'Hello World' + False + + In that case, use in_: + + >>> FoldedCase('hello').in_('Hello World') + True + + >>> FoldedCase('hello') > FoldedCase('Hello') + False + """ + + def __lt__(self, other): + return self.lower() < other.lower() + + def __gt__(self, other): + return self.lower() > other.lower() + + def __eq__(self, other): + return self.lower() == other.lower() + + def __ne__(self, other): + return self.lower() != other.lower() + + def __hash__(self): + return hash(self.lower()) + + def __contains__(self, other): + return super().lower().__contains__(other.lower()) + + def in_(self, other): + "Does self appear in other?" + return self in FoldedCase(other) + + # cache lower since it's likely to be called frequently. + @method_cache + def lower(self): + return super().lower() + + def index(self, sub): + return self.lower().index(sub.lower()) + + def split(self, splitter=' ', maxsplit=0): + pattern = re.compile(re.escape(splitter), re.I) + return pattern.split(self, maxsplit) diff --git a/metaflow/plugins/env_escape/configurations/__init__.py b/metaflow/_vendor/importlib_metadata/py.typed similarity index 100% rename from metaflow/plugins/env_escape/configurations/__init__.py rename to metaflow/_vendor/importlib_metadata/py.typed diff --git a/metaflow/_vendor/packaging.LICENSE b/metaflow/_vendor/packaging.LICENSE new file mode 100644 index 00000000000..42ce7b75c92 --- /dev/null +++ b/metaflow/_vendor/packaging.LICENSE @@ -0,0 +1,23 @@ +Copyright (c) Donald Stufft and individual contributors. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/metaflow/_vendor/packaging/__init__.py b/metaflow/_vendor/packaging/__init__.py new file mode 100644 index 00000000000..4112fec0a5a --- /dev/null +++ b/metaflow/_vendor/packaging/__init__.py @@ -0,0 +1,15 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +__title__ = "packaging" +__summary__ = "Core utilities for Python packages" +__uri__ = "https://github.com/pypa/packaging" + +__version__ = "23.0" + +__author__ = "Donald Stufft and individual contributors" +__email__ = "donald@stufft.io" + +__license__ = "BSD-2-Clause or Apache-2.0" +__copyright__ = "2014-2019 %s" % __author__ diff --git a/metaflow/_vendor/packaging/_elffile.py b/metaflow/_vendor/packaging/_elffile.py new file mode 100644 index 00000000000..6fb19b30bb5 --- /dev/null +++ b/metaflow/_vendor/packaging/_elffile.py @@ -0,0 +1,108 @@ +""" +ELF file parser. + +This provides a class ``ELFFile`` that parses an ELF executable in a similar +interface to ``ZipFile``. Only the read interface is implemented. + +Based on: https://gist.github.com/lyssdod/f51579ae8d93c8657a5564aefc2ffbca +ELF header: https://refspecs.linuxfoundation.org/elf/gabi4+/ch4.eheader.html +""" + +import enum +import os +import struct +from typing import IO, Optional, Tuple + + +class ELFInvalid(ValueError): + pass + + +class EIClass(enum.IntEnum): + C32 = 1 + C64 = 2 + + +class EIData(enum.IntEnum): + Lsb = 1 + Msb = 2 + + +class EMachine(enum.IntEnum): + I386 = 3 + S390 = 22 + Arm = 40 + X8664 = 62 + AArc64 = 183 + + +class ELFFile: + """ + Representation of an ELF executable. + """ + + def __init__(self, f: IO[bytes]) -> None: + self._f = f + + try: + ident = self._read("16B") + except struct.error: + raise ELFInvalid("unable to parse identification") + magic = bytes(ident[:4]) + if magic != b"\x7fELF": + raise ELFInvalid(f"invalid magic: {magic!r}") + + self.capacity = ident[4] # Format for program header (bitness). + self.encoding = ident[5] # Data structure encoding (endianness). + + try: + # e_fmt: Format for program header. + # p_fmt: Format for section header. + # p_idx: Indexes to find p_type, p_offset, and p_filesz. + e_fmt, self._p_fmt, self._p_idx = { + (1, 1): ("HHIIIIIHHH", ">IIIIIIII", (0, 1, 4)), # 32-bit MSB. + (2, 1): ("HHIQQQIHHH", ">IIQQQQQQ", (0, 2, 5)), # 64-bit MSB. + }[(self.capacity, self.encoding)] + except KeyError: + raise ELFInvalid( + f"unrecognized capacity ({self.capacity}) or " + f"encoding ({self.encoding})" + ) + + try: + ( + _, + self.machine, # Architecture type. + _, + _, + self._e_phoff, # Offset of program header. + _, + self.flags, # Processor-specific flags. + _, + self._e_phentsize, # Size of section. + self._e_phnum, # Number of sections. + ) = self._read(e_fmt) + except struct.error as e: + raise ELFInvalid("unable to parse machine and section information") from e + + def _read(self, fmt: str) -> Tuple[int, ...]: + return struct.unpack(fmt, self._f.read(struct.calcsize(fmt))) + + @property + def interpreter(self) -> Optional[str]: + """ + The path recorded in the ``PT_INTERP`` section header. + """ + for index in range(self._e_phnum): + self._f.seek(self._e_phoff + self._e_phentsize * index) + try: + data = self._read(self._p_fmt) + except struct.error: + continue + if data[self._p_idx[0]] != 3: # Not PT_INTERP. + continue + self._f.seek(data[self._p_idx[1]]) + return os.fsdecode(self._f.read(data[self._p_idx[2]])).strip("\0") + return None diff --git a/metaflow/_vendor/packaging/_manylinux.py b/metaflow/_vendor/packaging/_manylinux.py new file mode 100644 index 00000000000..2f0cc7439a0 --- /dev/null +++ b/metaflow/_vendor/packaging/_manylinux.py @@ -0,0 +1,238 @@ +import collections +import contextlib +import functools +import os +import re +import sys +import warnings +from typing import Dict, Generator, Iterator, NamedTuple, Optional, Tuple + +from ._elffile import EIClass, EIData, ELFFile, EMachine + +EF_ARM_ABIMASK = 0xFF000000 +EF_ARM_ABI_VER5 = 0x05000000 +EF_ARM_ABI_FLOAT_HARD = 0x00000400 + + +@contextlib.contextmanager +def _parse_elf(path: str) -> Generator[Optional[ELFFile], None, None]: + try: + with open(path, "rb") as f: + yield ELFFile(f) + except (OSError, TypeError, ValueError): + yield None + + +def _is_linux_armhf(executable: str) -> bool: + # hard-float ABI can be detected from the ELF header of the running + # process + # https://static.docs.arm.com/ihi0044/g/aaelf32.pdf + with _parse_elf(executable) as f: + return ( + f is not None + and f.capacity == EIClass.C32 + and f.encoding == EIData.Lsb + and f.machine == EMachine.Arm + and f.flags & EF_ARM_ABIMASK == EF_ARM_ABI_VER5 + and f.flags & EF_ARM_ABI_FLOAT_HARD == EF_ARM_ABI_FLOAT_HARD + ) + + +def _is_linux_i686(executable: str) -> bool: + with _parse_elf(executable) as f: + return ( + f is not None + and f.capacity == EIClass.C32 + and f.encoding == EIData.Lsb + and f.machine == EMachine.I386 + ) + + +def _have_compatible_abi(executable: str, arch: str) -> bool: + if arch == "armv7l": + return _is_linux_armhf(executable) + if arch == "i686": + return _is_linux_i686(executable) + return arch in {"x86_64", "aarch64", "ppc64", "ppc64le", "s390x"} + + +# If glibc ever changes its major version, we need to know what the last +# minor version was, so we can build the complete list of all versions. +# For now, guess what the highest minor version might be, assume it will +# be 50 for testing. Once this actually happens, update the dictionary +# with the actual value. +_LAST_GLIBC_MINOR: Dict[int, int] = collections.defaultdict(lambda: 50) + + +class _GLibCVersion(NamedTuple): + major: int + minor: int + + +def _glibc_version_string_confstr() -> Optional[str]: + """ + Primary implementation of glibc_version_string using os.confstr. + """ + # os.confstr is quite a bit faster than ctypes.DLL. It's also less likely + # to be broken or missing. This strategy is used in the standard library + # platform module. + # https://github.com/python/cpython/blob/fcf1d003bf4f0100c/Lib/platform.py#L175-L183 + try: + # Should be a string like "glibc 2.17". + version_string: str = getattr(os, "confstr")("CS_GNU_LIBC_VERSION") + assert version_string is not None + _, version = version_string.rsplit() + except (AssertionError, AttributeError, OSError, ValueError): + # os.confstr() or CS_GNU_LIBC_VERSION not available (or a bad value)... + return None + return version + + +def _glibc_version_string_ctypes() -> Optional[str]: + """ + Fallback implementation of glibc_version_string using ctypes. + """ + try: + import ctypes + except ImportError: + return None + + # ctypes.CDLL(None) internally calls dlopen(NULL), and as the dlopen + # manpage says, "If filename is NULL, then the returned handle is for the + # main program". This way we can let the linker do the work to figure out + # which libc our process is actually using. + # + # We must also handle the special case where the executable is not a + # dynamically linked executable. This can occur when using musl libc, + # for example. In this situation, dlopen() will error, leading to an + # OSError. Interestingly, at least in the case of musl, there is no + # errno set on the OSError. The single string argument used to construct + # OSError comes from libc itself and is therefore not portable to + # hard code here. In any case, failure to call dlopen() means we + # can proceed, so we bail on our attempt. + try: + process_namespace = ctypes.CDLL(None) + except OSError: + return None + + try: + gnu_get_libc_version = process_namespace.gnu_get_libc_version + except AttributeError: + # Symbol doesn't exist -> therefore, we are not linked to + # glibc. + return None + + # Call gnu_get_libc_version, which returns a string like "2.5" + gnu_get_libc_version.restype = ctypes.c_char_p + version_str: str = gnu_get_libc_version() + # py2 / py3 compatibility: + if not isinstance(version_str, str): + version_str = version_str.decode("ascii") + + return version_str + + +def _glibc_version_string() -> Optional[str]: + """Returns glibc version string, or None if not using glibc.""" + return _glibc_version_string_confstr() or _glibc_version_string_ctypes() + + +def _parse_glibc_version(version_str: str) -> Tuple[int, int]: + """Parse glibc version. + + We use a regexp instead of str.split because we want to discard any + random junk that might come after the minor version -- this might happen + in patched/forked versions of glibc (e.g. Linaro's version of glibc + uses version strings like "2.20-2014.11"). See gh-3588. + """ + m = re.match(r"(?P[0-9]+)\.(?P[0-9]+)", version_str) + if not m: + warnings.warn( + f"Expected glibc version with 2 components major.minor," + f" got: {version_str}", + RuntimeWarning, + ) + return -1, -1 + return int(m.group("major")), int(m.group("minor")) + + +@functools.lru_cache() +def _get_glibc_version() -> Tuple[int, int]: + version_str = _glibc_version_string() + if version_str is None: + return (-1, -1) + return _parse_glibc_version(version_str) + + +# From PEP 513, PEP 600 +def _is_compatible(name: str, arch: str, version: _GLibCVersion) -> bool: + sys_glibc = _get_glibc_version() + if sys_glibc < version: + return False + # Check for presence of _manylinux module. + try: + import _manylinux # noqa + except ImportError: + return True + if hasattr(_manylinux, "manylinux_compatible"): + result = _manylinux.manylinux_compatible(version[0], version[1], arch) + if result is not None: + return bool(result) + return True + if version == _GLibCVersion(2, 5): + if hasattr(_manylinux, "manylinux1_compatible"): + return bool(_manylinux.manylinux1_compatible) + if version == _GLibCVersion(2, 12): + if hasattr(_manylinux, "manylinux2010_compatible"): + return bool(_manylinux.manylinux2010_compatible) + if version == _GLibCVersion(2, 17): + if hasattr(_manylinux, "manylinux2014_compatible"): + return bool(_manylinux.manylinux2014_compatible) + return True + + +_LEGACY_MANYLINUX_MAP = { + # CentOS 7 w/ glibc 2.17 (PEP 599) + (2, 17): "manylinux2014", + # CentOS 6 w/ glibc 2.12 (PEP 571) + (2, 12): "manylinux2010", + # CentOS 5 w/ glibc 2.5 (PEP 513) + (2, 5): "manylinux1", +} + + +def platform_tags(linux: str, arch: str) -> Iterator[str]: + if not _have_compatible_abi(sys.executable, arch): + return + # Oldest glibc to be supported regardless of architecture is (2, 17). + too_old_glibc2 = _GLibCVersion(2, 16) + if arch in {"x86_64", "i686"}: + # On x86/i686 also oldest glibc to be supported is (2, 5). + too_old_glibc2 = _GLibCVersion(2, 4) + current_glibc = _GLibCVersion(*_get_glibc_version()) + glibc_max_list = [current_glibc] + # We can assume compatibility across glibc major versions. + # https://sourceware.org/bugzilla/show_bug.cgi?id=24636 + # + # Build a list of maximum glibc versions so that we can + # output the canonical list of all glibc from current_glibc + # down to too_old_glibc2, including all intermediary versions. + for glibc_major in range(current_glibc.major - 1, 1, -1): + glibc_minor = _LAST_GLIBC_MINOR[glibc_major] + glibc_max_list.append(_GLibCVersion(glibc_major, glibc_minor)) + for glibc_max in glibc_max_list: + if glibc_max.major == too_old_glibc2.major: + min_minor = too_old_glibc2.minor + else: + # For other glibc major versions oldest supported is (x, 0). + min_minor = -1 + for glibc_minor in range(glibc_max.minor, min_minor, -1): + glibc_version = _GLibCVersion(glibc_max.major, glibc_minor) + tag = "manylinux_{}_{}".format(*glibc_version) + if _is_compatible(tag, arch, glibc_version): + yield linux.replace("linux", tag) + # Handle the legacy manylinux1, manylinux2010, manylinux2014 tags. + if glibc_version in _LEGACY_MANYLINUX_MAP: + legacy_tag = _LEGACY_MANYLINUX_MAP[glibc_version] + if _is_compatible(legacy_tag, arch, glibc_version): + yield linux.replace("linux", legacy_tag) diff --git a/metaflow/_vendor/packaging/_musllinux.py b/metaflow/_vendor/packaging/_musllinux.py new file mode 100644 index 00000000000..706ba600a93 --- /dev/null +++ b/metaflow/_vendor/packaging/_musllinux.py @@ -0,0 +1,80 @@ +"""PEP 656 support. + +This module implements logic to detect if the currently running Python is +linked against musl, and what musl version is used. +""" + +import functools +import re +import subprocess +import sys +from typing import Iterator, NamedTuple, Optional + +from ._elffile import ELFFile + + +class _MuslVersion(NamedTuple): + major: int + minor: int + + +def _parse_musl_version(output: str) -> Optional[_MuslVersion]: + lines = [n for n in (n.strip() for n in output.splitlines()) if n] + if len(lines) < 2 or lines[0][:4] != "musl": + return None + m = re.match(r"Version (\d+)\.(\d+)", lines[1]) + if not m: + return None + return _MuslVersion(major=int(m.group(1)), minor=int(m.group(2))) + + +@functools.lru_cache() +def _get_musl_version(executable: str) -> Optional[_MuslVersion]: + """Detect currently-running musl runtime version. + + This is done by checking the specified executable's dynamic linking + information, and invoking the loader to parse its output for a version + string. If the loader is musl, the output would be something like:: + + musl libc (x86_64) + Version 1.2.2 + Dynamic Program Loader + """ + try: + with open(executable, "rb") as f: + ld = ELFFile(f).interpreter + except (OSError, TypeError, ValueError): + return None + if ld is None or "musl" not in ld: + return None + proc = subprocess.run([ld], stderr=subprocess.PIPE, universal_newlines=True) + return _parse_musl_version(proc.stderr) + + +def platform_tags(arch: str) -> Iterator[str]: + """Generate musllinux tags compatible to the current platform. + + :param arch: Should be the part of platform tag after the ``linux_`` + prefix, e.g. ``x86_64``. The ``linux_`` prefix is assumed as a + prerequisite for the current platform to be musllinux-compatible. + + :returns: An iterator of compatible musllinux tags. + """ + sys_musl = _get_musl_version(sys.executable) + if sys_musl is None: # Python not dynamically linked against musl. + return + for minor in range(sys_musl.minor, -1, -1): + yield f"musllinux_{sys_musl.major}_{minor}_{arch}" + + +if __name__ == "__main__": # pragma: no cover + import sysconfig + + plat = sysconfig.get_platform() + assert plat.startswith("linux-"), "not linux" + + print("plat:", plat) + print("musl:", _get_musl_version(sys.executable)) + print("tags:", end=" ") + for t in platform_tags(re.sub(r"[.-]", "_", plat.split("-", 1)[-1])): + print(t, end="\n ") diff --git a/metaflow/_vendor/packaging/_parser.py b/metaflow/_vendor/packaging/_parser.py new file mode 100644 index 00000000000..2bc6a8f98b2 --- /dev/null +++ b/metaflow/_vendor/packaging/_parser.py @@ -0,0 +1,328 @@ +"""Handwritten parser of dependency specifiers. + +The docstring for each __parse_* function contains ENBF-inspired grammar representing +the implementation. +""" + +import ast +from typing import Any, List, NamedTuple, Optional, Tuple, Union + +from ._tokenizer import DEFAULT_RULES, Tokenizer + + +class Node: + def __init__(self, value: str) -> None: + self.value = value + + def __str__(self) -> str: + return self.value + + def __repr__(self) -> str: + return f"<{self.__class__.__name__}('{self}')>" + + def serialize(self) -> str: + raise NotImplementedError + + +class Variable(Node): + def serialize(self) -> str: + return str(self) + + +class Value(Node): + def serialize(self) -> str: + return f'"{self}"' + + +class Op(Node): + def serialize(self) -> str: + return str(self) + + +MarkerVar = Union[Variable, Value] +MarkerItem = Tuple[MarkerVar, Op, MarkerVar] +# MarkerAtom = Union[MarkerItem, List["MarkerAtom"]] +# MarkerList = List[Union["MarkerList", MarkerAtom, str]] +# mypy does not support recursive type definition +# https://github.com/python/mypy/issues/731 +MarkerAtom = Any +MarkerList = List[Any] + + +class ParsedRequirement(NamedTuple): + name: str + url: str + extras: List[str] + specifier: str + marker: Optional[MarkerList] + + +# -------------------------------------------------------------------------------------- +# Recursive descent parser for dependency specifier +# -------------------------------------------------------------------------------------- +def parse_requirement(source: str) -> ParsedRequirement: + return _parse_requirement(Tokenizer(source, rules=DEFAULT_RULES)) + + +def _parse_requirement(tokenizer: Tokenizer) -> ParsedRequirement: + """ + requirement = WS? IDENTIFIER WS? extras WS? requirement_details + """ + tokenizer.consume("WS") + + name_token = tokenizer.expect( + "IDENTIFIER", expected="package name at the start of dependency specifier" + ) + name = name_token.text + tokenizer.consume("WS") + + extras = _parse_extras(tokenizer) + tokenizer.consume("WS") + + url, specifier, marker = _parse_requirement_details(tokenizer) + tokenizer.expect("END", expected="end of dependency specifier") + + return ParsedRequirement(name, url, extras, specifier, marker) + + +def _parse_requirement_details( + tokenizer: Tokenizer, +) -> Tuple[str, str, Optional[MarkerList]]: + """ + requirement_details = AT URL (WS requirement_marker?)? + | specifier WS? (requirement_marker)? + """ + + specifier = "" + url = "" + marker = None + + if tokenizer.check("AT"): + tokenizer.read() + tokenizer.consume("WS") + + url_start = tokenizer.position + url = tokenizer.expect("URL", expected="URL after @").text + if tokenizer.check("END", peek=True): + return (url, specifier, marker) + + tokenizer.expect("WS", expected="whitespace after URL") + + # The input might end after whitespace. + if tokenizer.check("END", peek=True): + return (url, specifier, marker) + + marker = _parse_requirement_marker( + tokenizer, span_start=url_start, after="URL and whitespace" + ) + else: + specifier_start = tokenizer.position + specifier = _parse_specifier(tokenizer) + tokenizer.consume("WS") + + if tokenizer.check("END", peek=True): + return (url, specifier, marker) + + marker = _parse_requirement_marker( + tokenizer, + span_start=specifier_start, + after=( + "version specifier" + if specifier + else "name and no valid version specifier" + ), + ) + + return (url, specifier, marker) + + +def _parse_requirement_marker( + tokenizer: Tokenizer, *, span_start: int, after: str +) -> MarkerList: + """ + requirement_marker = SEMICOLON marker WS? + """ + + if not tokenizer.check("SEMICOLON"): + tokenizer.raise_syntax_error( + f"Expected end or semicolon (after {after})", + span_start=span_start, + ) + tokenizer.read() + + marker = _parse_marker(tokenizer) + tokenizer.consume("WS") + + return marker + + +def _parse_extras(tokenizer: Tokenizer) -> List[str]: + """ + extras = (LEFT_BRACKET wsp* extras_list? wsp* RIGHT_BRACKET)? + """ + if not tokenizer.check("LEFT_BRACKET", peek=True): + return [] + + with tokenizer.enclosing_tokens("LEFT_BRACKET", "RIGHT_BRACKET"): + tokenizer.consume("WS") + extras = _parse_extras_list(tokenizer) + tokenizer.consume("WS") + + return extras + + +def _parse_extras_list(tokenizer: Tokenizer) -> List[str]: + """ + extras_list = identifier (wsp* ',' wsp* identifier)* + """ + extras: List[str] = [] + + if not tokenizer.check("IDENTIFIER"): + return extras + + extras.append(tokenizer.read().text) + + while True: + tokenizer.consume("WS") + if tokenizer.check("IDENTIFIER", peek=True): + tokenizer.raise_syntax_error("Expected comma between extra names") + elif not tokenizer.check("COMMA"): + break + + tokenizer.read() + tokenizer.consume("WS") + + extra_token = tokenizer.expect("IDENTIFIER", expected="extra name after comma") + extras.append(extra_token.text) + + return extras + + +def _parse_specifier(tokenizer: Tokenizer) -> str: + """ + specifier = LEFT_PARENTHESIS WS? version_many WS? RIGHT_PARENTHESIS + | WS? version_many WS? + """ + with tokenizer.enclosing_tokens("LEFT_PARENTHESIS", "RIGHT_PARENTHESIS"): + tokenizer.consume("WS") + parsed_specifiers = _parse_version_many(tokenizer) + tokenizer.consume("WS") + + return parsed_specifiers + + +def _parse_version_many(tokenizer: Tokenizer) -> str: + """ + version_many = (SPECIFIER (WS? COMMA WS? SPECIFIER)*)? + """ + parsed_specifiers = "" + while tokenizer.check("SPECIFIER"): + parsed_specifiers += tokenizer.read().text + tokenizer.consume("WS") + if not tokenizer.check("COMMA"): + break + parsed_specifiers += tokenizer.read().text + tokenizer.consume("WS") + + return parsed_specifiers + + +# -------------------------------------------------------------------------------------- +# Recursive descent parser for marker expression +# -------------------------------------------------------------------------------------- +def parse_marker(source: str) -> MarkerList: + return _parse_marker(Tokenizer(source, rules=DEFAULT_RULES)) + + +def _parse_marker(tokenizer: Tokenizer) -> MarkerList: + """ + marker = marker_atom (BOOLOP marker_atom)+ + """ + expression = [_parse_marker_atom(tokenizer)] + while tokenizer.check("BOOLOP"): + token = tokenizer.read() + expr_right = _parse_marker_atom(tokenizer) + expression.extend((token.text, expr_right)) + return expression + + +def _parse_marker_atom(tokenizer: Tokenizer) -> MarkerAtom: + """ + marker_atom = WS? LEFT_PARENTHESIS WS? marker WS? RIGHT_PARENTHESIS WS? + | WS? marker_item WS? + """ + + tokenizer.consume("WS") + if tokenizer.check("LEFT_PARENTHESIS", peek=True): + with tokenizer.enclosing_tokens("LEFT_PARENTHESIS", "RIGHT_PARENTHESIS"): + tokenizer.consume("WS") + marker: MarkerAtom = _parse_marker(tokenizer) + tokenizer.consume("WS") + else: + marker = _parse_marker_item(tokenizer) + tokenizer.consume("WS") + return marker + + +def _parse_marker_item(tokenizer: Tokenizer) -> MarkerItem: + """ + marker_item = WS? marker_var WS? marker_op WS? marker_var WS? + """ + tokenizer.consume("WS") + marker_var_left = _parse_marker_var(tokenizer) + tokenizer.consume("WS") + marker_op = _parse_marker_op(tokenizer) + tokenizer.consume("WS") + marker_var_right = _parse_marker_var(tokenizer) + tokenizer.consume("WS") + return (marker_var_left, marker_op, marker_var_right) + + +def _parse_marker_var(tokenizer: Tokenizer) -> MarkerVar: + """ + marker_var = VARIABLE | QUOTED_STRING + """ + if tokenizer.check("VARIABLE"): + return process_env_var(tokenizer.read().text.replace(".", "_")) + elif tokenizer.check("QUOTED_STRING"): + return process_python_str(tokenizer.read().text) + else: + tokenizer.raise_syntax_error( + message="Expected a marker variable or quoted string" + ) + + +def process_env_var(env_var: str) -> Variable: + if ( + env_var == "platform_python_implementation" + or env_var == "python_implementation" + ): + return Variable("platform_python_implementation") + else: + return Variable(env_var) + + +def process_python_str(python_str: str) -> Value: + value = ast.literal_eval(python_str) + return Value(str(value)) + + +def _parse_marker_op(tokenizer: Tokenizer) -> Op: + """ + marker_op = IN | NOT IN | OP + """ + if tokenizer.check("IN"): + tokenizer.read() + return Op("in") + elif tokenizer.check("NOT"): + tokenizer.read() + tokenizer.expect("WS", expected="whitespace after 'not'") + tokenizer.expect("IN", expected="'in' after 'not'") + return Op("not in") + elif tokenizer.check("OP"): + return Op(tokenizer.read().text) + else: + return tokenizer.raise_syntax_error( + "Expected marker operator, one of " + "<=, <, !=, ==, >=, >, ~=, ===, in, not in" + ) diff --git a/metaflow/_vendor/packaging/_structures.py b/metaflow/_vendor/packaging/_structures.py new file mode 100644 index 00000000000..90a6465f968 --- /dev/null +++ b/metaflow/_vendor/packaging/_structures.py @@ -0,0 +1,61 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + + +class InfinityType: + def __repr__(self) -> str: + return "Infinity" + + def __hash__(self) -> int: + return hash(repr(self)) + + def __lt__(self, other: object) -> bool: + return False + + def __le__(self, other: object) -> bool: + return False + + def __eq__(self, other: object) -> bool: + return isinstance(other, self.__class__) + + def __gt__(self, other: object) -> bool: + return True + + def __ge__(self, other: object) -> bool: + return True + + def __neg__(self: object) -> "NegativeInfinityType": + return NegativeInfinity + + +Infinity = InfinityType() + + +class NegativeInfinityType: + def __repr__(self) -> str: + return "-Infinity" + + def __hash__(self) -> int: + return hash(repr(self)) + + def __lt__(self, other: object) -> bool: + return True + + def __le__(self, other: object) -> bool: + return True + + def __eq__(self, other: object) -> bool: + return isinstance(other, self.__class__) + + def __gt__(self, other: object) -> bool: + return False + + def __ge__(self, other: object) -> bool: + return False + + def __neg__(self: object) -> InfinityType: + return Infinity + + +NegativeInfinity = NegativeInfinityType() diff --git a/metaflow/_vendor/packaging/_tokenizer.py b/metaflow/_vendor/packaging/_tokenizer.py new file mode 100644 index 00000000000..b1fb207c7f1 --- /dev/null +++ b/metaflow/_vendor/packaging/_tokenizer.py @@ -0,0 +1,188 @@ +import contextlib +import re +from dataclasses import dataclass +from typing import Dict, Iterator, NoReturn, Optional, Tuple, Union + +from .specifiers import Specifier + + +@dataclass +class Token: + name: str + text: str + position: int + + +class ParserSyntaxError(Exception): + """The provided source text could not be parsed correctly.""" + + def __init__( + self, + message: str, + *, + source: str, + span: Tuple[int, int], + ) -> None: + self.span = span + self.message = message + self.source = source + + super().__init__() + + def __str__(self) -> str: + marker = " " * self.span[0] + "~" * (self.span[1] - self.span[0]) + "^" + return "\n ".join([self.message, self.source, marker]) + + +DEFAULT_RULES: "Dict[str, Union[str, re.Pattern[str]]]" = { + "LEFT_PARENTHESIS": r"\(", + "RIGHT_PARENTHESIS": r"\)", + "LEFT_BRACKET": r"\[", + "RIGHT_BRACKET": r"\]", + "SEMICOLON": r";", + "COMMA": r",", + "QUOTED_STRING": re.compile( + r""" + ( + ('[^']*') + | + ("[^"]*") + ) + """, + re.VERBOSE, + ), + "OP": r"(===|==|~=|!=|<=|>=|<|>)", + "BOOLOP": r"\b(or|and)\b", + "IN": r"\bin\b", + "NOT": r"\bnot\b", + "VARIABLE": re.compile( + r""" + \b( + python_version + |python_full_version + |os[._]name + |sys[._]platform + |platform_(release|system) + |platform[._](version|machine|python_implementation) + |python_implementation + |implementation_(name|version) + |extra + )\b + """, + re.VERBOSE, + ), + "SPECIFIER": re.compile( + Specifier._operator_regex_str + Specifier._version_regex_str, + re.VERBOSE | re.IGNORECASE, + ), + "AT": r"\@", + "URL": r"[^ \t]+", + "IDENTIFIER": r"\b[a-zA-Z0-9][a-zA-Z0-9._-]*\b", + "WS": r"[ \t]+", + "END": r"$", +} + + +class Tokenizer: + """Context-sensitive token parsing. + + Provides methods to examine the input stream to check whether the next token + matches. + """ + + def __init__( + self, + source: str, + *, + rules: "Dict[str, Union[str, re.Pattern[str]]]", + ) -> None: + self.source = source + self.rules: Dict[str, re.Pattern[str]] = { + name: re.compile(pattern) for name, pattern in rules.items() + } + self.next_token: Optional[Token] = None + self.position = 0 + + def consume(self, name: str) -> None: + """Move beyond provided token name, if at current position.""" + if self.check(name): + self.read() + + def check(self, name: str, *, peek: bool = False) -> bool: + """Check whether the next token has the provided name. + + By default, if the check succeeds, the token *must* be read before + another check. If `peek` is set to `True`, the token is not loaded and + would need to be checked again. + """ + assert ( + self.next_token is None + ), f"Cannot check for {name!r}, already have {self.next_token!r}" + assert name in self.rules, f"Unknown token name: {name!r}" + + expression = self.rules[name] + + match = expression.match(self.source, self.position) + if match is None: + return False + if not peek: + self.next_token = Token(name, match[0], self.position) + return True + + def expect(self, name: str, *, expected: str) -> Token: + """Expect a certain token name next, failing with a syntax error otherwise. + + The token is *not* read. + """ + if not self.check(name): + raise self.raise_syntax_error(f"Expected {expected}") + return self.read() + + def read(self) -> Token: + """Consume the next token and return it.""" + token = self.next_token + assert token is not None + + self.position += len(token.text) + self.next_token = None + + return token + + def raise_syntax_error( + self, + message: str, + *, + span_start: Optional[int] = None, + span_end: Optional[int] = None, + ) -> NoReturn: + """Raise ParserSyntaxError at the given position.""" + span = ( + self.position if span_start is None else span_start, + self.position if span_end is None else span_end, + ) + raise ParserSyntaxError( + message, + source=self.source, + span=span, + ) + + @contextlib.contextmanager + def enclosing_tokens(self, open_token: str, close_token: str) -> Iterator[bool]: + if self.check(open_token): + open_position = self.position + self.read() + else: + open_position = None + + yield open_position is not None + + if open_position is None: + return + + if not self.check(close_token): + self.raise_syntax_error( + f"Expected closing {close_token}", + span_start=open_position, + ) + + self.read() diff --git a/metaflow/_vendor/packaging/markers.py b/metaflow/_vendor/packaging/markers.py new file mode 100644 index 00000000000..68369c981b1 --- /dev/null +++ b/metaflow/_vendor/packaging/markers.py @@ -0,0 +1,245 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import operator +import os +import platform +import sys +from typing import Any, Callable, Dict, List, Optional, Tuple, Union + +from ._parser import MarkerAtom, MarkerList, Op, Value, Variable, parse_marker +from ._tokenizer import ParserSyntaxError +from .specifiers import InvalidSpecifier, Specifier +from .utils import canonicalize_name + +__all__ = [ + "InvalidMarker", + "UndefinedComparison", + "UndefinedEnvironmentName", + "Marker", + "default_environment", +] + +Operator = Callable[[str, str], bool] + + +class InvalidMarker(ValueError): + """ + An invalid marker was found, users should refer to PEP 508. + """ + + +class UndefinedComparison(ValueError): + """ + An invalid operation was attempted on a value that doesn't support it. + """ + + +class UndefinedEnvironmentName(ValueError): + """ + A name was attempted to be used that does not exist inside of the + environment. + """ + + +def _normalize_extra_values(results: Any) -> Any: + """ + Normalize extra values. + """ + if isinstance(results[0], tuple): + lhs, op, rhs = results[0] + if isinstance(lhs, Variable) and lhs.value == "extra": + normalized_extra = canonicalize_name(rhs.value) + rhs = Value(normalized_extra) + elif isinstance(rhs, Variable) and rhs.value == "extra": + normalized_extra = canonicalize_name(lhs.value) + lhs = Value(normalized_extra) + results[0] = lhs, op, rhs + return results + + +def _format_marker( + marker: Union[List[str], MarkerAtom, str], first: Optional[bool] = True +) -> str: + + assert isinstance(marker, (list, tuple, str)) + + # Sometimes we have a structure like [[...]] which is a single item list + # where the single item is itself it's own list. In that case we want skip + # the rest of this function so that we don't get extraneous () on the + # outside. + if ( + isinstance(marker, list) + and len(marker) == 1 + and isinstance(marker[0], (list, tuple)) + ): + return _format_marker(marker[0]) + + if isinstance(marker, list): + inner = (_format_marker(m, first=False) for m in marker) + if first: + return " ".join(inner) + else: + return "(" + " ".join(inner) + ")" + elif isinstance(marker, tuple): + return " ".join([m.serialize() for m in marker]) + else: + return marker + + +_operators: Dict[str, Operator] = { + "in": lambda lhs, rhs: lhs in rhs, + "not in": lambda lhs, rhs: lhs not in rhs, + "<": operator.lt, + "<=": operator.le, + "==": operator.eq, + "!=": operator.ne, + ">=": operator.ge, + ">": operator.gt, +} + + +def _eval_op(lhs: str, op: Op, rhs: str) -> bool: + try: + spec = Specifier("".join([op.serialize(), rhs])) + except InvalidSpecifier: + pass + else: + return spec.contains(lhs, prereleases=True) + + oper: Optional[Operator] = _operators.get(op.serialize()) + if oper is None: + raise UndefinedComparison(f"Undefined {op!r} on {lhs!r} and {rhs!r}.") + + return oper(lhs, rhs) + + +def _normalize(*values: str, key: str) -> Tuple[str, ...]: + # PEP 685 – Comparison of extra names for optional distribution dependencies + # https://peps.python.org/pep-0685/ + # > When comparing extra names, tools MUST normalize the names being + # > compared using the semantics outlined in PEP 503 for names + if key == "extra": + return tuple(canonicalize_name(v) for v in values) + + # other environment markers don't have such standards + return values + + +def _evaluate_markers(markers: MarkerList, environment: Dict[str, str]) -> bool: + groups: List[List[bool]] = [[]] + + for marker in markers: + assert isinstance(marker, (list, tuple, str)) + + if isinstance(marker, list): + groups[-1].append(_evaluate_markers(marker, environment)) + elif isinstance(marker, tuple): + lhs, op, rhs = marker + + if isinstance(lhs, Variable): + environment_key = lhs.value + lhs_value = environment[environment_key] + rhs_value = rhs.value + else: + lhs_value = lhs.value + environment_key = rhs.value + rhs_value = environment[environment_key] + + lhs_value, rhs_value = _normalize(lhs_value, rhs_value, key=environment_key) + groups[-1].append(_eval_op(lhs_value, op, rhs_value)) + else: + assert marker in ["and", "or"] + if marker == "or": + groups.append([]) + + return any(all(item) for item in groups) + + +def format_full_version(info: "sys._version_info") -> str: + version = "{0.major}.{0.minor}.{0.micro}".format(info) + kind = info.releaselevel + if kind != "final": + version += kind[0] + str(info.serial) + return version + + +def default_environment() -> Dict[str, str]: + iver = format_full_version(sys.implementation.version) + implementation_name = sys.implementation.name + return { + "implementation_name": implementation_name, + "implementation_version": iver, + "os_name": os.name, + "platform_machine": platform.machine(), + "platform_release": platform.release(), + "platform_system": platform.system(), + "platform_version": platform.version(), + "python_full_version": platform.python_version(), + "platform_python_implementation": platform.python_implementation(), + "python_version": ".".join(platform.python_version_tuple()[:2]), + "sys_platform": sys.platform, + } + + +class Marker: + def __init__(self, marker: str) -> None: + # Note: We create a Marker object without calling this constructor in + # packaging.requirements.Requirement. If any additional logic is + # added here, make sure to mirror/adapt Requirement. + try: + self._markers = _normalize_extra_values(parse_marker(marker)) + # The attribute `_markers` can be described in terms of a recursive type: + # MarkerList = List[Union[Tuple[Node, ...], str, MarkerList]] + # + # For example, the following expression: + # python_version > "3.6" or (python_version == "3.6" and os_name == "unix") + # + # is parsed into: + # [ + # (, ')>, ), + # 'and', + # [ + # (, , ), + # 'or', + # (, , ) + # ] + # ] + except ParserSyntaxError as e: + raise InvalidMarker(str(e)) from e + + def __str__(self) -> str: + return _format_marker(self._markers) + + def __repr__(self) -> str: + return f"" + + def __hash__(self) -> int: + return hash((self.__class__.__name__, str(self))) + + def __eq__(self, other: Any) -> bool: + if not isinstance(other, Marker): + return NotImplemented + + return str(self) == str(other) + + def evaluate(self, environment: Optional[Dict[str, str]] = None) -> bool: + """Evaluate a marker. + + Return the boolean from evaluating the given marker against the + environment. environment is an optional argument to override all or + part of the determined environment. + + The environment is determined from the current Python process. + """ + current_environment = default_environment() + current_environment["extra"] = "" + if environment is not None: + current_environment.update(environment) + # The API used to allow setting extra to None. We need to handle this + # case for backwards compatibility. + if current_environment["extra"] is None: + current_environment["extra"] = "" + + return _evaluate_markers(self._markers, current_environment) diff --git a/metaflow/_vendor/packaging/py.typed b/metaflow/_vendor/packaging/py.typed new file mode 100644 index 00000000000..e69de29bb2d diff --git a/metaflow/_vendor/packaging/requirements.py b/metaflow/_vendor/packaging/requirements.py new file mode 100644 index 00000000000..a9f9b9c7c95 --- /dev/null +++ b/metaflow/_vendor/packaging/requirements.py @@ -0,0 +1,95 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import urllib.parse +from typing import Any, List, Optional, Set + +from ._parser import parse_requirement +from ._tokenizer import ParserSyntaxError +from .markers import Marker, _normalize_extra_values +from .specifiers import SpecifierSet + + +class InvalidRequirement(ValueError): + """ + An invalid requirement was found, users should refer to PEP 508. + """ + + +class Requirement: + """Parse a requirement. + + Parse a given requirement string into its parts, such as name, specifier, + URL, and extras. Raises InvalidRequirement on a badly-formed requirement + string. + """ + + # TODO: Can we test whether something is contained within a requirement? + # If so how do we do that? Do we need to test against the _name_ of + # the thing as well as the version? What about the markers? + # TODO: Can we normalize the name and extra name? + + def __init__(self, requirement_string: str) -> None: + try: + parsed = parse_requirement(requirement_string) + except ParserSyntaxError as e: + raise InvalidRequirement(str(e)) from e + + self.name: str = parsed.name + if parsed.url: + parsed_url = urllib.parse.urlparse(parsed.url) + if parsed_url.scheme == "file": + if urllib.parse.urlunparse(parsed_url) != parsed.url: + raise InvalidRequirement("Invalid URL given") + elif not (parsed_url.scheme and parsed_url.netloc) or ( + not parsed_url.scheme and not parsed_url.netloc + ): + raise InvalidRequirement(f"Invalid URL: {parsed.url}") + self.url: Optional[str] = parsed.url + else: + self.url = None + self.extras: Set[str] = set(parsed.extras if parsed.extras else []) + self.specifier: SpecifierSet = SpecifierSet(parsed.specifier) + self.marker: Optional[Marker] = None + if parsed.marker is not None: + self.marker = Marker.__new__(Marker) + self.marker._markers = _normalize_extra_values(parsed.marker) + + def __str__(self) -> str: + parts: List[str] = [self.name] + + if self.extras: + formatted_extras = ",".join(sorted(self.extras)) + parts.append(f"[{formatted_extras}]") + + if self.specifier: + parts.append(str(self.specifier)) + + if self.url: + parts.append(f"@ {self.url}") + if self.marker: + parts.append(" ") + + if self.marker: + parts.append(f"; {self.marker}") + + return "".join(parts) + + def __repr__(self) -> str: + return f"" + + def __hash__(self) -> int: + return hash((self.__class__.__name__, str(self))) + + def __eq__(self, other: Any) -> bool: + if not isinstance(other, Requirement): + return NotImplemented + + return ( + self.name == other.name + and self.extras == other.extras + and self.specifier == other.specifier + and self.url == other.url + and self.marker == other.marker + ) diff --git a/metaflow/_vendor/packaging/specifiers.py b/metaflow/_vendor/packaging/specifiers.py new file mode 100644 index 00000000000..207e9b41efd --- /dev/null +++ b/metaflow/_vendor/packaging/specifiers.py @@ -0,0 +1,1005 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +""" +.. testsetup:: + + from metaflow._vendor.packaging.specifiers import Specifier, SpecifierSet, InvalidSpecifier + from metaflow._vendor.packaging.version import Version +""" + +import abc +import itertools +import re +from typing import ( + Callable, + Iterable, + Iterator, + List, + Optional, + Set, + Tuple, + TypeVar, + Union, +) + +from .utils import canonicalize_version +from .version import Version + +UnparsedVersion = Union[Version, str] +UnparsedVersionVar = TypeVar("UnparsedVersionVar", bound=UnparsedVersion) +CallableOperator = Callable[[Version, str], bool] + + +def _coerce_version(version: UnparsedVersion) -> Version: + if not isinstance(version, Version): + version = Version(version) + return version + + +class InvalidSpecifier(ValueError): + """ + Raised when attempting to create a :class:`Specifier` with a specifier + string that is invalid. + + >>> Specifier("lolwat") + Traceback (most recent call last): + ... + packaging.specifiers.InvalidSpecifier: Invalid specifier: 'lolwat' + """ + + +class BaseSpecifier(metaclass=abc.ABCMeta): + @abc.abstractmethod + def __str__(self) -> str: + """ + Returns the str representation of this Specifier-like object. This + should be representative of the Specifier itself. + """ + + @abc.abstractmethod + def __hash__(self) -> int: + """ + Returns a hash value for this Specifier-like object. + """ + + @abc.abstractmethod + def __eq__(self, other: object) -> bool: + """ + Returns a boolean representing whether or not the two Specifier-like + objects are equal. + + :param other: The other object to check against. + """ + + @property + @abc.abstractmethod + def prereleases(self) -> Optional[bool]: + """Whether or not pre-releases as a whole are allowed. + + This can be set to either ``True`` or ``False`` to explicitly enable or disable + prereleases or it can be set to ``None`` (the default) to use default semantics. + """ + + @prereleases.setter + def prereleases(self, value: bool) -> None: + """Setter for :attr:`prereleases`. + + :param value: The value to set. + """ + + @abc.abstractmethod + def contains(self, item: str, prereleases: Optional[bool] = None) -> bool: + """ + Determines if the given item is contained within this specifier. + """ + + @abc.abstractmethod + def filter( + self, iterable: Iterable[UnparsedVersionVar], prereleases: Optional[bool] = None + ) -> Iterator[UnparsedVersionVar]: + """ + Takes an iterable of items and filters them so that only items which + are contained within this specifier are allowed in it. + """ + + +class Specifier(BaseSpecifier): + """This class abstracts handling of version specifiers. + + .. tip:: + + It is generally not required to instantiate this manually. You should instead + prefer to work with :class:`SpecifierSet` instead, which can parse + comma-separated version specifiers (which is what package metadata contains). + """ + + _operator_regex_str = r""" + (?P(~=|==|!=|<=|>=|<|>|===)) + """ + _version_regex_str = r""" + (?P + (?: + # The identity operators allow for an escape hatch that will + # do an exact string match of the version you wish to install. + # This will not be parsed by PEP 440 and we cannot determine + # any semantic meaning from it. This operator is discouraged + # but included entirely as an escape hatch. + (?<====) # Only match for the identity operator + \s* + [^\s;)]* # The arbitrary version can be just about anything, + # we match everything except for whitespace, a + # semi-colon for marker support, and a closing paren + # since versions can be enclosed in them. + ) + | + (?: + # The (non)equality operators allow for wild card and local + # versions to be specified so we have to define these two + # operators separately to enable that. + (?<===|!=) # Only match for equals and not equals + + \s* + v? + (?:[0-9]+!)? # epoch + [0-9]+(?:\.[0-9]+)* # release + + # You cannot use a wild card and a pre-release, post-release, a dev or + # local version together so group them with a | and make them optional. + (?: + \.\* # Wild card syntax of .* + | + (?: # pre release + [-_\.]? + (alpha|beta|preview|pre|a|b|c|rc) + [-_\.]? + [0-9]* + )? + (?: # post release + (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) + )? + (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release + (?:\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*)? # local + )? + ) + | + (?: + # The compatible operator requires at least two digits in the + # release segment. + (?<=~=) # Only match for the compatible operator + + \s* + v? + (?:[0-9]+!)? # epoch + [0-9]+(?:\.[0-9]+)+ # release (We have a + instead of a *) + (?: # pre release + [-_\.]? + (alpha|beta|preview|pre|a|b|c|rc) + [-_\.]? + [0-9]* + )? + (?: # post release + (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) + )? + (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release + ) + | + (?: + # All other operators only allow a sub set of what the + # (non)equality operators do. Specifically they do not allow + # local versions to be specified nor do they allow the prefix + # matching wild cards. + (?=": "greater_than_equal", + "<": "less_than", + ">": "greater_than", + "===": "arbitrary", + } + + def __init__(self, spec: str = "", prereleases: Optional[bool] = None) -> None: + """Initialize a Specifier instance. + + :param spec: + The string representation of a specifier which will be parsed and + normalized before use. + :param prereleases: + This tells the specifier if it should accept prerelease versions if + applicable or not. The default of ``None`` will autodetect it from the + given specifiers. + :raises InvalidSpecifier: + If the given specifier is invalid (i.e. bad syntax). + """ + match = self._regex.search(spec) + if not match: + raise InvalidSpecifier(f"Invalid specifier: '{spec}'") + + self._spec: Tuple[str, str] = ( + match.group("operator").strip(), + match.group("version").strip(), + ) + + # Store whether or not this Specifier should accept prereleases + self._prereleases = prereleases + + @property + def prereleases(self) -> bool: + # If there is an explicit prereleases set for this, then we'll just + # blindly use that. + if self._prereleases is not None: + return self._prereleases + + # Look at all of our specifiers and determine if they are inclusive + # operators, and if they are if they are including an explicit + # prerelease. + operator, version = self._spec + if operator in ["==", ">=", "<=", "~=", "==="]: + # The == specifier can include a trailing .*, if it does we + # want to remove before parsing. + if operator == "==" and version.endswith(".*"): + version = version[:-2] + + # Parse the version, and if it is a pre-release than this + # specifier allows pre-releases. + if Version(version).is_prerelease: + return True + + return False + + @prereleases.setter + def prereleases(self, value: bool) -> None: + self._prereleases = value + + @property + def operator(self) -> str: + """The operator of this specifier. + + >>> Specifier("==1.2.3").operator + '==' + """ + return self._spec[0] + + @property + def version(self) -> str: + """The version of this specifier. + + >>> Specifier("==1.2.3").version + '1.2.3' + """ + return self._spec[1] + + def __repr__(self) -> str: + """A representation of the Specifier that shows all internal state. + + >>> Specifier('>=1.0.0') + =1.0.0')> + >>> Specifier('>=1.0.0', prereleases=False) + =1.0.0', prereleases=False)> + >>> Specifier('>=1.0.0', prereleases=True) + =1.0.0', prereleases=True)> + """ + pre = ( + f", prereleases={self.prereleases!r}" + if self._prereleases is not None + else "" + ) + + return f"<{self.__class__.__name__}({str(self)!r}{pre})>" + + def __str__(self) -> str: + """A string representation of the Specifier that can be round-tripped. + + >>> str(Specifier('>=1.0.0')) + '>=1.0.0' + >>> str(Specifier('>=1.0.0', prereleases=False)) + '>=1.0.0' + """ + return "{}{}".format(*self._spec) + + @property + def _canonical_spec(self) -> Tuple[str, str]: + canonical_version = canonicalize_version( + self._spec[1], + strip_trailing_zero=(self._spec[0] != "~="), + ) + return self._spec[0], canonical_version + + def __hash__(self) -> int: + return hash(self._canonical_spec) + + def __eq__(self, other: object) -> bool: + """Whether or not the two Specifier-like objects are equal. + + :param other: The other object to check against. + + The value of :attr:`prereleases` is ignored. + + >>> Specifier("==1.2.3") == Specifier("== 1.2.3.0") + True + >>> (Specifier("==1.2.3", prereleases=False) == + ... Specifier("==1.2.3", prereleases=True)) + True + >>> Specifier("==1.2.3") == "==1.2.3" + True + >>> Specifier("==1.2.3") == Specifier("==1.2.4") + False + >>> Specifier("==1.2.3") == Specifier("~=1.2.3") + False + """ + if isinstance(other, str): + try: + other = self.__class__(str(other)) + except InvalidSpecifier: + return NotImplemented + elif not isinstance(other, self.__class__): + return NotImplemented + + return self._canonical_spec == other._canonical_spec + + def _get_operator(self, op: str) -> CallableOperator: + operator_callable: CallableOperator = getattr( + self, f"_compare_{self._operators[op]}" + ) + return operator_callable + + def _compare_compatible(self, prospective: Version, spec: str) -> bool: + + # Compatible releases have an equivalent combination of >= and ==. That + # is that ~=2.2 is equivalent to >=2.2,==2.*. This allows us to + # implement this in terms of the other specifiers instead of + # implementing it ourselves. The only thing we need to do is construct + # the other specifiers. + + # We want everything but the last item in the version, but we want to + # ignore suffix segments. + prefix = ".".join( + list(itertools.takewhile(_is_not_suffix, _version_split(spec)))[:-1] + ) + + # Add the prefix notation to the end of our string + prefix += ".*" + + return self._get_operator(">=")(prospective, spec) and self._get_operator("==")( + prospective, prefix + ) + + def _compare_equal(self, prospective: Version, spec: str) -> bool: + + # We need special logic to handle prefix matching + if spec.endswith(".*"): + # In the case of prefix matching we want to ignore local segment. + normalized_prospective = canonicalize_version(prospective.public) + # Get the normalized version string ignoring the trailing .* + normalized_spec = canonicalize_version(spec[:-2], strip_trailing_zero=False) + # Split the spec out by dots, and pretend that there is an implicit + # dot in between a release segment and a pre-release segment. + split_spec = _version_split(normalized_spec) + + # Split the prospective version out by dots, and pretend that there + # is an implicit dot in between a release segment and a pre-release + # segment. + split_prospective = _version_split(normalized_prospective) + + # 0-pad the prospective version before shortening it to get the correct + # shortened version. + padded_prospective, _ = _pad_version(split_prospective, split_spec) + + # Shorten the prospective version to be the same length as the spec + # so that we can determine if the specifier is a prefix of the + # prospective version or not. + shortened_prospective = padded_prospective[: len(split_spec)] + + return shortened_prospective == split_spec + else: + # Convert our spec string into a Version + spec_version = Version(spec) + + # If the specifier does not have a local segment, then we want to + # act as if the prospective version also does not have a local + # segment. + if not spec_version.local: + prospective = Version(prospective.public) + + return prospective == spec_version + + def _compare_not_equal(self, prospective: Version, spec: str) -> bool: + return not self._compare_equal(prospective, spec) + + def _compare_less_than_equal(self, prospective: Version, spec: str) -> bool: + + # NB: Local version identifiers are NOT permitted in the version + # specifier, so local version labels can be universally removed from + # the prospective version. + return Version(prospective.public) <= Version(spec) + + def _compare_greater_than_equal(self, prospective: Version, spec: str) -> bool: + + # NB: Local version identifiers are NOT permitted in the version + # specifier, so local version labels can be universally removed from + # the prospective version. + return Version(prospective.public) >= Version(spec) + + def _compare_less_than(self, prospective: Version, spec_str: str) -> bool: + + # Convert our spec to a Version instance, since we'll want to work with + # it as a version. + spec = Version(spec_str) + + # Check to see if the prospective version is less than the spec + # version. If it's not we can short circuit and just return False now + # instead of doing extra unneeded work. + if not prospective < spec: + return False + + # This special case is here so that, unless the specifier itself + # includes is a pre-release version, that we do not accept pre-release + # versions for the version mentioned in the specifier (e.g. <3.1 should + # not match 3.1.dev0, but should match 3.0.dev0). + if not spec.is_prerelease and prospective.is_prerelease: + if Version(prospective.base_version) == Version(spec.base_version): + return False + + # If we've gotten to here, it means that prospective version is both + # less than the spec version *and* it's not a pre-release of the same + # version in the spec. + return True + + def _compare_greater_than(self, prospective: Version, spec_str: str) -> bool: + + # Convert our spec to a Version instance, since we'll want to work with + # it as a version. + spec = Version(spec_str) + + # Check to see if the prospective version is greater than the spec + # version. If it's not we can short circuit and just return False now + # instead of doing extra unneeded work. + if not prospective > spec: + return False + + # This special case is here so that, unless the specifier itself + # includes is a post-release version, that we do not accept + # post-release versions for the version mentioned in the specifier + # (e.g. >3.1 should not match 3.0.post0, but should match 3.2.post0). + if not spec.is_postrelease and prospective.is_postrelease: + if Version(prospective.base_version) == Version(spec.base_version): + return False + + # Ensure that we do not allow a local version of the version mentioned + # in the specifier, which is technically greater than, to match. + if prospective.local is not None: + if Version(prospective.base_version) == Version(spec.base_version): + return False + + # If we've gotten to here, it means that prospective version is both + # greater than the spec version *and* it's not a pre-release of the + # same version in the spec. + return True + + def _compare_arbitrary(self, prospective: Version, spec: str) -> bool: + return str(prospective).lower() == str(spec).lower() + + def __contains__(self, item: Union[str, Version]) -> bool: + """Return whether or not the item is contained in this specifier. + + :param item: The item to check for. + + This is used for the ``in`` operator and behaves the same as + :meth:`contains` with no ``prereleases`` argument passed. + + >>> "1.2.3" in Specifier(">=1.2.3") + True + >>> Version("1.2.3") in Specifier(">=1.2.3") + True + >>> "1.0.0" in Specifier(">=1.2.3") + False + >>> "1.3.0a1" in Specifier(">=1.2.3") + False + >>> "1.3.0a1" in Specifier(">=1.2.3", prereleases=True) + True + """ + return self.contains(item) + + def contains( + self, item: UnparsedVersion, prereleases: Optional[bool] = None + ) -> bool: + """Return whether or not the item is contained in this specifier. + + :param item: + The item to check for, which can be a version string or a + :class:`Version` instance. + :param prereleases: + Whether or not to match prereleases with this Specifier. If set to + ``None`` (the default), it uses :attr:`prereleases` to determine + whether or not prereleases are allowed. + + >>> Specifier(">=1.2.3").contains("1.2.3") + True + >>> Specifier(">=1.2.3").contains(Version("1.2.3")) + True + >>> Specifier(">=1.2.3").contains("1.0.0") + False + >>> Specifier(">=1.2.3").contains("1.3.0a1") + False + >>> Specifier(">=1.2.3", prereleases=True).contains("1.3.0a1") + True + >>> Specifier(">=1.2.3").contains("1.3.0a1", prereleases=True) + True + """ + + # Determine if prereleases are to be allowed or not. + if prereleases is None: + prereleases = self.prereleases + + # Normalize item to a Version, this allows us to have a shortcut for + # "2.0" in Specifier(">=2") + normalized_item = _coerce_version(item) + + # Determine if we should be supporting prereleases in this specifier + # or not, if we do not support prereleases than we can short circuit + # logic if this version is a prereleases. + if normalized_item.is_prerelease and not prereleases: + return False + + # Actually do the comparison to determine if this item is contained + # within this Specifier or not. + operator_callable: CallableOperator = self._get_operator(self.operator) + return operator_callable(normalized_item, self.version) + + def filter( + self, iterable: Iterable[UnparsedVersionVar], prereleases: Optional[bool] = None + ) -> Iterator[UnparsedVersionVar]: + """Filter items in the given iterable, that match the specifier. + + :param iterable: + An iterable that can contain version strings and :class:`Version` instances. + The items in the iterable will be filtered according to the specifier. + :param prereleases: + Whether or not to allow prereleases in the returned iterator. If set to + ``None`` (the default), it will be intelligently decide whether to allow + prereleases or not (based on the :attr:`prereleases` attribute, and + whether the only versions matching are prereleases). + + This method is smarter than just ``filter(Specifier().contains, [...])`` + because it implements the rule from :pep:`440` that a prerelease item + SHOULD be accepted if no other versions match the given specifier. + + >>> list(Specifier(">=1.2.3").filter(["1.2", "1.3", "1.5a1"])) + ['1.3'] + >>> list(Specifier(">=1.2.3").filter(["1.2", "1.2.3", "1.3", Version("1.4")])) + ['1.2.3', '1.3', ] + >>> list(Specifier(">=1.2.3").filter(["1.2", "1.5a1"])) + ['1.5a1'] + >>> list(Specifier(">=1.2.3").filter(["1.3", "1.5a1"], prereleases=True)) + ['1.3', '1.5a1'] + >>> list(Specifier(">=1.2.3", prereleases=True).filter(["1.3", "1.5a1"])) + ['1.3', '1.5a1'] + """ + + yielded = False + found_prereleases = [] + + kw = {"prereleases": prereleases if prereleases is not None else True} + + # Attempt to iterate over all the values in the iterable and if any of + # them match, yield them. + for version in iterable: + parsed_version = _coerce_version(version) + + if self.contains(parsed_version, **kw): + # If our version is a prerelease, and we were not set to allow + # prereleases, then we'll store it for later in case nothing + # else matches this specifier. + if parsed_version.is_prerelease and not ( + prereleases or self.prereleases + ): + found_prereleases.append(version) + # Either this is not a prerelease, or we should have been + # accepting prereleases from the beginning. + else: + yielded = True + yield version + + # Now that we've iterated over everything, determine if we've yielded + # any values, and if we have not and we have any prereleases stored up + # then we will go ahead and yield the prereleases. + if not yielded and found_prereleases: + for version in found_prereleases: + yield version + + +_prefix_regex = re.compile(r"^([0-9]+)((?:a|b|c|rc)[0-9]+)$") + + +def _version_split(version: str) -> List[str]: + result: List[str] = [] + for item in version.split("."): + match = _prefix_regex.search(item) + if match: + result.extend(match.groups()) + else: + result.append(item) + return result + + +def _is_not_suffix(segment: str) -> bool: + return not any( + segment.startswith(prefix) for prefix in ("dev", "a", "b", "rc", "post") + ) + + +def _pad_version(left: List[str], right: List[str]) -> Tuple[List[str], List[str]]: + left_split, right_split = [], [] + + # Get the release segment of our versions + left_split.append(list(itertools.takewhile(lambda x: x.isdigit(), left))) + right_split.append(list(itertools.takewhile(lambda x: x.isdigit(), right))) + + # Get the rest of our versions + left_split.append(left[len(left_split[0]) :]) + right_split.append(right[len(right_split[0]) :]) + + # Insert our padding + left_split.insert(1, ["0"] * max(0, len(right_split[0]) - len(left_split[0]))) + right_split.insert(1, ["0"] * max(0, len(left_split[0]) - len(right_split[0]))) + + return (list(itertools.chain(*left_split)), list(itertools.chain(*right_split))) + + +class SpecifierSet(BaseSpecifier): + """This class abstracts handling of a set of version specifiers. + + It can be passed a single specifier (``>=3.0``), a comma-separated list of + specifiers (``>=3.0,!=3.1``), or no specifier at all. + """ + + def __init__( + self, specifiers: str = "", prereleases: Optional[bool] = None + ) -> None: + """Initialize a SpecifierSet instance. + + :param specifiers: + The string representation of a specifier or a comma-separated list of + specifiers which will be parsed and normalized before use. + :param prereleases: + This tells the SpecifierSet if it should accept prerelease versions if + applicable or not. The default of ``None`` will autodetect it from the + given specifiers. + + :raises InvalidSpecifier: + If the given ``specifiers`` are not parseable than this exception will be + raised. + """ + + # Split on `,` to break each individual specifier into it's own item, and + # strip each item to remove leading/trailing whitespace. + split_specifiers = [s.strip() for s in specifiers.split(",") if s.strip()] + + # Parsed each individual specifier, attempting first to make it a + # Specifier. + parsed: Set[Specifier] = set() + for specifier in split_specifiers: + parsed.add(Specifier(specifier)) + + # Turn our parsed specifiers into a frozen set and save them for later. + self._specs = frozenset(parsed) + + # Store our prereleases value so we can use it later to determine if + # we accept prereleases or not. + self._prereleases = prereleases + + @property + def prereleases(self) -> Optional[bool]: + # If we have been given an explicit prerelease modifier, then we'll + # pass that through here. + if self._prereleases is not None: + return self._prereleases + + # If we don't have any specifiers, and we don't have a forced value, + # then we'll just return None since we don't know if this should have + # pre-releases or not. + if not self._specs: + return None + + # Otherwise we'll see if any of the given specifiers accept + # prereleases, if any of them do we'll return True, otherwise False. + return any(s.prereleases for s in self._specs) + + @prereleases.setter + def prereleases(self, value: bool) -> None: + self._prereleases = value + + def __repr__(self) -> str: + """A representation of the specifier set that shows all internal state. + + Note that the ordering of the individual specifiers within the set may not + match the input string. + + >>> SpecifierSet('>=1.0.0,!=2.0.0') + =1.0.0')> + >>> SpecifierSet('>=1.0.0,!=2.0.0', prereleases=False) + =1.0.0', prereleases=False)> + >>> SpecifierSet('>=1.0.0,!=2.0.0', prereleases=True) + =1.0.0', prereleases=True)> + """ + pre = ( + f", prereleases={self.prereleases!r}" + if self._prereleases is not None + else "" + ) + + return f"" + + def __str__(self) -> str: + """A string representation of the specifier set that can be round-tripped. + + Note that the ordering of the individual specifiers within the set may not + match the input string. + + >>> str(SpecifierSet(">=1.0.0,!=1.0.1")) + '!=1.0.1,>=1.0.0' + >>> str(SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False)) + '!=1.0.1,>=1.0.0' + """ + return ",".join(sorted(str(s) for s in self._specs)) + + def __hash__(self) -> int: + return hash(self._specs) + + def __and__(self, other: Union["SpecifierSet", str]) -> "SpecifierSet": + """Return a SpecifierSet which is a combination of the two sets. + + :param other: The other object to combine with. + + >>> SpecifierSet(">=1.0.0,!=1.0.1") & '<=2.0.0,!=2.0.1' + =1.0.0')> + >>> SpecifierSet(">=1.0.0,!=1.0.1") & SpecifierSet('<=2.0.0,!=2.0.1') + =1.0.0')> + """ + if isinstance(other, str): + other = SpecifierSet(other) + elif not isinstance(other, SpecifierSet): + return NotImplemented + + specifier = SpecifierSet() + specifier._specs = frozenset(self._specs | other._specs) + + if self._prereleases is None and other._prereleases is not None: + specifier._prereleases = other._prereleases + elif self._prereleases is not None and other._prereleases is None: + specifier._prereleases = self._prereleases + elif self._prereleases == other._prereleases: + specifier._prereleases = self._prereleases + else: + raise ValueError( + "Cannot combine SpecifierSets with True and False prerelease " + "overrides." + ) + + return specifier + + def __eq__(self, other: object) -> bool: + """Whether or not the two SpecifierSet-like objects are equal. + + :param other: The other object to check against. + + The value of :attr:`prereleases` is ignored. + + >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0,!=1.0.1") + True + >>> (SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False) == + ... SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True)) + True + >>> SpecifierSet(">=1.0.0,!=1.0.1") == ">=1.0.0,!=1.0.1" + True + >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0") + False + >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0,!=1.0.2") + False + """ + if isinstance(other, (str, Specifier)): + other = SpecifierSet(str(other)) + elif not isinstance(other, SpecifierSet): + return NotImplemented + + return self._specs == other._specs + + def __len__(self) -> int: + """Returns the number of specifiers in this specifier set.""" + return len(self._specs) + + def __iter__(self) -> Iterator[Specifier]: + """ + Returns an iterator over all the underlying :class:`Specifier` instances + in this specifier set. + + >>> sorted(SpecifierSet(">=1.0.0,!=1.0.1"), key=str) + [, =1.0.0')>] + """ + return iter(self._specs) + + def __contains__(self, item: UnparsedVersion) -> bool: + """Return whether or not the item is contained in this specifier. + + :param item: The item to check for. + + This is used for the ``in`` operator and behaves the same as + :meth:`contains` with no ``prereleases`` argument passed. + + >>> "1.2.3" in SpecifierSet(">=1.0.0,!=1.0.1") + True + >>> Version("1.2.3") in SpecifierSet(">=1.0.0,!=1.0.1") + True + >>> "1.0.1" in SpecifierSet(">=1.0.0,!=1.0.1") + False + >>> "1.3.0a1" in SpecifierSet(">=1.0.0,!=1.0.1") + False + >>> "1.3.0a1" in SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True) + True + """ + return self.contains(item) + + def contains( + self, + item: UnparsedVersion, + prereleases: Optional[bool] = None, + installed: Optional[bool] = None, + ) -> bool: + """Return whether or not the item is contained in this SpecifierSet. + + :param item: + The item to check for, which can be a version string or a + :class:`Version` instance. + :param prereleases: + Whether or not to match prereleases with this SpecifierSet. If set to + ``None`` (the default), it uses :attr:`prereleases` to determine + whether or not prereleases are allowed. + + >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.2.3") + True + >>> SpecifierSet(">=1.0.0,!=1.0.1").contains(Version("1.2.3")) + True + >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.0.1") + False + >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.3.0a1") + False + >>> SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True).contains("1.3.0a1") + True + >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.3.0a1", prereleases=True) + True + """ + # Ensure that our item is a Version instance. + if not isinstance(item, Version): + item = Version(item) + + # Determine if we're forcing a prerelease or not, if we're not forcing + # one for this particular filter call, then we'll use whatever the + # SpecifierSet thinks for whether or not we should support prereleases. + if prereleases is None: + prereleases = self.prereleases + + # We can determine if we're going to allow pre-releases by looking to + # see if any of the underlying items supports them. If none of them do + # and this item is a pre-release then we do not allow it and we can + # short circuit that here. + # Note: This means that 1.0.dev1 would not be contained in something + # like >=1.0.devabc however it would be in >=1.0.debabc,>0.0.dev0 + if not prereleases and item.is_prerelease: + return False + + if installed and item.is_prerelease: + item = Version(item.base_version) + + # We simply dispatch to the underlying specs here to make sure that the + # given version is contained within all of them. + # Note: This use of all() here means that an empty set of specifiers + # will always return True, this is an explicit design decision. + return all(s.contains(item, prereleases=prereleases) for s in self._specs) + + def filter( + self, iterable: Iterable[UnparsedVersionVar], prereleases: Optional[bool] = None + ) -> Iterator[UnparsedVersionVar]: + """Filter items in the given iterable, that match the specifiers in this set. + + :param iterable: + An iterable that can contain version strings and :class:`Version` instances. + The items in the iterable will be filtered according to the specifier. + :param prereleases: + Whether or not to allow prereleases in the returned iterator. If set to + ``None`` (the default), it will be intelligently decide whether to allow + prereleases or not (based on the :attr:`prereleases` attribute, and + whether the only versions matching are prereleases). + + This method is smarter than just ``filter(SpecifierSet(...).contains, [...])`` + because it implements the rule from :pep:`440` that a prerelease item + SHOULD be accepted if no other versions match the given specifier. + + >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.3", "1.5a1"])) + ['1.3'] + >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.3", Version("1.4")])) + ['1.3', ] + >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.5a1"])) + [] + >>> list(SpecifierSet(">=1.2.3").filter(["1.3", "1.5a1"], prereleases=True)) + ['1.3', '1.5a1'] + >>> list(SpecifierSet(">=1.2.3", prereleases=True).filter(["1.3", "1.5a1"])) + ['1.3', '1.5a1'] + + An "empty" SpecifierSet will filter items based on the presence of prerelease + versions in the set. + + >>> list(SpecifierSet("").filter(["1.3", "1.5a1"])) + ['1.3'] + >>> list(SpecifierSet("").filter(["1.5a1"])) + ['1.5a1'] + >>> list(SpecifierSet("", prereleases=True).filter(["1.3", "1.5a1"])) + ['1.3', '1.5a1'] + >>> list(SpecifierSet("").filter(["1.3", "1.5a1"], prereleases=True)) + ['1.3', '1.5a1'] + """ + # Determine if we're forcing a prerelease or not, if we're not forcing + # one for this particular filter call, then we'll use whatever the + # SpecifierSet thinks for whether or not we should support prereleases. + if prereleases is None: + prereleases = self.prereleases + + # If we have any specifiers, then we want to wrap our iterable in the + # filter method for each one, this will act as a logical AND amongst + # each specifier. + if self._specs: + for spec in self._specs: + iterable = spec.filter(iterable, prereleases=bool(prereleases)) + return iter(iterable) + # If we do not have any specifiers, then we need to have a rough filter + # which will filter out any pre-releases, unless there are no final + # releases. + else: + filtered: List[UnparsedVersionVar] = [] + found_prereleases: List[UnparsedVersionVar] = [] + + for item in iterable: + parsed_version = _coerce_version(item) + + # Store any item which is a pre-release for later unless we've + # already found a final version or we are accepting prereleases + if parsed_version.is_prerelease and not prereleases: + if not filtered: + found_prereleases.append(item) + else: + filtered.append(item) + + # If we've found no items except for pre-releases, then we'll go + # ahead and use the pre-releases + if not filtered and found_prereleases and prereleases is None: + return iter(found_prereleases) + + return iter(filtered) diff --git a/metaflow/_vendor/packaging/tags.py b/metaflow/_vendor/packaging/tags.py new file mode 100644 index 00000000000..19ccbde3ea2 --- /dev/null +++ b/metaflow/_vendor/packaging/tags.py @@ -0,0 +1,546 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import logging +import platform +import subprocess +import sys +import sysconfig +from importlib.machinery import EXTENSION_SUFFIXES +from typing import ( + Dict, + FrozenSet, + Iterable, + Iterator, + List, + Optional, + Sequence, + Tuple, + Union, + cast, +) + +from . import _manylinux, _musllinux + +logger = logging.getLogger(__name__) + +PythonVersion = Sequence[int] +MacVersion = Tuple[int, int] + +INTERPRETER_SHORT_NAMES: Dict[str, str] = { + "python": "py", # Generic. + "cpython": "cp", + "pypy": "pp", + "ironpython": "ip", + "jython": "jy", +} + + +_32_BIT_INTERPRETER = sys.maxsize <= 2**32 + + +class Tag: + """ + A representation of the tag triple for a wheel. + + Instances are considered immutable and thus are hashable. Equality checking + is also supported. + """ + + __slots__ = ["_interpreter", "_abi", "_platform", "_hash"] + + def __init__(self, interpreter: str, abi: str, platform: str) -> None: + self._interpreter = interpreter.lower() + self._abi = abi.lower() + self._platform = platform.lower() + # The __hash__ of every single element in a Set[Tag] will be evaluated each time + # that a set calls its `.disjoint()` method, which may be called hundreds of + # times when scanning a page of links for packages with tags matching that + # Set[Tag]. Pre-computing the value here produces significant speedups for + # downstream consumers. + self._hash = hash((self._interpreter, self._abi, self._platform)) + + @property + def interpreter(self) -> str: + return self._interpreter + + @property + def abi(self) -> str: + return self._abi + + @property + def platform(self) -> str: + return self._platform + + def __eq__(self, other: object) -> bool: + if not isinstance(other, Tag): + return NotImplemented + + return ( + (self._hash == other._hash) # Short-circuit ASAP for perf reasons. + and (self._platform == other._platform) + and (self._abi == other._abi) + and (self._interpreter == other._interpreter) + ) + + def __hash__(self) -> int: + return self._hash + + def __str__(self) -> str: + return f"{self._interpreter}-{self._abi}-{self._platform}" + + def __repr__(self) -> str: + return f"<{self} @ {id(self)}>" + + +def parse_tag(tag: str) -> FrozenSet[Tag]: + """ + Parses the provided tag (e.g. `py3-none-any`) into a frozenset of Tag instances. + + Returning a set is required due to the possibility that the tag is a + compressed tag set. + """ + tags = set() + interpreters, abis, platforms = tag.split("-") + for interpreter in interpreters.split("."): + for abi in abis.split("."): + for platform_ in platforms.split("."): + tags.add(Tag(interpreter, abi, platform_)) + return frozenset(tags) + + +def _get_config_var(name: str, warn: bool = False) -> Union[int, str, None]: + value = sysconfig.get_config_var(name) + if value is None and warn: + logger.debug( + "Config variable '%s' is unset, Python ABI tag may be incorrect", name + ) + return value + + +def _normalize_string(string: str) -> str: + return string.replace(".", "_").replace("-", "_") + + +def _abi3_applies(python_version: PythonVersion) -> bool: + """ + Determine if the Python version supports abi3. + + PEP 384 was first implemented in Python 3.2. + """ + return len(python_version) > 1 and tuple(python_version) >= (3, 2) + + +def _cpython_abis(py_version: PythonVersion, warn: bool = False) -> List[str]: + py_version = tuple(py_version) # To allow for version comparison. + abis = [] + version = _version_nodot(py_version[:2]) + debug = pymalloc = ucs4 = "" + with_debug = _get_config_var("Py_DEBUG", warn) + has_refcount = hasattr(sys, "gettotalrefcount") + # Windows doesn't set Py_DEBUG, so checking for support of debug-compiled + # extension modules is the best option. + # https://github.com/pypa/pip/issues/3383#issuecomment-173267692 + has_ext = "_d.pyd" in EXTENSION_SUFFIXES + if with_debug or (with_debug is None and (has_refcount or has_ext)): + debug = "d" + if py_version < (3, 8): + with_pymalloc = _get_config_var("WITH_PYMALLOC", warn) + if with_pymalloc or with_pymalloc is None: + pymalloc = "m" + if py_version < (3, 3): + unicode_size = _get_config_var("Py_UNICODE_SIZE", warn) + if unicode_size == 4 or ( + unicode_size is None and sys.maxunicode == 0x10FFFF + ): + ucs4 = "u" + elif debug: + # Debug builds can also load "normal" extension modules. + # We can also assume no UCS-4 or pymalloc requirement. + abis.append(f"cp{version}") + abis.insert( + 0, + "cp{version}{debug}{pymalloc}{ucs4}".format( + version=version, debug=debug, pymalloc=pymalloc, ucs4=ucs4 + ), + ) + return abis + + +def cpython_tags( + python_version: Optional[PythonVersion] = None, + abis: Optional[Iterable[str]] = None, + platforms: Optional[Iterable[str]] = None, + *, + warn: bool = False, +) -> Iterator[Tag]: + """ + Yields the tags for a CPython interpreter. + + The tags consist of: + - cp-- + - cp-abi3- + - cp-none- + - cp-abi3- # Older Python versions down to 3.2. + + If python_version only specifies a major version then user-provided ABIs and + the 'none' ABItag will be used. + + If 'abi3' or 'none' are specified in 'abis' then they will be yielded at + their normal position and not at the beginning. + """ + if not python_version: + python_version = sys.version_info[:2] + + interpreter = f"cp{_version_nodot(python_version[:2])}" + + if abis is None: + if len(python_version) > 1: + abis = _cpython_abis(python_version, warn) + else: + abis = [] + abis = list(abis) + # 'abi3' and 'none' are explicitly handled later. + for explicit_abi in ("abi3", "none"): + try: + abis.remove(explicit_abi) + except ValueError: + pass + + platforms = list(platforms or platform_tags()) + for abi in abis: + for platform_ in platforms: + yield Tag(interpreter, abi, platform_) + if _abi3_applies(python_version): + yield from (Tag(interpreter, "abi3", platform_) for platform_ in platforms) + yield from (Tag(interpreter, "none", platform_) for platform_ in platforms) + + if _abi3_applies(python_version): + for minor_version in range(python_version[1] - 1, 1, -1): + for platform_ in platforms: + interpreter = "cp{version}".format( + version=_version_nodot((python_version[0], minor_version)) + ) + yield Tag(interpreter, "abi3", platform_) + + +def _generic_abi() -> List[str]: + """ + Return the ABI tag based on EXT_SUFFIX. + """ + # The following are examples of `EXT_SUFFIX`. + # We want to keep the parts which are related to the ABI and remove the + # parts which are related to the platform: + # - linux: '.cpython-310-x86_64-linux-gnu.so' => cp310 + # - mac: '.cpython-310-darwin.so' => cp310 + # - win: '.cp310-win_amd64.pyd' => cp310 + # - win: '.pyd' => cp37 (uses _cpython_abis()) + # - pypy: '.pypy38-pp73-x86_64-linux-gnu.so' => pypy38_pp73 + # - graalpy: '.graalpy-38-native-x86_64-darwin.dylib' + # => graalpy_38_native + + ext_suffix = _get_config_var("EXT_SUFFIX", warn=True) + if not isinstance(ext_suffix, str) or ext_suffix[0] != ".": + raise SystemError("invalid sysconfig.get_config_var('EXT_SUFFIX')") + parts = ext_suffix.split(".") + if len(parts) < 3: + # CPython3.7 and earlier uses ".pyd" on Windows. + return _cpython_abis(sys.version_info[:2]) + soabi = parts[1] + if soabi.startswith("cpython"): + # non-windows + abi = "cp" + soabi.split("-")[1] + elif soabi.startswith("cp"): + # windows + abi = soabi.split("-")[0] + elif soabi.startswith("pypy"): + abi = "-".join(soabi.split("-")[:2]) + elif soabi.startswith("graalpy"): + abi = "-".join(soabi.split("-")[:3]) + elif soabi: + # pyston, ironpython, others? + abi = soabi + else: + return [] + return [_normalize_string(abi)] + + +def generic_tags( + interpreter: Optional[str] = None, + abis: Optional[Iterable[str]] = None, + platforms: Optional[Iterable[str]] = None, + *, + warn: bool = False, +) -> Iterator[Tag]: + """ + Yields the tags for a generic interpreter. + + The tags consist of: + - -- + + The "none" ABI will be added if it was not explicitly provided. + """ + if not interpreter: + interp_name = interpreter_name() + interp_version = interpreter_version(warn=warn) + interpreter = "".join([interp_name, interp_version]) + if abis is None: + abis = _generic_abi() + else: + abis = list(abis) + platforms = list(platforms or platform_tags()) + if "none" not in abis: + abis.append("none") + for abi in abis: + for platform_ in platforms: + yield Tag(interpreter, abi, platform_) + + +def _py_interpreter_range(py_version: PythonVersion) -> Iterator[str]: + """ + Yields Python versions in descending order. + + After the latest version, the major-only version will be yielded, and then + all previous versions of that major version. + """ + if len(py_version) > 1: + yield f"py{_version_nodot(py_version[:2])}" + yield f"py{py_version[0]}" + if len(py_version) > 1: + for minor in range(py_version[1] - 1, -1, -1): + yield f"py{_version_nodot((py_version[0], minor))}" + + +def compatible_tags( + python_version: Optional[PythonVersion] = None, + interpreter: Optional[str] = None, + platforms: Optional[Iterable[str]] = None, +) -> Iterator[Tag]: + """ + Yields the sequence of tags that are compatible with a specific version of Python. + + The tags consist of: + - py*-none- + - -none-any # ... if `interpreter` is provided. + - py*-none-any + """ + if not python_version: + python_version = sys.version_info[:2] + platforms = list(platforms or platform_tags()) + for version in _py_interpreter_range(python_version): + for platform_ in platforms: + yield Tag(version, "none", platform_) + if interpreter: + yield Tag(interpreter, "none", "any") + for version in _py_interpreter_range(python_version): + yield Tag(version, "none", "any") + + +def _mac_arch(arch: str, is_32bit: bool = _32_BIT_INTERPRETER) -> str: + if not is_32bit: + return arch + + if arch.startswith("ppc"): + return "ppc" + + return "i386" + + +def _mac_binary_formats(version: MacVersion, cpu_arch: str) -> List[str]: + formats = [cpu_arch] + if cpu_arch == "x86_64": + if version < (10, 4): + return [] + formats.extend(["intel", "fat64", "fat32"]) + + elif cpu_arch == "i386": + if version < (10, 4): + return [] + formats.extend(["intel", "fat32", "fat"]) + + elif cpu_arch == "ppc64": + # TODO: Need to care about 32-bit PPC for ppc64 through 10.2? + if version > (10, 5) or version < (10, 4): + return [] + formats.append("fat64") + + elif cpu_arch == "ppc": + if version > (10, 6): + return [] + formats.extend(["fat32", "fat"]) + + if cpu_arch in {"arm64", "x86_64"}: + formats.append("universal2") + + if cpu_arch in {"x86_64", "i386", "ppc64", "ppc", "intel"}: + formats.append("universal") + + return formats + + +def mac_platforms( + version: Optional[MacVersion] = None, arch: Optional[str] = None +) -> Iterator[str]: + """ + Yields the platform tags for a macOS system. + + The `version` parameter is a two-item tuple specifying the macOS version to + generate platform tags for. The `arch` parameter is the CPU architecture to + generate platform tags for. Both parameters default to the appropriate value + for the current system. + """ + version_str, _, cpu_arch = platform.mac_ver() + if version is None: + version = cast("MacVersion", tuple(map(int, version_str.split(".")[:2]))) + if version == (10, 16): + # When built against an older macOS SDK, Python will report macOS 10.16 + # instead of the real version. + version_str = subprocess.run( + [ + sys.executable, + "-sS", + "-c", + "import platform; print(platform.mac_ver()[0])", + ], + check=True, + env={"SYSTEM_VERSION_COMPAT": "0"}, + stdout=subprocess.PIPE, + universal_newlines=True, + ).stdout + version = cast("MacVersion", tuple(map(int, version_str.split(".")[:2]))) + else: + version = version + if arch is None: + arch = _mac_arch(cpu_arch) + else: + arch = arch + + if (10, 0) <= version and version < (11, 0): + # Prior to Mac OS 11, each yearly release of Mac OS bumped the + # "minor" version number. The major version was always 10. + for minor_version in range(version[1], -1, -1): + compat_version = 10, minor_version + binary_formats = _mac_binary_formats(compat_version, arch) + for binary_format in binary_formats: + yield "macosx_{major}_{minor}_{binary_format}".format( + major=10, minor=minor_version, binary_format=binary_format + ) + + if version >= (11, 0): + # Starting with Mac OS 11, each yearly release bumps the major version + # number. The minor versions are now the midyear updates. + for major_version in range(version[0], 10, -1): + compat_version = major_version, 0 + binary_formats = _mac_binary_formats(compat_version, arch) + for binary_format in binary_formats: + yield "macosx_{major}_{minor}_{binary_format}".format( + major=major_version, minor=0, binary_format=binary_format + ) + + if version >= (11, 0): + # Mac OS 11 on x86_64 is compatible with binaries from previous releases. + # Arm64 support was introduced in 11.0, so no Arm binaries from previous + # releases exist. + # + # However, the "universal2" binary format can have a + # macOS version earlier than 11.0 when the x86_64 part of the binary supports + # that version of macOS. + if arch == "x86_64": + for minor_version in range(16, 3, -1): + compat_version = 10, minor_version + binary_formats = _mac_binary_formats(compat_version, arch) + for binary_format in binary_formats: + yield "macosx_{major}_{minor}_{binary_format}".format( + major=compat_version[0], + minor=compat_version[1], + binary_format=binary_format, + ) + else: + for minor_version in range(16, 3, -1): + compat_version = 10, minor_version + binary_format = "universal2" + yield "macosx_{major}_{minor}_{binary_format}".format( + major=compat_version[0], + minor=compat_version[1], + binary_format=binary_format, + ) + + +def _linux_platforms(is_32bit: bool = _32_BIT_INTERPRETER) -> Iterator[str]: + linux = _normalize_string(sysconfig.get_platform()) + if is_32bit: + if linux == "linux_x86_64": + linux = "linux_i686" + elif linux == "linux_aarch64": + linux = "linux_armv7l" + _, arch = linux.split("_", 1) + yield from _manylinux.platform_tags(linux, arch) + yield from _musllinux.platform_tags(arch) + yield linux + + +def _generic_platforms() -> Iterator[str]: + yield _normalize_string(sysconfig.get_platform()) + + +def platform_tags() -> Iterator[str]: + """ + Provides the platform tags for this installation. + """ + if platform.system() == "Darwin": + return mac_platforms() + elif platform.system() == "Linux": + return _linux_platforms() + else: + return _generic_platforms() + + +def interpreter_name() -> str: + """ + Returns the name of the running interpreter. + + Some implementations have a reserved, two-letter abbreviation which will + be returned when appropriate. + """ + name = sys.implementation.name + return INTERPRETER_SHORT_NAMES.get(name) or name + + +def interpreter_version(*, warn: bool = False) -> str: + """ + Returns the version of the running interpreter. + """ + version = _get_config_var("py_version_nodot", warn=warn) + if version: + version = str(version) + else: + version = _version_nodot(sys.version_info[:2]) + return version + + +def _version_nodot(version: PythonVersion) -> str: + return "".join(map(str, version)) + + +def sys_tags(*, warn: bool = False) -> Iterator[Tag]: + """ + Returns the sequence of tag triples for the running interpreter. + + The order of the sequence corresponds to priority order for the + interpreter, from most to least important. + """ + + interp_name = interpreter_name() + if interp_name == "cp": + yield from cpython_tags(warn=warn) + else: + yield from generic_tags() + + if interp_name == "pp": + interp = "pp3" + elif interp_name == "cp": + interp = "cp" + interpreter_version(warn=warn) + else: + interp = None + yield from compatible_tags(interpreter=interp) diff --git a/metaflow/_vendor/packaging/utils.py b/metaflow/_vendor/packaging/utils.py new file mode 100644 index 00000000000..33c613b749a --- /dev/null +++ b/metaflow/_vendor/packaging/utils.py @@ -0,0 +1,141 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import re +from typing import FrozenSet, NewType, Tuple, Union, cast + +from .tags import Tag, parse_tag +from .version import InvalidVersion, Version + +BuildTag = Union[Tuple[()], Tuple[int, str]] +NormalizedName = NewType("NormalizedName", str) + + +class InvalidWheelFilename(ValueError): + """ + An invalid wheel filename was found, users should refer to PEP 427. + """ + + +class InvalidSdistFilename(ValueError): + """ + An invalid sdist filename was found, users should refer to the packaging user guide. + """ + + +_canonicalize_regex = re.compile(r"[-_.]+") +# PEP 427: The build number must start with a digit. +_build_tag_regex = re.compile(r"(\d+)(.*)") + + +def canonicalize_name(name: str) -> NormalizedName: + # This is taken from PEP 503. + value = _canonicalize_regex.sub("-", name).lower() + return cast(NormalizedName, value) + + +def canonicalize_version( + version: Union[Version, str], *, strip_trailing_zero: bool = True +) -> str: + """ + This is very similar to Version.__str__, but has one subtle difference + with the way it handles the release segment. + """ + if isinstance(version, str): + try: + parsed = Version(version) + except InvalidVersion: + # Legacy versions cannot be normalized + return version + else: + parsed = version + + parts = [] + + # Epoch + if parsed.epoch != 0: + parts.append(f"{parsed.epoch}!") + + # Release segment + release_segment = ".".join(str(x) for x in parsed.release) + if strip_trailing_zero: + # NB: This strips trailing '.0's to normalize + release_segment = re.sub(r"(\.0)+$", "", release_segment) + parts.append(release_segment) + + # Pre-release + if parsed.pre is not None: + parts.append("".join(str(x) for x in parsed.pre)) + + # Post-release + if parsed.post is not None: + parts.append(f".post{parsed.post}") + + # Development release + if parsed.dev is not None: + parts.append(f".dev{parsed.dev}") + + # Local version segment + if parsed.local is not None: + parts.append(f"+{parsed.local}") + + return "".join(parts) + + +def parse_wheel_filename( + filename: str, +) -> Tuple[NormalizedName, Version, BuildTag, FrozenSet[Tag]]: + if not filename.endswith(".whl"): + raise InvalidWheelFilename( + f"Invalid wheel filename (extension must be '.whl'): {filename}" + ) + + filename = filename[:-4] + dashes = filename.count("-") + if dashes not in (4, 5): + raise InvalidWheelFilename( + f"Invalid wheel filename (wrong number of parts): {filename}" + ) + + parts = filename.split("-", dashes - 2) + name_part = parts[0] + # See PEP 427 for the rules on escaping the project name + if "__" in name_part or re.match(r"^[\w\d._]*$", name_part, re.UNICODE) is None: + raise InvalidWheelFilename(f"Invalid project name: {filename}") + name = canonicalize_name(name_part) + version = Version(parts[1]) + if dashes == 5: + build_part = parts[2] + build_match = _build_tag_regex.match(build_part) + if build_match is None: + raise InvalidWheelFilename( + f"Invalid build number: {build_part} in '{filename}'" + ) + build = cast(BuildTag, (int(build_match.group(1)), build_match.group(2))) + else: + build = () + tags = parse_tag(parts[-1]) + return (name, version, build, tags) + + +def parse_sdist_filename(filename: str) -> Tuple[NormalizedName, Version]: + if filename.endswith(".tar.gz"): + file_stem = filename[: -len(".tar.gz")] + elif filename.endswith(".zip"): + file_stem = filename[: -len(".zip")] + else: + raise InvalidSdistFilename( + f"Invalid sdist filename (extension must be '.tar.gz' or '.zip'):" + f" {filename}" + ) + + # We are requiring a PEP 440 version, which cannot contain dashes, + # so we split on the last dash. + name_part, sep, version_part = file_stem.rpartition("-") + if not sep: + raise InvalidSdistFilename(f"Invalid sdist filename: {filename}") + + name = canonicalize_name(name_part) + version = Version(version_part) + return (name, version) diff --git a/metaflow/_vendor/packaging/version.py b/metaflow/_vendor/packaging/version.py new file mode 100644 index 00000000000..df5bbcbe1ab --- /dev/null +++ b/metaflow/_vendor/packaging/version.py @@ -0,0 +1,563 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +""" +.. testsetup:: + + from metaflow._vendor.packaging.version import parse, Version +""" + +import collections +import itertools +import re +from typing import Callable, Optional, SupportsInt, Tuple, Union + +from ._structures import Infinity, InfinityType, NegativeInfinity, NegativeInfinityType + +__all__ = ["VERSION_PATTERN", "parse", "Version", "InvalidVersion"] + +InfiniteTypes = Union[InfinityType, NegativeInfinityType] +PrePostDevType = Union[InfiniteTypes, Tuple[str, int]] +SubLocalType = Union[InfiniteTypes, int, str] +LocalType = Union[ + NegativeInfinityType, + Tuple[ + Union[ + SubLocalType, + Tuple[SubLocalType, str], + Tuple[NegativeInfinityType, SubLocalType], + ], + ..., + ], +] +CmpKey = Tuple[ + int, Tuple[int, ...], PrePostDevType, PrePostDevType, PrePostDevType, LocalType +] +VersionComparisonMethod = Callable[[CmpKey, CmpKey], bool] + +_Version = collections.namedtuple( + "_Version", ["epoch", "release", "dev", "pre", "post", "local"] +) + + +def parse(version: str) -> "Version": + """Parse the given version string. + + >>> parse('1.0.dev1') + + + :param version: The version string to parse. + :raises InvalidVersion: When the version string is not a valid version. + """ + return Version(version) + + +class InvalidVersion(ValueError): + """Raised when a version string is not a valid version. + + >>> Version("invalid") + Traceback (most recent call last): + ... + packaging.version.InvalidVersion: Invalid version: 'invalid' + """ + + +class _BaseVersion: + _key: CmpKey + + def __hash__(self) -> int: + return hash(self._key) + + # Please keep the duplicated `isinstance` check + # in the six comparisons hereunder + # unless you find a way to avoid adding overhead function calls. + def __lt__(self, other: "_BaseVersion") -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key < other._key + + def __le__(self, other: "_BaseVersion") -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key <= other._key + + def __eq__(self, other: object) -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key == other._key + + def __ge__(self, other: "_BaseVersion") -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key >= other._key + + def __gt__(self, other: "_BaseVersion") -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key > other._key + + def __ne__(self, other: object) -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key != other._key + + +# Deliberately not anchored to the start and end of the string, to make it +# easier for 3rd party code to reuse +_VERSION_PATTERN = r""" + v? + (?: + (?:(?P[0-9]+)!)? # epoch + (?P[0-9]+(?:\.[0-9]+)*) # release segment + (?P
                                          # pre-release
+            [-_\.]?
+            (?P(a|b|c|rc|alpha|beta|pre|preview))
+            [-_\.]?
+            (?P[0-9]+)?
+        )?
+        (?P                                         # post release
+            (?:-(?P[0-9]+))
+            |
+            (?:
+                [-_\.]?
+                (?Ppost|rev|r)
+                [-_\.]?
+                (?P[0-9]+)?
+            )
+        )?
+        (?P                                          # dev release
+            [-_\.]?
+            (?Pdev)
+            [-_\.]?
+            (?P[0-9]+)?
+        )?
+    )
+    (?:\+(?P[a-z0-9]+(?:[-_\.][a-z0-9]+)*))?       # local version
+"""
+
+VERSION_PATTERN = _VERSION_PATTERN
+"""
+A string containing the regular expression used to match a valid version.
+
+The pattern is not anchored at either end, and is intended for embedding in larger
+expressions (for example, matching a version number as part of a file name). The
+regular expression should be compiled with the ``re.VERBOSE`` and ``re.IGNORECASE``
+flags set.
+
+:meta hide-value:
+"""
+
+
+class Version(_BaseVersion):
+    """This class abstracts handling of a project's versions.
+
+    A :class:`Version` instance is comparison aware and can be compared and
+    sorted using the standard Python interfaces.
+
+    >>> v1 = Version("1.0a5")
+    >>> v2 = Version("1.0")
+    >>> v1
+    
+    >>> v2
+    
+    >>> v1 < v2
+    True
+    >>> v1 == v2
+    False
+    >>> v1 > v2
+    False
+    >>> v1 >= v2
+    False
+    >>> v1 <= v2
+    True
+    """
+
+    _regex = re.compile(r"^\s*" + VERSION_PATTERN + r"\s*$", re.VERBOSE | re.IGNORECASE)
+
+    def __init__(self, version: str) -> None:
+        """Initialize a Version object.
+
+        :param version:
+            The string representation of a version which will be parsed and normalized
+            before use.
+        :raises InvalidVersion:
+            If the ``version`` does not conform to PEP 440 in any way then this
+            exception will be raised.
+        """
+
+        # Validate the version and parse it into pieces
+        match = self._regex.search(version)
+        if not match:
+            raise InvalidVersion(f"Invalid version: '{version}'")
+
+        # Store the parsed out pieces of the version
+        self._version = _Version(
+            epoch=int(match.group("epoch")) if match.group("epoch") else 0,
+            release=tuple(int(i) for i in match.group("release").split(".")),
+            pre=_parse_letter_version(match.group("pre_l"), match.group("pre_n")),
+            post=_parse_letter_version(
+                match.group("post_l"), match.group("post_n1") or match.group("post_n2")
+            ),
+            dev=_parse_letter_version(match.group("dev_l"), match.group("dev_n")),
+            local=_parse_local_version(match.group("local")),
+        )
+
+        # Generate a key which will be used for sorting
+        self._key = _cmpkey(
+            self._version.epoch,
+            self._version.release,
+            self._version.pre,
+            self._version.post,
+            self._version.dev,
+            self._version.local,
+        )
+
+    def __repr__(self) -> str:
+        """A representation of the Version that shows all internal state.
+
+        >>> Version('1.0.0')
+        
+        """
+        return f""
+
+    def __str__(self) -> str:
+        """A string representation of the version that can be rounded-tripped.
+
+        >>> str(Version("1.0a5"))
+        '1.0a5'
+        """
+        parts = []
+
+        # Epoch
+        if self.epoch != 0:
+            parts.append(f"{self.epoch}!")
+
+        # Release segment
+        parts.append(".".join(str(x) for x in self.release))
+
+        # Pre-release
+        if self.pre is not None:
+            parts.append("".join(str(x) for x in self.pre))
+
+        # Post-release
+        if self.post is not None:
+            parts.append(f".post{self.post}")
+
+        # Development release
+        if self.dev is not None:
+            parts.append(f".dev{self.dev}")
+
+        # Local version segment
+        if self.local is not None:
+            parts.append(f"+{self.local}")
+
+        return "".join(parts)
+
+    @property
+    def epoch(self) -> int:
+        """The epoch of the version.
+
+        >>> Version("2.0.0").epoch
+        0
+        >>> Version("1!2.0.0").epoch
+        1
+        """
+        _epoch: int = self._version.epoch
+        return _epoch
+
+    @property
+    def release(self) -> Tuple[int, ...]:
+        """The components of the "release" segment of the version.
+
+        >>> Version("1.2.3").release
+        (1, 2, 3)
+        >>> Version("2.0.0").release
+        (2, 0, 0)
+        >>> Version("1!2.0.0.post0").release
+        (2, 0, 0)
+
+        Includes trailing zeroes but not the epoch or any pre-release / development /
+        post-release suffixes.
+        """
+        _release: Tuple[int, ...] = self._version.release
+        return _release
+
+    @property
+    def pre(self) -> Optional[Tuple[str, int]]:
+        """The pre-release segment of the version.
+
+        >>> print(Version("1.2.3").pre)
+        None
+        >>> Version("1.2.3a1").pre
+        ('a', 1)
+        >>> Version("1.2.3b1").pre
+        ('b', 1)
+        >>> Version("1.2.3rc1").pre
+        ('rc', 1)
+        """
+        _pre: Optional[Tuple[str, int]] = self._version.pre
+        return _pre
+
+    @property
+    def post(self) -> Optional[int]:
+        """The post-release number of the version.
+
+        >>> print(Version("1.2.3").post)
+        None
+        >>> Version("1.2.3.post1").post
+        1
+        """
+        return self._version.post[1] if self._version.post else None
+
+    @property
+    def dev(self) -> Optional[int]:
+        """The development number of the version.
+
+        >>> print(Version("1.2.3").dev)
+        None
+        >>> Version("1.2.3.dev1").dev
+        1
+        """
+        return self._version.dev[1] if self._version.dev else None
+
+    @property
+    def local(self) -> Optional[str]:
+        """The local version segment of the version.
+
+        >>> print(Version("1.2.3").local)
+        None
+        >>> Version("1.2.3+abc").local
+        'abc'
+        """
+        if self._version.local:
+            return ".".join(str(x) for x in self._version.local)
+        else:
+            return None
+
+    @property
+    def public(self) -> str:
+        """The public portion of the version.
+
+        >>> Version("1.2.3").public
+        '1.2.3'
+        >>> Version("1.2.3+abc").public
+        '1.2.3'
+        >>> Version("1.2.3+abc.dev1").public
+        '1.2.3'
+        """
+        return str(self).split("+", 1)[0]
+
+    @property
+    def base_version(self) -> str:
+        """The "base version" of the version.
+
+        >>> Version("1.2.3").base_version
+        '1.2.3'
+        >>> Version("1.2.3+abc").base_version
+        '1.2.3'
+        >>> Version("1!1.2.3+abc.dev1").base_version
+        '1!1.2.3'
+
+        The "base version" is the public version of the project without any pre or post
+        release markers.
+        """
+        parts = []
+
+        # Epoch
+        if self.epoch != 0:
+            parts.append(f"{self.epoch}!")
+
+        # Release segment
+        parts.append(".".join(str(x) for x in self.release))
+
+        return "".join(parts)
+
+    @property
+    def is_prerelease(self) -> bool:
+        """Whether this version is a pre-release.
+
+        >>> Version("1.2.3").is_prerelease
+        False
+        >>> Version("1.2.3a1").is_prerelease
+        True
+        >>> Version("1.2.3b1").is_prerelease
+        True
+        >>> Version("1.2.3rc1").is_prerelease
+        True
+        >>> Version("1.2.3dev1").is_prerelease
+        True
+        """
+        return self.dev is not None or self.pre is not None
+
+    @property
+    def is_postrelease(self) -> bool:
+        """Whether this version is a post-release.
+
+        >>> Version("1.2.3").is_postrelease
+        False
+        >>> Version("1.2.3.post1").is_postrelease
+        True
+        """
+        return self.post is not None
+
+    @property
+    def is_devrelease(self) -> bool:
+        """Whether this version is a development release.
+
+        >>> Version("1.2.3").is_devrelease
+        False
+        >>> Version("1.2.3.dev1").is_devrelease
+        True
+        """
+        return self.dev is not None
+
+    @property
+    def major(self) -> int:
+        """The first item of :attr:`release` or ``0`` if unavailable.
+
+        >>> Version("1.2.3").major
+        1
+        """
+        return self.release[0] if len(self.release) >= 1 else 0
+
+    @property
+    def minor(self) -> int:
+        """The second item of :attr:`release` or ``0`` if unavailable.
+
+        >>> Version("1.2.3").minor
+        2
+        >>> Version("1").minor
+        0
+        """
+        return self.release[1] if len(self.release) >= 2 else 0
+
+    @property
+    def micro(self) -> int:
+        """The third item of :attr:`release` or ``0`` if unavailable.
+
+        >>> Version("1.2.3").micro
+        3
+        >>> Version("1").micro
+        0
+        """
+        return self.release[2] if len(self.release) >= 3 else 0
+
+
+def _parse_letter_version(
+    letter: str, number: Union[str, bytes, SupportsInt]
+) -> Optional[Tuple[str, int]]:
+
+    if letter:
+        # We consider there to be an implicit 0 in a pre-release if there is
+        # not a numeral associated with it.
+        if number is None:
+            number = 0
+
+        # We normalize any letters to their lower case form
+        letter = letter.lower()
+
+        # We consider some words to be alternate spellings of other words and
+        # in those cases we want to normalize the spellings to our preferred
+        # spelling.
+        if letter == "alpha":
+            letter = "a"
+        elif letter == "beta":
+            letter = "b"
+        elif letter in ["c", "pre", "preview"]:
+            letter = "rc"
+        elif letter in ["rev", "r"]:
+            letter = "post"
+
+        return letter, int(number)
+    if not letter and number:
+        # We assume if we are given a number, but we are not given a letter
+        # then this is using the implicit post release syntax (e.g. 1.0-1)
+        letter = "post"
+
+        return letter, int(number)
+
+    return None
+
+
+_local_version_separators = re.compile(r"[\._-]")
+
+
+def _parse_local_version(local: str) -> Optional[LocalType]:
+    """
+    Takes a string like abc.1.twelve and turns it into ("abc", 1, "twelve").
+    """
+    if local is not None:
+        return tuple(
+            part.lower() if not part.isdigit() else int(part)
+            for part in _local_version_separators.split(local)
+        )
+    return None
+
+
+def _cmpkey(
+    epoch: int,
+    release: Tuple[int, ...],
+    pre: Optional[Tuple[str, int]],
+    post: Optional[Tuple[str, int]],
+    dev: Optional[Tuple[str, int]],
+    local: Optional[Tuple[SubLocalType]],
+) -> CmpKey:
+
+    # When we compare a release version, we want to compare it with all of the
+    # trailing zeros removed. So we'll use a reverse the list, drop all the now
+    # leading zeros until we come to something non zero, then take the rest
+    # re-reverse it back into the correct order and make it a tuple and use
+    # that for our sorting key.
+    _release = tuple(
+        reversed(list(itertools.dropwhile(lambda x: x == 0, reversed(release))))
+    )
+
+    # We need to "trick" the sorting algorithm to put 1.0.dev0 before 1.0a0.
+    # We'll do this by abusing the pre segment, but we _only_ want to do this
+    # if there is not a pre or a post segment. If we have one of those then
+    # the normal sorting rules will handle this case correctly.
+    if pre is None and post is None and dev is not None:
+        _pre: PrePostDevType = NegativeInfinity
+    # Versions without a pre-release (except as noted above) should sort after
+    # those with one.
+    elif pre is None:
+        _pre = Infinity
+    else:
+        _pre = pre
+
+    # Versions without a post segment should sort before those with one.
+    if post is None:
+        _post: PrePostDevType = NegativeInfinity
+
+    else:
+        _post = post
+
+    # Versions without a development segment should sort after those with one.
+    if dev is None:
+        _dev: PrePostDevType = Infinity
+
+    else:
+        _dev = dev
+
+    if local is None:
+        # Versions without a local segment should sort before those with one.
+        _local: LocalType = NegativeInfinity
+    else:
+        # Versions with a local segment need that segment parsed to implement
+        # the sorting rules in PEP440.
+        # - Alpha numeric segments sort before numeric segments
+        # - Alpha numeric segments sort lexicographically
+        # - Numeric segments sort numerically
+        # - Shorter versions sort before longer versions when the prefixes
+        #   match exactly
+        _local = tuple(
+            (i, "") if isinstance(i, int) else (NegativeInfinity, i) for i in local
+        )
+
+    return epoch, _release, _pre, _post, _dev, _local
diff --git a/metaflow/_vendor/typeguard.LICENSE b/metaflow/_vendor/typeguard.LICENSE
new file mode 100644
index 00000000000..07806f8af9d
--- /dev/null
+++ b/metaflow/_vendor/typeguard.LICENSE
@@ -0,0 +1,19 @@
+This is the MIT license: http://www.opensource.org/licenses/mit-license.php
+
+Copyright (c) Alex GrΓΆnholm
+
+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/metaflow/_vendor/typeguard/__init__.py b/metaflow/_vendor/typeguard/__init__.py
new file mode 100644
index 00000000000..6781cad094b
--- /dev/null
+++ b/metaflow/_vendor/typeguard/__init__.py
@@ -0,0 +1,48 @@
+import os
+from typing import Any
+
+from ._checkers import TypeCheckerCallable as TypeCheckerCallable
+from ._checkers import TypeCheckLookupCallback as TypeCheckLookupCallback
+from ._checkers import check_type_internal as check_type_internal
+from ._checkers import checker_lookup_functions as checker_lookup_functions
+from ._checkers import load_plugins as load_plugins
+from ._config import CollectionCheckStrategy as CollectionCheckStrategy
+from ._config import ForwardRefPolicy as ForwardRefPolicy
+from ._config import TypeCheckConfiguration as TypeCheckConfiguration
+from ._decorators import typechecked as typechecked
+from ._decorators import typeguard_ignore as typeguard_ignore
+from ._exceptions import InstrumentationWarning as InstrumentationWarning
+from ._exceptions import TypeCheckError as TypeCheckError
+from ._exceptions import TypeCheckWarning as TypeCheckWarning
+from ._exceptions import TypeHintWarning as TypeHintWarning
+from ._functions import TypeCheckFailCallback as TypeCheckFailCallback
+from ._functions import check_type as check_type
+from ._functions import warn_on_error as warn_on_error
+from ._importhook import ImportHookManager as ImportHookManager
+from ._importhook import TypeguardFinder as TypeguardFinder
+from ._importhook import install_import_hook as install_import_hook
+from ._memo import TypeCheckMemo as TypeCheckMemo
+from ._suppression import suppress_type_checks as suppress_type_checks
+from ._utils import Unset as Unset
+
+# Re-export imports so they look like they live directly in this package
+for value in list(locals().values()):
+    if getattr(value, "__module__", "").startswith(f"{__name__}."):
+        value.__module__ = __name__
+
+
+config: TypeCheckConfiguration
+
+
+def __getattr__(name: str) -> Any:
+    if name == "config":
+        from ._config import global_config
+
+        return global_config
+
+    raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+
+
+# Automatically load checker lookup functions unless explicitly disabled
+if "TYPEGUARD_DISABLE_PLUGIN_AUTOLOAD" not in os.environ:
+    load_plugins()
diff --git a/metaflow/_vendor/typeguard/_checkers.py b/metaflow/_vendor/typeguard/_checkers.py
new file mode 100644
index 00000000000..3603a302740
--- /dev/null
+++ b/metaflow/_vendor/typeguard/_checkers.py
@@ -0,0 +1,1070 @@
+from __future__ import annotations
+
+import collections.abc
+import inspect
+import sys
+import types
+import typing
+import warnings
+from enum import Enum
+from inspect import Parameter, isclass, isfunction
+from io import BufferedIOBase, IOBase, RawIOBase, TextIOBase
+from itertools import zip_longest
+from textwrap import indent
+from typing import (
+    IO,
+    AbstractSet,
+    Any,
+    BinaryIO,
+    Callable,
+    Dict,
+    ForwardRef,
+    List,
+    Mapping,
+    MutableMapping,
+    NewType,
+    Optional,
+    Sequence,
+    Set,
+    TextIO,
+    Tuple,
+    Type,
+    TypeVar,
+    Union,
+)
+from unittest.mock import Mock
+
+from metaflow._vendor import typing_extensions
+
+# Must use this because typing.is_typeddict does not recognize
+# TypedDict from typing_extensions, and as of version 4.12.0
+# typing_extensions.TypedDict is different from typing.TypedDict
+# on all versions.
+from metaflow._vendor.typing_extensions import is_typeddict
+
+from ._config import ForwardRefPolicy
+from ._exceptions import TypeCheckError, TypeHintWarning
+from ._memo import TypeCheckMemo
+from ._utils import evaluate_forwardref, get_stacklevel, get_type_name, qualified_name
+
+if sys.version_info >= (3, 11):
+    from typing import (
+        Annotated,
+        NotRequired,
+        TypeAlias,
+        get_args,
+        get_origin,
+    )
+
+    SubclassableAny = Any
+else:
+    from metaflow._vendor.typing_extensions import (
+        Annotated,
+        NotRequired,
+        TypeAlias,
+        get_args,
+        get_origin,
+    )
+    from metaflow._vendor.typing_extensions import Any as SubclassableAny
+
+if sys.version_info >= (3, 10):
+    from importlib.metadata import entry_points
+    from typing import ParamSpec
+else:
+    from metaflow._vendor.importlib_metadata import entry_points
+    from metaflow._vendor.typing_extensions import ParamSpec
+
+TypeCheckerCallable: TypeAlias = Callable[
+    [Any, Any, Tuple[Any, ...], TypeCheckMemo], Any
+]
+TypeCheckLookupCallback: TypeAlias = Callable[
+    [Any, Tuple[Any, ...], Tuple[Any, ...]], Optional[TypeCheckerCallable]
+]
+
+checker_lookup_functions: list[TypeCheckLookupCallback] = []
+generic_alias_types: tuple[type, ...] = (type(List), type(List[Any]))
+if sys.version_info >= (3, 9):
+    generic_alias_types += (types.GenericAlias,)
+
+# Sentinel
+_missing = object()
+
+# Lifted from mypy.sharedparse
+BINARY_MAGIC_METHODS = {
+    "__add__",
+    "__and__",
+    "__cmp__",
+    "__divmod__",
+    "__div__",
+    "__eq__",
+    "__floordiv__",
+    "__ge__",
+    "__gt__",
+    "__iadd__",
+    "__iand__",
+    "__idiv__",
+    "__ifloordiv__",
+    "__ilshift__",
+    "__imatmul__",
+    "__imod__",
+    "__imul__",
+    "__ior__",
+    "__ipow__",
+    "__irshift__",
+    "__isub__",
+    "__itruediv__",
+    "__ixor__",
+    "__le__",
+    "__lshift__",
+    "__lt__",
+    "__matmul__",
+    "__mod__",
+    "__mul__",
+    "__ne__",
+    "__or__",
+    "__pow__",
+    "__radd__",
+    "__rand__",
+    "__rdiv__",
+    "__rfloordiv__",
+    "__rlshift__",
+    "__rmatmul__",
+    "__rmod__",
+    "__rmul__",
+    "__ror__",
+    "__rpow__",
+    "__rrshift__",
+    "__rshift__",
+    "__rsub__",
+    "__rtruediv__",
+    "__rxor__",
+    "__sub__",
+    "__truediv__",
+    "__xor__",
+}
+
+
+def check_callable(
+    value: Any,
+    origin_type: Any,
+    args: tuple[Any, ...],
+    memo: TypeCheckMemo,
+) -> None:
+    if not callable(value):
+        raise TypeCheckError("is not callable")
+
+    if args:
+        try:
+            signature = inspect.signature(value)
+        except (TypeError, ValueError):
+            return
+
+        argument_types = args[0]
+        if isinstance(argument_types, list) and not any(
+            type(item) is ParamSpec for item in argument_types
+        ):
+            # The callable must not have keyword-only arguments without defaults
+            unfulfilled_kwonlyargs = [
+                param.name
+                for param in signature.parameters.values()
+                if param.kind == Parameter.KEYWORD_ONLY
+                and param.default == Parameter.empty
+            ]
+            if unfulfilled_kwonlyargs:
+                raise TypeCheckError(
+                    f"has mandatory keyword-only arguments in its declaration: "
+                    f'{", ".join(unfulfilled_kwonlyargs)}'
+                )
+
+            num_positional_args = num_mandatory_pos_args = 0
+            has_varargs = False
+            for param in signature.parameters.values():
+                if param.kind in (
+                    Parameter.POSITIONAL_ONLY,
+                    Parameter.POSITIONAL_OR_KEYWORD,
+                ):
+                    num_positional_args += 1
+                    if param.default is Parameter.empty:
+                        num_mandatory_pos_args += 1
+                elif param.kind == Parameter.VAR_POSITIONAL:
+                    has_varargs = True
+
+            if num_mandatory_pos_args > len(argument_types):
+                raise TypeCheckError(
+                    f"has too many mandatory positional arguments in its declaration; "
+                    f"expected {len(argument_types)} but {num_mandatory_pos_args} "
+                    f"mandatory positional argument(s) declared"
+                )
+            elif not has_varargs and num_positional_args < len(argument_types):
+                raise TypeCheckError(
+                    f"has too few arguments in its declaration; expected "
+                    f"{len(argument_types)} but {num_positional_args} argument(s) "
+                    f"declared"
+                )
+
+
+def check_mapping(
+    value: Any,
+    origin_type: Any,
+    args: tuple[Any, ...],
+    memo: TypeCheckMemo,
+) -> None:
+    if origin_type is Dict or origin_type is dict:
+        if not isinstance(value, dict):
+            raise TypeCheckError("is not a dict")
+    if origin_type is MutableMapping or origin_type is collections.abc.MutableMapping:
+        if not isinstance(value, collections.abc.MutableMapping):
+            raise TypeCheckError("is not a mutable mapping")
+    elif not isinstance(value, collections.abc.Mapping):
+        raise TypeCheckError("is not a mapping")
+
+    if args:
+        key_type, value_type = args
+        if key_type is not Any or value_type is not Any:
+            samples = memo.config.collection_check_strategy.iterate_samples(
+                value.items()
+            )
+            for k, v in samples:
+                try:
+                    check_type_internal(k, key_type, memo)
+                except TypeCheckError as exc:
+                    exc.append_path_element(f"key {k!r}")
+                    raise
+
+                try:
+                    check_type_internal(v, value_type, memo)
+                except TypeCheckError as exc:
+                    exc.append_path_element(f"value of key {k!r}")
+                    raise
+
+
+def check_typed_dict(
+    value: Any,
+    origin_type: Any,
+    args: tuple[Any, ...],
+    memo: TypeCheckMemo,
+) -> None:
+    if not isinstance(value, dict):
+        raise TypeCheckError("is not a dict")
+
+    declared_keys = frozenset(origin_type.__annotations__)
+    if hasattr(origin_type, "__required_keys__"):
+        required_keys = set(origin_type.__required_keys__)
+    else:  # py3.8 and lower
+        required_keys = set(declared_keys) if origin_type.__total__ else set()
+
+    existing_keys = set(value)
+    extra_keys = existing_keys - declared_keys
+    if extra_keys:
+        keys_formatted = ", ".join(f'"{key}"' for key in sorted(extra_keys, key=repr))
+        raise TypeCheckError(f"has unexpected extra key(s): {keys_formatted}")
+
+    # Detect NotRequired fields which are hidden by get_type_hints()
+    type_hints: dict[str, type] = {}
+    for key, annotation in origin_type.__annotations__.items():
+        if isinstance(annotation, ForwardRef):
+            annotation = evaluate_forwardref(annotation, memo)
+            if get_origin(annotation) is NotRequired:
+                required_keys.discard(key)
+                annotation = get_args(annotation)[0]
+
+        type_hints[key] = annotation
+
+    missing_keys = required_keys - existing_keys
+    if missing_keys:
+        keys_formatted = ", ".join(f'"{key}"' for key in sorted(missing_keys, key=repr))
+        raise TypeCheckError(f"is missing required key(s): {keys_formatted}")
+
+    for key, argtype in type_hints.items():
+        argvalue = value.get(key, _missing)
+        if argvalue is not _missing:
+            try:
+                check_type_internal(argvalue, argtype, memo)
+            except TypeCheckError as exc:
+                exc.append_path_element(f"value of key {key!r}")
+                raise
+
+
+def check_list(
+    value: Any,
+    origin_type: Any,
+    args: tuple[Any, ...],
+    memo: TypeCheckMemo,
+) -> None:
+    if not isinstance(value, list):
+        raise TypeCheckError("is not a list")
+
+    if args and args != (Any,):
+        samples = memo.config.collection_check_strategy.iterate_samples(value)
+        for i, v in enumerate(samples):
+            try:
+                check_type_internal(v, args[0], memo)
+            except TypeCheckError as exc:
+                exc.append_path_element(f"item {i}")
+                raise
+
+
+def check_sequence(
+    value: Any,
+    origin_type: Any,
+    args: tuple[Any, ...],
+    memo: TypeCheckMemo,
+) -> None:
+    if not isinstance(value, collections.abc.Sequence):
+        raise TypeCheckError("is not a sequence")
+
+    if args and args != (Any,):
+        samples = memo.config.collection_check_strategy.iterate_samples(value)
+        for i, v in enumerate(samples):
+            try:
+                check_type_internal(v, args[0], memo)
+            except TypeCheckError as exc:
+                exc.append_path_element(f"item {i}")
+                raise
+
+
+def check_set(
+    value: Any,
+    origin_type: Any,
+    args: tuple[Any, ...],
+    memo: TypeCheckMemo,
+) -> None:
+    if origin_type is frozenset:
+        if not isinstance(value, frozenset):
+            raise TypeCheckError("is not a frozenset")
+    elif not isinstance(value, AbstractSet):
+        raise TypeCheckError("is not a set")
+
+    if args and args != (Any,):
+        samples = memo.config.collection_check_strategy.iterate_samples(value)
+        for v in samples:
+            try:
+                check_type_internal(v, args[0], memo)
+            except TypeCheckError as exc:
+                exc.append_path_element(f"[{v}]")
+                raise
+
+
+def check_tuple(
+    value: Any,
+    origin_type: Any,
+    args: tuple[Any, ...],
+    memo: TypeCheckMemo,
+) -> None:
+    # Specialized check for NamedTuples
+    if field_types := getattr(origin_type, "__annotations__", None):
+        if not isinstance(value, origin_type):
+            raise TypeCheckError(
+                f"is not a named tuple of type {qualified_name(origin_type)}"
+            )
+
+        for name, field_type in field_types.items():
+            try:
+                check_type_internal(getattr(value, name), field_type, memo)
+            except TypeCheckError as exc:
+                exc.append_path_element(f"attribute {name!r}")
+                raise
+
+        return
+    elif not isinstance(value, tuple):
+        raise TypeCheckError("is not a tuple")
+
+    if args:
+        use_ellipsis = args[-1] is Ellipsis
+        tuple_params = args[: -1 if use_ellipsis else None]
+    else:
+        # Unparametrized Tuple or plain tuple
+        return
+
+    if use_ellipsis:
+        element_type = tuple_params[0]
+        samples = memo.config.collection_check_strategy.iterate_samples(value)
+        for i, element in enumerate(samples):
+            try:
+                check_type_internal(element, element_type, memo)
+            except TypeCheckError as exc:
+                exc.append_path_element(f"item {i}")
+                raise
+    elif tuple_params == ((),):
+        if value != ():
+            raise TypeCheckError("is not an empty tuple")
+    else:
+        if len(value) != len(tuple_params):
+            raise TypeCheckError(
+                f"has wrong number of elements (expected {len(tuple_params)}, got "
+                f"{len(value)} instead)"
+            )
+
+        for i, (element, element_type) in enumerate(zip(value, tuple_params)):
+            try:
+                check_type_internal(element, element_type, memo)
+            except TypeCheckError as exc:
+                exc.append_path_element(f"item {i}")
+                raise
+
+
+def check_union(
+    value: Any,
+    origin_type: Any,
+    args: tuple[Any, ...],
+    memo: TypeCheckMemo,
+) -> None:
+    errors: dict[str, TypeCheckError] = {}
+    try:
+        for type_ in args:
+            try:
+                check_type_internal(value, type_, memo)
+                return
+            except TypeCheckError as exc:
+                errors[get_type_name(type_)] = exc
+
+        formatted_errors = indent(
+            "\n".join(f"{key}: {error}" for key, error in errors.items()), "  "
+        )
+    finally:
+        del errors  # avoid creating ref cycle
+    raise TypeCheckError(f"did not match any element in the union:\n{formatted_errors}")
+
+
+def check_uniontype(
+    value: Any,
+    origin_type: Any,
+    args: tuple[Any, ...],
+    memo: TypeCheckMemo,
+) -> None:
+    errors: dict[str, TypeCheckError] = {}
+    for type_ in args:
+        try:
+            check_type_internal(value, type_, memo)
+            return
+        except TypeCheckError as exc:
+            errors[get_type_name(type_)] = exc
+
+    formatted_errors = indent(
+        "\n".join(f"{key}: {error}" for key, error in errors.items()), "  "
+    )
+    raise TypeCheckError(f"did not match any element in the union:\n{formatted_errors}")
+
+
+def check_class(
+    value: Any,
+    origin_type: Any,
+    args: tuple[Any, ...],
+    memo: TypeCheckMemo,
+) -> None:
+    if not isclass(value) and not isinstance(value, generic_alias_types):
+        raise TypeCheckError("is not a class")
+
+    if not args:
+        return
+
+    if isinstance(args[0], ForwardRef):
+        expected_class = evaluate_forwardref(args[0], memo)
+    else:
+        expected_class = args[0]
+
+    if expected_class is Any:
+        return
+    elif getattr(expected_class, "_is_protocol", False):
+        check_protocol(value, expected_class, (), memo)
+    elif isinstance(expected_class, TypeVar):
+        check_typevar(value, expected_class, (), memo, subclass_check=True)
+    elif get_origin(expected_class) is Union:
+        errors: dict[str, TypeCheckError] = {}
+        for arg in get_args(expected_class):
+            if arg is Any:
+                return
+
+            try:
+                check_class(value, type, (arg,), memo)
+                return
+            except TypeCheckError as exc:
+                errors[get_type_name(arg)] = exc
+        else:
+            formatted_errors = indent(
+                "\n".join(f"{key}: {error}" for key, error in errors.items()), "  "
+            )
+            raise TypeCheckError(
+                f"did not match any element in the union:\n{formatted_errors}"
+            )
+    elif not issubclass(value, expected_class):  # type: ignore[arg-type]
+        raise TypeCheckError(f"is not a subclass of {qualified_name(expected_class)}")
+
+
+def check_newtype(
+    value: Any,
+    origin_type: Any,
+    args: tuple[Any, ...],
+    memo: TypeCheckMemo,
+) -> None:
+    check_type_internal(value, origin_type.__supertype__, memo)
+
+
+def check_instance(
+    value: Any,
+    origin_type: Any,
+    args: tuple[Any, ...],
+    memo: TypeCheckMemo,
+) -> None:
+    if not isinstance(value, origin_type):
+        raise TypeCheckError(f"is not an instance of {qualified_name(origin_type)}")
+
+
+def check_typevar(
+    value: Any,
+    origin_type: TypeVar,
+    args: tuple[Any, ...],
+    memo: TypeCheckMemo,
+    *,
+    subclass_check: bool = False,
+) -> None:
+    if origin_type.__bound__ is not None:
+        annotation = (
+            Type[origin_type.__bound__] if subclass_check else origin_type.__bound__
+        )
+        check_type_internal(value, annotation, memo)
+    elif origin_type.__constraints__:
+        for constraint in origin_type.__constraints__:
+            annotation = Type[constraint] if subclass_check else constraint
+            try:
+                check_type_internal(value, annotation, memo)
+            except TypeCheckError:
+                pass
+            else:
+                break
+        else:
+            formatted_constraints = ", ".join(
+                get_type_name(constraint) for constraint in origin_type.__constraints__
+            )
+            raise TypeCheckError(
+                f"does not match any of the constraints " f"({formatted_constraints})"
+            )
+
+
+def _is_literal_type(typ: object) -> bool:
+    return typ is typing.Literal or typ is typing_extensions.Literal
+
+
+def check_literal(
+    value: Any,
+    origin_type: Any,
+    args: tuple[Any, ...],
+    memo: TypeCheckMemo,
+) -> None:
+    def get_literal_args(literal_args: tuple[Any, ...]) -> tuple[Any, ...]:
+        retval: list[Any] = []
+        for arg in literal_args:
+            if _is_literal_type(get_origin(arg)):
+                retval.extend(get_literal_args(arg.__args__))
+            elif arg is None or isinstance(arg, (int, str, bytes, bool, Enum)):
+                retval.append(arg)
+            else:
+                raise TypeError(
+                    f"Illegal literal value: {arg}"
+                )  # TypeError here is deliberate
+
+        return tuple(retval)
+
+    final_args = tuple(get_literal_args(args))
+    try:
+        index = final_args.index(value)
+    except ValueError:
+        pass
+    else:
+        if type(final_args[index]) is type(value):
+            return
+
+    formatted_args = ", ".join(repr(arg) for arg in final_args)
+    raise TypeCheckError(f"is not any of ({formatted_args})") from None
+
+
+def check_literal_string(
+    value: Any,
+    origin_type: Any,
+    args: tuple[Any, ...],
+    memo: TypeCheckMemo,
+) -> None:
+    check_type_internal(value, str, memo)
+
+
+def check_typeguard(
+    value: Any,
+    origin_type: Any,
+    args: tuple[Any, ...],
+    memo: TypeCheckMemo,
+) -> None:
+    check_type_internal(value, bool, memo)
+
+
+def check_none(
+    value: Any,
+    origin_type: Any,
+    args: tuple[Any, ...],
+    memo: TypeCheckMemo,
+) -> None:
+    if value is not None:
+        raise TypeCheckError("is not None")
+
+
+def check_number(
+    value: Any,
+    origin_type: Any,
+    args: tuple[Any, ...],
+    memo: TypeCheckMemo,
+) -> None:
+    if origin_type is complex and not isinstance(value, (complex, float, int)):
+        raise TypeCheckError("is neither complex, float or int")
+    elif origin_type is float and not isinstance(value, (float, int)):
+        raise TypeCheckError("is neither float or int")
+
+
+def check_io(
+    value: Any,
+    origin_type: Any,
+    args: tuple[Any, ...],
+    memo: TypeCheckMemo,
+) -> None:
+    if origin_type is TextIO or (origin_type is IO and args == (str,)):
+        if not isinstance(value, TextIOBase):
+            raise TypeCheckError("is not a text based I/O object")
+    elif origin_type is BinaryIO or (origin_type is IO and args == (bytes,)):
+        if not isinstance(value, (RawIOBase, BufferedIOBase)):
+            raise TypeCheckError("is not a binary I/O object")
+    elif not isinstance(value, IOBase):
+        raise TypeCheckError("is not an I/O object")
+
+
+def check_signature_compatible(
+    subject_callable: Callable[..., Any], protocol: type, attrname: str
+) -> None:
+    subject_sig = inspect.signature(subject_callable)
+    protocol_sig = inspect.signature(getattr(protocol, attrname))
+    protocol_type: typing.Literal["instance", "class", "static"] = "instance"
+    subject_type: typing.Literal["instance", "class", "static"] = "instance"
+
+    # Check if the protocol-side method is a class method or static method
+    if attrname in protocol.__dict__:
+        descriptor = protocol.__dict__[attrname]
+        if isinstance(descriptor, staticmethod):
+            protocol_type = "static"
+        elif isinstance(descriptor, classmethod):
+            protocol_type = "class"
+
+    # Check if the subject-side method is a class method or static method
+    if inspect.ismethod(subject_callable) and inspect.isclass(
+        subject_callable.__self__
+    ):
+        subject_type = "class"
+    elif not hasattr(subject_callable, "__self__"):
+        subject_type = "static"
+
+    if protocol_type == "instance" and subject_type != "instance":
+        raise TypeCheckError(
+            f"should be an instance method but it's a {subject_type} method"
+        )
+    elif protocol_type != "instance" and subject_type == "instance":
+        raise TypeCheckError(
+            f"should be a {protocol_type} method but it's an instance method"
+        )
+
+    expected_varargs = any(
+        param
+        for param in protocol_sig.parameters.values()
+        if param.kind is Parameter.VAR_POSITIONAL
+    )
+    has_varargs = any(
+        param
+        for param in subject_sig.parameters.values()
+        if param.kind is Parameter.VAR_POSITIONAL
+    )
+    if expected_varargs and not has_varargs:
+        raise TypeCheckError("should accept variable positional arguments but doesn't")
+
+    protocol_has_varkwargs = any(
+        param
+        for param in protocol_sig.parameters.values()
+        if param.kind is Parameter.VAR_KEYWORD
+    )
+    subject_has_varkwargs = any(
+        param
+        for param in subject_sig.parameters.values()
+        if param.kind is Parameter.VAR_KEYWORD
+    )
+    if protocol_has_varkwargs and not subject_has_varkwargs:
+        raise TypeCheckError("should accept variable keyword arguments but doesn't")
+
+    # Check that the callable has at least the expect amount of positional-only
+    # arguments (and no extra positional-only arguments without default values)
+    if not has_varargs:
+        protocol_args = [
+            param
+            for param in protocol_sig.parameters.values()
+            if param.kind
+            in (Parameter.POSITIONAL_ONLY, Parameter.POSITIONAL_OR_KEYWORD)
+        ]
+        subject_args = [
+            param
+            for param in subject_sig.parameters.values()
+            if param.kind
+            in (Parameter.POSITIONAL_ONLY, Parameter.POSITIONAL_OR_KEYWORD)
+        ]
+
+        # Remove the "self" parameter from the protocol arguments to match
+        if protocol_type == "instance":
+            protocol_args.pop(0)
+
+        for protocol_arg, subject_arg in zip_longest(protocol_args, subject_args):
+            if protocol_arg is None:
+                if subject_arg.default is Parameter.empty:
+                    raise TypeCheckError("has too many mandatory positional arguments")
+
+                break
+
+            if subject_arg is None:
+                raise TypeCheckError("has too few positional arguments")
+
+            if (
+                protocol_arg.kind is Parameter.POSITIONAL_OR_KEYWORD
+                and subject_arg.kind is Parameter.POSITIONAL_ONLY
+            ):
+                raise TypeCheckError(
+                    f"has an argument ({subject_arg.name}) that should not be "
+                    f"positional-only"
+                )
+
+            if (
+                protocol_arg.kind is Parameter.POSITIONAL_OR_KEYWORD
+                and protocol_arg.name != subject_arg.name
+            ):
+                raise TypeCheckError(
+                    f"has a positional argument ({subject_arg.name}) that should be "
+                    f"named {protocol_arg.name!r} at this position"
+                )
+
+    protocol_kwonlyargs = {
+        param.name: param
+        for param in protocol_sig.parameters.values()
+        if param.kind is Parameter.KEYWORD_ONLY
+    }
+    subject_kwonlyargs = {
+        param.name: param
+        for param in subject_sig.parameters.values()
+        if param.kind is Parameter.KEYWORD_ONLY
+    }
+    if not subject_has_varkwargs:
+        # Check that the signature has at least the required keyword-only arguments, and
+        # no extra mandatory keyword-only arguments
+        if missing_kwonlyargs := [
+            param.name
+            for param in protocol_kwonlyargs.values()
+            if param.name not in subject_kwonlyargs
+        ]:
+            raise TypeCheckError(
+                "is missing keyword-only arguments: " + ", ".join(missing_kwonlyargs)
+            )
+
+    if not protocol_has_varkwargs:
+        if extra_kwonlyargs := [
+            param.name
+            for param in subject_kwonlyargs.values()
+            if param.default is Parameter.empty
+            and param.name not in protocol_kwonlyargs
+        ]:
+            raise TypeCheckError(
+                "has mandatory keyword-only arguments not present in the protocol: "
+                + ", ".join(extra_kwonlyargs)
+            )
+
+
+def check_protocol(
+    value: Any,
+    origin_type: Any,
+    args: tuple[Any, ...],
+    memo: TypeCheckMemo,
+) -> None:
+    origin_annotations = typing.get_type_hints(origin_type)
+    for attrname in sorted(typing_extensions.get_protocol_members(origin_type)):
+        if (annotation := origin_annotations.get(attrname)) is not None:
+            try:
+                subject_member = getattr(value, attrname)
+            except AttributeError:
+                raise TypeCheckError(
+                    f"is not compatible with the {origin_type.__qualname__} "
+                    f"protocol because it has no attribute named {attrname!r}"
+                ) from None
+
+            try:
+                check_type_internal(subject_member, annotation, memo)
+            except TypeCheckError as exc:
+                raise TypeCheckError(
+                    f"is not compatible with the {origin_type.__qualname__} "
+                    f"protocol because its {attrname!r} attribute {exc}"
+                ) from None
+        elif callable(getattr(origin_type, attrname)):
+            try:
+                subject_member = getattr(value, attrname)
+            except AttributeError:
+                raise TypeCheckError(
+                    f"is not compatible with the {origin_type.__qualname__} "
+                    f"protocol because it has no method named {attrname!r}"
+                ) from None
+
+            if not callable(subject_member):
+                raise TypeCheckError(
+                    f"is not compatible with the {origin_type.__qualname__} "
+                    f"protocol because its {attrname!r} attribute is not a callable"
+                )
+
+            # TODO: implement assignability checks for parameter and return value
+            #  annotations
+            try:
+                check_signature_compatible(subject_member, origin_type, attrname)
+            except TypeCheckError as exc:
+                raise TypeCheckError(
+                    f"is not compatible with the {origin_type.__qualname__} "
+                    f"protocol because its {attrname!r} method {exc}"
+                ) from None
+
+
+def check_byteslike(
+    value: Any,
+    origin_type: Any,
+    args: tuple[Any, ...],
+    memo: TypeCheckMemo,
+) -> None:
+    if not isinstance(value, (bytearray, bytes, memoryview)):
+        raise TypeCheckError("is not bytes-like")
+
+
+def check_self(
+    value: Any,
+    origin_type: Any,
+    args: tuple[Any, ...],
+    memo: TypeCheckMemo,
+) -> None:
+    if memo.self_type is None:
+        raise TypeCheckError("cannot be checked against Self outside of a method call")
+
+    if isclass(value):
+        if not issubclass(value, memo.self_type):
+            raise TypeCheckError(
+                f"is not an instance of the self type "
+                f"({qualified_name(memo.self_type)})"
+            )
+    elif not isinstance(value, memo.self_type):
+        raise TypeCheckError(
+            f"is not an instance of the self type ({qualified_name(memo.self_type)})"
+        )
+
+
+def check_paramspec(
+    value: Any,
+    origin_type: Any,
+    args: tuple[Any, ...],
+    memo: TypeCheckMemo,
+) -> None:
+    pass  # No-op for now
+
+
+def check_instanceof(
+    value: Any,
+    origin_type: Any,
+    args: tuple[Any, ...],
+    memo: TypeCheckMemo,
+) -> None:
+    if not isinstance(value, origin_type):
+        raise TypeCheckError(f"is not an instance of {qualified_name(origin_type)}")
+
+
+def check_type_internal(
+    value: Any,
+    annotation: Any,
+    memo: TypeCheckMemo,
+) -> None:
+    """
+    Check that the given object is compatible with the given type annotation.
+
+    This function should only be used by type checker callables. Applications should use
+    :func:`~.check_type` instead.
+
+    :param value: the value to check
+    :param annotation: the type annotation to check against
+    :param memo: a memo object containing configuration and information necessary for
+        looking up forward references
+    """
+
+    if isinstance(annotation, ForwardRef):
+        try:
+            annotation = evaluate_forwardref(annotation, memo)
+        except NameError:
+            if memo.config.forward_ref_policy is ForwardRefPolicy.ERROR:
+                raise
+            elif memo.config.forward_ref_policy is ForwardRefPolicy.WARN:
+                warnings.warn(
+                    f"Cannot resolve forward reference {annotation.__forward_arg__!r}",
+                    TypeHintWarning,
+                    stacklevel=get_stacklevel(),
+                )
+
+            return
+
+    if annotation is Any or annotation is SubclassableAny or isinstance(value, Mock):
+        return
+
+    # Skip type checks if value is an instance of a class that inherits from Any
+    if not isclass(value) and SubclassableAny in type(value).__bases__:
+        return
+
+    extras: tuple[Any, ...]
+    origin_type = get_origin(annotation)
+    if origin_type is Annotated:
+        annotation, *extras_ = get_args(annotation)
+        extras = tuple(extras_)
+        origin_type = get_origin(annotation)
+    else:
+        extras = ()
+
+    if origin_type is not None:
+        args = get_args(annotation)
+
+        # Compatibility hack to distinguish between unparametrized and empty tuple
+        # (tuple[()]), necessary due to https://github.com/python/cpython/issues/91137
+        if origin_type in (tuple, Tuple) and annotation is not Tuple and not args:
+            args = ((),)
+    else:
+        origin_type = annotation
+        args = ()
+
+    for lookup_func in checker_lookup_functions:
+        checker = lookup_func(origin_type, args, extras)
+        if checker:
+            checker(value, origin_type, args, memo)
+            return
+
+    if isclass(origin_type):
+        if not isinstance(value, origin_type):
+            raise TypeCheckError(f"is not an instance of {qualified_name(origin_type)}")
+    elif type(origin_type) is str:  # noqa: E721
+        warnings.warn(
+            f"Skipping type check against {origin_type!r}; this looks like a "
+            f"string-form forward reference imported from another module",
+            TypeHintWarning,
+            stacklevel=get_stacklevel(),
+        )
+
+
+# Equality checks are applied to these
+origin_type_checkers = {
+    bytes: check_byteslike,
+    AbstractSet: check_set,
+    BinaryIO: check_io,
+    Callable: check_callable,
+    collections.abc.Callable: check_callable,
+    complex: check_number,
+    dict: check_mapping,
+    Dict: check_mapping,
+    float: check_number,
+    frozenset: check_set,
+    IO: check_io,
+    list: check_list,
+    List: check_list,
+    typing.Literal: check_literal,
+    Mapping: check_mapping,
+    MutableMapping: check_mapping,
+    None: check_none,
+    collections.abc.Mapping: check_mapping,
+    collections.abc.MutableMapping: check_mapping,
+    Sequence: check_sequence,
+    collections.abc.Sequence: check_sequence,
+    collections.abc.Set: check_set,
+    set: check_set,
+    Set: check_set,
+    TextIO: check_io,
+    tuple: check_tuple,
+    Tuple: check_tuple,
+    type: check_class,
+    Type: check_class,
+    Union: check_union,
+    # On some versions of Python, these may simply be re-exports from "typing",
+    # but exactly which Python versions is subject to change.
+    # It's best to err on the safe side and just always specify these.
+    typing_extensions.Literal: check_literal,
+    typing_extensions.LiteralString: check_literal_string,
+    typing_extensions.Self: check_self,
+    typing_extensions.TypeGuard: check_typeguard,
+}
+if sys.version_info >= (3, 10):
+    origin_type_checkers[types.UnionType] = check_uniontype
+    origin_type_checkers[typing.TypeGuard] = check_typeguard
+if sys.version_info >= (3, 11):
+    origin_type_checkers.update(
+        {typing.LiteralString: check_literal_string, typing.Self: check_self}
+    )
+
+
+def builtin_checker_lookup(
+    origin_type: Any, args: tuple[Any, ...], extras: tuple[Any, ...]
+) -> TypeCheckerCallable | None:
+    checker = origin_type_checkers.get(origin_type)
+    if checker is not None:
+        return checker
+    elif is_typeddict(origin_type):
+        return check_typed_dict
+    elif isclass(origin_type) and issubclass(
+        origin_type,
+        Tuple,  # type: ignore[arg-type]
+    ):
+        # NamedTuple
+        return check_tuple
+    elif getattr(origin_type, "_is_protocol", False):
+        return check_protocol
+    elif isinstance(origin_type, ParamSpec):
+        return check_paramspec
+    elif isinstance(origin_type, TypeVar):
+        return check_typevar
+    elif origin_type.__class__ is NewType:
+        # typing.NewType on Python 3.10+
+        return check_newtype
+    elif (
+        isfunction(origin_type)
+        and getattr(origin_type, "__module__", None) == "typing"
+        and getattr(origin_type, "__qualname__", "").startswith("NewType.")
+        and hasattr(origin_type, "__supertype__")
+    ):
+        # typing.NewType on Python 3.9 and below
+        return check_newtype
+
+    return None
+
+
+checker_lookup_functions.append(builtin_checker_lookup)
+
+
+def load_plugins() -> None:
+    """
+    Load all type checker lookup functions from entry points.
+
+    All entry points from the ``typeguard.checker_lookup`` group are loaded, and the
+    returned lookup functions are added to :data:`typeguard.checker_lookup_functions`.
+
+    .. note:: This function is called implicitly on import, unless the
+        ``TYPEGUARD_DISABLE_PLUGIN_AUTOLOAD`` environment variable is present.
+    """
+
+    for ep in entry_points(group="typeguard.checker_lookup"):
+        try:
+            plugin = ep.load()
+        except Exception as exc:
+            warnings.warn(
+                f"Failed to load plugin {ep.name!r}: " f"{qualified_name(exc)}: {exc}",
+                stacklevel=2,
+            )
+            continue
+
+        if not callable(plugin):
+            warnings.warn(
+                f"Plugin {ep} returned a non-callable object: {plugin!r}", stacklevel=2
+            )
+            continue
+
+        checker_lookup_functions.insert(0, plugin)
diff --git a/metaflow/_vendor/typeguard/_config.py b/metaflow/_vendor/typeguard/_config.py
new file mode 100644
index 00000000000..36efad53965
--- /dev/null
+++ b/metaflow/_vendor/typeguard/_config.py
@@ -0,0 +1,108 @@
+from __future__ import annotations
+
+from collections.abc import Iterable
+from dataclasses import dataclass
+from enum import Enum, auto
+from typing import TYPE_CHECKING, TypeVar
+
+if TYPE_CHECKING:
+    from ._functions import TypeCheckFailCallback
+
+T = TypeVar("T")
+
+
+class ForwardRefPolicy(Enum):
+    """
+    Defines how unresolved forward references are handled.
+
+    Members:
+
+    * ``ERROR``: propagate the :exc:`NameError` when the forward reference lookup fails
+    * ``WARN``: emit a :class:`~.TypeHintWarning` if the forward reference lookup fails
+    * ``IGNORE``: silently skip checks for unresolveable forward references
+    """
+
+    ERROR = auto()
+    WARN = auto()
+    IGNORE = auto()
+
+
+class CollectionCheckStrategy(Enum):
+    """
+    Specifies how thoroughly the contents of collections are type checked.
+
+    This has an effect on the following built-in checkers:
+
+    * ``AbstractSet``
+    * ``Dict``
+    * ``List``
+    * ``Mapping``
+    * ``Set``
+    * ``Tuple[, ...]`` (arbitrarily sized tuples)
+
+    Members:
+
+    * ``FIRST_ITEM``: check only the first item
+    * ``ALL_ITEMS``: check all items
+    """
+
+    FIRST_ITEM = auto()
+    ALL_ITEMS = auto()
+
+    def iterate_samples(self, collection: Iterable[T]) -> Iterable[T]:
+        if self is CollectionCheckStrategy.FIRST_ITEM:
+            try:
+                return [next(iter(collection))]
+            except StopIteration:
+                return ()
+        else:
+            return collection
+
+
+@dataclass
+class TypeCheckConfiguration:
+    """
+     You can change Typeguard's behavior with these settings.
+
+    .. attribute:: typecheck_fail_callback
+       :type: Callable[[TypeCheckError, TypeCheckMemo], Any]
+
+         Callable that is called when type checking fails.
+
+         Default: ``None`` (the :exc:`~.TypeCheckError` is raised directly)
+
+    .. attribute:: forward_ref_policy
+       :type: ForwardRefPolicy
+
+         Specifies what to do when a forward reference fails to resolve.
+
+         Default: ``WARN``
+
+    .. attribute:: collection_check_strategy
+       :type: CollectionCheckStrategy
+
+         Specifies how thoroughly the contents of collections (list, dict, etc.) are
+         type checked.
+
+         Default: ``FIRST_ITEM``
+
+    .. attribute:: debug_instrumentation
+       :type: bool
+
+         If set to ``True``, the code of modules or functions instrumented by typeguard
+         is printed to ``sys.stderr`` after the instrumentation is done
+
+         Requires Python 3.9 or newer.
+
+         Default: ``False``
+    """
+
+    forward_ref_policy: ForwardRefPolicy = ForwardRefPolicy.WARN
+    typecheck_fail_callback: TypeCheckFailCallback | None = None
+    collection_check_strategy: CollectionCheckStrategy = (
+        CollectionCheckStrategy.FIRST_ITEM
+    )
+    debug_instrumentation: bool = False
+
+
+global_config = TypeCheckConfiguration()
diff --git a/metaflow/_vendor/typeguard/_decorators.py b/metaflow/_vendor/typeguard/_decorators.py
new file mode 100644
index 00000000000..af6f82beec7
--- /dev/null
+++ b/metaflow/_vendor/typeguard/_decorators.py
@@ -0,0 +1,233 @@
+from __future__ import annotations
+
+import ast
+import inspect
+import sys
+from collections.abc import Sequence
+from functools import partial
+from inspect import isclass, isfunction
+from types import CodeType, FrameType, FunctionType
+from typing import TYPE_CHECKING, Any, Callable, ForwardRef, TypeVar, cast, overload
+from warnings import warn
+
+from ._config import CollectionCheckStrategy, ForwardRefPolicy, global_config
+from ._exceptions import InstrumentationWarning
+from ._functions import TypeCheckFailCallback
+from ._transformer import TypeguardTransformer
+from ._utils import Unset, function_name, get_stacklevel, is_method_of, unset
+
+T_CallableOrType = TypeVar("T_CallableOrType", bound=Callable[..., Any])
+
+if TYPE_CHECKING:
+    from typeshed.stdlib.types import _Cell
+
+    def typeguard_ignore(f: T_CallableOrType) -> T_CallableOrType:
+        """This decorator is a noop during static type-checking."""
+        return f
+
+else:
+    from typing import no_type_check as typeguard_ignore  # noqa: F401
+
+
+def make_cell(value: object) -> _Cell:
+    return (lambda: value).__closure__[0]  # type: ignore[index]
+
+
+def find_target_function(
+    new_code: CodeType, target_path: Sequence[str], firstlineno: int
+) -> CodeType | None:
+    target_name = target_path[0]
+    for const in new_code.co_consts:
+        if isinstance(const, CodeType):
+            if const.co_name == target_name:
+                if const.co_firstlineno == firstlineno:
+                    return const
+                elif len(target_path) > 1:
+                    target_code = find_target_function(
+                        const, target_path[1:], firstlineno
+                    )
+                    if target_code:
+                        return target_code
+
+    return None
+
+
+def instrument(f: T_CallableOrType) -> FunctionType | str:
+    if not getattr(f, "__code__", None):
+        return "no code associated"
+    elif not getattr(f, "__module__", None):
+        return "__module__ attribute is not set"
+    elif f.__code__.co_filename == "":
+        return "cannot instrument functions defined in a REPL"
+    elif hasattr(f, "__wrapped__"):
+        return (
+            "@typechecked only supports instrumenting functions wrapped with "
+            "@classmethod, @staticmethod or @property"
+        )
+
+    target_path = [item for item in f.__qualname__.split(".") if item != ""]
+    module_source = inspect.getsource(sys.modules[f.__module__])
+    module_ast = ast.parse(module_source)
+    instrumentor = TypeguardTransformer(target_path, f.__code__.co_firstlineno)
+    instrumentor.visit(module_ast)
+
+    if not instrumentor.target_node or instrumentor.target_lineno is None:
+        return "instrumentor did not find the target function"
+
+    module_code = compile(module_ast, f.__code__.co_filename, "exec", dont_inherit=True)
+    new_code = find_target_function(
+        module_code, target_path, instrumentor.target_lineno
+    )
+    if not new_code:
+        return "cannot find the target function in the AST"
+
+    if global_config.debug_instrumentation and sys.version_info >= (3, 9):
+        # Find the matching AST node, then unparse it to source and print to stdout
+        print(
+            f"Source code of {f.__qualname__}() after instrumentation:"
+            "\n----------------------------------------------",
+            file=sys.stderr,
+        )
+        print(ast.unparse(instrumentor.target_node), file=sys.stderr)
+        print(
+            "----------------------------------------------",
+            file=sys.stderr,
+        )
+
+    closure = f.__closure__
+    if new_code.co_freevars != f.__code__.co_freevars:
+        # Create a new closure and find values for the new free variables
+        frame = cast(FrameType, inspect.currentframe())
+        frame = cast(FrameType, frame.f_back)
+        frame_locals = cast(FrameType, frame.f_back).f_locals
+        cells: list[_Cell] = []
+        for key in new_code.co_freevars:
+            if key in instrumentor.names_used_in_annotations:
+                # Find the value and make a new cell from it
+                value = frame_locals.get(key) or ForwardRef(key)
+                cells.append(make_cell(value))
+            else:
+                # Reuse the cell from the existing closure
+                assert f.__closure__
+                cells.append(f.__closure__[f.__code__.co_freevars.index(key)])
+
+        closure = tuple(cells)
+
+    new_function = FunctionType(new_code, f.__globals__, f.__name__, closure=closure)
+    new_function.__module__ = f.__module__
+    new_function.__name__ = f.__name__
+    new_function.__qualname__ = f.__qualname__
+    new_function.__annotations__ = f.__annotations__
+    new_function.__doc__ = f.__doc__
+    new_function.__defaults__ = f.__defaults__
+    new_function.__kwdefaults__ = f.__kwdefaults__
+    return new_function
+
+
+@overload
+def typechecked(
+    *,
+    forward_ref_policy: ForwardRefPolicy | Unset = unset,
+    typecheck_fail_callback: TypeCheckFailCallback | Unset = unset,
+    collection_check_strategy: CollectionCheckStrategy | Unset = unset,
+    debug_instrumentation: bool | Unset = unset,
+) -> Callable[[T_CallableOrType], T_CallableOrType]: ...
+
+
+@overload
+def typechecked(target: T_CallableOrType) -> T_CallableOrType: ...
+
+
+def typechecked(
+    target: T_CallableOrType | None = None,
+    *,
+    forward_ref_policy: ForwardRefPolicy | Unset = unset,
+    typecheck_fail_callback: TypeCheckFailCallback | Unset = unset,
+    collection_check_strategy: CollectionCheckStrategy | Unset = unset,
+    debug_instrumentation: bool | Unset = unset,
+) -> Any:
+    """
+    Instrument the target function to perform run-time type checking.
+
+    This decorator recompiles the target function, injecting code to type check
+    arguments, return values, yield values (excluding ``yield from``) and assignments to
+    annotated local variables.
+
+    This can also be used as a class decorator. This will instrument all type annotated
+    methods, including :func:`@classmethod `,
+    :func:`@staticmethod `,  and :class:`@property ` decorated
+    methods in the class.
+
+    .. note:: When Python is run in optimized mode (``-O`` or ``-OO``, this decorator
+        is a no-op). This is a feature meant for selectively introducing type checking
+        into a code base where the checks aren't meant to be run in production.
+
+    :param target: the function or class to enable type checking for
+    :param forward_ref_policy: override for
+        :attr:`.TypeCheckConfiguration.forward_ref_policy`
+    :param typecheck_fail_callback: override for
+        :attr:`.TypeCheckConfiguration.typecheck_fail_callback`
+    :param collection_check_strategy: override for
+        :attr:`.TypeCheckConfiguration.collection_check_strategy`
+    :param debug_instrumentation: override for
+        :attr:`.TypeCheckConfiguration.debug_instrumentation`
+
+    """
+    if target is None:
+        return partial(
+            typechecked,
+            forward_ref_policy=forward_ref_policy,
+            typecheck_fail_callback=typecheck_fail_callback,
+            collection_check_strategy=collection_check_strategy,
+            debug_instrumentation=debug_instrumentation,
+        )
+
+    if not __debug__:
+        return target
+
+    if isclass(target):
+        for key, attr in target.__dict__.items():
+            if is_method_of(attr, target):
+                retval = instrument(attr)
+                if isfunction(retval):
+                    setattr(target, key, retval)
+            elif isinstance(attr, (classmethod, staticmethod)):
+                if is_method_of(attr.__func__, target):
+                    retval = instrument(attr.__func__)
+                    if isfunction(retval):
+                        wrapper = attr.__class__(retval)
+                        setattr(target, key, wrapper)
+            elif isinstance(attr, property):
+                kwargs: dict[str, Any] = dict(doc=attr.__doc__)
+                for name in ("fset", "fget", "fdel"):
+                    property_func = kwargs[name] = getattr(attr, name)
+                    if is_method_of(property_func, target):
+                        retval = instrument(property_func)
+                        if isfunction(retval):
+                            kwargs[name] = retval
+
+                setattr(target, key, attr.__class__(**kwargs))
+
+        return target
+
+    # Find either the first Python wrapper or the actual function
+    wrapper_class: (
+        type[classmethod[Any, Any, Any]] | type[staticmethod[Any, Any]] | None
+    ) = None
+    if isinstance(target, (classmethod, staticmethod)):
+        wrapper_class = target.__class__
+        target = target.__func__
+
+    retval = instrument(target)
+    if isinstance(retval, str):
+        warn(
+            f"{retval} -- not typechecking {function_name(target)}",
+            InstrumentationWarning,
+            stacklevel=get_stacklevel(),
+        )
+        return target
+
+    if wrapper_class is None:
+        return retval
+    else:
+        return wrapper_class(retval)
diff --git a/metaflow/_vendor/typeguard/_exceptions.py b/metaflow/_vendor/typeguard/_exceptions.py
new file mode 100644
index 00000000000..625437a6499
--- /dev/null
+++ b/metaflow/_vendor/typeguard/_exceptions.py
@@ -0,0 +1,42 @@
+from collections import deque
+from typing import Deque
+
+
+class TypeHintWarning(UserWarning):
+    """
+    A warning that is emitted when a type hint in string form could not be resolved to
+    an actual type.
+    """
+
+
+class TypeCheckWarning(UserWarning):
+    """Emitted by typeguard's type checkers when a type mismatch is detected."""
+
+    def __init__(self, message: str):
+        super().__init__(message)
+
+
+class InstrumentationWarning(UserWarning):
+    """Emitted when there's a problem with instrumenting a function for type checks."""
+
+    def __init__(self, message: str):
+        super().__init__(message)
+
+
+class TypeCheckError(Exception):
+    """
+    Raised by typeguard's type checkers when a type mismatch is detected.
+    """
+
+    def __init__(self, message: str):
+        super().__init__(message)
+        self._path: Deque[str] = deque()
+
+    def append_path_element(self, element: str) -> None:
+        self._path.append(element)
+
+    def __str__(self) -> str:
+        if self._path:
+            return " of ".join(self._path) + " " + str(self.args[0])
+        else:
+            return str(self.args[0])
diff --git a/metaflow/_vendor/typeguard/_functions.py b/metaflow/_vendor/typeguard/_functions.py
new file mode 100644
index 00000000000..d8a2339a5d7
--- /dev/null
+++ b/metaflow/_vendor/typeguard/_functions.py
@@ -0,0 +1,308 @@
+from __future__ import annotations
+
+import sys
+import warnings
+from typing import Any, Callable, NoReturn, TypeVar, Union, overload
+
+from . import _suppression
+from ._checkers import BINARY_MAGIC_METHODS, check_type_internal
+from ._config import (
+    CollectionCheckStrategy,
+    ForwardRefPolicy,
+    TypeCheckConfiguration,
+)
+from ._exceptions import TypeCheckError, TypeCheckWarning
+from ._memo import TypeCheckMemo
+from ._utils import get_stacklevel, qualified_name
+
+if sys.version_info >= (3, 11):
+    from typing import Literal, Never, TypeAlias
+else:
+    from metaflow._vendor.typing_extensions import Literal, Never, TypeAlias
+
+T = TypeVar("T")
+TypeCheckFailCallback: TypeAlias = Callable[[TypeCheckError, TypeCheckMemo], Any]
+
+
+@overload
+def check_type(
+    value: object,
+    expected_type: type[T],
+    *,
+    forward_ref_policy: ForwardRefPolicy = ...,
+    typecheck_fail_callback: TypeCheckFailCallback | None = ...,
+    collection_check_strategy: CollectionCheckStrategy = ...,
+) -> T: ...
+
+
+@overload
+def check_type(
+    value: object,
+    expected_type: Any,
+    *,
+    forward_ref_policy: ForwardRefPolicy = ...,
+    typecheck_fail_callback: TypeCheckFailCallback | None = ...,
+    collection_check_strategy: CollectionCheckStrategy = ...,
+) -> Any: ...
+
+
+def check_type(
+    value: object,
+    expected_type: Any,
+    *,
+    forward_ref_policy: ForwardRefPolicy = TypeCheckConfiguration().forward_ref_policy,
+    typecheck_fail_callback: TypeCheckFailCallback | None = (
+        TypeCheckConfiguration().typecheck_fail_callback
+    ),
+    collection_check_strategy: CollectionCheckStrategy = (
+        TypeCheckConfiguration().collection_check_strategy
+    ),
+) -> Any:
+    """
+    Ensure that ``value`` matches ``expected_type``.
+
+    The types from the :mod:`typing` module do not support :func:`isinstance` or
+    :func:`issubclass` so a number of type specific checks are required. This function
+    knows which checker to call for which type.
+
+    This function wraps :func:`~.check_type_internal` in the following ways:
+
+    * Respects type checking suppression (:func:`~.suppress_type_checks`)
+    * Forms a :class:`~.TypeCheckMemo` from the current stack frame
+    * Calls the configured type check fail callback if the check fails
+
+    Note that this function is independent of the globally shared configuration in
+    :data:`typeguard.config`. This means that usage within libraries is safe from being
+    affected configuration changes made by other libraries or by the integrating
+    application. Instead, configuration options have the same default values as their
+    corresponding fields in :class:`TypeCheckConfiguration`.
+
+    :param value: value to be checked against ``expected_type``
+    :param expected_type: a class or generic type instance, or a tuple of such things
+    :param forward_ref_policy: see :attr:`TypeCheckConfiguration.forward_ref_policy`
+    :param typecheck_fail_callback:
+        see :attr`TypeCheckConfiguration.typecheck_fail_callback`
+    :param collection_check_strategy:
+        see :attr:`TypeCheckConfiguration.collection_check_strategy`
+    :return: ``value``, unmodified
+    :raises TypeCheckError: if there is a type mismatch
+
+    """
+    if type(expected_type) is tuple:
+        expected_type = Union[expected_type]
+
+    config = TypeCheckConfiguration(
+        forward_ref_policy=forward_ref_policy,
+        typecheck_fail_callback=typecheck_fail_callback,
+        collection_check_strategy=collection_check_strategy,
+    )
+
+    if _suppression.type_checks_suppressed or expected_type is Any:
+        return value
+
+    frame = sys._getframe(1)
+    memo = TypeCheckMemo(frame.f_globals, frame.f_locals, config=config)
+    try:
+        check_type_internal(value, expected_type, memo)
+    except TypeCheckError as exc:
+        exc.append_path_element(qualified_name(value, add_class_prefix=True))
+        if config.typecheck_fail_callback:
+            config.typecheck_fail_callback(exc, memo)
+        else:
+            raise
+
+    return value
+
+
+def check_argument_types(
+    func_name: str,
+    arguments: dict[str, tuple[Any, Any]],
+    memo: TypeCheckMemo,
+) -> Literal[True]:
+    if _suppression.type_checks_suppressed:
+        return True
+
+    for argname, (value, annotation) in arguments.items():
+        if annotation is NoReturn or annotation is Never:
+            exc = TypeCheckError(
+                f"{func_name}() was declared never to be called but it was"
+            )
+            if memo.config.typecheck_fail_callback:
+                memo.config.typecheck_fail_callback(exc, memo)
+            else:
+                raise exc
+
+        try:
+            check_type_internal(value, annotation, memo)
+        except TypeCheckError as exc:
+            qualname = qualified_name(value, add_class_prefix=True)
+            exc.append_path_element(f'argument "{argname}" ({qualname})')
+            if memo.config.typecheck_fail_callback:
+                memo.config.typecheck_fail_callback(exc, memo)
+            else:
+                raise
+
+    return True
+
+
+def check_return_type(
+    func_name: str,
+    retval: T,
+    annotation: Any,
+    memo: TypeCheckMemo,
+) -> T:
+    if _suppression.type_checks_suppressed:
+        return retval
+
+    if annotation is NoReturn or annotation is Never:
+        exc = TypeCheckError(f"{func_name}() was declared never to return but it did")
+        if memo.config.typecheck_fail_callback:
+            memo.config.typecheck_fail_callback(exc, memo)
+        else:
+            raise exc
+
+    try:
+        check_type_internal(retval, annotation, memo)
+    except TypeCheckError as exc:
+        # Allow NotImplemented if this is a binary magic method (__eq__() et al)
+        if retval is NotImplemented and annotation is bool:
+            # This does (and cannot) not check if it's actually a method
+            func_name = func_name.rsplit(".", 1)[-1]
+            if func_name in BINARY_MAGIC_METHODS:
+                return retval
+
+        qualname = qualified_name(retval, add_class_prefix=True)
+        exc.append_path_element(f"the return value ({qualname})")
+        if memo.config.typecheck_fail_callback:
+            memo.config.typecheck_fail_callback(exc, memo)
+        else:
+            raise
+
+    return retval
+
+
+def check_send_type(
+    func_name: str,
+    sendval: T,
+    annotation: Any,
+    memo: TypeCheckMemo,
+) -> T:
+    if _suppression.type_checks_suppressed:
+        return sendval
+
+    if annotation is NoReturn or annotation is Never:
+        exc = TypeCheckError(
+            f"{func_name}() was declared never to be sent a value to but it was"
+        )
+        if memo.config.typecheck_fail_callback:
+            memo.config.typecheck_fail_callback(exc, memo)
+        else:
+            raise exc
+
+    try:
+        check_type_internal(sendval, annotation, memo)
+    except TypeCheckError as exc:
+        qualname = qualified_name(sendval, add_class_prefix=True)
+        exc.append_path_element(f"the value sent to generator ({qualname})")
+        if memo.config.typecheck_fail_callback:
+            memo.config.typecheck_fail_callback(exc, memo)
+        else:
+            raise
+
+    return sendval
+
+
+def check_yield_type(
+    func_name: str,
+    yieldval: T,
+    annotation: Any,
+    memo: TypeCheckMemo,
+) -> T:
+    if _suppression.type_checks_suppressed:
+        return yieldval
+
+    if annotation is NoReturn or annotation is Never:
+        exc = TypeCheckError(f"{func_name}() was declared never to yield but it did")
+        if memo.config.typecheck_fail_callback:
+            memo.config.typecheck_fail_callback(exc, memo)
+        else:
+            raise exc
+
+    try:
+        check_type_internal(yieldval, annotation, memo)
+    except TypeCheckError as exc:
+        qualname = qualified_name(yieldval, add_class_prefix=True)
+        exc.append_path_element(f"the yielded value ({qualname})")
+        if memo.config.typecheck_fail_callback:
+            memo.config.typecheck_fail_callback(exc, memo)
+        else:
+            raise
+
+    return yieldval
+
+
+def check_variable_assignment(
+    value: object, varname: str, annotation: Any, memo: TypeCheckMemo
+) -> Any:
+    if _suppression.type_checks_suppressed:
+        return value
+
+    try:
+        check_type_internal(value, annotation, memo)
+    except TypeCheckError as exc:
+        qualname = qualified_name(value, add_class_prefix=True)
+        exc.append_path_element(f"value assigned to {varname} ({qualname})")
+        if memo.config.typecheck_fail_callback:
+            memo.config.typecheck_fail_callback(exc, memo)
+        else:
+            raise
+
+    return value
+
+
+def check_multi_variable_assignment(
+    value: Any, targets: list[dict[str, Any]], memo: TypeCheckMemo
+) -> Any:
+    if max(len(target) for target in targets) == 1:
+        iterated_values = [value]
+    else:
+        iterated_values = list(value)
+
+    if not _suppression.type_checks_suppressed:
+        for expected_types in targets:
+            value_index = 0
+            for ann_index, (varname, expected_type) in enumerate(
+                expected_types.items()
+            ):
+                if varname.startswith("*"):
+                    varname = varname[1:]
+                    keys_left = len(expected_types) - 1 - ann_index
+                    next_value_index = len(iterated_values) - keys_left
+                    obj: object = iterated_values[value_index:next_value_index]
+                    value_index = next_value_index
+                else:
+                    obj = iterated_values[value_index]
+                    value_index += 1
+
+                try:
+                    check_type_internal(obj, expected_type, memo)
+                except TypeCheckError as exc:
+                    qualname = qualified_name(obj, add_class_prefix=True)
+                    exc.append_path_element(f"value assigned to {varname} ({qualname})")
+                    if memo.config.typecheck_fail_callback:
+                        memo.config.typecheck_fail_callback(exc, memo)
+                    else:
+                        raise
+
+    return iterated_values[0] if len(iterated_values) == 1 else iterated_values
+
+
+def warn_on_error(exc: TypeCheckError, memo: TypeCheckMemo) -> None:
+    """
+    Emit a warning on a type mismatch.
+
+    This is intended to be used as an error handler in
+    :attr:`TypeCheckConfiguration.typecheck_fail_callback`.
+
+    """
+    warnings.warn(TypeCheckWarning(str(exc)), stacklevel=get_stacklevel())
diff --git a/metaflow/_vendor/typeguard/_importhook.py b/metaflow/_vendor/typeguard/_importhook.py
new file mode 100644
index 00000000000..11342951737
--- /dev/null
+++ b/metaflow/_vendor/typeguard/_importhook.py
@@ -0,0 +1,213 @@
+from __future__ import annotations
+
+import ast
+import sys
+import types
+from collections.abc import Callable, Iterable
+from importlib.abc import MetaPathFinder
+from importlib.machinery import ModuleSpec, SourceFileLoader
+from importlib.util import cache_from_source, decode_source
+from inspect import isclass
+from os import PathLike
+from types import CodeType, ModuleType, TracebackType
+from typing import Sequence, TypeVar
+from unittest.mock import patch
+
+from ._config import global_config
+from ._transformer import TypeguardTransformer
+
+if sys.version_info >= (3, 12):
+    from collections.abc import Buffer
+else:
+    from metaflow._vendor.typing_extensions import Buffer
+
+if sys.version_info >= (3, 11):
+    from typing import ParamSpec
+else:
+    from metaflow._vendor.typing_extensions import ParamSpec
+
+if sys.version_info >= (3, 10):
+    from importlib.metadata import PackageNotFoundError, version
+else:
+    from metaflow._vendor.importlib_metadata import PackageNotFoundError, version
+
+try:
+    OPTIMIZATION = "typeguard" + "".join(version("typeguard").split(".")[:3])
+except PackageNotFoundError:
+    OPTIMIZATION = "typeguard"
+
+P = ParamSpec("P")
+T = TypeVar("T")
+
+
+# The name of this function is magical
+def _call_with_frames_removed(
+    f: Callable[P, T], *args: P.args, **kwargs: P.kwargs
+) -> T:
+    return f(*args, **kwargs)
+
+
+def optimized_cache_from_source(path: str, debug_override: bool | None = None) -> str:
+    return cache_from_source(path, debug_override, optimization=OPTIMIZATION)
+
+
+class TypeguardLoader(SourceFileLoader):
+    @staticmethod
+    def source_to_code(
+        data: Buffer | str | ast.Module | ast.Expression | ast.Interactive,
+        path: Buffer | str | PathLike[str] = "",
+    ) -> CodeType:
+        if isinstance(data, (ast.Module, ast.Expression, ast.Interactive)):
+            tree = data
+        else:
+            if isinstance(data, str):
+                source = data
+            else:
+                source = decode_source(data)
+
+            tree = _call_with_frames_removed(
+                ast.parse,
+                source,
+                path,
+                "exec",
+            )
+
+        tree = TypeguardTransformer().visit(tree)
+        ast.fix_missing_locations(tree)
+
+        if global_config.debug_instrumentation and sys.version_info >= (3, 9):
+            print(
+                f"Source code of {path!r} after instrumentation:\n"
+                "----------------------------------------------",
+                file=sys.stderr,
+            )
+            print(ast.unparse(tree), file=sys.stderr)
+            print("----------------------------------------------", file=sys.stderr)
+
+        return _call_with_frames_removed(
+            compile, tree, path, "exec", 0, dont_inherit=True
+        )
+
+    def exec_module(self, module: ModuleType) -> None:
+        # Use a custom optimization marker – the import lock should make this monkey
+        # patch safe
+        with patch(
+            "importlib._bootstrap_external.cache_from_source",
+            optimized_cache_from_source,
+        ):
+            super().exec_module(module)
+
+
+class TypeguardFinder(MetaPathFinder):
+    """
+    Wraps another path finder and instruments the module with
+    :func:`@typechecked ` if :meth:`should_instrument` returns
+    ``True``.
+
+    Should not be used directly, but rather via :func:`~.install_import_hook`.
+
+    .. versionadded:: 2.6
+    """
+
+    def __init__(self, packages: list[str] | None, original_pathfinder: MetaPathFinder):
+        self.packages = packages
+        self._original_pathfinder = original_pathfinder
+
+    def find_spec(
+        self,
+        fullname: str,
+        path: Sequence[str] | None,
+        target: types.ModuleType | None = None,
+    ) -> ModuleSpec | None:
+        if self.should_instrument(fullname):
+            spec = self._original_pathfinder.find_spec(fullname, path, target)
+            if spec is not None and isinstance(spec.loader, SourceFileLoader):
+                spec.loader = TypeguardLoader(spec.loader.name, spec.loader.path)
+                return spec
+
+        return None
+
+    def should_instrument(self, module_name: str) -> bool:
+        """
+        Determine whether the module with the given name should be instrumented.
+
+        :param module_name: full name of the module that is about to be imported (e.g.
+            ``xyz.abc``)
+
+        """
+        if self.packages is None:
+            return True
+
+        for package in self.packages:
+            if module_name == package or module_name.startswith(package + "."):
+                return True
+
+        return False
+
+
+class ImportHookManager:
+    """
+    A handle that can be used to uninstall the Typeguard import hook.
+    """
+
+    def __init__(self, hook: MetaPathFinder):
+        self.hook = hook
+
+    def __enter__(self) -> None:
+        pass
+
+    def __exit__(
+        self,
+        exc_type: type[BaseException],
+        exc_val: BaseException,
+        exc_tb: TracebackType,
+    ) -> None:
+        self.uninstall()
+
+    def uninstall(self) -> None:
+        """Uninstall the import hook."""
+        try:
+            sys.meta_path.remove(self.hook)
+        except ValueError:
+            pass  # already removed
+
+
+def install_import_hook(
+    packages: Iterable[str] | None = None,
+    *,
+    cls: type[TypeguardFinder] = TypeguardFinder,
+) -> ImportHookManager:
+    """
+    Install an import hook that instruments functions for automatic type checking.
+
+    This only affects modules loaded **after** this hook has been installed.
+
+    :param packages: an iterable of package names to instrument, or ``None`` to
+        instrument all packages
+    :param cls: a custom meta path finder class
+    :return: a context manager that uninstalls the hook on exit (or when you call
+        ``.uninstall()``)
+
+    .. versionadded:: 2.6
+
+    """
+    if packages is None:
+        target_packages: list[str] | None = None
+    elif isinstance(packages, str):
+        target_packages = [packages]
+    else:
+        target_packages = list(packages)
+
+    for finder in sys.meta_path:
+        if (
+            isclass(finder)
+            and finder.__name__ == "PathFinder"
+            and hasattr(finder, "find_spec")
+        ):
+            break
+    else:
+        raise RuntimeError("Cannot find a PathFinder in sys.meta_path")
+
+    hook = cls(target_packages, finder)
+    sys.meta_path.insert(0, hook)
+    return ImportHookManager(hook)
diff --git a/metaflow/_vendor/typeguard/_memo.py b/metaflow/_vendor/typeguard/_memo.py
new file mode 100644
index 00000000000..988a27122a9
--- /dev/null
+++ b/metaflow/_vendor/typeguard/_memo.py
@@ -0,0 +1,48 @@
+from __future__ import annotations
+
+from typing import Any
+
+from metaflow._vendor.typeguard._config import TypeCheckConfiguration, global_config
+
+
+class TypeCheckMemo:
+    """
+    Contains information necessary for type checkers to do their work.
+
+    .. attribute:: globals
+       :type: dict[str, Any]
+
+        Dictionary of global variables to use for resolving forward references.
+
+    .. attribute:: locals
+       :type: dict[str, Any]
+
+        Dictionary of local variables to use for resolving forward references.
+
+    .. attribute:: self_type
+       :type: type | None
+
+        When running type checks within an instance method or class method, this is the
+        class object that the first argument (usually named ``self`` or ``cls``) refers
+        to.
+
+    .. attribute:: config
+       :type: TypeCheckConfiguration
+
+         Contains the configuration for a particular set of type checking operations.
+    """
+
+    __slots__ = "globals", "locals", "self_type", "config"
+
+    def __init__(
+        self,
+        globals: dict[str, Any],
+        locals: dict[str, Any],
+        *,
+        self_type: type | None = None,
+        config: TypeCheckConfiguration = global_config,
+    ):
+        self.globals = globals
+        self.locals = locals
+        self.self_type = self_type
+        self.config = config
diff --git a/metaflow/_vendor/typeguard/_pytest_plugin.py b/metaflow/_vendor/typeguard/_pytest_plugin.py
new file mode 100644
index 00000000000..5272be04366
--- /dev/null
+++ b/metaflow/_vendor/typeguard/_pytest_plugin.py
@@ -0,0 +1,127 @@
+from __future__ import annotations
+
+import sys
+import warnings
+from typing import TYPE_CHECKING, Any, Literal
+
+from metaflow._vendor.typeguard._config import CollectionCheckStrategy, ForwardRefPolicy, global_config
+from metaflow._vendor.typeguard._exceptions import InstrumentationWarning
+from metaflow._vendor.typeguard._importhook import install_import_hook
+from metaflow._vendor.typeguard._utils import qualified_name, resolve_reference
+
+if TYPE_CHECKING:
+    from pytest import Config, Parser
+
+
+def pytest_addoption(parser: Parser) -> None:
+    def add_ini_option(
+        opt_type: (
+            Literal["string", "paths", "pathlist", "args", "linelist", "bool"] | None
+        ),
+    ) -> None:
+        parser.addini(
+            group.options[-1].names()[0][2:],
+            group.options[-1].attrs()["help"],
+            opt_type,
+        )
+
+    group = parser.getgroup("typeguard")
+    group.addoption(
+        "--typeguard-packages",
+        action="store",
+        help="comma separated name list of packages and modules to instrument for "
+        "type checking, or :all: to instrument all modules loaded after typeguard",
+    )
+    add_ini_option("linelist")
+
+    group.addoption(
+        "--typeguard-debug-instrumentation",
+        action="store_true",
+        help="print all instrumented code to stderr",
+    )
+    add_ini_option("bool")
+
+    group.addoption(
+        "--typeguard-typecheck-fail-callback",
+        action="store",
+        help=(
+            "a module:varname (e.g. typeguard:warn_on_error) reference to a function "
+            "that is called (with the exception, and memo object as arguments) to "
+            "handle a TypeCheckError"
+        ),
+    )
+    add_ini_option("string")
+
+    group.addoption(
+        "--typeguard-forward-ref-policy",
+        action="store",
+        choices=list(ForwardRefPolicy.__members__),
+        help=(
+            "determines how to deal with unresolveable forward references in type "
+            "annotations"
+        ),
+    )
+    add_ini_option("string")
+
+    group.addoption(
+        "--typeguard-collection-check-strategy",
+        action="store",
+        choices=list(CollectionCheckStrategy.__members__),
+        help="determines how thoroughly to check collections (list, dict, etc)",
+    )
+    add_ini_option("string")
+
+
+def pytest_configure(config: Config) -> None:
+    def getoption(name: str) -> Any:
+        return config.getoption(name.replace("-", "_")) or config.getini(name)
+
+    packages: list[str] | None = []
+    if packages_option := config.getoption("typeguard_packages"):
+        packages = [pkg.strip() for pkg in packages_option.split(",")]
+    elif packages_ini := config.getini("typeguard-packages"):
+        packages = packages_ini
+
+    if packages:
+        if packages == [":all:"]:
+            packages = None
+        else:
+            already_imported_packages = sorted(
+                package for package in packages if package in sys.modules
+            )
+            if already_imported_packages:
+                warnings.warn(
+                    f"typeguard cannot check these packages because they are already "
+                    f"imported: {', '.join(already_imported_packages)}",
+                    InstrumentationWarning,
+                    stacklevel=1,
+                )
+
+        install_import_hook(packages=packages)
+
+    debug_option = getoption("typeguard-debug-instrumentation")
+    if debug_option:
+        global_config.debug_instrumentation = True
+
+    fail_callback_option = getoption("typeguard-typecheck-fail-callback")
+    if fail_callback_option:
+        callback = resolve_reference(fail_callback_option)
+        if not callable(callback):
+            raise TypeError(
+                f"{fail_callback_option} ({qualified_name(callback.__class__)}) is not "
+                f"a callable"
+            )
+
+        global_config.typecheck_fail_callback = callback
+
+    forward_ref_policy_option = getoption("typeguard-forward-ref-policy")
+    if forward_ref_policy_option:
+        forward_ref_policy = ForwardRefPolicy.__members__[forward_ref_policy_option]
+        global_config.forward_ref_policy = forward_ref_policy
+
+    collection_check_strategy_option = getoption("typeguard-collection-check-strategy")
+    if collection_check_strategy_option:
+        collection_check_strategy = CollectionCheckStrategy.__members__[
+            collection_check_strategy_option
+        ]
+        global_config.collection_check_strategy = collection_check_strategy
diff --git a/metaflow/_vendor/typeguard/_suppression.py b/metaflow/_vendor/typeguard/_suppression.py
new file mode 100644
index 00000000000..a8866020c3e
--- /dev/null
+++ b/metaflow/_vendor/typeguard/_suppression.py
@@ -0,0 +1,86 @@
+from __future__ import annotations
+
+import sys
+from collections.abc import Callable, Generator
+from contextlib import contextmanager
+from functools import update_wrapper
+from threading import Lock
+from typing import ContextManager, TypeVar, overload
+
+if sys.version_info >= (3, 10):
+    from typing import ParamSpec
+else:
+    from metaflow._vendor.typing_extensions import ParamSpec
+
+P = ParamSpec("P")
+T = TypeVar("T")
+
+type_checks_suppressed = 0
+type_checks_suppress_lock = Lock()
+
+
+@overload
+def suppress_type_checks(func: Callable[P, T]) -> Callable[P, T]: ...
+
+
+@overload
+def suppress_type_checks() -> ContextManager[None]: ...
+
+
+def suppress_type_checks(
+    func: Callable[P, T] | None = None,
+) -> Callable[P, T] | ContextManager[None]:
+    """
+    Temporarily suppress all type checking.
+
+    This function has two operating modes, based on how it's used:
+
+    #. as a context manager (``with suppress_type_checks(): ...``)
+    #. as a decorator (``@suppress_type_checks``)
+
+    When used as a context manager, :func:`check_type` and any automatically
+    instrumented functions skip the actual type checking. These context managers can be
+    nested.
+
+    When used as a decorator, all type checking is suppressed while the function is
+    running.
+
+    Type checking will resume once no more context managers are active and no decorated
+    functions are running.
+
+    Both operating modes are thread-safe.
+
+    """
+
+    def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
+        global type_checks_suppressed
+
+        with type_checks_suppress_lock:
+            type_checks_suppressed += 1
+
+        assert func is not None
+        try:
+            return func(*args, **kwargs)
+        finally:
+            with type_checks_suppress_lock:
+                type_checks_suppressed -= 1
+
+    def cm() -> Generator[None, None, None]:
+        global type_checks_suppressed
+
+        with type_checks_suppress_lock:
+            type_checks_suppressed += 1
+
+        try:
+            yield
+        finally:
+            with type_checks_suppress_lock:
+                type_checks_suppressed -= 1
+
+    if func is None:
+        # Context manager mode
+        return contextmanager(cm)()
+    else:
+        # Decorator mode
+        update_wrapper(wrapper, func)
+        return wrapper
diff --git a/metaflow/_vendor/typeguard/_transformer.py b/metaflow/_vendor/typeguard/_transformer.py
new file mode 100644
index 00000000000..13ac3630e6c
--- /dev/null
+++ b/metaflow/_vendor/typeguard/_transformer.py
@@ -0,0 +1,1229 @@
+from __future__ import annotations
+
+import ast
+import builtins
+import sys
+import typing
+from ast import (
+    AST,
+    Add,
+    AnnAssign,
+    Assign,
+    AsyncFunctionDef,
+    Attribute,
+    AugAssign,
+    BinOp,
+    BitAnd,
+    BitOr,
+    BitXor,
+    Call,
+    ClassDef,
+    Constant,
+    Dict,
+    Div,
+    Expr,
+    Expression,
+    FloorDiv,
+    FunctionDef,
+    If,
+    Import,
+    ImportFrom,
+    Index,
+    List,
+    Load,
+    LShift,
+    MatMult,
+    Mod,
+    Module,
+    Mult,
+    Name,
+    NamedExpr,
+    NodeTransformer,
+    NodeVisitor,
+    Pass,
+    Pow,
+    Return,
+    RShift,
+    Starred,
+    Store,
+    Sub,
+    Subscript,
+    Tuple,
+    Yield,
+    YieldFrom,
+    alias,
+    copy_location,
+    expr,
+    fix_missing_locations,
+    keyword,
+    walk,
+)
+from collections import defaultdict
+from collections.abc import Generator, Sequence
+from contextlib import contextmanager
+from copy import deepcopy
+from dataclasses import dataclass, field
+from typing import Any, ClassVar, cast, overload
+
+generator_names = (
+    "typing.Generator",
+    "collections.abc.Generator",
+    "typing.Iterator",
+    "collections.abc.Iterator",
+    "typing.Iterable",
+    "collections.abc.Iterable",
+    "typing.AsyncIterator",
+    "collections.abc.AsyncIterator",
+    "typing.AsyncIterable",
+    "collections.abc.AsyncIterable",
+    "typing.AsyncGenerator",
+    "collections.abc.AsyncGenerator",
+)
+anytype_names = (
+    "typing.Any",
+    "typing_extensions.Any",
+)
+literal_names = (
+    "typing.Literal",
+    "typing_extensions.Literal",
+)
+annotated_names = (
+    "typing.Annotated",
+    "typing_extensions.Annotated",
+)
+ignore_decorators = (
+    "typing.no_type_check",
+    "typeguard.typeguard_ignore",
+)
+aug_assign_functions = {
+    Add: "iadd",
+    Sub: "isub",
+    Mult: "imul",
+    MatMult: "imatmul",
+    Div: "itruediv",
+    FloorDiv: "ifloordiv",
+    Mod: "imod",
+    Pow: "ipow",
+    LShift: "ilshift",
+    RShift: "irshift",
+    BitAnd: "iand",
+    BitXor: "ixor",
+    BitOr: "ior",
+}
+
+
+@dataclass
+class TransformMemo:
+    node: Module | ClassDef | FunctionDef | AsyncFunctionDef | None
+    parent: TransformMemo | None
+    path: tuple[str, ...]
+    joined_path: Constant = field(init=False)
+    return_annotation: expr | None = None
+    yield_annotation: expr | None = None
+    send_annotation: expr | None = None
+    is_async: bool = False
+    local_names: set[str] = field(init=False, default_factory=set)
+    imported_names: dict[str, str] = field(init=False, default_factory=dict)
+    ignored_names: set[str] = field(init=False, default_factory=set)
+    load_names: defaultdict[str, dict[str, Name]] = field(
+        init=False, default_factory=lambda: defaultdict(dict)
+    )
+    has_yield_expressions: bool = field(init=False, default=False)
+    has_return_expressions: bool = field(init=False, default=False)
+    memo_var_name: Name | None = field(init=False, default=None)
+    should_instrument: bool = field(init=False, default=True)
+    variable_annotations: dict[str, expr] = field(init=False, default_factory=dict)
+    configuration_overrides: dict[str, Any] = field(init=False, default_factory=dict)
+    code_inject_index: int = field(init=False, default=0)
+
+    def __post_init__(self) -> None:
+        elements: list[str] = []
+        memo = self
+        while isinstance(memo.node, (ClassDef, FunctionDef, AsyncFunctionDef)):
+            elements.insert(0, memo.node.name)
+            if not memo.parent:
+                break
+
+            memo = memo.parent
+            if isinstance(memo.node, (FunctionDef, AsyncFunctionDef)):
+                elements.insert(0, "")
+
+        self.joined_path = Constant(".".join(elements))
+
+        # Figure out where to insert instrumentation code
+        if self.node:
+            for index, child in enumerate(self.node.body):
+                if isinstance(child, ImportFrom) and child.module == "__future__":
+                    # (module only) __future__ imports must come first
+                    continue
+                elif (
+                    isinstance(child, Expr)
+                    and isinstance(child.value, Constant)
+                    and isinstance(child.value.value, str)
+                ):
+                    continue  # docstring
+
+                self.code_inject_index = index
+                break
+
+    def get_unused_name(self, name: str) -> str:
+        memo: TransformMemo | None = self
+        while memo is not None:
+            if name in memo.local_names:
+                memo = self
+                name += "_"
+            else:
+                memo = memo.parent
+
+        self.local_names.add(name)
+        return name
+
+    def is_ignored_name(self, expression: expr | Expr | None) -> bool:
+        top_expression = (
+            expression.value if isinstance(expression, Expr) else expression
+        )
+
+        if isinstance(top_expression, Attribute) and isinstance(
+            top_expression.value, Name
+        ):
+            name = top_expression.value.id
+        elif isinstance(top_expression, Name):
+            name = top_expression.id
+        else:
+            return False
+
+        memo: TransformMemo | None = self
+        while memo is not None:
+            if name in memo.ignored_names:
+                return True
+
+            memo = memo.parent
+
+        return False
+
+    def get_memo_name(self) -> Name:
+        if not self.memo_var_name:
+            self.memo_var_name = Name(id="memo", ctx=Load())
+
+        return self.memo_var_name
+
+    def get_import(self, module: str, name: str) -> Name:
+        if module in self.load_names and name in self.load_names[module]:
+            return self.load_names[module][name]
+
+        qualified_name = f"{module}.{name}"
+        if name in self.imported_names and self.imported_names[name] == qualified_name:
+            return Name(id=name, ctx=Load())
+
+        alias = self.get_unused_name(name)
+        node = self.load_names[module][name] = Name(id=alias, ctx=Load())
+        self.imported_names[name] = qualified_name
+        return node
+
+    def insert_imports(self, node: Module | FunctionDef | AsyncFunctionDef) -> None:
+        """Insert imports needed by injected code."""
+        if not self.load_names:
+            return
+
+        # Insert imports after any "from __future__ ..." imports and any docstring
+        for modulename, names in self.load_names.items():
+            aliases = [
+                alias(orig_name, new_name.id if orig_name != new_name.id else None)
+                for orig_name, new_name in sorted(names.items())
+            ]
+            node.body.insert(self.code_inject_index, ImportFrom(modulename, aliases, 0))
+
+    def name_matches(self, expression: expr | Expr | None, *names: str) -> bool:
+        if expression is None:
+            return False
+
+        path: list[str] = []
+        top_expression = (
+            expression.value if isinstance(expression, Expr) else expression
+        )
+
+        if isinstance(top_expression, Subscript):
+            top_expression = top_expression.value
+        elif isinstance(top_expression, Call):
+            top_expression = top_expression.func
+
+        while isinstance(top_expression, Attribute):
+            path.insert(0, top_expression.attr)
+            top_expression = top_expression.value
+
+        if not isinstance(top_expression, Name):
+            return False
+
+        if top_expression.id in self.imported_names:
+            translated = self.imported_names[top_expression.id]
+        elif hasattr(builtins, top_expression.id):
+            translated = "builtins." + top_expression.id
+        else:
+            translated = top_expression.id
+
+        path.insert(0, translated)
+        joined_path = ".".join(path)
+        if joined_path in names:
+            return True
+        elif self.parent:
+            return self.parent.name_matches(expression, *names)
+        else:
+            return False
+
+    def get_config_keywords(self) -> list[keyword]:
+        if self.parent and isinstance(self.parent.node, ClassDef):
+            overrides = self.parent.configuration_overrides.copy()
+        else:
+            overrides = {}
+
+        overrides.update(self.configuration_overrides)
+        return [keyword(key, value) for key, value in overrides.items()]
+
+
+class NameCollector(NodeVisitor):
+    def __init__(self) -> None:
+        self.names: set[str] = set()
+
+    def visit_Import(self, node: Import) -> None:
+        for name in node.names:
+            self.names.add(name.asname or name.name)
+
+    def visit_ImportFrom(self, node: ImportFrom) -> None:
+        for name in node.names:
+            self.names.add(name.asname or name.name)
+
+    def visit_Assign(self, node: Assign) -> None:
+        for target in node.targets:
+            if isinstance(target, Name):
+                self.names.add(target.id)
+
+    def visit_NamedExpr(self, node: NamedExpr) -> Any:
+        if isinstance(node.target, Name):
+            self.names.add(node.target.id)
+
+    def visit_FunctionDef(self, node: FunctionDef) -> None:
+        pass
+
+    def visit_ClassDef(self, node: ClassDef) -> None:
+        pass
+
+
+class GeneratorDetector(NodeVisitor):
+    """Detects if a function node is a generator function."""
+
+    contains_yields: bool = False
+    in_root_function: bool = False
+
+    def visit_Yield(self, node: Yield) -> Any:
+        self.contains_yields = True
+
+    def visit_YieldFrom(self, node: YieldFrom) -> Any:
+        self.contains_yields = True
+
+    def visit_ClassDef(self, node: ClassDef) -> Any:
+        pass
+
+    def visit_FunctionDef(self, node: FunctionDef | AsyncFunctionDef) -> Any:
+        if not self.in_root_function:
+            self.in_root_function = True
+            self.generic_visit(node)
+            self.in_root_function = False
+
+    def visit_AsyncFunctionDef(self, node: AsyncFunctionDef) -> Any:
+        self.visit_FunctionDef(node)
+
+
+class AnnotationTransformer(NodeTransformer):
+    type_substitutions: ClassVar[dict[str, tuple[str, str]]] = {
+        "builtins.dict": ("typing", "Dict"),
+        "builtins.list": ("typing", "List"),
+        "builtins.tuple": ("typing", "Tuple"),
+        "builtins.set": ("typing", "Set"),
+        "builtins.frozenset": ("typing", "FrozenSet"),
+    }
+
+    def __init__(self, transformer: TypeguardTransformer):
+        self.transformer = transformer
+        self._memo = transformer._memo
+        self._level = 0
+
+    def visit(self, node: AST) -> Any:
+        # Don't process Literals
+        if isinstance(node, expr) and self._memo.name_matches(node, *literal_names):
+            return node
+
+        self._level += 1
+        new_node = super().visit(node)
+        self._level -= 1
+
+        if isinstance(new_node, Expression) and not hasattr(new_node, "body"):
+            return None
+
+        # Return None if this new node matches a variation of typing.Any
+        if (
+            self._level == 0
+            and isinstance(new_node, expr)
+            and self._memo.name_matches(new_node, *anytype_names)
+        ):
+            return None
+
+        return new_node
+
+    def visit_BinOp(self, node: BinOp) -> Any:
+        self.generic_visit(node)
+
+        if isinstance(node.op, BitOr):
+            # If either branch of the BinOp has been transformed to `None`, it means
+            # that a type in the union was ignored, so the entire annotation should e
+            # ignored
+            if not hasattr(node, "left") or not hasattr(node, "right"):
+                return None
+
+            # Return Any if either side is Any
+            if self._memo.name_matches(node.left, *anytype_names):
+                return node.left
+            elif self._memo.name_matches(node.right, *anytype_names):
+                return node.right
+
+            if sys.version_info < (3, 10):
+                union_name = self.transformer._get_import("typing", "Union")
+                return Subscript(
+                    value=union_name,
+                    slice=Index(
+                        Tuple(elts=[node.left, node.right], ctx=Load()), ctx=Load()
+                    ),
+                    ctx=Load(),
+                )
+
+        return node
+
+    def visit_Attribute(self, node: Attribute) -> Any:
+        if self._memo.is_ignored_name(node):
+            return None
+
+        return node
+
+    def visit_Subscript(self, node: Subscript) -> Any:
+        if self._memo.is_ignored_name(node.value):
+            return None
+
+        # The subscript of typing(_extensions).Literal can be any arbitrary string, so
+        # don't try to evaluate it as code
+        if node.slice:
+            if isinstance(node.slice, Index):
+                # Python 3.8
+                slice_value = node.slice.value  # type: ignore[attr-defined]
+            else:
+                slice_value = node.slice
+
+            if isinstance(slice_value, Tuple):
+                if self._memo.name_matches(node.value, *annotated_names):
+                    # Only treat the first argument to typing.Annotated as a potential
+                    # forward reference
+                    items = cast(
+                        typing.List[expr],
+                        [self.visit(slice_value.elts[0])] + slice_value.elts[1:],
+                    )
+                else:
+                    items = cast(
+                        typing.List[expr],
+                        [self.visit(item) for item in slice_value.elts],
+                    )
+
+                # If this is a Union and any of the items is Any, erase the entire
+                # annotation
+                if self._memo.name_matches(node.value, "typing.Union") and any(
+                    item is None
+                    or (
+                        isinstance(item, expr)
+                        and self._memo.name_matches(item, *anytype_names)
+                    )
+                    for item in items
+                ):
+                    return None
+
+                # If all items in the subscript were Any, erase the subscript entirely
+                if all(item is None for item in items):
+                    return node.value
+
+                for index, item in enumerate(items):
+                    if item is None:
+                        items[index] = self.transformer._get_import("typing", "Any")
+
+                slice_value.elts = items
+            else:
+                self.generic_visit(node)
+
+                # If the transformer erased the slice entirely, just return the node
+                # value without the subscript (unless it's Optional, in which case erase
+                # the node entirely
+                if self._memo.name_matches(
+                    node.value, "typing.Optional"
+                ) and not hasattr(node, "slice"):
+                    return None
+                if sys.version_info >= (3, 9) and not hasattr(node, "slice"):
+                    return node.value
+                elif sys.version_info < (3, 9) and not hasattr(node.slice, "value"):
+                    return node.value
+
+        return node
+
+    def visit_Name(self, node: Name) -> Any:
+        if self._memo.is_ignored_name(node):
+            return None
+
+        if sys.version_info < (3, 9):
+            for typename, substitute in self.type_substitutions.items():
+                if self._memo.name_matches(node, typename):
+                    new_node = self.transformer._get_import(*substitute)
+                    return copy_location(new_node, node)
+
+        return node
+
+    def visit_Call(self, node: Call) -> Any:
+        # Don't recurse into calls
+        return node
+
+    def visit_Constant(self, node: Constant) -> Any:
+        if isinstance(node.value, str):
+            expression = ast.parse(node.value, mode="eval")
+            new_node = self.visit(expression)
+            if new_node:
+                return copy_location(new_node.body, node)
+            else:
+                return None
+
+        return node
+
+
+class TypeguardTransformer(NodeTransformer):
+    def __init__(
+        self, target_path: Sequence[str] | None = None, target_lineno: int | None = None
+    ) -> None:
+        self._target_path = tuple(target_path) if target_path else None
+        self._memo = self._module_memo = TransformMemo(None, None, ())
+        self.names_used_in_annotations: set[str] = set()
+        self.target_node: FunctionDef | AsyncFunctionDef | None = None
+        self.target_lineno = target_lineno
+
+    def generic_visit(self, node: AST) -> AST:
+        has_non_empty_body_initially = bool(getattr(node, "body", None))
+        initial_type = type(node)
+
+        node = super().generic_visit(node)
+
+        if (
+            type(node) is initial_type
+            and has_non_empty_body_initially
+            and hasattr(node, "body")
+            and not node.body
+        ):
+            # If we have still the same node type after transformation
+            # but we've optimised it's body away, we add a `pass` statement.
+            node.body = [Pass()]
+
+        return node
+
+    @contextmanager
+    def _use_memo(
+        self, node: ClassDef | FunctionDef | AsyncFunctionDef
+    ) -> Generator[None, Any, None]:
+        new_memo = TransformMemo(node, self._memo, self._memo.path + (node.name,))
+        old_memo = self._memo
+        self._memo = new_memo
+
+        if isinstance(node, (FunctionDef, AsyncFunctionDef)):
+            new_memo.should_instrument = (
+                self._target_path is None or new_memo.path == self._target_path
+            )
+            if new_memo.should_instrument:
+                # Check if the function is a generator function
+                detector = GeneratorDetector()
+                detector.visit(node)
+
+                # Extract yield, send and return types where possible from a subscripted
+                # annotation like Generator[int, str, bool]
+                return_annotation = deepcopy(node.returns)
+                if detector.contains_yields and new_memo.name_matches(
+                    return_annotation, *generator_names
+                ):
+                    if isinstance(return_annotation, Subscript):
+                        annotation_slice = return_annotation.slice
+
+                        # Python < 3.9
+                        if isinstance(annotation_slice, Index):
+                            annotation_slice = (
+                                annotation_slice.value  # type: ignore[attr-defined]
+                            )
+
+                        if isinstance(annotation_slice, Tuple):
+                            items = annotation_slice.elts
+                        else:
+                            items = [annotation_slice]
+
+                        if len(items) > 0:
+                            new_memo.yield_annotation = self._convert_annotation(
+                                items[0]
+                            )
+
+                        if len(items) > 1:
+                            new_memo.send_annotation = self._convert_annotation(
+                                items[1]
+                            )
+
+                        if len(items) > 2:
+                            new_memo.return_annotation = self._convert_annotation(
+                                items[2]
+                            )
+                else:
+                    new_memo.return_annotation = self._convert_annotation(
+                        return_annotation
+                    )
+
+        if isinstance(node, AsyncFunctionDef):
+            new_memo.is_async = True
+
+        yield
+        self._memo = old_memo
+
+    def _get_import(self, module: str, name: str) -> Name:
+        memo = self._memo if self._target_path else self._module_memo
+        return memo.get_import(module, name)
+
+    @overload
+    def _convert_annotation(self, annotation: None) -> None: ...
+
+    @overload
+    def _convert_annotation(self, annotation: expr) -> expr: ...
+
+    def _convert_annotation(self, annotation: expr | None) -> expr | None:
+        if annotation is None:
+            return None
+
+        # Convert PEP 604 unions (x | y) and generic built-in collections where
+        # necessary, and undo forward references
+        new_annotation = cast(expr, AnnotationTransformer(self).visit(annotation))
+        if isinstance(new_annotation, expr):
+            new_annotation = ast.copy_location(new_annotation, annotation)
+
+            # Store names used in the annotation
+            names = {node.id for node in walk(new_annotation) if isinstance(node, Name)}
+            self.names_used_in_annotations.update(names)
+
+        return new_annotation
+
+    def visit_Name(self, node: Name) -> Name:
+        self._memo.local_names.add(node.id)
+        return node
+
+    def visit_Module(self, node: Module) -> Module:
+        self._module_memo = self._memo = TransformMemo(node, None, ())
+        self.generic_visit(node)
+        self._module_memo.insert_imports(node)
+
+        fix_missing_locations(node)
+        return node
+
+    def visit_Import(self, node: Import) -> Import:
+        for name in node.names:
+            self._memo.local_names.add(name.asname or name.name)
+            self._memo.imported_names[name.asname or name.name] = name.name
+
+        return node
+
+    def visit_ImportFrom(self, node: ImportFrom) -> ImportFrom:
+        for name in node.names:
+            if name.name != "*":
+                alias = name.asname or name.name
+                self._memo.local_names.add(alias)
+                self._memo.imported_names[alias] = f"{node.module}.{name.name}"
+
+        return node
+
+    def visit_ClassDef(self, node: ClassDef) -> ClassDef | None:
+        self._memo.local_names.add(node.name)
+
+        # Eliminate top level classes not belonging to the target path
+        if (
+            self._target_path is not None
+            and not self._memo.path
+            and node.name != self._target_path[0]
+        ):
+            return None
+
+        with self._use_memo(node):
+            for decorator in node.decorator_list.copy():
+                if self._memo.name_matches(decorator, "typeguard.typechecked"):
+                    # Remove the decorator to prevent duplicate instrumentation
+                    node.decorator_list.remove(decorator)
+
+                    # Store any configuration overrides
+                    if isinstance(decorator, Call) and decorator.keywords:
+                        self._memo.configuration_overrides.update(
+                            {kw.arg: kw.value for kw in decorator.keywords if kw.arg}
+                        )
+
+            self.generic_visit(node)
+            return node
+
+    def visit_FunctionDef(
+        self, node: FunctionDef | AsyncFunctionDef
+    ) -> FunctionDef | AsyncFunctionDef | None:
+        """
+        Injects type checks for function arguments, and for a return of None if the
+        function is annotated to return something else than Any or None, and the body
+        ends without an explicit "return".
+
+        """
+        self._memo.local_names.add(node.name)
+
+        # Eliminate top level functions not belonging to the target path
+        if (
+            self._target_path is not None
+            and not self._memo.path
+            and node.name != self._target_path[0]
+        ):
+            return None
+
+        # Skip instrumentation if we're instrumenting the whole module and the function
+        # contains either @no_type_check or @typeguard_ignore
+        if self._target_path is None:
+            for decorator in node.decorator_list:
+                if self._memo.name_matches(decorator, *ignore_decorators):
+                    return node
+
+        with self._use_memo(node):
+            arg_annotations: dict[str, Any] = {}
+            if self._target_path is None or self._memo.path == self._target_path:
+                # Find line number we're supposed to match against
+                if node.decorator_list:
+                    first_lineno = node.decorator_list[0].lineno
+                else:
+                    first_lineno = node.lineno
+
+                for decorator in node.decorator_list.copy():
+                    if self._memo.name_matches(decorator, "typing.overload"):
+                        # Remove overloads entirely
+                        return None
+                    elif self._memo.name_matches(decorator, "typeguard.typechecked"):
+                        # Remove the decorator to prevent duplicate instrumentation
+                        node.decorator_list.remove(decorator)
+
+                        # Store any configuration overrides
+                        if isinstance(decorator, Call) and decorator.keywords:
+                            self._memo.configuration_overrides = {
+                                kw.arg: kw.value for kw in decorator.keywords if kw.arg
+                            }
+
+                if self.target_lineno == first_lineno:
+                    assert self.target_node is None
+                    self.target_node = node
+                    if node.decorator_list:
+                        self.target_lineno = node.decorator_list[0].lineno
+                    else:
+                        self.target_lineno = node.lineno
+
+                all_args = node.args.args + node.args.kwonlyargs + node.args.posonlyargs
+
+                # Ensure that any type shadowed by the positional or keyword-only
+                # argument names are ignored in this function
+                for arg in all_args:
+                    self._memo.ignored_names.add(arg.arg)
+
+                # Ensure that any type shadowed by the variable positional argument name
+                # (e.g. "args" in *args) is ignored this function
+                if node.args.vararg:
+                    self._memo.ignored_names.add(node.args.vararg.arg)
+
+                # Ensure that any type shadowed by the variable keywrod argument name
+                # (e.g. "kwargs" in *kwargs) is ignored this function
+                if node.args.kwarg:
+                    self._memo.ignored_names.add(node.args.kwarg.arg)
+
+                for arg in all_args:
+                    annotation = self._convert_annotation(deepcopy(arg.annotation))
+                    if annotation:
+                        arg_annotations[arg.arg] = annotation
+
+                if node.args.vararg:
+                    annotation_ = self._convert_annotation(node.args.vararg.annotation)
+                    if annotation_:
+                        if sys.version_info >= (3, 9):
+                            container = Name("tuple", ctx=Load())
+                        else:
+                            container = self._get_import("typing", "Tuple")
+
+                        subscript_slice: Tuple | Index = Tuple(
+                            [
+                                annotation_,
+                                Constant(Ellipsis),
+                            ],
+                            ctx=Load(),
+                        )
+                        if sys.version_info < (3, 9):
+                            subscript_slice = Index(subscript_slice, ctx=Load())
+
+                        arg_annotations[node.args.vararg.arg] = Subscript(
+                            container, subscript_slice, ctx=Load()
+                        )
+
+                if node.args.kwarg:
+                    annotation_ = self._convert_annotation(node.args.kwarg.annotation)
+                    if annotation_:
+                        if sys.version_info >= (3, 9):
+                            container = Name("dict", ctx=Load())
+                        else:
+                            container = self._get_import("typing", "Dict")
+
+                        subscript_slice = Tuple(
+                            [
+                                Name("str", ctx=Load()),
+                                annotation_,
+                            ],
+                            ctx=Load(),
+                        )
+                        if sys.version_info < (3, 9):
+                            subscript_slice = Index(subscript_slice, ctx=Load())
+
+                        arg_annotations[node.args.kwarg.arg] = Subscript(
+                            container, subscript_slice, ctx=Load()
+                        )
+
+                if arg_annotations:
+                    self._memo.variable_annotations.update(arg_annotations)
+
+            self.generic_visit(node)
+
+            if arg_annotations:
+                annotations_dict = Dict(
+                    keys=[Constant(key) for key in arg_annotations.keys()],
+                    values=[
+                        Tuple([Name(key, ctx=Load()), annotation], ctx=Load())
+                        for key, annotation in arg_annotations.items()
+                    ],
+                )
+                func_name = self._get_import(
+                    "typeguard._functions", "check_argument_types"
+                )
+                args = [
+                    self._memo.joined_path,
+                    annotations_dict,
+                    self._memo.get_memo_name(),
+                ]
+                node.body.insert(
+                    self._memo.code_inject_index, Expr(Call(func_name, args, []))
+                )
+
+            # Add a checked "return None" to the end if there's no explicit return
+            # Skip if the return annotation is None or Any
+            if (
+                self._memo.return_annotation
+                and (not self._memo.is_async or not self._memo.has_yield_expressions)
+                and not isinstance(node.body[-1], Return)
+                and (
+                    not isinstance(self._memo.return_annotation, Constant)
+                    or self._memo.return_annotation.value is not None
+                )
+            ):
+                func_name = self._get_import(
+                    "typeguard._functions", "check_return_type"
+                )
+                return_node = Return(
+                    Call(
+                        func_name,
+                        [
+                            self._memo.joined_path,
+                            Constant(None),
+                            self._memo.return_annotation,
+                            self._memo.get_memo_name(),
+                        ],
+                        [],
+                    )
+                )
+
+                # Replace a placeholder "pass" at the end
+                if isinstance(node.body[-1], Pass):
+                    copy_location(return_node, node.body[-1])
+                    del node.body[-1]
+
+                node.body.append(return_node)
+
+            # Insert code to create the call memo, if it was ever needed for this
+            # function
+            if self._memo.memo_var_name:
+                memo_kwargs: dict[str, Any] = {}
+                if self._memo.parent and isinstance(self._memo.parent.node, ClassDef):
+                    for decorator in node.decorator_list:
+                        if (
+                            isinstance(decorator, Name)
+                            and decorator.id == "staticmethod"
+                        ):
+                            break
+                        elif (
+                            isinstance(decorator, Name)
+                            and decorator.id == "classmethod"
+                        ):
+                            memo_kwargs["self_type"] = Name(
+                                id=node.args.args[0].arg, ctx=Load()
+                            )
+                            break
+                    else:
+                        if node.args.args:
+                            if node.name == "__new__":
+                                memo_kwargs["self_type"] = Name(
+                                    id=node.args.args[0].arg, ctx=Load()
+                                )
+                            else:
+                                memo_kwargs["self_type"] = Attribute(
+                                    Name(id=node.args.args[0].arg, ctx=Load()),
+                                    "__class__",
+                                    ctx=Load(),
+                                )
+
+                # Construct the function reference
+                # Nested functions get special treatment: the function name is added
+                # to free variables (and the closure of the resulting function)
+                names: list[str] = [node.name]
+                memo = self._memo.parent
+                while memo:
+                    if isinstance(memo.node, (FunctionDef, AsyncFunctionDef)):
+                        # This is a nested function. Use the function name as-is.
+                        del names[:-1]
+                        break
+                    elif not isinstance(memo.node, ClassDef):
+                        break
+
+                    names.insert(0, memo.node.name)
+                    memo = memo.parent
+
+                config_keywords = self._memo.get_config_keywords()
+                if config_keywords:
+                    memo_kwargs["config"] = Call(
+                        self._get_import("dataclasses", "replace"),
+                        [self._get_import("typeguard._config", "global_config")],
+                        config_keywords,
+                    )
+
+                self._memo.memo_var_name.id = self._memo.get_unused_name("memo")
+                memo_store_name = Name(id=self._memo.memo_var_name.id, ctx=Store())
+                globals_call = Call(Name(id="globals", ctx=Load()), [], [])
+                locals_call = Call(Name(id="locals", ctx=Load()), [], [])
+                memo_expr = Call(
+                    self._get_import("typeguard", "TypeCheckMemo"),
+                    [globals_call, locals_call],
+                    [keyword(key, value) for key, value in memo_kwargs.items()],
+                )
+                node.body.insert(
+                    self._memo.code_inject_index,
+                    Assign([memo_store_name], memo_expr),
+                )
+
+                self._memo.insert_imports(node)
+
+                # Special case the __new__() method to create a local alias from the
+                # class name to the first argument (usually "cls")
+                if (
+                    isinstance(node, FunctionDef)
+                    and node.args
+                    and self._memo.parent is not None
+                    and isinstance(self._memo.parent.node, ClassDef)
+                    and node.name == "__new__"
+                ):
+                    first_args_expr = Name(node.args.args[0].arg, ctx=Load())
+                    cls_name = Name(self._memo.parent.node.name, ctx=Store())
+                    node.body.insert(
+                        self._memo.code_inject_index,
+                        Assign([cls_name], first_args_expr),
+                    )
+
+                # Rmove any placeholder "pass" at the end
+                if isinstance(node.body[-1], Pass):
+                    del node.body[-1]
+
+        return node
+
+    def visit_AsyncFunctionDef(
+        self, node: AsyncFunctionDef
+    ) -> FunctionDef | AsyncFunctionDef | None:
+        return self.visit_FunctionDef(node)
+
+    def visit_Return(self, node: Return) -> Return:
+        """This injects type checks into "return" statements."""
+        self.generic_visit(node)
+        if (
+            self._memo.return_annotation
+            and self._memo.should_instrument
+            and not self._memo.is_ignored_name(self._memo.return_annotation)
+        ):
+            func_name = self._get_import("typeguard._functions", "check_return_type")
+            old_node = node
+            retval = old_node.value or Constant(None)
+            node = Return(
+                Call(
+                    func_name,
+                    [
+                        self._memo.joined_path,
+                        retval,
+                        self._memo.return_annotation,
+                        self._memo.get_memo_name(),
+                    ],
+                    [],
+                )
+            )
+            copy_location(node, old_node)
+
+        return node
+
+    def visit_Yield(self, node: Yield) -> Yield | Call:
+        """
+        This injects type checks into "yield" expressions, checking both the yielded
+        value and the value sent back to the generator, when appropriate.
+
+        """
+        self._memo.has_yield_expressions = True
+        self.generic_visit(node)
+
+        if (
+            self._memo.yield_annotation
+            and self._memo.should_instrument
+            and not self._memo.is_ignored_name(self._memo.yield_annotation)
+        ):
+            func_name = self._get_import("typeguard._functions", "check_yield_type")
+            yieldval = node.value or Constant(None)
+            node.value = Call(
+                func_name,
+                [
+                    self._memo.joined_path,
+                    yieldval,
+                    self._memo.yield_annotation,
+                    self._memo.get_memo_name(),
+                ],
+                [],
+            )
+
+        if (
+            self._memo.send_annotation
+            and self._memo.should_instrument
+            and not self._memo.is_ignored_name(self._memo.send_annotation)
+        ):
+            func_name = self._get_import("typeguard._functions", "check_send_type")
+            old_node = node
+            call_node = Call(
+                func_name,
+                [
+                    self._memo.joined_path,
+                    old_node,
+                    self._memo.send_annotation,
+                    self._memo.get_memo_name(),
+                ],
+                [],
+            )
+            copy_location(call_node, old_node)
+            return call_node
+
+        return node
+
+    def visit_AnnAssign(self, node: AnnAssign) -> Any:
+        """
+        This injects a type check into a local variable annotation-assignment within a
+        function body.
+
+        """
+        self.generic_visit(node)
+
+        if (
+            isinstance(self._memo.node, (FunctionDef, AsyncFunctionDef))
+            and node.annotation
+            and isinstance(node.target, Name)
+        ):
+            self._memo.ignored_names.add(node.target.id)
+            annotation = self._convert_annotation(deepcopy(node.annotation))
+            if annotation:
+                self._memo.variable_annotations[node.target.id] = annotation
+                if node.value:
+                    func_name = self._get_import(
+                        "typeguard._functions", "check_variable_assignment"
+                    )
+                    node.value = Call(
+                        func_name,
+                        [
+                            node.value,
+                            Constant(node.target.id),
+                            annotation,
+                            self._memo.get_memo_name(),
+                        ],
+                        [],
+                    )
+
+        return node
+
+    def visit_Assign(self, node: Assign) -> Any:
+        """
+        This injects a type check into a local variable assignment within a function
+        body. The variable must have been annotated earlier in the function body.
+
+        """
+        self.generic_visit(node)
+
+        # Only instrument function-local assignments
+        if isinstance(self._memo.node, (FunctionDef, AsyncFunctionDef)):
+            targets: list[dict[Constant, expr | None]] = []
+            check_required = False
+            for target in node.targets:
+                elts: Sequence[expr]
+                if isinstance(target, Name):
+                    elts = [target]
+                elif isinstance(target, Tuple):
+                    elts = target.elts
+                else:
+                    continue
+
+                annotations_: dict[Constant, expr | None] = {}
+                for exp in elts:
+                    prefix = ""
+                    if isinstance(exp, Starred):
+                        exp = exp.value
+                        prefix = "*"
+
+                    if isinstance(exp, Name):
+                        self._memo.ignored_names.add(exp.id)
+                        name = prefix + exp.id
+                        annotation = self._memo.variable_annotations.get(exp.id)
+                        if annotation:
+                            annotations_[Constant(name)] = annotation
+                            check_required = True
+                        else:
+                            annotations_[Constant(name)] = None
+
+                targets.append(annotations_)
+
+            if check_required:
+                # Replace missing annotations with typing.Any
+                for item in targets:
+                    for key, expression in item.items():
+                        if expression is None:
+                            item[key] = self._get_import("typing", "Any")
+
+                if len(targets) == 1 and len(targets[0]) == 1:
+                    func_name = self._get_import(
+                        "typeguard._functions", "check_variable_assignment"
+                    )
+                    target_varname = next(iter(targets[0]))
+                    node.value = Call(
+                        func_name,
+                        [
+                            node.value,
+                            target_varname,
+                            targets[0][target_varname],
+                            self._memo.get_memo_name(),
+                        ],
+                        [],
+                    )
+                elif targets:
+                    func_name = self._get_import(
+                        "typeguard._functions", "check_multi_variable_assignment"
+                    )
+                    targets_arg = List(
+                        [
+                            Dict(keys=list(target), values=list(target.values()))
+                            for target in targets
+                        ],
+                        ctx=Load(),
+                    )
+                    node.value = Call(
+                        func_name,
+                        [node.value, targets_arg, self._memo.get_memo_name()],
+                        [],
+                    )
+
+        return node
+
+    def visit_NamedExpr(self, node: NamedExpr) -> Any:
+        """This injects a type check into an assignment expression (a := foo())."""
+        self.generic_visit(node)
+
+        # Only instrument function-local assignments
+        if isinstance(self._memo.node, (FunctionDef, AsyncFunctionDef)) and isinstance(
+            node.target, Name
+        ):
+            self._memo.ignored_names.add(node.target.id)
+
+            # Bail out if no matching annotation is found
+            annotation = self._memo.variable_annotations.get(node.target.id)
+            if annotation is None:
+                return node
+
+            func_name = self._get_import(
+                "typeguard._functions", "check_variable_assignment"
+            )
+            node.value = Call(
+                func_name,
+                [
+                    node.value,
+                    Constant(node.target.id),
+                    annotation,
+                    self._memo.get_memo_name(),
+                ],
+                [],
+            )
+
+        return node
+
+    def visit_AugAssign(self, node: AugAssign) -> Any:
+        """
+        This injects a type check into an augmented assignment expression (a += 1).
+
+        """
+        self.generic_visit(node)
+
+        # Only instrument function-local assignments
+        if isinstance(self._memo.node, (FunctionDef, AsyncFunctionDef)) and isinstance(
+            node.target, Name
+        ):
+            # Bail out if no matching annotation is found
+            annotation = self._memo.variable_annotations.get(node.target.id)
+            if annotation is None:
+                return node
+
+            # Bail out if the operator is not found (newer Python version?)
+            try:
+                operator_func_name = aug_assign_functions[node.op.__class__]
+            except KeyError:
+                return node
+
+            operator_func = self._get_import("operator", operator_func_name)
+            operator_call = Call(
+                operator_func, [Name(node.target.id, ctx=Load()), node.value], []
+            )
+            check_call = Call(
+                self._get_import("typeguard._functions", "check_variable_assignment"),
+                [
+                    operator_call,
+                    Constant(node.target.id),
+                    annotation,
+                    self._memo.get_memo_name(),
+                ],
+                [],
+            )
+            return Assign(targets=[node.target], value=check_call)
+
+        return node
+
+    def visit_If(self, node: If) -> Any:
+        """
+        This blocks names from being collected from a module-level
+        "if typing.TYPE_CHECKING:" block, so that they won't be type checked.
+
+        """
+        self.generic_visit(node)
+
+        if (
+            self._memo is self._module_memo
+            and isinstance(node.test, Name)
+            and self._memo.name_matches(node.test, "typing.TYPE_CHECKING")
+        ):
+            collector = NameCollector()
+            collector.visit(node)
+            self._memo.ignored_names.update(collector.names)
+
+        return node
diff --git a/metaflow/_vendor/typeguard/_union_transformer.py b/metaflow/_vendor/typeguard/_union_transformer.py
new file mode 100644
index 00000000000..19617e6af5a
--- /dev/null
+++ b/metaflow/_vendor/typeguard/_union_transformer.py
@@ -0,0 +1,55 @@
+"""
+Transforms lazily evaluated PEP 604 unions into typing.Unions, for compatibility with
+Python versions older than 3.10.
+"""
+
+from __future__ import annotations
+
+from ast import (
+    BinOp,
+    BitOr,
+    Index,
+    Load,
+    Name,
+    NodeTransformer,
+    Subscript,
+    fix_missing_locations,
+    parse,
+)
+from ast import Tuple as ASTTuple
+from types import CodeType
+from typing import Any, Dict, FrozenSet, List, Set, Tuple, Union
+
+type_substitutions = {
+    "dict": Dict,
+    "list": List,
+    "tuple": Tuple,
+    "set": Set,
+    "frozenset": FrozenSet,
+    "Union": Union,
+}
+
+
+class UnionTransformer(NodeTransformer):
+    def __init__(self, union_name: Name | None = None):
+        self.union_name = union_name or Name(id="Union", ctx=Load())
+
+    def visit_BinOp(self, node: BinOp) -> Any:
+        self.generic_visit(node)
+        if isinstance(node.op, BitOr):
+            return Subscript(
+                value=self.union_name,
+                slice=Index(
+                    ASTTuple(elts=[node.left, node.right], ctx=Load()), ctx=Load()
+                ),
+                ctx=Load(),
+            )
+
+        return node
+
+
+def compile_type_hint(hint: str) -> CodeType:
+    parsed = parse(hint, "", "eval")
+    UnionTransformer().visit(parsed)
+    fix_missing_locations(parsed)
+    return compile(parsed, "", "eval", flags=0)
diff --git a/metaflow/_vendor/typeguard/_utils.py b/metaflow/_vendor/typeguard/_utils.py
new file mode 100644
index 00000000000..ee326a47b29
--- /dev/null
+++ b/metaflow/_vendor/typeguard/_utils.py
@@ -0,0 +1,173 @@
+from __future__ import annotations
+
+import inspect
+import sys
+from importlib import import_module
+from inspect import currentframe
+from types import CodeType, FrameType, FunctionType
+from typing import TYPE_CHECKING, Any, Callable, ForwardRef, Union, cast, final
+from weakref import WeakValueDictionary
+
+if TYPE_CHECKING:
+    from ._memo import TypeCheckMemo
+
+if sys.version_info >= (3, 13):
+    from typing import get_args, get_origin
+
+    def evaluate_forwardref(forwardref: ForwardRef, memo: TypeCheckMemo) -> Any:
+        return forwardref._evaluate(
+            memo.globals, memo.locals, type_params=(), recursive_guard=frozenset()
+        )
+
+elif sys.version_info >= (3, 10):
+    from typing import get_args, get_origin
+
+    def evaluate_forwardref(forwardref: ForwardRef, memo: TypeCheckMemo) -> Any:
+        return forwardref._evaluate(
+            memo.globals, memo.locals, recursive_guard=frozenset()
+        )
+
+else:
+    from metaflow._vendor.typing_extensions import get_args, get_origin
+
+    evaluate_extra_args: tuple[frozenset[Any], ...] = (
+        (frozenset(),) if sys.version_info >= (3, 9) else ()
+    )
+
+    def evaluate_forwardref(forwardref: ForwardRef, memo: TypeCheckMemo) -> Any:
+        from ._union_transformer import compile_type_hint, type_substitutions
+
+        if not forwardref.__forward_evaluated__:
+            forwardref.__forward_code__ = compile_type_hint(forwardref.__forward_arg__)
+
+        try:
+            return forwardref._evaluate(memo.globals, memo.locals, *evaluate_extra_args)
+        except NameError:
+            if sys.version_info < (3, 10):
+                # Try again, with the type substitutions (list -> List etc.) in place
+                new_globals = memo.globals.copy()
+                new_globals.setdefault("Union", Union)
+                if sys.version_info < (3, 9):
+                    new_globals.update(type_substitutions)
+
+                return forwardref._evaluate(
+                    new_globals, memo.locals or new_globals, *evaluate_extra_args
+                )
+
+            raise
+
+
+_functions_map: WeakValueDictionary[CodeType, FunctionType] = WeakValueDictionary()
+
+
+def get_type_name(type_: Any) -> str:
+    name: str
+    for attrname in "__name__", "_name", "__forward_arg__":
+        candidate = getattr(type_, attrname, None)
+        if isinstance(candidate, str):
+            name = candidate
+            break
+    else:
+        origin = get_origin(type_)
+        candidate = getattr(origin, "_name", None)
+        if candidate is None:
+            candidate = type_.__class__.__name__.strip("_")
+
+        if isinstance(candidate, str):
+            name = candidate
+        else:
+            return "(unknown)"
+
+    args = get_args(type_)
+    if args:
+        if name == "Literal":
+            formatted_args = ", ".join(repr(arg) for arg in args)
+        else:
+            formatted_args = ", ".join(get_type_name(arg) for arg in args)
+
+        name += f"[{formatted_args}]"
+
+    module = getattr(type_, "__module__", None)
+    if module and module not in (None, "typing", "typing_extensions", "builtins"):
+        name = module + "." + name
+
+    return name
+
+
+def qualified_name(obj: Any, *, add_class_prefix: bool = False) -> str:
+    """
+    Return the qualified name (e.g. package.module.Type) for the given object.
+
+    Builtins and types from the :mod:`typing` package get special treatment by having
+    the module name stripped from the generated name.
+
+    """
+    if obj is None:
+        return "None"
+    elif inspect.isclass(obj):
+        prefix = "class " if add_class_prefix else ""
+        type_ = obj
+    else:
+        prefix = ""
+        type_ = type(obj)
+
+    module = type_.__module__
+    qualname = type_.__qualname__
+    name = qualname if module in ("typing", "builtins") else f"{module}.{qualname}"
+    return prefix + name
+
+
+def function_name(func: Callable[..., Any]) -> str:
+    """
+    Return the qualified name of the given function.
+
+    Builtins and types from the :mod:`typing` package get special treatment by having
+    the module name stripped from the generated name.
+
+    """
+    # For partial functions and objects with __call__ defined, __qualname__ does not
+    # exist
+    module = getattr(func, "__module__", "")
+    qualname = (module + ".") if module not in ("builtins", "") else ""
+    return qualname + getattr(func, "__qualname__", repr(func))
+
+
+def resolve_reference(reference: str) -> Any:
+    modulename, varname = reference.partition(":")[::2]
+    if not modulename or not varname:
+        raise ValueError(f"{reference!r} is not a module:varname reference")
+
+    obj = import_module(modulename)
+    for attr in varname.split("."):
+        obj = getattr(obj, attr)
+
+    return obj
+
+
+def is_method_of(obj: object, cls: type) -> bool:
+    return (
+        inspect.isfunction(obj)
+        and obj.__module__ == cls.__module__
+        and obj.__qualname__.startswith(cls.__qualname__ + ".")
+    )
+
+
+def get_stacklevel() -> int:
+    level = 1
+    frame = cast(FrameType, currentframe()).f_back
+    while frame and frame.f_globals.get("__name__", "").startswith("typeguard."):
+        level += 1
+        frame = frame.f_back
+
+    return level
+
+
+@final
+class Unset:
+    __slots__ = ()
+
+    def __repr__(self) -> str:
+        return ""
+
+
+unset = Unset()
diff --git a/metaflow/_vendor/typeguard/py.typed b/metaflow/_vendor/typeguard/py.typed
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/metaflow/_vendor/typing_extensions.LICENSE b/metaflow/_vendor/typing_extensions.LICENSE
new file mode 100644
index 00000000000..f26bcf4d2de
--- /dev/null
+++ b/metaflow/_vendor/typing_extensions.LICENSE
@@ -0,0 +1,279 @@
+A. HISTORY OF THE SOFTWARE
+==========================
+
+Python was created in the early 1990s by Guido van Rossum at Stichting
+Mathematisch Centrum (CWI, see https://www.cwi.nl) in the Netherlands
+as a successor of a language called ABC.  Guido remains Python's
+principal author, although it includes many contributions from others.
+
+In 1995, Guido continued his work on Python at the Corporation for
+National Research Initiatives (CNRI, see https://www.cnri.reston.va.us)
+in Reston, Virginia where he released several versions of the
+software.
+
+In May 2000, Guido and the Python core development team moved to
+BeOpen.com to form the BeOpen PythonLabs team.  In October of the same
+year, the PythonLabs team moved to Digital Creations, which became
+Zope Corporation.  In 2001, the Python Software Foundation (PSF, see
+https://www.python.org/psf/) was formed, a non-profit organization
+created specifically to own Python-related Intellectual Property.
+Zope Corporation was a sponsoring member of the PSF.
+
+All Python releases are Open Source (see https://opensource.org for
+the Open Source Definition).  Historically, most, but not all, Python
+releases have also been GPL-compatible; the table below summarizes
+the various releases.
+
+    Release         Derived     Year        Owner       GPL-
+                    from                                compatible? (1)
+
+    0.9.0 thru 1.2              1991-1995   CWI         yes
+    1.3 thru 1.5.2  1.2         1995-1999   CNRI        yes
+    1.6             1.5.2       2000        CNRI        no
+    2.0             1.6         2000        BeOpen.com  no
+    1.6.1           1.6         2001        CNRI        yes (2)
+    2.1             2.0+1.6.1   2001        PSF         no
+    2.0.1           2.0+1.6.1   2001        PSF         yes
+    2.1.1           2.1+2.0.1   2001        PSF         yes
+    2.1.2           2.1.1       2002        PSF         yes
+    2.1.3           2.1.2       2002        PSF         yes
+    2.2 and above   2.1.1       2001-now    PSF         yes
+
+Footnotes:
+
+(1) GPL-compatible doesn't mean that we're distributing Python under
+    the GPL.  All Python licenses, unlike the GPL, let you distribute
+    a modified version without making your changes open source.  The
+    GPL-compatible licenses make it possible to combine Python with
+    other software that is released under the GPL; the others don't.
+
+(2) According to Richard Stallman, 1.6.1 is not GPL-compatible,
+    because its license has a choice of law clause.  According to
+    CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1
+    is "not incompatible" with the GPL.
+
+Thanks to the many outside volunteers who have worked under Guido's
+direction to make these releases possible.
+
+
+B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON
+===============================================================
+
+Python software and documentation are licensed under the
+Python Software Foundation License Version 2.
+
+Starting with Python 3.8.6, examples, recipes, and other code in
+the documentation are dual licensed under the PSF License Version 2
+and the Zero-Clause BSD license.
+
+Some software incorporated into Python is under different licenses.
+The licenses are listed with code falling under that license.
+
+
+PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
+--------------------------------------------
+
+1. This LICENSE AGREEMENT is between the Python Software Foundation
+("PSF"), and the Individual or Organization ("Licensee") accessing and
+otherwise using this software ("Python") in source or binary form and
+its associated documentation.
+
+2. Subject to the terms and conditions of this License Agreement, PSF hereby
+grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
+analyze, test, perform and/or display publicly, prepare derivative works,
+distribute, and otherwise use Python alone or in any derivative version,
+provided, however, that PSF's License Agreement and PSF's notice of copyright,
+i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 Python Software Foundation;
+All Rights Reserved" are retained in Python alone or in any derivative version
+prepared by Licensee.
+
+3. In the event Licensee prepares a derivative work that is based on
+or incorporates Python or any part thereof, and wants to make
+the derivative work available to others as provided herein, then
+Licensee hereby agrees to include in any such work a brief summary of
+the changes made to Python.
+
+4. PSF is making Python available to Licensee on an "AS IS"
+basis.  PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
+IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
+DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
+FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
+INFRINGE ANY THIRD PARTY RIGHTS.
+
+5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
+FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
+A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
+OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
+
+6. This License Agreement will automatically terminate upon a material
+breach of its terms and conditions.
+
+7. Nothing in this License Agreement shall be deemed to create any
+relationship of agency, partnership, or joint venture between PSF and
+Licensee.  This License Agreement does not grant permission to use PSF
+trademarks or trade name in a trademark sense to endorse or promote
+products or services of Licensee, or any third party.
+
+8. By copying, installing or otherwise using Python, Licensee
+agrees to be bound by the terms and conditions of this License
+Agreement.
+
+
+BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0
+-------------------------------------------
+
+BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1
+
+1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an
+office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the
+Individual or Organization ("Licensee") accessing and otherwise using
+this software in source or binary form and its associated
+documentation ("the Software").
+
+2. Subject to the terms and conditions of this BeOpen Python License
+Agreement, BeOpen hereby grants Licensee a non-exclusive,
+royalty-free, world-wide license to reproduce, analyze, test, perform
+and/or display publicly, prepare derivative works, distribute, and
+otherwise use the Software alone or in any derivative version,
+provided, however, that the BeOpen Python License is retained in the
+Software, alone or in any derivative version prepared by Licensee.
+
+3. BeOpen is making the Software available to Licensee on an "AS IS"
+basis.  BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
+IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND
+DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
+FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT
+INFRINGE ANY THIRD PARTY RIGHTS.
+
+4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE
+SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS
+AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY
+DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
+
+5. This License Agreement will automatically terminate upon a material
+breach of its terms and conditions.
+
+6. This License Agreement shall be governed by and interpreted in all
+respects by the law of the State of California, excluding conflict of
+law provisions.  Nothing in this License Agreement shall be deemed to
+create any relationship of agency, partnership, or joint venture
+between BeOpen and Licensee.  This License Agreement does not grant
+permission to use BeOpen trademarks or trade names in a trademark
+sense to endorse or promote products or services of Licensee, or any
+third party.  As an exception, the "BeOpen Python" logos available at
+http://www.pythonlabs.com/logos.html may be used according to the
+permissions granted on that web page.
+
+7. By copying, installing or otherwise using the software, Licensee
+agrees to be bound by the terms and conditions of this License
+Agreement.
+
+
+CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1
+---------------------------------------
+
+1. This LICENSE AGREEMENT is between the Corporation for National
+Research Initiatives, having an office at 1895 Preston White Drive,
+Reston, VA 20191 ("CNRI"), and the Individual or Organization
+("Licensee") accessing and otherwise using Python 1.6.1 software in
+source or binary form and its associated documentation.
+
+2. Subject to the terms and conditions of this License Agreement, CNRI
+hereby grants Licensee a nonexclusive, royalty-free, world-wide
+license to reproduce, analyze, test, perform and/or display publicly,
+prepare derivative works, distribute, and otherwise use Python 1.6.1
+alone or in any derivative version, provided, however, that CNRI's
+License Agreement and CNRI's notice of copyright, i.e., "Copyright (c)
+1995-2001 Corporation for National Research Initiatives; All Rights
+Reserved" are retained in Python 1.6.1 alone or in any derivative
+version prepared by Licensee.  Alternately, in lieu of CNRI's License
+Agreement, Licensee may substitute the following text (omitting the
+quotes): "Python 1.6.1 is made available subject to the terms and
+conditions in CNRI's License Agreement.  This Agreement together with
+Python 1.6.1 may be located on the internet using the following
+unique, persistent identifier (known as a handle): 1895.22/1013.  This
+Agreement may also be obtained from a proxy server on the internet
+using the following URL: http://hdl.handle.net/1895.22/1013".
+
+3. In the event Licensee prepares a derivative work that is based on
+or incorporates Python 1.6.1 or any part thereof, and wants to make
+the derivative work available to others as provided herein, then
+Licensee hereby agrees to include in any such work a brief summary of
+the changes made to Python 1.6.1.
+
+4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS"
+basis.  CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
+IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND
+DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
+FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT
+INFRINGE ANY THIRD PARTY RIGHTS.
+
+5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
+1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
+A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1,
+OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
+
+6. This License Agreement will automatically terminate upon a material
+breach of its terms and conditions.
+
+7. This License Agreement shall be governed by the federal
+intellectual property law of the United States, including without
+limitation the federal copyright law, and, to the extent such
+U.S. federal law does not apply, by the law of the Commonwealth of
+Virginia, excluding Virginia's conflict of law provisions.
+Notwithstanding the foregoing, with regard to derivative works based
+on Python 1.6.1 that incorporate non-separable material that was
+previously distributed under the GNU General Public License (GPL), the
+law of the Commonwealth of Virginia shall govern this License
+Agreement only as to issues arising under or with respect to
+Paragraphs 4, 5, and 7 of this License Agreement.  Nothing in this
+License Agreement shall be deemed to create any relationship of
+agency, partnership, or joint venture between CNRI and Licensee.  This
+License Agreement does not grant permission to use CNRI trademarks or
+trade name in a trademark sense to endorse or promote products or
+services of Licensee, or any third party.
+
+8. By clicking on the "ACCEPT" button where indicated, or by copying,
+installing or otherwise using Python 1.6.1, Licensee agrees to be
+bound by the terms and conditions of this License Agreement.
+
+        ACCEPT
+
+
+CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2
+--------------------------------------------------
+
+Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam,
+The Netherlands.  All rights reserved.
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Stichting Mathematisch
+Centrum or CWI not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.
+
+STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
+FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+ZERO-CLAUSE BSD LICENSE FOR CODE IN THE PYTHON DOCUMENTATION
+----------------------------------------------------------------------
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
diff --git a/metaflow/_vendor/typing_extensions.py b/metaflow/_vendor/typing_extensions.py
new file mode 100644
index 00000000000..edf1805f00f
--- /dev/null
+++ b/metaflow/_vendor/typing_extensions.py
@@ -0,0 +1,3641 @@
+import abc
+import collections
+import collections.abc
+import contextlib
+import functools
+import inspect
+import operator
+import sys
+import types as _types
+import typing
+import warnings
+
+__all__ = [
+    # Super-special typing primitives.
+    'Any',
+    'ClassVar',
+    'Concatenate',
+    'Final',
+    'LiteralString',
+    'ParamSpec',
+    'ParamSpecArgs',
+    'ParamSpecKwargs',
+    'Self',
+    'Type',
+    'TypeVar',
+    'TypeVarTuple',
+    'Unpack',
+
+    # ABCs (from collections.abc).
+    'Awaitable',
+    'AsyncIterator',
+    'AsyncIterable',
+    'Coroutine',
+    'AsyncGenerator',
+    'AsyncContextManager',
+    'Buffer',
+    'ChainMap',
+
+    # Concrete collection types.
+    'ContextManager',
+    'Counter',
+    'Deque',
+    'DefaultDict',
+    'NamedTuple',
+    'OrderedDict',
+    'TypedDict',
+
+    # Structural checks, a.k.a. protocols.
+    'SupportsAbs',
+    'SupportsBytes',
+    'SupportsComplex',
+    'SupportsFloat',
+    'SupportsIndex',
+    'SupportsInt',
+    'SupportsRound',
+
+    # One-off things.
+    'Annotated',
+    'assert_never',
+    'assert_type',
+    'clear_overloads',
+    'dataclass_transform',
+    'deprecated',
+    'Doc',
+    'get_overloads',
+    'final',
+    'get_args',
+    'get_origin',
+    'get_original_bases',
+    'get_protocol_members',
+    'get_type_hints',
+    'IntVar',
+    'is_protocol',
+    'is_typeddict',
+    'Literal',
+    'NewType',
+    'overload',
+    'override',
+    'Protocol',
+    'reveal_type',
+    'runtime',
+    'runtime_checkable',
+    'Text',
+    'TypeAlias',
+    'TypeAliasType',
+    'TypeGuard',
+    'TypeIs',
+    'TYPE_CHECKING',
+    'Never',
+    'NoReturn',
+    'ReadOnly',
+    'Required',
+    'NotRequired',
+
+    # Pure aliases, have always been in typing
+    'AbstractSet',
+    'AnyStr',
+    'BinaryIO',
+    'Callable',
+    'Collection',
+    'Container',
+    'Dict',
+    'ForwardRef',
+    'FrozenSet',
+    'Generator',
+    'Generic',
+    'Hashable',
+    'IO',
+    'ItemsView',
+    'Iterable',
+    'Iterator',
+    'KeysView',
+    'List',
+    'Mapping',
+    'MappingView',
+    'Match',
+    'MutableMapping',
+    'MutableSequence',
+    'MutableSet',
+    'NoDefault',
+    'Optional',
+    'Pattern',
+    'Reversible',
+    'Sequence',
+    'Set',
+    'Sized',
+    'TextIO',
+    'Tuple',
+    'Union',
+    'ValuesView',
+    'cast',
+    'no_type_check',
+    'no_type_check_decorator',
+]
+
+# for backward compatibility
+PEP_560 = True
+GenericMeta = type
+_PEP_696_IMPLEMENTED = sys.version_info >= (3, 13, 0, "beta")
+
+# The functions below are modified copies of typing internal helpers.
+# They are needed by _ProtocolMeta and they provide support for PEP 646.
+
+
+class _Sentinel:
+    def __repr__(self):
+        return ""
+
+
+_marker = _Sentinel()
+
+
+if sys.version_info >= (3, 10):
+    def _should_collect_from_parameters(t):
+        return isinstance(
+            t, (typing._GenericAlias, _types.GenericAlias, _types.UnionType)
+        )
+elif sys.version_info >= (3, 9):
+    def _should_collect_from_parameters(t):
+        return isinstance(t, (typing._GenericAlias, _types.GenericAlias))
+else:
+    def _should_collect_from_parameters(t):
+        return isinstance(t, typing._GenericAlias) and not t._special
+
+
+NoReturn = typing.NoReturn
+
+# Some unconstrained type variables.  These are used by the container types.
+# (These are not for export.)
+T = typing.TypeVar('T')  # Any type.
+KT = typing.TypeVar('KT')  # Key type.
+VT = typing.TypeVar('VT')  # Value type.
+T_co = typing.TypeVar('T_co', covariant=True)  # Any type covariant containers.
+T_contra = typing.TypeVar('T_contra', contravariant=True)  # Ditto contravariant.
+
+
+if sys.version_info >= (3, 11):
+    from typing import Any
+else:
+
+    class _AnyMeta(type):
+        def __instancecheck__(self, obj):
+            if self is Any:
+                raise TypeError("typing_extensions.Any cannot be used with isinstance()")
+            return super().__instancecheck__(obj)
+
+        def __repr__(self):
+            if self is Any:
+                return "typing_extensions.Any"
+            return super().__repr__()
+
+    class Any(metaclass=_AnyMeta):
+        """Special type indicating an unconstrained type.
+        - Any is compatible with every type.
+        - Any assumed to have all methods.
+        - All values assumed to be instances of Any.
+        Note that all the above statements are true from the point of view of
+        static type checkers. At runtime, Any should not be used with instance
+        checks.
+        """
+        def __new__(cls, *args, **kwargs):
+            if cls is Any:
+                raise TypeError("Any cannot be instantiated")
+            return super().__new__(cls, *args, **kwargs)
+
+
+ClassVar = typing.ClassVar
+
+
+class _ExtensionsSpecialForm(typing._SpecialForm, _root=True):
+    def __repr__(self):
+        return 'typing_extensions.' + self._name
+
+
+Final = typing.Final
+
+if sys.version_info >= (3, 11):
+    final = typing.final
+else:
+    # @final exists in 3.8+, but we backport it for all versions
+    # before 3.11 to keep support for the __final__ attribute.
+    # See https://bugs.python.org/issue46342
+    def final(f):
+        """This decorator can be used to indicate to type checkers that
+        the decorated method cannot be overridden, and decorated class
+        cannot be subclassed. For example:
+
+            class Base:
+                @final
+                def done(self) -> None:
+                    ...
+            class Sub(Base):
+                def done(self) -> None:  # Error reported by type checker
+                    ...
+            @final
+            class Leaf:
+                ...
+            class Other(Leaf):  # Error reported by type checker
+                ...
+
+        There is no runtime checking of these properties. The decorator
+        sets the ``__final__`` attribute to ``True`` on the decorated object
+        to allow runtime introspection.
+        """
+        try:
+            f.__final__ = True
+        except (AttributeError, TypeError):
+            # Skip the attribute silently if it is not writable.
+            # AttributeError happens if the object has __slots__ or a
+            # read-only property, TypeError if it's a builtin class.
+            pass
+        return f
+
+
+def IntVar(name):
+    return typing.TypeVar(name)
+
+
+# A Literal bug was fixed in 3.11.0, 3.10.1 and 3.9.8
+if sys.version_info >= (3, 10, 1):
+    Literal = typing.Literal
+else:
+    def _flatten_literal_params(parameters):
+        """An internal helper for Literal creation: flatten Literals among parameters"""
+        params = []
+        for p in parameters:
+            if isinstance(p, _LiteralGenericAlias):
+                params.extend(p.__args__)
+            else:
+                params.append(p)
+        return tuple(params)
+
+    def _value_and_type_iter(params):
+        for p in params:
+            yield p, type(p)
+
+    class _LiteralGenericAlias(typing._GenericAlias, _root=True):
+        def __eq__(self, other):
+            if not isinstance(other, _LiteralGenericAlias):
+                return NotImplemented
+            these_args_deduped = set(_value_and_type_iter(self.__args__))
+            other_args_deduped = set(_value_and_type_iter(other.__args__))
+            return these_args_deduped == other_args_deduped
+
+        def __hash__(self):
+            return hash(frozenset(_value_and_type_iter(self.__args__)))
+
+    class _LiteralForm(_ExtensionsSpecialForm, _root=True):
+        def __init__(self, doc: str):
+            self._name = 'Literal'
+            self._doc = self.__doc__ = doc
+
+        def __getitem__(self, parameters):
+            if not isinstance(parameters, tuple):
+                parameters = (parameters,)
+
+            parameters = _flatten_literal_params(parameters)
+
+            val_type_pairs = list(_value_and_type_iter(parameters))
+            try:
+                deduped_pairs = set(val_type_pairs)
+            except TypeError:
+                # unhashable parameters
+                pass
+            else:
+                # similar logic to typing._deduplicate on Python 3.9+
+                if len(deduped_pairs) < len(val_type_pairs):
+                    new_parameters = []
+                    for pair in val_type_pairs:
+                        if pair in deduped_pairs:
+                            new_parameters.append(pair[0])
+                            deduped_pairs.remove(pair)
+                    assert not deduped_pairs, deduped_pairs
+                    parameters = tuple(new_parameters)
+
+            return _LiteralGenericAlias(self, parameters)
+
+    Literal = _LiteralForm(doc="""\
+                           A type that can be used to indicate to type checkers
+                           that the corresponding value has a value literally equivalent
+                           to the provided parameter. For example:
+
+                               var: Literal[4] = 4
+
+                           The type checker understands that 'var' is literally equal to
+                           the value 4 and no other value.
+
+                           Literal[...] cannot be subclassed. There is no runtime
+                           checking verifying that the parameter is actually a value
+                           instead of a type.""")
+
+
+_overload_dummy = typing._overload_dummy
+
+
+if hasattr(typing, "get_overloads"):  # 3.11+
+    overload = typing.overload
+    get_overloads = typing.get_overloads
+    clear_overloads = typing.clear_overloads
+else:
+    # {module: {qualname: {firstlineno: func}}}
+    _overload_registry = collections.defaultdict(
+        functools.partial(collections.defaultdict, dict)
+    )
+
+    def overload(func):
+        """Decorator for overloaded functions/methods.
+
+        In a stub file, place two or more stub definitions for the same
+        function in a row, each decorated with @overload.  For example:
+
+        @overload
+        def utf8(value: None) -> None: ...
+        @overload
+        def utf8(value: bytes) -> bytes: ...
+        @overload
+        def utf8(value: str) -> bytes: ...
+
+        In a non-stub file (i.e. a regular .py file), do the same but
+        follow it with an implementation.  The implementation should *not*
+        be decorated with @overload.  For example:
+
+        @overload
+        def utf8(value: None) -> None: ...
+        @overload
+        def utf8(value: bytes) -> bytes: ...
+        @overload
+        def utf8(value: str) -> bytes: ...
+        def utf8(value):
+            # implementation goes here
+
+        The overloads for a function can be retrieved at runtime using the
+        get_overloads() function.
+        """
+        # classmethod and staticmethod
+        f = getattr(func, "__func__", func)
+        try:
+            _overload_registry[f.__module__][f.__qualname__][
+                f.__code__.co_firstlineno
+            ] = func
+        except AttributeError:
+            # Not a normal function; ignore.
+            pass
+        return _overload_dummy
+
+    def get_overloads(func):
+        """Return all defined overloads for *func* as a sequence."""
+        # classmethod and staticmethod
+        f = getattr(func, "__func__", func)
+        if f.__module__ not in _overload_registry:
+            return []
+        mod_dict = _overload_registry[f.__module__]
+        if f.__qualname__ not in mod_dict:
+            return []
+        return list(mod_dict[f.__qualname__].values())
+
+    def clear_overloads():
+        """Clear all overloads in the registry."""
+        _overload_registry.clear()
+
+
+# This is not a real generic class.  Don't use outside annotations.
+Type = typing.Type
+
+# Various ABCs mimicking those in collections.abc.
+# A few are simply re-exported for completeness.
+Awaitable = typing.Awaitable
+Coroutine = typing.Coroutine
+AsyncIterable = typing.AsyncIterable
+AsyncIterator = typing.AsyncIterator
+Deque = typing.Deque
+DefaultDict = typing.DefaultDict
+OrderedDict = typing.OrderedDict
+Counter = typing.Counter
+ChainMap = typing.ChainMap
+Text = typing.Text
+TYPE_CHECKING = typing.TYPE_CHECKING
+
+
+if sys.version_info >= (3, 13, 0, "beta"):
+    from typing import AsyncContextManager, AsyncGenerator, ContextManager, Generator
+else:
+    def _is_dunder(attr):
+        return attr.startswith('__') and attr.endswith('__')
+
+    # Python <3.9 doesn't have typing._SpecialGenericAlias
+    _special_generic_alias_base = getattr(
+        typing, "_SpecialGenericAlias", typing._GenericAlias
+    )
+
+    class _SpecialGenericAlias(_special_generic_alias_base, _root=True):
+        def __init__(self, origin, nparams, *, inst=True, name=None, defaults=()):
+            if _special_generic_alias_base is typing._GenericAlias:
+                # Python <3.9
+                self.__origin__ = origin
+                self._nparams = nparams
+                super().__init__(origin, nparams, special=True, inst=inst, name=name)
+            else:
+                # Python >= 3.9
+                super().__init__(origin, nparams, inst=inst, name=name)
+            self._defaults = defaults
+
+        def __setattr__(self, attr, val):
+            allowed_attrs = {'_name', '_inst', '_nparams', '_defaults'}
+            if _special_generic_alias_base is typing._GenericAlias:
+                # Python <3.9
+                allowed_attrs.add("__origin__")
+            if _is_dunder(attr) or attr in allowed_attrs:
+                object.__setattr__(self, attr, val)
+            else:
+                setattr(self.__origin__, attr, val)
+
+        @typing._tp_cache
+        def __getitem__(self, params):
+            if not isinstance(params, tuple):
+                params = (params,)
+            msg = "Parameters to generic types must be types."
+            params = tuple(typing._type_check(p, msg) for p in params)
+            if (
+                self._defaults
+                and len(params) < self._nparams
+                and len(params) + len(self._defaults) >= self._nparams
+            ):
+                params = (*params, *self._defaults[len(params) - self._nparams:])
+            actual_len = len(params)
+
+            if actual_len != self._nparams:
+                if self._defaults:
+                    expected = f"at least {self._nparams - len(self._defaults)}"
+                else:
+                    expected = str(self._nparams)
+                if not self._nparams:
+                    raise TypeError(f"{self} is not a generic class")
+                raise TypeError(
+                    f"Too {'many' if actual_len > self._nparams else 'few'}"
+                    f" arguments for {self};"
+                    f" actual {actual_len}, expected {expected}"
+                )
+            return self.copy_with(params)
+
+    _NoneType = type(None)
+    Generator = _SpecialGenericAlias(
+        collections.abc.Generator, 3, defaults=(_NoneType, _NoneType)
+    )
+    AsyncGenerator = _SpecialGenericAlias(
+        collections.abc.AsyncGenerator, 2, defaults=(_NoneType,)
+    )
+    ContextManager = _SpecialGenericAlias(
+        contextlib.AbstractContextManager,
+        2,
+        name="ContextManager",
+        defaults=(typing.Optional[bool],)
+    )
+    AsyncContextManager = _SpecialGenericAlias(
+        contextlib.AbstractAsyncContextManager,
+        2,
+        name="AsyncContextManager",
+        defaults=(typing.Optional[bool],)
+    )
+
+
+_PROTO_ALLOWLIST = {
+    'collections.abc': [
+        'Callable', 'Awaitable', 'Iterable', 'Iterator', 'AsyncIterable',
+        'Hashable', 'Sized', 'Container', 'Collection', 'Reversible', 'Buffer',
+    ],
+    'contextlib': ['AbstractContextManager', 'AbstractAsyncContextManager'],
+    'typing_extensions': ['Buffer'],
+}
+
+
+_EXCLUDED_ATTRS = frozenset(typing.EXCLUDED_ATTRIBUTES) | {
+    "__match_args__", "__protocol_attrs__", "__non_callable_proto_members__",
+    "__final__",
+}
+
+
+def _get_protocol_attrs(cls):
+    attrs = set()
+    for base in cls.__mro__[:-1]:  # without object
+        if base.__name__ in {'Protocol', 'Generic'}:
+            continue
+        annotations = getattr(base, '__annotations__', {})
+        for attr in (*base.__dict__, *annotations):
+            if (not attr.startswith('_abc_') and attr not in _EXCLUDED_ATTRS):
+                attrs.add(attr)
+    return attrs
+
+
+def _caller(depth=2):
+    try:
+        return sys._getframe(depth).f_globals.get('__name__', '__main__')
+    except (AttributeError, ValueError):  # For platforms without _getframe()
+        return None
+
+
+# `__match_args__` attribute was removed from protocol members in 3.13,
+# we want to backport this change to older Python versions.
+if sys.version_info >= (3, 13):
+    Protocol = typing.Protocol
+else:
+    def _allow_reckless_class_checks(depth=3):
+        """Allow instance and class checks for special stdlib modules.
+        The abc and functools modules indiscriminately call isinstance() and
+        issubclass() on the whole MRO of a user class, which may contain protocols.
+        """
+        return _caller(depth) in {'abc', 'functools', None}
+
+    def _no_init(self, *args, **kwargs):
+        if type(self)._is_protocol:
+            raise TypeError('Protocols cannot be instantiated')
+
+    def _type_check_issubclass_arg_1(arg):
+        """Raise TypeError if `arg` is not an instance of `type`
+        in `issubclass(arg, )`.
+
+        In most cases, this is verified by type.__subclasscheck__.
+        Checking it again unnecessarily would slow down issubclass() checks,
+        so, we don't perform this check unless we absolutely have to.
+
+        For various error paths, however,
+        we want to ensure that *this* error message is shown to the user
+        where relevant, rather than a typing.py-specific error message.
+        """
+        if not isinstance(arg, type):
+            # Same error message as for issubclass(1, int).
+            raise TypeError('issubclass() arg 1 must be a class')
+
+    # Inheriting from typing._ProtocolMeta isn't actually desirable,
+    # but is necessary to allow typing.Protocol and typing_extensions.Protocol
+    # to mix without getting TypeErrors about "metaclass conflict"
+    class _ProtocolMeta(type(typing.Protocol)):
+        # This metaclass is somewhat unfortunate,
+        # but is necessary for several reasons...
+        #
+        # NOTE: DO NOT call super() in any methods in this class
+        # That would call the methods on typing._ProtocolMeta on Python 3.8-3.11
+        # and those are slow
+        def __new__(mcls, name, bases, namespace, **kwargs):
+            if name == "Protocol" and len(bases) < 2:
+                pass
+            elif {Protocol, typing.Protocol} & set(bases):
+                for base in bases:
+                    if not (
+                        base in {object, typing.Generic, Protocol, typing.Protocol}
+                        or base.__name__ in _PROTO_ALLOWLIST.get(base.__module__, [])
+                        or is_protocol(base)
+                    ):
+                        raise TypeError(
+                            f"Protocols can only inherit from other protocols, "
+                            f"got {base!r}"
+                        )
+            return abc.ABCMeta.__new__(mcls, name, bases, namespace, **kwargs)
+
+        def __init__(cls, *args, **kwargs):
+            abc.ABCMeta.__init__(cls, *args, **kwargs)
+            if getattr(cls, "_is_protocol", False):
+                cls.__protocol_attrs__ = _get_protocol_attrs(cls)
+
+        def __subclasscheck__(cls, other):
+            if cls is Protocol:
+                return type.__subclasscheck__(cls, other)
+            if (
+                getattr(cls, '_is_protocol', False)
+                and not _allow_reckless_class_checks()
+            ):
+                if not getattr(cls, '_is_runtime_protocol', False):
+                    _type_check_issubclass_arg_1(other)
+                    raise TypeError(
+                        "Instance and class checks can only be used with "
+                        "@runtime_checkable protocols"
+                    )
+                if (
+                    # this attribute is set by @runtime_checkable:
+                    cls.__non_callable_proto_members__
+                    and cls.__dict__.get("__subclasshook__") is _proto_hook
+                ):
+                    _type_check_issubclass_arg_1(other)
+                    non_method_attrs = sorted(cls.__non_callable_proto_members__)
+                    raise TypeError(
+                        "Protocols with non-method members don't support issubclass()."
+                        f" Non-method members: {str(non_method_attrs)[1:-1]}."
+                    )
+            return abc.ABCMeta.__subclasscheck__(cls, other)
+
+        def __instancecheck__(cls, instance):
+            # We need this method for situations where attributes are
+            # assigned in __init__.
+            if cls is Protocol:
+                return type.__instancecheck__(cls, instance)
+            if not getattr(cls, "_is_protocol", False):
+                # i.e., it's a concrete subclass of a protocol
+                return abc.ABCMeta.__instancecheck__(cls, instance)
+
+            if (
+                not getattr(cls, '_is_runtime_protocol', False) and
+                not _allow_reckless_class_checks()
+            ):
+                raise TypeError("Instance and class checks can only be used with"
+                                " @runtime_checkable protocols")
+
+            if abc.ABCMeta.__instancecheck__(cls, instance):
+                return True
+
+            for attr in cls.__protocol_attrs__:
+                try:
+                    val = inspect.getattr_static(instance, attr)
+                except AttributeError:
+                    break
+                # this attribute is set by @runtime_checkable:
+                if val is None and attr not in cls.__non_callable_proto_members__:
+                    break
+            else:
+                return True
+
+            return False
+
+        def __eq__(cls, other):
+            # Hack so that typing.Generic.__class_getitem__
+            # treats typing_extensions.Protocol
+            # as equivalent to typing.Protocol
+            if abc.ABCMeta.__eq__(cls, other) is True:
+                return True
+            return cls is Protocol and other is typing.Protocol
+
+        # This has to be defined, or the abc-module cache
+        # complains about classes with this metaclass being unhashable,
+        # if we define only __eq__!
+        def __hash__(cls) -> int:
+            return type.__hash__(cls)
+
+    @classmethod
+    def _proto_hook(cls, other):
+        if not cls.__dict__.get('_is_protocol', False):
+            return NotImplemented
+
+        for attr in cls.__protocol_attrs__:
+            for base in other.__mro__:
+                # Check if the members appears in the class dictionary...
+                if attr in base.__dict__:
+                    if base.__dict__[attr] is None:
+                        return NotImplemented
+                    break
+
+                # ...or in annotations, if it is a sub-protocol.
+                annotations = getattr(base, '__annotations__', {})
+                if (
+                    isinstance(annotations, collections.abc.Mapping)
+                    and attr in annotations
+                    and is_protocol(other)
+                ):
+                    break
+            else:
+                return NotImplemented
+        return True
+
+    class Protocol(typing.Generic, metaclass=_ProtocolMeta):
+        __doc__ = typing.Protocol.__doc__
+        __slots__ = ()
+        _is_protocol = True
+        _is_runtime_protocol = False
+
+        def __init_subclass__(cls, *args, **kwargs):
+            super().__init_subclass__(*args, **kwargs)
+
+            # Determine if this is a protocol or a concrete subclass.
+            if not cls.__dict__.get('_is_protocol', False):
+                cls._is_protocol = any(b is Protocol for b in cls.__bases__)
+
+            # Set (or override) the protocol subclass hook.
+            if '__subclasshook__' not in cls.__dict__:
+                cls.__subclasshook__ = _proto_hook
+
+            # Prohibit instantiation for protocol classes
+            if cls._is_protocol and cls.__init__ is Protocol.__init__:
+                cls.__init__ = _no_init
+
+
+if sys.version_info >= (3, 13):
+    runtime_checkable = typing.runtime_checkable
+else:
+    def runtime_checkable(cls):
+        """Mark a protocol class as a runtime protocol.
+
+        Such protocol can be used with isinstance() and issubclass().
+        Raise TypeError if applied to a non-protocol class.
+        This allows a simple-minded structural check very similar to
+        one trick ponies in collections.abc such as Iterable.
+
+        For example::
+
+            @runtime_checkable
+            class Closable(Protocol):
+                def close(self): ...
+
+            assert isinstance(open('/some/file'), Closable)
+
+        Warning: this will check only the presence of the required methods,
+        not their type signatures!
+        """
+        if not issubclass(cls, typing.Generic) or not getattr(cls, '_is_protocol', False):
+            raise TypeError(f'@runtime_checkable can be only applied to protocol classes,'
+                            f' got {cls!r}')
+        cls._is_runtime_protocol = True
+
+        # typing.Protocol classes on <=3.11 break if we execute this block,
+        # because typing.Protocol classes on <=3.11 don't have a
+        # `__protocol_attrs__` attribute, and this block relies on the
+        # `__protocol_attrs__` attribute. Meanwhile, typing.Protocol classes on 3.12.2+
+        # break if we *don't* execute this block, because *they* assume that all
+        # protocol classes have a `__non_callable_proto_members__` attribute
+        # (which this block sets)
+        if isinstance(cls, _ProtocolMeta) or sys.version_info >= (3, 12, 2):
+            # PEP 544 prohibits using issubclass()
+            # with protocols that have non-method members.
+            # See gh-113320 for why we compute this attribute here,
+            # rather than in `_ProtocolMeta.__init__`
+            cls.__non_callable_proto_members__ = set()
+            for attr in cls.__protocol_attrs__:
+                try:
+                    is_callable = callable(getattr(cls, attr, None))
+                except Exception as e:
+                    raise TypeError(
+                        f"Failed to determine whether protocol member {attr!r} "
+                        "is a method member"
+                    ) from e
+                else:
+                    if not is_callable:
+                        cls.__non_callable_proto_members__.add(attr)
+
+        return cls
+
+
+# The "runtime" alias exists for backwards compatibility.
+runtime = runtime_checkable
+
+
+# Our version of runtime-checkable protocols is faster on Python 3.8-3.11
+if sys.version_info >= (3, 12):
+    SupportsInt = typing.SupportsInt
+    SupportsFloat = typing.SupportsFloat
+    SupportsComplex = typing.SupportsComplex
+    SupportsBytes = typing.SupportsBytes
+    SupportsIndex = typing.SupportsIndex
+    SupportsAbs = typing.SupportsAbs
+    SupportsRound = typing.SupportsRound
+else:
+    @runtime_checkable
+    class SupportsInt(Protocol):
+        """An ABC with one abstract method __int__."""
+        __slots__ = ()
+
+        @abc.abstractmethod
+        def __int__(self) -> int:
+            pass
+
+    @runtime_checkable
+    class SupportsFloat(Protocol):
+        """An ABC with one abstract method __float__."""
+        __slots__ = ()
+
+        @abc.abstractmethod
+        def __float__(self) -> float:
+            pass
+
+    @runtime_checkable
+    class SupportsComplex(Protocol):
+        """An ABC with one abstract method __complex__."""
+        __slots__ = ()
+
+        @abc.abstractmethod
+        def __complex__(self) -> complex:
+            pass
+
+    @runtime_checkable
+    class SupportsBytes(Protocol):
+        """An ABC with one abstract method __bytes__."""
+        __slots__ = ()
+
+        @abc.abstractmethod
+        def __bytes__(self) -> bytes:
+            pass
+
+    @runtime_checkable
+    class SupportsIndex(Protocol):
+        __slots__ = ()
+
+        @abc.abstractmethod
+        def __index__(self) -> int:
+            pass
+
+    @runtime_checkable
+    class SupportsAbs(Protocol[T_co]):
+        """
+        An ABC with one abstract method __abs__ that is covariant in its return type.
+        """
+        __slots__ = ()
+
+        @abc.abstractmethod
+        def __abs__(self) -> T_co:
+            pass
+
+    @runtime_checkable
+    class SupportsRound(Protocol[T_co]):
+        """
+        An ABC with one abstract method __round__ that is covariant in its return type.
+        """
+        __slots__ = ()
+
+        @abc.abstractmethod
+        def __round__(self, ndigits: int = 0) -> T_co:
+            pass
+
+
+def _ensure_subclassable(mro_entries):
+    def inner(func):
+        if sys.implementation.name == "pypy" and sys.version_info < (3, 9):
+            cls_dict = {
+                "__call__": staticmethod(func),
+                "__mro_entries__": staticmethod(mro_entries)
+            }
+            t = type(func.__name__, (), cls_dict)
+            return functools.update_wrapper(t(), func)
+        else:
+            func.__mro_entries__ = mro_entries
+            return func
+    return inner
+
+
+# Update this to something like >=3.13.0b1 if and when
+# PEP 728 is implemented in CPython
+_PEP_728_IMPLEMENTED = False
+
+if _PEP_728_IMPLEMENTED:
+    # The standard library TypedDict in Python 3.8 does not store runtime information
+    # about which (if any) keys are optional.  See https://bugs.python.org/issue38834
+    # The standard library TypedDict in Python 3.9.0/1 does not honour the "total"
+    # keyword with old-style TypedDict().  See https://bugs.python.org/issue42059
+    # The standard library TypedDict below Python 3.11 does not store runtime
+    # information about optional and required keys when using Required or NotRequired.
+    # Generic TypedDicts are also impossible using typing.TypedDict on Python <3.11.
+    # Aaaand on 3.12 we add __orig_bases__ to TypedDict
+    # to enable better runtime introspection.
+    # On 3.13 we deprecate some odd ways of creating TypedDicts.
+    # Also on 3.13, PEP 705 adds the ReadOnly[] qualifier.
+    # PEP 728 (still pending) makes more changes.
+    TypedDict = typing.TypedDict
+    _TypedDictMeta = typing._TypedDictMeta
+    is_typeddict = typing.is_typeddict
+else:
+    # 3.10.0 and later
+    _TAKES_MODULE = "module" in inspect.signature(typing._type_check).parameters
+
+    def _get_typeddict_qualifiers(annotation_type):
+        while True:
+            annotation_origin = get_origin(annotation_type)
+            if annotation_origin is Annotated:
+                annotation_args = get_args(annotation_type)
+                if annotation_args:
+                    annotation_type = annotation_args[0]
+                else:
+                    break
+            elif annotation_origin is Required:
+                yield Required
+                annotation_type, = get_args(annotation_type)
+            elif annotation_origin is NotRequired:
+                yield NotRequired
+                annotation_type, = get_args(annotation_type)
+            elif annotation_origin is ReadOnly:
+                yield ReadOnly
+                annotation_type, = get_args(annotation_type)
+            else:
+                break
+
+    class _TypedDictMeta(type):
+        def __new__(cls, name, bases, ns, *, total=True, closed=False):
+            """Create new typed dict class object.
+
+            This method is called when TypedDict is subclassed,
+            or when TypedDict is instantiated. This way
+            TypedDict supports all three syntax forms described in its docstring.
+            Subclasses and instances of TypedDict return actual dictionaries.
+            """
+            for base in bases:
+                if type(base) is not _TypedDictMeta and base is not typing.Generic:
+                    raise TypeError('cannot inherit from both a TypedDict type '
+                                    'and a non-TypedDict base class')
+
+            if any(issubclass(b, typing.Generic) for b in bases):
+                generic_base = (typing.Generic,)
+            else:
+                generic_base = ()
+
+            # typing.py generally doesn't let you inherit from plain Generic, unless
+            # the name of the class happens to be "Protocol"
+            tp_dict = type.__new__(_TypedDictMeta, "Protocol", (*generic_base, dict), ns)
+            tp_dict.__name__ = name
+            if tp_dict.__qualname__ == "Protocol":
+                tp_dict.__qualname__ = name
+
+            if not hasattr(tp_dict, '__orig_bases__'):
+                tp_dict.__orig_bases__ = bases
+
+            annotations = {}
+            if "__annotations__" in ns:
+                own_annotations = ns["__annotations__"]
+            elif "__annotate__" in ns:
+                # TODO: Use inspect.VALUE here, and make the annotations lazily evaluated
+                own_annotations = ns["__annotate__"](1)
+            else:
+                own_annotations = {}
+            msg = "TypedDict('Name', {f0: t0, f1: t1, ...}); each t must be a type"
+            if _TAKES_MODULE:
+                own_annotations = {
+                    n: typing._type_check(tp, msg, module=tp_dict.__module__)
+                    for n, tp in own_annotations.items()
+                }
+            else:
+                own_annotations = {
+                    n: typing._type_check(tp, msg)
+                    for n, tp in own_annotations.items()
+                }
+            required_keys = set()
+            optional_keys = set()
+            readonly_keys = set()
+            mutable_keys = set()
+            extra_items_type = None
+
+            for base in bases:
+                base_dict = base.__dict__
+
+                annotations.update(base_dict.get('__annotations__', {}))
+                required_keys.update(base_dict.get('__required_keys__', ()))
+                optional_keys.update(base_dict.get('__optional_keys__', ()))
+                readonly_keys.update(base_dict.get('__readonly_keys__', ()))
+                mutable_keys.update(base_dict.get('__mutable_keys__', ()))
+                base_extra_items_type = base_dict.get('__extra_items__', None)
+                if base_extra_items_type is not None:
+                    extra_items_type = base_extra_items_type
+
+            if closed and extra_items_type is None:
+                extra_items_type = Never
+            if closed and "__extra_items__" in own_annotations:
+                annotation_type = own_annotations.pop("__extra_items__")
+                qualifiers = set(_get_typeddict_qualifiers(annotation_type))
+                if Required in qualifiers:
+                    raise TypeError(
+                        "Special key __extra_items__ does not support "
+                        "Required"
+                    )
+                if NotRequired in qualifiers:
+                    raise TypeError(
+                        "Special key __extra_items__ does not support "
+                        "NotRequired"
+                    )
+                extra_items_type = annotation_type
+
+            annotations.update(own_annotations)
+            for annotation_key, annotation_type in own_annotations.items():
+                qualifiers = set(_get_typeddict_qualifiers(annotation_type))
+
+                if Required in qualifiers:
+                    required_keys.add(annotation_key)
+                elif NotRequired in qualifiers:
+                    optional_keys.add(annotation_key)
+                elif total:
+                    required_keys.add(annotation_key)
+                else:
+                    optional_keys.add(annotation_key)
+                if ReadOnly in qualifiers:
+                    mutable_keys.discard(annotation_key)
+                    readonly_keys.add(annotation_key)
+                else:
+                    mutable_keys.add(annotation_key)
+                    readonly_keys.discard(annotation_key)
+
+            tp_dict.__annotations__ = annotations
+            tp_dict.__required_keys__ = frozenset(required_keys)
+            tp_dict.__optional_keys__ = frozenset(optional_keys)
+            tp_dict.__readonly_keys__ = frozenset(readonly_keys)
+            tp_dict.__mutable_keys__ = frozenset(mutable_keys)
+            if not hasattr(tp_dict, '__total__'):
+                tp_dict.__total__ = total
+            tp_dict.__closed__ = closed
+            tp_dict.__extra_items__ = extra_items_type
+            return tp_dict
+
+        __call__ = dict  # static method
+
+        def __subclasscheck__(cls, other):
+            # Typed dicts are only for static structural subtyping.
+            raise TypeError('TypedDict does not support instance and class checks')
+
+        __instancecheck__ = __subclasscheck__
+
+    _TypedDict = type.__new__(_TypedDictMeta, 'TypedDict', (), {})
+
+    @_ensure_subclassable(lambda bases: (_TypedDict,))
+    def TypedDict(typename, fields=_marker, /, *, total=True, closed=False, **kwargs):
+        """A simple typed namespace. At runtime it is equivalent to a plain dict.
+
+        TypedDict creates a dictionary type such that a type checker will expect all
+        instances to have a certain set of keys, where each key is
+        associated with a value of a consistent type. This expectation
+        is not checked at runtime.
+
+        Usage::
+
+            class Point2D(TypedDict):
+                x: int
+                y: int
+                label: str
+
+            a: Point2D = {'x': 1, 'y': 2, 'label': 'good'}  # OK
+            b: Point2D = {'z': 3, 'label': 'bad'}           # Fails type check
+
+            assert Point2D(x=1, y=2, label='first') == dict(x=1, y=2, label='first')
+
+        The type info can be accessed via the Point2D.__annotations__ dict, and
+        the Point2D.__required_keys__ and Point2D.__optional_keys__ frozensets.
+        TypedDict supports an additional equivalent form::
+
+            Point2D = TypedDict('Point2D', {'x': int, 'y': int, 'label': str})
+
+        By default, all keys must be present in a TypedDict. It is possible
+        to override this by specifying totality::
+
+            class Point2D(TypedDict, total=False):
+                x: int
+                y: int
+
+        This means that a Point2D TypedDict can have any of the keys omitted. A type
+        checker is only expected to support a literal False or True as the value of
+        the total argument. True is the default, and makes all items defined in the
+        class body be required.
+
+        The Required and NotRequired special forms can also be used to mark
+        individual keys as being required or not required::
+
+            class Point2D(TypedDict):
+                x: int  # the "x" key must always be present (Required is the default)
+                y: NotRequired[int]  # the "y" key can be omitted
+
+        See PEP 655 for more details on Required and NotRequired.
+        """
+        if fields is _marker or fields is None:
+            if fields is _marker:
+                deprecated_thing = "Failing to pass a value for the 'fields' parameter"
+            else:
+                deprecated_thing = "Passing `None` as the 'fields' parameter"
+
+            example = f"`{typename} = TypedDict({typename!r}, {{}})`"
+            deprecation_msg = (
+                f"{deprecated_thing} is deprecated and will be disallowed in "
+                "Python 3.15. To create a TypedDict class with 0 fields "
+                "using the functional syntax, pass an empty dictionary, e.g. "
+            ) + example + "."
+            warnings.warn(deprecation_msg, DeprecationWarning, stacklevel=2)
+            if closed is not False and closed is not True:
+                kwargs["closed"] = closed
+                closed = False
+            fields = kwargs
+        elif kwargs:
+            raise TypeError("TypedDict takes either a dict or keyword arguments,"
+                            " but not both")
+        if kwargs:
+            if sys.version_info >= (3, 13):
+                raise TypeError("TypedDict takes no keyword arguments")
+            warnings.warn(
+                "The kwargs-based syntax for TypedDict definitions is deprecated "
+                "in Python 3.11, will be removed in Python 3.13, and may not be "
+                "understood by third-party type checkers.",
+                DeprecationWarning,
+                stacklevel=2,
+            )
+
+        ns = {'__annotations__': dict(fields)}
+        module = _caller()
+        if module is not None:
+            # Setting correct module is necessary to make typed dict classes pickleable.
+            ns['__module__'] = module
+
+        td = _TypedDictMeta(typename, (), ns, total=total, closed=closed)
+        td.__orig_bases__ = (TypedDict,)
+        return td
+
+    if hasattr(typing, "_TypedDictMeta"):
+        _TYPEDDICT_TYPES = (typing._TypedDictMeta, _TypedDictMeta)
+    else:
+        _TYPEDDICT_TYPES = (_TypedDictMeta,)
+
+    def is_typeddict(tp):
+        """Check if an annotation is a TypedDict class
+
+        For example::
+            class Film(TypedDict):
+                title: str
+                year: int
+
+            is_typeddict(Film)  # => True
+            is_typeddict(Union[list, str])  # => False
+        """
+        # On 3.8, this would otherwise return True
+        if hasattr(typing, "TypedDict") and tp is typing.TypedDict:
+            return False
+        return isinstance(tp, _TYPEDDICT_TYPES)
+
+
+if hasattr(typing, "assert_type"):
+    assert_type = typing.assert_type
+
+else:
+    def assert_type(val, typ, /):
+        """Assert (to the type checker) that the value is of the given type.
+
+        When the type checker encounters a call to assert_type(), it
+        emits an error if the value is not of the specified type::
+
+            def greet(name: str) -> None:
+                assert_type(name, str)  # ok
+                assert_type(name, int)  # type checker error
+
+        At runtime this returns the first argument unchanged and otherwise
+        does nothing.
+        """
+        return val
+
+
+if hasattr(typing, "ReadOnly"):  # 3.13+
+    get_type_hints = typing.get_type_hints
+else:  # <=3.13
+    # replaces _strip_annotations()
+    def _strip_extras(t):
+        """Strips Annotated, Required and NotRequired from a given type."""
+        if isinstance(t, _AnnotatedAlias):
+            return _strip_extras(t.__origin__)
+        if hasattr(t, "__origin__") and t.__origin__ in (Required, NotRequired, ReadOnly):
+            return _strip_extras(t.__args__[0])
+        if isinstance(t, typing._GenericAlias):
+            stripped_args = tuple(_strip_extras(a) for a in t.__args__)
+            if stripped_args == t.__args__:
+                return t
+            return t.copy_with(stripped_args)
+        if hasattr(_types, "GenericAlias") and isinstance(t, _types.GenericAlias):
+            stripped_args = tuple(_strip_extras(a) for a in t.__args__)
+            if stripped_args == t.__args__:
+                return t
+            return _types.GenericAlias(t.__origin__, stripped_args)
+        if hasattr(_types, "UnionType") and isinstance(t, _types.UnionType):
+            stripped_args = tuple(_strip_extras(a) for a in t.__args__)
+            if stripped_args == t.__args__:
+                return t
+            return functools.reduce(operator.or_, stripped_args)
+
+        return t
+
+    def get_type_hints(obj, globalns=None, localns=None, include_extras=False):
+        """Return type hints for an object.
+
+        This is often the same as obj.__annotations__, but it handles
+        forward references encoded as string literals, adds Optional[t] if a
+        default value equal to None is set and recursively replaces all
+        'Annotated[T, ...]', 'Required[T]' or 'NotRequired[T]' with 'T'
+        (unless 'include_extras=True').
+
+        The argument may be a module, class, method, or function. The annotations
+        are returned as a dictionary. For classes, annotations include also
+        inherited members.
+
+        TypeError is raised if the argument is not of a type that can contain
+        annotations, and an empty dictionary is returned if no annotations are
+        present.
+
+        BEWARE -- the behavior of globalns and localns is counterintuitive
+        (unless you are familiar with how eval() and exec() work).  The
+        search order is locals first, then globals.
+
+        - If no dict arguments are passed, an attempt is made to use the
+          globals from obj (or the respective module's globals for classes),
+          and these are also used as the locals.  If the object does not appear
+          to have globals, an empty dictionary is used.
+
+        - If one dict argument is passed, it is used for both globals and
+          locals.
+
+        - If two dict arguments are passed, they specify globals and
+          locals, respectively.
+        """
+        if hasattr(typing, "Annotated"):  # 3.9+
+            hint = typing.get_type_hints(
+                obj, globalns=globalns, localns=localns, include_extras=True
+            )
+        else:  # 3.8
+            hint = typing.get_type_hints(obj, globalns=globalns, localns=localns)
+        if include_extras:
+            return hint
+        return {k: _strip_extras(t) for k, t in hint.items()}
+
+
+# Python 3.9+ has PEP 593 (Annotated)
+if hasattr(typing, 'Annotated'):
+    Annotated = typing.Annotated
+    # Not exported and not a public API, but needed for get_origin() and get_args()
+    # to work.
+    _AnnotatedAlias = typing._AnnotatedAlias
+# 3.8
+else:
+    class _AnnotatedAlias(typing._GenericAlias, _root=True):
+        """Runtime representation of an annotated type.
+
+        At its core 'Annotated[t, dec1, dec2, ...]' is an alias for the type 't'
+        with extra annotations. The alias behaves like a normal typing alias,
+        instantiating is the same as instantiating the underlying type, binding
+        it to types is also the same.
+        """
+        def __init__(self, origin, metadata):
+            if isinstance(origin, _AnnotatedAlias):
+                metadata = origin.__metadata__ + metadata
+                origin = origin.__origin__
+            super().__init__(origin, origin)
+            self.__metadata__ = metadata
+
+        def copy_with(self, params):
+            assert len(params) == 1
+            new_type = params[0]
+            return _AnnotatedAlias(new_type, self.__metadata__)
+
+        def __repr__(self):
+            return (f"typing_extensions.Annotated[{typing._type_repr(self.__origin__)}, "
+                    f"{', '.join(repr(a) for a in self.__metadata__)}]")
+
+        def __reduce__(self):
+            return operator.getitem, (
+                Annotated, (self.__origin__, *self.__metadata__)
+            )
+
+        def __eq__(self, other):
+            if not isinstance(other, _AnnotatedAlias):
+                return NotImplemented
+            if self.__origin__ != other.__origin__:
+                return False
+            return self.__metadata__ == other.__metadata__
+
+        def __hash__(self):
+            return hash((self.__origin__, self.__metadata__))
+
+    class Annotated:
+        """Add context specific metadata to a type.
+
+        Example: Annotated[int, runtime_check.Unsigned] indicates to the
+        hypothetical runtime_check module that this type is an unsigned int.
+        Every other consumer of this type can ignore this metadata and treat
+        this type as int.
+
+        The first argument to Annotated must be a valid type (and will be in
+        the __origin__ field), the remaining arguments are kept as a tuple in
+        the __extra__ field.
+
+        Details:
+
+        - It's an error to call `Annotated` with less than two arguments.
+        - Nested Annotated are flattened::
+
+            Annotated[Annotated[T, Ann1, Ann2], Ann3] == Annotated[T, Ann1, Ann2, Ann3]
+
+        - Instantiating an annotated type is equivalent to instantiating the
+        underlying type::
+
+            Annotated[C, Ann1](5) == C(5)
+
+        - Annotated can be used as a generic type alias::
+
+            Optimized = Annotated[T, runtime.Optimize()]
+            Optimized[int] == Annotated[int, runtime.Optimize()]
+
+            OptimizedList = Annotated[List[T], runtime.Optimize()]
+            OptimizedList[int] == Annotated[List[int], runtime.Optimize()]
+        """
+
+        __slots__ = ()
+
+        def __new__(cls, *args, **kwargs):
+            raise TypeError("Type Annotated cannot be instantiated.")
+
+        @typing._tp_cache
+        def __class_getitem__(cls, params):
+            if not isinstance(params, tuple) or len(params) < 2:
+                raise TypeError("Annotated[...] should be used "
+                                "with at least two arguments (a type and an "
+                                "annotation).")
+            allowed_special_forms = (ClassVar, Final)
+            if get_origin(params[0]) in allowed_special_forms:
+                origin = params[0]
+            else:
+                msg = "Annotated[t, ...]: t must be a type."
+                origin = typing._type_check(params[0], msg)
+            metadata = tuple(params[1:])
+            return _AnnotatedAlias(origin, metadata)
+
+        def __init_subclass__(cls, *args, **kwargs):
+            raise TypeError(
+                f"Cannot subclass {cls.__module__}.Annotated"
+            )
+
+# Python 3.8 has get_origin() and get_args() but those implementations aren't
+# Annotated-aware, so we can't use those. Python 3.9's versions don't support
+# ParamSpecArgs and ParamSpecKwargs, so only Python 3.10's versions will do.
+if sys.version_info[:2] >= (3, 10):
+    get_origin = typing.get_origin
+    get_args = typing.get_args
+# 3.8-3.9
+else:
+    try:
+        # 3.9+
+        from typing import _BaseGenericAlias
+    except ImportError:
+        _BaseGenericAlias = typing._GenericAlias
+    try:
+        # 3.9+
+        from typing import GenericAlias as _typing_GenericAlias
+    except ImportError:
+        _typing_GenericAlias = typing._GenericAlias
+
+    def get_origin(tp):
+        """Get the unsubscripted version of a type.
+
+        This supports generic types, Callable, Tuple, Union, Literal, Final, ClassVar
+        and Annotated. Return None for unsupported types. Examples::
+
+            get_origin(Literal[42]) is Literal
+            get_origin(int) is None
+            get_origin(ClassVar[int]) is ClassVar
+            get_origin(Generic) is Generic
+            get_origin(Generic[T]) is Generic
+            get_origin(Union[T, int]) is Union
+            get_origin(List[Tuple[T, T]][int]) == list
+            get_origin(P.args) is P
+        """
+        if isinstance(tp, _AnnotatedAlias):
+            return Annotated
+        if isinstance(tp, (typing._GenericAlias, _typing_GenericAlias, _BaseGenericAlias,
+                           ParamSpecArgs, ParamSpecKwargs)):
+            return tp.__origin__
+        if tp is typing.Generic:
+            return typing.Generic
+        return None
+
+    def get_args(tp):
+        """Get type arguments with all substitutions performed.
+
+        For unions, basic simplifications used by Union constructor are performed.
+        Examples::
+            get_args(Dict[str, int]) == (str, int)
+            get_args(int) == ()
+            get_args(Union[int, Union[T, int], str][int]) == (int, str)
+            get_args(Union[int, Tuple[T, int]][str]) == (int, Tuple[str, int])
+            get_args(Callable[[], T][int]) == ([], int)
+        """
+        if isinstance(tp, _AnnotatedAlias):
+            return (tp.__origin__, *tp.__metadata__)
+        if isinstance(tp, (typing._GenericAlias, _typing_GenericAlias)):
+            if getattr(tp, "_special", False):
+                return ()
+            res = tp.__args__
+            if get_origin(tp) is collections.abc.Callable and res[0] is not Ellipsis:
+                res = (list(res[:-1]), res[-1])
+            return res
+        return ()
+
+
+# 3.10+
+if hasattr(typing, 'TypeAlias'):
+    TypeAlias = typing.TypeAlias
+# 3.9
+elif sys.version_info[:2] >= (3, 9):
+    @_ExtensionsSpecialForm
+    def TypeAlias(self, parameters):
+        """Special marker indicating that an assignment should
+        be recognized as a proper type alias definition by type
+        checkers.
+
+        For example::
+
+            Predicate: TypeAlias = Callable[..., bool]
+
+        It's invalid when used anywhere except as in the example above.
+        """
+        raise TypeError(f"{self} is not subscriptable")
+# 3.8
+else:
+    TypeAlias = _ExtensionsSpecialForm(
+        'TypeAlias',
+        doc="""Special marker indicating that an assignment should
+        be recognized as a proper type alias definition by type
+        checkers.
+
+        For example::
+
+            Predicate: TypeAlias = Callable[..., bool]
+
+        It's invalid when used anywhere except as in the example
+        above."""
+    )
+
+
+if hasattr(typing, "NoDefault"):
+    NoDefault = typing.NoDefault
+else:
+    class NoDefaultTypeMeta(type):
+        def __setattr__(cls, attr, value):
+            # TypeError is consistent with the behavior of NoneType
+            raise TypeError(
+                f"cannot set {attr!r} attribute of immutable type {cls.__name__!r}"
+            )
+
+    class NoDefaultType(metaclass=NoDefaultTypeMeta):
+        """The type of the NoDefault singleton."""
+
+        __slots__ = ()
+
+        def __new__(cls):
+            return globals().get("NoDefault") or object.__new__(cls)
+
+        def __repr__(self):
+            return "typing_extensions.NoDefault"
+
+        def __reduce__(self):
+            return "NoDefault"
+
+    NoDefault = NoDefaultType()
+    del NoDefaultType, NoDefaultTypeMeta
+
+
+def _set_default(type_param, default):
+    type_param.has_default = lambda: default is not NoDefault
+    type_param.__default__ = default
+
+
+def _set_module(typevarlike):
+    # for pickling:
+    def_mod = _caller(depth=3)
+    if def_mod != 'typing_extensions':
+        typevarlike.__module__ = def_mod
+
+
+class _DefaultMixin:
+    """Mixin for TypeVarLike defaults."""
+
+    __slots__ = ()
+    __init__ = _set_default
+
+
+# Classes using this metaclass must provide a _backported_typevarlike ClassVar
+class _TypeVarLikeMeta(type):
+    def __instancecheck__(cls, __instance: Any) -> bool:
+        return isinstance(__instance, cls._backported_typevarlike)
+
+
+if _PEP_696_IMPLEMENTED:
+    from typing import TypeVar
+else:
+    # Add default and infer_variance parameters from PEP 696 and 695
+    class TypeVar(metaclass=_TypeVarLikeMeta):
+        """Type variable."""
+
+        _backported_typevarlike = typing.TypeVar
+
+        def __new__(cls, name, *constraints, bound=None,
+                    covariant=False, contravariant=False,
+                    default=NoDefault, infer_variance=False):
+            if hasattr(typing, "TypeAliasType"):
+                # PEP 695 implemented (3.12+), can pass infer_variance to typing.TypeVar
+                typevar = typing.TypeVar(name, *constraints, bound=bound,
+                                         covariant=covariant, contravariant=contravariant,
+                                         infer_variance=infer_variance)
+            else:
+                typevar = typing.TypeVar(name, *constraints, bound=bound,
+                                         covariant=covariant, contravariant=contravariant)
+                if infer_variance and (covariant or contravariant):
+                    raise ValueError("Variance cannot be specified with infer_variance.")
+                typevar.__infer_variance__ = infer_variance
+
+            _set_default(typevar, default)
+            _set_module(typevar)
+
+            def _tvar_prepare_subst(alias, args):
+                if (
+                    typevar.has_default()
+                    and alias.__parameters__.index(typevar) == len(args)
+                ):
+                    args += (typevar.__default__,)
+                return args
+
+            typevar.__typing_prepare_subst__ = _tvar_prepare_subst
+            return typevar
+
+        def __init_subclass__(cls) -> None:
+            raise TypeError(f"type '{__name__}.TypeVar' is not an acceptable base type")
+
+
+# Python 3.10+ has PEP 612
+if hasattr(typing, 'ParamSpecArgs'):
+    ParamSpecArgs = typing.ParamSpecArgs
+    ParamSpecKwargs = typing.ParamSpecKwargs
+# 3.8-3.9
+else:
+    class _Immutable:
+        """Mixin to indicate that object should not be copied."""
+        __slots__ = ()
+
+        def __copy__(self):
+            return self
+
+        def __deepcopy__(self, memo):
+            return self
+
+    class ParamSpecArgs(_Immutable):
+        """The args for a ParamSpec object.
+
+        Given a ParamSpec object P, P.args is an instance of ParamSpecArgs.
+
+        ParamSpecArgs objects have a reference back to their ParamSpec:
+
+        P.args.__origin__ is P
+
+        This type is meant for runtime introspection and has no special meaning to
+        static type checkers.
+        """
+        def __init__(self, origin):
+            self.__origin__ = origin
+
+        def __repr__(self):
+            return f"{self.__origin__.__name__}.args"
+
+        def __eq__(self, other):
+            if not isinstance(other, ParamSpecArgs):
+                return NotImplemented
+            return self.__origin__ == other.__origin__
+
+    class ParamSpecKwargs(_Immutable):
+        """The kwargs for a ParamSpec object.
+
+        Given a ParamSpec object P, P.kwargs is an instance of ParamSpecKwargs.
+
+        ParamSpecKwargs objects have a reference back to their ParamSpec:
+
+        P.kwargs.__origin__ is P
+
+        This type is meant for runtime introspection and has no special meaning to
+        static type checkers.
+        """
+        def __init__(self, origin):
+            self.__origin__ = origin
+
+        def __repr__(self):
+            return f"{self.__origin__.__name__}.kwargs"
+
+        def __eq__(self, other):
+            if not isinstance(other, ParamSpecKwargs):
+                return NotImplemented
+            return self.__origin__ == other.__origin__
+
+
+if _PEP_696_IMPLEMENTED:
+    from typing import ParamSpec
+
+# 3.10+
+elif hasattr(typing, 'ParamSpec'):
+
+    # Add default parameter - PEP 696
+    class ParamSpec(metaclass=_TypeVarLikeMeta):
+        """Parameter specification."""
+
+        _backported_typevarlike = typing.ParamSpec
+
+        def __new__(cls, name, *, bound=None,
+                    covariant=False, contravariant=False,
+                    infer_variance=False, default=NoDefault):
+            if hasattr(typing, "TypeAliasType"):
+                # PEP 695 implemented, can pass infer_variance to typing.TypeVar
+                paramspec = typing.ParamSpec(name, bound=bound,
+                                             covariant=covariant,
+                                             contravariant=contravariant,
+                                             infer_variance=infer_variance)
+            else:
+                paramspec = typing.ParamSpec(name, bound=bound,
+                                             covariant=covariant,
+                                             contravariant=contravariant)
+                paramspec.__infer_variance__ = infer_variance
+
+            _set_default(paramspec, default)
+            _set_module(paramspec)
+
+            def _paramspec_prepare_subst(alias, args):
+                params = alias.__parameters__
+                i = params.index(paramspec)
+                if i == len(args) and paramspec.has_default():
+                    args = [*args, paramspec.__default__]
+                if i >= len(args):
+                    raise TypeError(f"Too few arguments for {alias}")
+                # Special case where Z[[int, str, bool]] == Z[int, str, bool] in PEP 612.
+                if len(params) == 1 and not typing._is_param_expr(args[0]):
+                    assert i == 0
+                    args = (args,)
+                # Convert lists to tuples to help other libraries cache the results.
+                elif isinstance(args[i], list):
+                    args = (*args[:i], tuple(args[i]), *args[i + 1:])
+                return args
+
+            paramspec.__typing_prepare_subst__ = _paramspec_prepare_subst
+            return paramspec
+
+        def __init_subclass__(cls) -> None:
+            raise TypeError(f"type '{__name__}.ParamSpec' is not an acceptable base type")
+
+# 3.8-3.9
+else:
+
+    # Inherits from list as a workaround for Callable checks in Python < 3.9.2.
+    class ParamSpec(list, _DefaultMixin):
+        """Parameter specification variable.
+
+        Usage::
+
+           P = ParamSpec('P')
+
+        Parameter specification variables exist primarily for the benefit of static
+        type checkers.  They are used to forward the parameter types of one
+        callable to another callable, a pattern commonly found in higher order
+        functions and decorators.  They are only valid when used in ``Concatenate``,
+        or s the first argument to ``Callable``. In Python 3.10 and higher,
+        they are also supported in user-defined Generics at runtime.
+        See class Generic for more information on generic types.  An
+        example for annotating a decorator::
+
+           T = TypeVar('T')
+           P = ParamSpec('P')
+
+           def add_logging(f: Callable[P, T]) -> Callable[P, T]:
+               '''A type-safe decorator to add logging to a function.'''
+               def inner(*args: P.args, **kwargs: P.kwargs) -> T:
+                   logging.info(f'{f.__name__} was called')
+                   return f(*args, **kwargs)
+               return inner
+
+           @add_logging
+           def add_two(x: float, y: float) -> float:
+               '''Add two numbers together.'''
+               return x + y
+
+        Parameter specification variables defined with covariant=True or
+        contravariant=True can be used to declare covariant or contravariant
+        generic types.  These keyword arguments are valid, but their actual semantics
+        are yet to be decided.  See PEP 612 for details.
+
+        Parameter specification variables can be introspected. e.g.:
+
+           P.__name__ == 'T'
+           P.__bound__ == None
+           P.__covariant__ == False
+           P.__contravariant__ == False
+
+        Note that only parameter specification variables defined in global scope can
+        be pickled.
+        """
+
+        # Trick Generic __parameters__.
+        __class__ = typing.TypeVar
+
+        @property
+        def args(self):
+            return ParamSpecArgs(self)
+
+        @property
+        def kwargs(self):
+            return ParamSpecKwargs(self)
+
+        def __init__(self, name, *, bound=None, covariant=False, contravariant=False,
+                     infer_variance=False, default=NoDefault):
+            list.__init__(self, [self])
+            self.__name__ = name
+            self.__covariant__ = bool(covariant)
+            self.__contravariant__ = bool(contravariant)
+            self.__infer_variance__ = bool(infer_variance)
+            if bound:
+                self.__bound__ = typing._type_check(bound, 'Bound must be a type.')
+            else:
+                self.__bound__ = None
+            _DefaultMixin.__init__(self, default)
+
+            # for pickling:
+            def_mod = _caller()
+            if def_mod != 'typing_extensions':
+                self.__module__ = def_mod
+
+        def __repr__(self):
+            if self.__infer_variance__:
+                prefix = ''
+            elif self.__covariant__:
+                prefix = '+'
+            elif self.__contravariant__:
+                prefix = '-'
+            else:
+                prefix = '~'
+            return prefix + self.__name__
+
+        def __hash__(self):
+            return object.__hash__(self)
+
+        def __eq__(self, other):
+            return self is other
+
+        def __reduce__(self):
+            return self.__name__
+
+        # Hack to get typing._type_check to pass.
+        def __call__(self, *args, **kwargs):
+            pass
+
+
+# 3.8-3.9
+if not hasattr(typing, 'Concatenate'):
+    # Inherits from list as a workaround for Callable checks in Python < 3.9.2.
+    class _ConcatenateGenericAlias(list):
+
+        # Trick Generic into looking into this for __parameters__.
+        __class__ = typing._GenericAlias
+
+        # Flag in 3.8.
+        _special = False
+
+        def __init__(self, origin, args):
+            super().__init__(args)
+            self.__origin__ = origin
+            self.__args__ = args
+
+        def __repr__(self):
+            _type_repr = typing._type_repr
+            return (f'{_type_repr(self.__origin__)}'
+                    f'[{", ".join(_type_repr(arg) for arg in self.__args__)}]')
+
+        def __hash__(self):
+            return hash((self.__origin__, self.__args__))
+
+        # Hack to get typing._type_check to pass in Generic.
+        def __call__(self, *args, **kwargs):
+            pass
+
+        @property
+        def __parameters__(self):
+            return tuple(
+                tp for tp in self.__args__ if isinstance(tp, (typing.TypeVar, ParamSpec))
+            )
+
+
+# 3.8-3.9
+@typing._tp_cache
+def _concatenate_getitem(self, parameters):
+    if parameters == ():
+        raise TypeError("Cannot take a Concatenate of no types.")
+    if not isinstance(parameters, tuple):
+        parameters = (parameters,)
+    if not isinstance(parameters[-1], ParamSpec):
+        raise TypeError("The last parameter to Concatenate should be a "
+                        "ParamSpec variable.")
+    msg = "Concatenate[arg, ...]: each arg must be a type."
+    parameters = tuple(typing._type_check(p, msg) for p in parameters)
+    return _ConcatenateGenericAlias(self, parameters)
+
+
+# 3.10+
+if hasattr(typing, 'Concatenate'):
+    Concatenate = typing.Concatenate
+    _ConcatenateGenericAlias = typing._ConcatenateGenericAlias
+# 3.9
+elif sys.version_info[:2] >= (3, 9):
+    @_ExtensionsSpecialForm
+    def Concatenate(self, parameters):
+        """Used in conjunction with ``ParamSpec`` and ``Callable`` to represent a
+        higher order function which adds, removes or transforms parameters of a
+        callable.
+
+        For example::
+
+           Callable[Concatenate[int, P], int]
+
+        See PEP 612 for detailed information.
+        """
+        return _concatenate_getitem(self, parameters)
+# 3.8
+else:
+    class _ConcatenateForm(_ExtensionsSpecialForm, _root=True):
+        def __getitem__(self, parameters):
+            return _concatenate_getitem(self, parameters)
+
+    Concatenate = _ConcatenateForm(
+        'Concatenate',
+        doc="""Used in conjunction with ``ParamSpec`` and ``Callable`` to represent a
+        higher order function which adds, removes or transforms parameters of a
+        callable.
+
+        For example::
+
+           Callable[Concatenate[int, P], int]
+
+        See PEP 612 for detailed information.
+        """)
+
+# 3.10+
+if hasattr(typing, 'TypeGuard'):
+    TypeGuard = typing.TypeGuard
+# 3.9
+elif sys.version_info[:2] >= (3, 9):
+    @_ExtensionsSpecialForm
+    def TypeGuard(self, parameters):
+        """Special typing form used to annotate the return type of a user-defined
+        type guard function.  ``TypeGuard`` only accepts a single type argument.
+        At runtime, functions marked this way should return a boolean.
+
+        ``TypeGuard`` aims to benefit *type narrowing* -- a technique used by static
+        type checkers to determine a more precise type of an expression within a
+        program's code flow.  Usually type narrowing is done by analyzing
+        conditional code flow and applying the narrowing to a block of code.  The
+        conditional expression here is sometimes referred to as a "type guard".
+
+        Sometimes it would be convenient to use a user-defined boolean function
+        as a type guard.  Such a function should use ``TypeGuard[...]`` as its
+        return type to alert static type checkers to this intention.
+
+        Using  ``-> TypeGuard`` tells the static type checker that for a given
+        function:
+
+        1. The return value is a boolean.
+        2. If the return value is ``True``, the type of its argument
+        is the type inside ``TypeGuard``.
+
+        For example::
+
+            def is_str(val: Union[str, float]):
+                # "isinstance" type guard
+                if isinstance(val, str):
+                    # Type of ``val`` is narrowed to ``str``
+                    ...
+                else:
+                    # Else, type of ``val`` is narrowed to ``float``.
+                    ...
+
+        Strict type narrowing is not enforced -- ``TypeB`` need not be a narrower
+        form of ``TypeA`` (it can even be a wider form) and this may lead to
+        type-unsafe results.  The main reason is to allow for things like
+        narrowing ``List[object]`` to ``List[str]`` even though the latter is not
+        a subtype of the former, since ``List`` is invariant.  The responsibility of
+        writing type-safe type guards is left to the user.
+
+        ``TypeGuard`` also works with type variables.  For more information, see
+        PEP 647 (User-Defined Type Guards).
+        """
+        item = typing._type_check(parameters, f'{self} accepts only a single type.')
+        return typing._GenericAlias(self, (item,))
+# 3.8
+else:
+    class _TypeGuardForm(_ExtensionsSpecialForm, _root=True):
+        def __getitem__(self, parameters):
+            item = typing._type_check(parameters,
+                                      f'{self._name} accepts only a single type')
+            return typing._GenericAlias(self, (item,))
+
+    TypeGuard = _TypeGuardForm(
+        'TypeGuard',
+        doc="""Special typing form used to annotate the return type of a user-defined
+        type guard function.  ``TypeGuard`` only accepts a single type argument.
+        At runtime, functions marked this way should return a boolean.
+
+        ``TypeGuard`` aims to benefit *type narrowing* -- a technique used by static
+        type checkers to determine a more precise type of an expression within a
+        program's code flow.  Usually type narrowing is done by analyzing
+        conditional code flow and applying the narrowing to a block of code.  The
+        conditional expression here is sometimes referred to as a "type guard".
+
+        Sometimes it would be convenient to use a user-defined boolean function
+        as a type guard.  Such a function should use ``TypeGuard[...]`` as its
+        return type to alert static type checkers to this intention.
+
+        Using  ``-> TypeGuard`` tells the static type checker that for a given
+        function:
+
+        1. The return value is a boolean.
+        2. If the return value is ``True``, the type of its argument
+        is the type inside ``TypeGuard``.
+
+        For example::
+
+            def is_str(val: Union[str, float]):
+                # "isinstance" type guard
+                if isinstance(val, str):
+                    # Type of ``val`` is narrowed to ``str``
+                    ...
+                else:
+                    # Else, type of ``val`` is narrowed to ``float``.
+                    ...
+
+        Strict type narrowing is not enforced -- ``TypeB`` need not be a narrower
+        form of ``TypeA`` (it can even be a wider form) and this may lead to
+        type-unsafe results.  The main reason is to allow for things like
+        narrowing ``List[object]`` to ``List[str]`` even though the latter is not
+        a subtype of the former, since ``List`` is invariant.  The responsibility of
+        writing type-safe type guards is left to the user.
+
+        ``TypeGuard`` also works with type variables.  For more information, see
+        PEP 647 (User-Defined Type Guards).
+        """)
+
+# 3.13+
+if hasattr(typing, 'TypeIs'):
+    TypeIs = typing.TypeIs
+# 3.9
+elif sys.version_info[:2] >= (3, 9):
+    @_ExtensionsSpecialForm
+    def TypeIs(self, parameters):
+        """Special typing form used to annotate the return type of a user-defined
+        type narrower function.  ``TypeIs`` only accepts a single type argument.
+        At runtime, functions marked this way should return a boolean.
+
+        ``TypeIs`` aims to benefit *type narrowing* -- a technique used by static
+        type checkers to determine a more precise type of an expression within a
+        program's code flow.  Usually type narrowing is done by analyzing
+        conditional code flow and applying the narrowing to a block of code.  The
+        conditional expression here is sometimes referred to as a "type guard".
+
+        Sometimes it would be convenient to use a user-defined boolean function
+        as a type guard.  Such a function should use ``TypeIs[...]`` as its
+        return type to alert static type checkers to this intention.
+
+        Using  ``-> TypeIs`` tells the static type checker that for a given
+        function:
+
+        1. The return value is a boolean.
+        2. If the return value is ``True``, the type of its argument
+        is the intersection of the type inside ``TypeGuard`` and the argument's
+        previously known type.
+
+        For example::
+
+            def is_awaitable(val: object) -> TypeIs[Awaitable[Any]]:
+                return hasattr(val, '__await__')
+
+            def f(val: Union[int, Awaitable[int]]) -> int:
+                if is_awaitable(val):
+                    assert_type(val, Awaitable[int])
+                else:
+                    assert_type(val, int)
+
+        ``TypeIs`` also works with type variables.  For more information, see
+        PEP 742 (Narrowing types with TypeIs).
+        """
+        item = typing._type_check(parameters, f'{self} accepts only a single type.')
+        return typing._GenericAlias(self, (item,))
+# 3.8
+else:
+    class _TypeIsForm(_ExtensionsSpecialForm, _root=True):
+        def __getitem__(self, parameters):
+            item = typing._type_check(parameters,
+                                      f'{self._name} accepts only a single type')
+            return typing._GenericAlias(self, (item,))
+
+    TypeIs = _TypeIsForm(
+        'TypeIs',
+        doc="""Special typing form used to annotate the return type of a user-defined
+        type narrower function.  ``TypeIs`` only accepts a single type argument.
+        At runtime, functions marked this way should return a boolean.
+
+        ``TypeIs`` aims to benefit *type narrowing* -- a technique used by static
+        type checkers to determine a more precise type of an expression within a
+        program's code flow.  Usually type narrowing is done by analyzing
+        conditional code flow and applying the narrowing to a block of code.  The
+        conditional expression here is sometimes referred to as a "type guard".
+
+        Sometimes it would be convenient to use a user-defined boolean function
+        as a type guard.  Such a function should use ``TypeIs[...]`` as its
+        return type to alert static type checkers to this intention.
+
+        Using  ``-> TypeIs`` tells the static type checker that for a given
+        function:
+
+        1. The return value is a boolean.
+        2. If the return value is ``True``, the type of its argument
+        is the intersection of the type inside ``TypeGuard`` and the argument's
+        previously known type.
+
+        For example::
+
+            def is_awaitable(val: object) -> TypeIs[Awaitable[Any]]:
+                return hasattr(val, '__await__')
+
+            def f(val: Union[int, Awaitable[int]]) -> int:
+                if is_awaitable(val):
+                    assert_type(val, Awaitable[int])
+                else:
+                    assert_type(val, int)
+
+        ``TypeIs`` also works with type variables.  For more information, see
+        PEP 742 (Narrowing types with TypeIs).
+        """)
+
+
+# Vendored from cpython typing._SpecialFrom
+class _SpecialForm(typing._Final, _root=True):
+    __slots__ = ('_name', '__doc__', '_getitem')
+
+    def __init__(self, getitem):
+        self._getitem = getitem
+        self._name = getitem.__name__
+        self.__doc__ = getitem.__doc__
+
+    def __getattr__(self, item):
+        if item in {'__name__', '__qualname__'}:
+            return self._name
+
+        raise AttributeError(item)
+
+    def __mro_entries__(self, bases):
+        raise TypeError(f"Cannot subclass {self!r}")
+
+    def __repr__(self):
+        return f'typing_extensions.{self._name}'
+
+    def __reduce__(self):
+        return self._name
+
+    def __call__(self, *args, **kwds):
+        raise TypeError(f"Cannot instantiate {self!r}")
+
+    def __or__(self, other):
+        return typing.Union[self, other]
+
+    def __ror__(self, other):
+        return typing.Union[other, self]
+
+    def __instancecheck__(self, obj):
+        raise TypeError(f"{self} cannot be used with isinstance()")
+
+    def __subclasscheck__(self, cls):
+        raise TypeError(f"{self} cannot be used with issubclass()")
+
+    @typing._tp_cache
+    def __getitem__(self, parameters):
+        return self._getitem(self, parameters)
+
+
+if hasattr(typing, "LiteralString"):  # 3.11+
+    LiteralString = typing.LiteralString
+else:
+    @_SpecialForm
+    def LiteralString(self, params):
+        """Represents an arbitrary literal string.
+
+        Example::
+
+          from metaflow._vendor.typing_extensions import LiteralString
+
+          def query(sql: LiteralString) -> ...:
+              ...
+
+          query("SELECT * FROM table")  # ok
+          query(f"SELECT * FROM {input()}")  # not ok
+
+        See PEP 675 for details.
+
+        """
+        raise TypeError(f"{self} is not subscriptable")
+
+
+if hasattr(typing, "Self"):  # 3.11+
+    Self = typing.Self
+else:
+    @_SpecialForm
+    def Self(self, params):
+        """Used to spell the type of "self" in classes.
+
+        Example::
+
+          from typing import Self
+
+          class ReturnsSelf:
+              def parse(self, data: bytes) -> Self:
+                  ...
+                  return self
+
+        """
+
+        raise TypeError(f"{self} is not subscriptable")
+
+
+if hasattr(typing, "Never"):  # 3.11+
+    Never = typing.Never
+else:
+    @_SpecialForm
+    def Never(self, params):
+        """The bottom type, a type that has no members.
+
+        This can be used to define a function that should never be
+        called, or a function that never returns::
+
+            from metaflow._vendor.typing_extensions import Never
+
+            def never_call_me(arg: Never) -> None:
+                pass
+
+            def int_or_str(arg: int | str) -> None:
+                never_call_me(arg)  # type checker error
+                match arg:
+                    case int():
+                        print("It's an int")
+                    case str():
+                        print("It's a str")
+                    case _:
+                        never_call_me(arg)  # ok, arg is of type Never
+
+        """
+
+        raise TypeError(f"{self} is not subscriptable")
+
+
+if hasattr(typing, 'Required'):  # 3.11+
+    Required = typing.Required
+    NotRequired = typing.NotRequired
+elif sys.version_info[:2] >= (3, 9):  # 3.9-3.10
+    @_ExtensionsSpecialForm
+    def Required(self, parameters):
+        """A special typing construct to mark a key of a total=False TypedDict
+        as required. For example:
+
+            class Movie(TypedDict, total=False):
+                title: Required[str]
+                year: int
+
+            m = Movie(
+                title='The Matrix',  # typechecker error if key is omitted
+                year=1999,
+            )
+
+        There is no runtime checking that a required key is actually provided
+        when instantiating a related TypedDict.
+        """
+        item = typing._type_check(parameters, f'{self._name} accepts only a single type.')
+        return typing._GenericAlias(self, (item,))
+
+    @_ExtensionsSpecialForm
+    def NotRequired(self, parameters):
+        """A special typing construct to mark a key of a TypedDict as
+        potentially missing. For example:
+
+            class Movie(TypedDict):
+                title: str
+                year: NotRequired[int]
+
+            m = Movie(
+                title='The Matrix',  # typechecker error if key is omitted
+                year=1999,
+            )
+        """
+        item = typing._type_check(parameters, f'{self._name} accepts only a single type.')
+        return typing._GenericAlias(self, (item,))
+
+else:  # 3.8
+    class _RequiredForm(_ExtensionsSpecialForm, _root=True):
+        def __getitem__(self, parameters):
+            item = typing._type_check(parameters,
+                                      f'{self._name} accepts only a single type.')
+            return typing._GenericAlias(self, (item,))
+
+    Required = _RequiredForm(
+        'Required',
+        doc="""A special typing construct to mark a key of a total=False TypedDict
+        as required. For example:
+
+            class Movie(TypedDict, total=False):
+                title: Required[str]
+                year: int
+
+            m = Movie(
+                title='The Matrix',  # typechecker error if key is omitted
+                year=1999,
+            )
+
+        There is no runtime checking that a required key is actually provided
+        when instantiating a related TypedDict.
+        """)
+    NotRequired = _RequiredForm(
+        'NotRequired',
+        doc="""A special typing construct to mark a key of a TypedDict as
+        potentially missing. For example:
+
+            class Movie(TypedDict):
+                title: str
+                year: NotRequired[int]
+
+            m = Movie(
+                title='The Matrix',  # typechecker error if key is omitted
+                year=1999,
+            )
+        """)
+
+
+if hasattr(typing, 'ReadOnly'):
+    ReadOnly = typing.ReadOnly
+elif sys.version_info[:2] >= (3, 9):  # 3.9-3.12
+    @_ExtensionsSpecialForm
+    def ReadOnly(self, parameters):
+        """A special typing construct to mark an item of a TypedDict as read-only.
+
+        For example:
+
+            class Movie(TypedDict):
+                title: ReadOnly[str]
+                year: int
+
+            def mutate_movie(m: Movie) -> None:
+                m["year"] = 1992  # allowed
+                m["title"] = "The Matrix"  # typechecker error
+
+        There is no runtime checking for this property.
+        """
+        item = typing._type_check(parameters, f'{self._name} accepts only a single type.')
+        return typing._GenericAlias(self, (item,))
+
+else:  # 3.8
+    class _ReadOnlyForm(_ExtensionsSpecialForm, _root=True):
+        def __getitem__(self, parameters):
+            item = typing._type_check(parameters,
+                                      f'{self._name} accepts only a single type.')
+            return typing._GenericAlias(self, (item,))
+
+    ReadOnly = _ReadOnlyForm(
+        'ReadOnly',
+        doc="""A special typing construct to mark a key of a TypedDict as read-only.
+
+        For example:
+
+            class Movie(TypedDict):
+                title: ReadOnly[str]
+                year: int
+
+            def mutate_movie(m: Movie) -> None:
+                m["year"] = 1992  # allowed
+                m["title"] = "The Matrix"  # typechecker error
+
+        There is no runtime checking for this propery.
+        """)
+
+
+_UNPACK_DOC = """\
+Type unpack operator.
+
+The type unpack operator takes the child types from some container type,
+such as `tuple[int, str]` or a `TypeVarTuple`, and 'pulls them out'. For
+example:
+
+  # For some generic class `Foo`:
+  Foo[Unpack[tuple[int, str]]]  # Equivalent to Foo[int, str]
+
+  Ts = TypeVarTuple('Ts')
+  # Specifies that `Bar` is generic in an arbitrary number of types.
+  # (Think of `Ts` as a tuple of an arbitrary number of individual
+  #  `TypeVar`s, which the `Unpack` is 'pulling out' directly into the
+  #  `Generic[]`.)
+  class Bar(Generic[Unpack[Ts]]): ...
+  Bar[int]  # Valid
+  Bar[int, str]  # Also valid
+
+From Python 3.11, this can also be done using the `*` operator:
+
+    Foo[*tuple[int, str]]
+    class Bar(Generic[*Ts]): ...
+
+The operator can also be used along with a `TypedDict` to annotate
+`**kwargs` in a function signature. For instance:
+
+  class Movie(TypedDict):
+    name: str
+    year: int
+
+  # This function expects two keyword arguments - *name* of type `str` and
+  # *year* of type `int`.
+  def foo(**kwargs: Unpack[Movie]): ...
+
+Note that there is only some runtime checking of this operator. Not
+everything the runtime allows may be accepted by static type checkers.
+
+For more information, see PEP 646 and PEP 692.
+"""
+
+
+if sys.version_info >= (3, 12):  # PEP 692 changed the repr of Unpack[]
+    Unpack = typing.Unpack
+
+    def _is_unpack(obj):
+        return get_origin(obj) is Unpack
+
+elif sys.version_info[:2] >= (3, 9):  # 3.9+
+    class _UnpackSpecialForm(_ExtensionsSpecialForm, _root=True):
+        def __init__(self, getitem):
+            super().__init__(getitem)
+            self.__doc__ = _UNPACK_DOC
+
+    class _UnpackAlias(typing._GenericAlias, _root=True):
+        __class__ = typing.TypeVar
+
+        @property
+        def __typing_unpacked_tuple_args__(self):
+            assert self.__origin__ is Unpack
+            assert len(self.__args__) == 1
+            arg, = self.__args__
+            if isinstance(arg, (typing._GenericAlias, _types.GenericAlias)):
+                if arg.__origin__ is not tuple:
+                    raise TypeError("Unpack[...] must be used with a tuple type")
+                return arg.__args__
+            return None
+
+    @_UnpackSpecialForm
+    def Unpack(self, parameters):
+        item = typing._type_check(parameters, f'{self._name} accepts only a single type.')
+        return _UnpackAlias(self, (item,))
+
+    def _is_unpack(obj):
+        return isinstance(obj, _UnpackAlias)
+
+else:  # 3.8
+    class _UnpackAlias(typing._GenericAlias, _root=True):
+        __class__ = typing.TypeVar
+
+    class _UnpackForm(_ExtensionsSpecialForm, _root=True):
+        def __getitem__(self, parameters):
+            item = typing._type_check(parameters,
+                                      f'{self._name} accepts only a single type.')
+            return _UnpackAlias(self, (item,))
+
+    Unpack = _UnpackForm('Unpack', doc=_UNPACK_DOC)
+
+    def _is_unpack(obj):
+        return isinstance(obj, _UnpackAlias)
+
+
+if _PEP_696_IMPLEMENTED:
+    from typing import TypeVarTuple
+
+elif hasattr(typing, "TypeVarTuple"):  # 3.11+
+
+    def _unpack_args(*args):
+        newargs = []
+        for arg in args:
+            subargs = getattr(arg, '__typing_unpacked_tuple_args__', None)
+            if subargs is not None and not (subargs and subargs[-1] is ...):
+                newargs.extend(subargs)
+            else:
+                newargs.append(arg)
+        return newargs
+
+    # Add default parameter - PEP 696
+    class TypeVarTuple(metaclass=_TypeVarLikeMeta):
+        """Type variable tuple."""
+
+        _backported_typevarlike = typing.TypeVarTuple
+
+        def __new__(cls, name, *, default=NoDefault):
+            tvt = typing.TypeVarTuple(name)
+            _set_default(tvt, default)
+            _set_module(tvt)
+
+            def _typevartuple_prepare_subst(alias, args):
+                params = alias.__parameters__
+                typevartuple_index = params.index(tvt)
+                for param in params[typevartuple_index + 1:]:
+                    if isinstance(param, TypeVarTuple):
+                        raise TypeError(
+                            f"More than one TypeVarTuple parameter in {alias}"
+                        )
+
+                alen = len(args)
+                plen = len(params)
+                left = typevartuple_index
+                right = plen - typevartuple_index - 1
+                var_tuple_index = None
+                fillarg = None
+                for k, arg in enumerate(args):
+                    if not isinstance(arg, type):
+                        subargs = getattr(arg, '__typing_unpacked_tuple_args__', None)
+                        if subargs and len(subargs) == 2 and subargs[-1] is ...:
+                            if var_tuple_index is not None:
+                                raise TypeError(
+                                    "More than one unpacked "
+                                    "arbitrary-length tuple argument"
+                                )
+                            var_tuple_index = k
+                            fillarg = subargs[0]
+                if var_tuple_index is not None:
+                    left = min(left, var_tuple_index)
+                    right = min(right, alen - var_tuple_index - 1)
+                elif left + right > alen:
+                    raise TypeError(f"Too few arguments for {alias};"
+                                    f" actual {alen}, expected at least {plen - 1}")
+                if left == alen - right and tvt.has_default():
+                    replacement = _unpack_args(tvt.__default__)
+                else:
+                    replacement = args[left: alen - right]
+
+                return (
+                    *args[:left],
+                    *([fillarg] * (typevartuple_index - left)),
+                    replacement,
+                    *([fillarg] * (plen - right - left - typevartuple_index - 1)),
+                    *args[alen - right:],
+                )
+
+            tvt.__typing_prepare_subst__ = _typevartuple_prepare_subst
+            return tvt
+
+        def __init_subclass__(self, *args, **kwds):
+            raise TypeError("Cannot subclass special typing classes")
+
+else:  # <=3.10
+    class TypeVarTuple(_DefaultMixin):
+        """Type variable tuple.
+
+        Usage::
+
+            Ts = TypeVarTuple('Ts')
+
+        In the same way that a normal type variable is a stand-in for a single
+        type such as ``int``, a type variable *tuple* is a stand-in for a *tuple*
+        type such as ``Tuple[int, str]``.
+
+        Type variable tuples can be used in ``Generic`` declarations.
+        Consider the following example::
+
+            class Array(Generic[*Ts]): ...
+
+        The ``Ts`` type variable tuple here behaves like ``tuple[T1, T2]``,
+        where ``T1`` and ``T2`` are type variables. To use these type variables
+        as type parameters of ``Array``, we must *unpack* the type variable tuple using
+        the star operator: ``*Ts``. The signature of ``Array`` then behaves
+        as if we had simply written ``class Array(Generic[T1, T2]): ...``.
+        In contrast to ``Generic[T1, T2]``, however, ``Generic[*Shape]`` allows
+        us to parameterise the class with an *arbitrary* number of type parameters.
+
+        Type variable tuples can be used anywhere a normal ``TypeVar`` can.
+        This includes class definitions, as shown above, as well as function
+        signatures and variable annotations::
+
+            class Array(Generic[*Ts]):
+
+                def __init__(self, shape: Tuple[*Ts]):
+                    self._shape: Tuple[*Ts] = shape
+
+                def get_shape(self) -> Tuple[*Ts]:
+                    return self._shape
+
+            shape = (Height(480), Width(640))
+            x: Array[Height, Width] = Array(shape)
+            y = abs(x)  # Inferred type is Array[Height, Width]
+            z = x + x   #        ...    is Array[Height, Width]
+            x.get_shape()  #     ...    is tuple[Height, Width]
+
+        """
+
+        # Trick Generic __parameters__.
+        __class__ = typing.TypeVar
+
+        def __iter__(self):
+            yield self.__unpacked__
+
+        def __init__(self, name, *, default=NoDefault):
+            self.__name__ = name
+            _DefaultMixin.__init__(self, default)
+
+            # for pickling:
+            def_mod = _caller()
+            if def_mod != 'typing_extensions':
+                self.__module__ = def_mod
+
+            self.__unpacked__ = Unpack[self]
+
+        def __repr__(self):
+            return self.__name__
+
+        def __hash__(self):
+            return object.__hash__(self)
+
+        def __eq__(self, other):
+            return self is other
+
+        def __reduce__(self):
+            return self.__name__
+
+        def __init_subclass__(self, *args, **kwds):
+            if '_root' not in kwds:
+                raise TypeError("Cannot subclass special typing classes")
+
+
+if hasattr(typing, "reveal_type"):  # 3.11+
+    reveal_type = typing.reveal_type
+else:  # <=3.10
+    def reveal_type(obj: T, /) -> T:
+        """Reveal the inferred type of a variable.
+
+        When a static type checker encounters a call to ``reveal_type()``,
+        it will emit the inferred type of the argument::
+
+            x: int = 1
+            reveal_type(x)
+
+        Running a static type checker (e.g., ``mypy``) on this example
+        will produce output similar to 'Revealed type is "builtins.int"'.
+
+        At runtime, the function prints the runtime type of the
+        argument and returns it unchanged.
+
+        """
+        print(f"Runtime type is {type(obj).__name__!r}", file=sys.stderr)
+        return obj
+
+
+if hasattr(typing, "_ASSERT_NEVER_REPR_MAX_LENGTH"):  # 3.11+
+    _ASSERT_NEVER_REPR_MAX_LENGTH = typing._ASSERT_NEVER_REPR_MAX_LENGTH
+else:  # <=3.10
+    _ASSERT_NEVER_REPR_MAX_LENGTH = 100
+
+
+if hasattr(typing, "assert_never"):  # 3.11+
+    assert_never = typing.assert_never
+else:  # <=3.10
+    def assert_never(arg: Never, /) -> Never:
+        """Assert to the type checker that a line of code is unreachable.
+
+        Example::
+
+            def int_or_str(arg: int | str) -> None:
+                match arg:
+                    case int():
+                        print("It's an int")
+                    case str():
+                        print("It's a str")
+                    case _:
+                        assert_never(arg)
+
+        If a type checker finds that a call to assert_never() is
+        reachable, it will emit an error.
+
+        At runtime, this throws an exception when called.
+
+        """
+        value = repr(arg)
+        if len(value) > _ASSERT_NEVER_REPR_MAX_LENGTH:
+            value = value[:_ASSERT_NEVER_REPR_MAX_LENGTH] + '...'
+        raise AssertionError(f"Expected code to be unreachable, but got: {value}")
+
+
+if sys.version_info >= (3, 12):  # 3.12+
+    # dataclass_transform exists in 3.11 but lacks the frozen_default parameter
+    dataclass_transform = typing.dataclass_transform
+else:  # <=3.11
+    def dataclass_transform(
+        *,
+        eq_default: bool = True,
+        order_default: bool = False,
+        kw_only_default: bool = False,
+        frozen_default: bool = False,
+        field_specifiers: typing.Tuple[
+            typing.Union[typing.Type[typing.Any], typing.Callable[..., typing.Any]],
+            ...
+        ] = (),
+        **kwargs: typing.Any,
+    ) -> typing.Callable[[T], T]:
+        """Decorator that marks a function, class, or metaclass as providing
+        dataclass-like behavior.
+
+        Example:
+
+            from metaflow._vendor.typing_extensions import dataclass_transform
+
+            _T = TypeVar("_T")
+
+            # Used on a decorator function
+            @dataclass_transform()
+            def create_model(cls: type[_T]) -> type[_T]:
+                ...
+                return cls
+
+            @create_model
+            class CustomerModel:
+                id: int
+                name: str
+
+            # Used on a base class
+            @dataclass_transform()
+            class ModelBase: ...
+
+            class CustomerModel(ModelBase):
+                id: int
+                name: str
+
+            # Used on a metaclass
+            @dataclass_transform()
+            class ModelMeta(type): ...
+
+            class ModelBase(metaclass=ModelMeta): ...
+
+            class CustomerModel(ModelBase):
+                id: int
+                name: str
+
+        Each of the ``CustomerModel`` classes defined in this example will now
+        behave similarly to a dataclass created with the ``@dataclasses.dataclass``
+        decorator. For example, the type checker will synthesize an ``__init__``
+        method.
+
+        The arguments to this decorator can be used to customize this behavior:
+        - ``eq_default`` indicates whether the ``eq`` parameter is assumed to be
+          True or False if it is omitted by the caller.
+        - ``order_default`` indicates whether the ``order`` parameter is
+          assumed to be True or False if it is omitted by the caller.
+        - ``kw_only_default`` indicates whether the ``kw_only`` parameter is
+          assumed to be True or False if it is omitted by the caller.
+        - ``frozen_default`` indicates whether the ``frozen`` parameter is
+          assumed to be True or False if it is omitted by the caller.
+        - ``field_specifiers`` specifies a static list of supported classes
+          or functions that describe fields, similar to ``dataclasses.field()``.
+
+        At runtime, this decorator records its arguments in the
+        ``__dataclass_transform__`` attribute on the decorated object.
+
+        See PEP 681 for details.
+
+        """
+        def decorator(cls_or_fn):
+            cls_or_fn.__dataclass_transform__ = {
+                "eq_default": eq_default,
+                "order_default": order_default,
+                "kw_only_default": kw_only_default,
+                "frozen_default": frozen_default,
+                "field_specifiers": field_specifiers,
+                "kwargs": kwargs,
+            }
+            return cls_or_fn
+        return decorator
+
+
+if hasattr(typing, "override"):  # 3.12+
+    override = typing.override
+else:  # <=3.11
+    _F = typing.TypeVar("_F", bound=typing.Callable[..., typing.Any])
+
+    def override(arg: _F, /) -> _F:
+        """Indicate that a method is intended to override a method in a base class.
+
+        Usage:
+
+            class Base:
+                def method(self) -> None:
+                    pass
+
+            class Child(Base):
+                @override
+                def method(self) -> None:
+                    super().method()
+
+        When this decorator is applied to a method, the type checker will
+        validate that it overrides a method with the same name on a base class.
+        This helps prevent bugs that may occur when a base class is changed
+        without an equivalent change to a child class.
+
+        There is no runtime checking of these properties. The decorator
+        sets the ``__override__`` attribute to ``True`` on the decorated object
+        to allow runtime introspection.
+
+        See PEP 698 for details.
+
+        """
+        try:
+            arg.__override__ = True
+        except (AttributeError, TypeError):
+            # Skip the attribute silently if it is not writable.
+            # AttributeError happens if the object has __slots__ or a
+            # read-only property, TypeError if it's a builtin class.
+            pass
+        return arg
+
+
+if hasattr(warnings, "deprecated"):
+    deprecated = warnings.deprecated
+else:
+    _T = typing.TypeVar("_T")
+
+    class deprecated:
+        """Indicate that a class, function or overload is deprecated.
+
+        When this decorator is applied to an object, the type checker
+        will generate a diagnostic on usage of the deprecated object.
+
+        Usage:
+
+            @deprecated("Use B instead")
+            class A:
+                pass
+
+            @deprecated("Use g instead")
+            def f():
+                pass
+
+            @overload
+            @deprecated("int support is deprecated")
+            def g(x: int) -> int: ...
+            @overload
+            def g(x: str) -> int: ...
+
+        The warning specified by *category* will be emitted at runtime
+        on use of deprecated objects. For functions, that happens on calls;
+        for classes, on instantiation and on creation of subclasses.
+        If the *category* is ``None``, no warning is emitted at runtime.
+        The *stacklevel* determines where the
+        warning is emitted. If it is ``1`` (the default), the warning
+        is emitted at the direct caller of the deprecated object; if it
+        is higher, it is emitted further up the stack.
+        Static type checker behavior is not affected by the *category*
+        and *stacklevel* arguments.
+
+        The deprecation message passed to the decorator is saved in the
+        ``__deprecated__`` attribute on the decorated object.
+        If applied to an overload, the decorator
+        must be after the ``@overload`` decorator for the attribute to
+        exist on the overload as returned by ``get_overloads()``.
+
+        See PEP 702 for details.
+
+        """
+        def __init__(
+            self,
+            message: str,
+            /,
+            *,
+            category: typing.Optional[typing.Type[Warning]] = DeprecationWarning,
+            stacklevel: int = 1,
+        ) -> None:
+            if not isinstance(message, str):
+                raise TypeError(
+                    "Expected an object of type str for 'message', not "
+                    f"{type(message).__name__!r}"
+                )
+            self.message = message
+            self.category = category
+            self.stacklevel = stacklevel
+
+        def __call__(self, arg: _T, /) -> _T:
+            # Make sure the inner functions created below don't
+            # retain a reference to self.
+            msg = self.message
+            category = self.category
+            stacklevel = self.stacklevel
+            if category is None:
+                arg.__deprecated__ = msg
+                return arg
+            elif isinstance(arg, type):
+                import functools
+                from types import MethodType
+
+                original_new = arg.__new__
+
+                @functools.wraps(original_new)
+                def __new__(cls, *args, **kwargs):
+                    if cls is arg:
+                        warnings.warn(msg, category=category, stacklevel=stacklevel + 1)
+                    if original_new is not object.__new__:
+                        return original_new(cls, *args, **kwargs)
+                    # Mirrors a similar check in object.__new__.
+                    elif cls.__init__ is object.__init__ and (args or kwargs):
+                        raise TypeError(f"{cls.__name__}() takes no arguments")
+                    else:
+                        return original_new(cls)
+
+                arg.__new__ = staticmethod(__new__)
+
+                original_init_subclass = arg.__init_subclass__
+                # We need slightly different behavior if __init_subclass__
+                # is a bound method (likely if it was implemented in Python)
+                if isinstance(original_init_subclass, MethodType):
+                    original_init_subclass = original_init_subclass.__func__
+
+                    @functools.wraps(original_init_subclass)
+                    def __init_subclass__(*args, **kwargs):
+                        warnings.warn(msg, category=category, stacklevel=stacklevel + 1)
+                        return original_init_subclass(*args, **kwargs)
+
+                    arg.__init_subclass__ = classmethod(__init_subclass__)
+                # Or otherwise, which likely means it's a builtin such as
+                # object's implementation of __init_subclass__.
+                else:
+                    @functools.wraps(original_init_subclass)
+                    def __init_subclass__(*args, **kwargs):
+                        warnings.warn(msg, category=category, stacklevel=stacklevel + 1)
+                        return original_init_subclass(*args, **kwargs)
+
+                    arg.__init_subclass__ = __init_subclass__
+
+                arg.__deprecated__ = __new__.__deprecated__ = msg
+                __init_subclass__.__deprecated__ = msg
+                return arg
+            elif callable(arg):
+                import functools
+
+                @functools.wraps(arg)
+                def wrapper(*args, **kwargs):
+                    warnings.warn(msg, category=category, stacklevel=stacklevel + 1)
+                    return arg(*args, **kwargs)
+
+                arg.__deprecated__ = wrapper.__deprecated__ = msg
+                return wrapper
+            else:
+                raise TypeError(
+                    "@deprecated decorator with non-None category must be applied to "
+                    f"a class or callable, not {arg!r}"
+                )
+
+
+# We have to do some monkey patching to deal with the dual nature of
+# Unpack/TypeVarTuple:
+# - We want Unpack to be a kind of TypeVar so it gets accepted in
+#   Generic[Unpack[Ts]]
+# - We want it to *not* be treated as a TypeVar for the purposes of
+#   counting generic parameters, so that when we subscript a generic,
+#   the runtime doesn't try to substitute the Unpack with the subscripted type.
+if not hasattr(typing, "TypeVarTuple"):
+    def _check_generic(cls, parameters, elen=_marker):
+        """Check correct count for parameters of a generic cls (internal helper).
+
+        This gives a nice error message in case of count mismatch.
+        """
+        if not elen:
+            raise TypeError(f"{cls} is not a generic class")
+        if elen is _marker:
+            if not hasattr(cls, "__parameters__") or not cls.__parameters__:
+                raise TypeError(f"{cls} is not a generic class")
+            elen = len(cls.__parameters__)
+        alen = len(parameters)
+        if alen != elen:
+            expect_val = elen
+            if hasattr(cls, "__parameters__"):
+                parameters = [p for p in cls.__parameters__ if not _is_unpack(p)]
+                num_tv_tuples = sum(isinstance(p, TypeVarTuple) for p in parameters)
+                if (num_tv_tuples > 0) and (alen >= elen - num_tv_tuples):
+                    return
+
+                # deal with TypeVarLike defaults
+                # required TypeVarLikes cannot appear after a defaulted one.
+                if alen < elen:
+                    # since we validate TypeVarLike default in _collect_type_vars
+                    # or _collect_parameters we can safely check parameters[alen]
+                    if (
+                        getattr(parameters[alen], '__default__', NoDefault)
+                        is not NoDefault
+                    ):
+                        return
+
+                    num_default_tv = sum(getattr(p, '__default__', NoDefault)
+                                         is not NoDefault for p in parameters)
+
+                    elen -= num_default_tv
+
+                    expect_val = f"at least {elen}"
+
+            things = "arguments" if sys.version_info >= (3, 10) else "parameters"
+            raise TypeError(f"Too {'many' if alen > elen else 'few'} {things}"
+                            f" for {cls}; actual {alen}, expected {expect_val}")
+else:
+    # Python 3.11+
+
+    def _check_generic(cls, parameters, elen):
+        """Check correct count for parameters of a generic cls (internal helper).
+
+        This gives a nice error message in case of count mismatch.
+        """
+        if not elen:
+            raise TypeError(f"{cls} is not a generic class")
+        alen = len(parameters)
+        if alen != elen:
+            expect_val = elen
+            if hasattr(cls, "__parameters__"):
+                parameters = [p for p in cls.__parameters__ if not _is_unpack(p)]
+
+                # deal with TypeVarLike defaults
+                # required TypeVarLikes cannot appear after a defaulted one.
+                if alen < elen:
+                    # since we validate TypeVarLike default in _collect_type_vars
+                    # or _collect_parameters we can safely check parameters[alen]
+                    if (
+                        getattr(parameters[alen], '__default__', NoDefault)
+                        is not NoDefault
+                    ):
+                        return
+
+                    num_default_tv = sum(getattr(p, '__default__', NoDefault)
+                                         is not NoDefault for p in parameters)
+
+                    elen -= num_default_tv
+
+                    expect_val = f"at least {elen}"
+
+            raise TypeError(f"Too {'many' if alen > elen else 'few'} arguments"
+                            f" for {cls}; actual {alen}, expected {expect_val}")
+
+if not _PEP_696_IMPLEMENTED:
+    typing._check_generic = _check_generic
+
+
+def _has_generic_or_protocol_as_origin() -> bool:
+    try:
+        frame = sys._getframe(2)
+    # - Catch AttributeError: not all Python implementations have sys._getframe()
+    # - Catch ValueError: maybe we're called from an unexpected module
+    #   and the call stack isn't deep enough
+    except (AttributeError, ValueError):
+        return False  # err on the side of leniency
+    else:
+        # If we somehow get invoked from outside typing.py,
+        # also err on the side of leniency
+        if frame.f_globals.get("__name__") != "typing":
+            return False
+        origin = frame.f_locals.get("origin")
+        # Cannot use "in" because origin may be an object with a buggy __eq__ that
+        # throws an error.
+        return origin is typing.Generic or origin is Protocol or origin is typing.Protocol
+
+
+_TYPEVARTUPLE_TYPES = {TypeVarTuple, getattr(typing, "TypeVarTuple", None)}
+
+
+def _is_unpacked_typevartuple(x) -> bool:
+    if get_origin(x) is not Unpack:
+        return False
+    args = get_args(x)
+    return (
+        bool(args)
+        and len(args) == 1
+        and type(args[0]) in _TYPEVARTUPLE_TYPES
+    )
+
+
+# Python 3.11+ _collect_type_vars was renamed to _collect_parameters
+if hasattr(typing, '_collect_type_vars'):
+    def _collect_type_vars(types, typevar_types=None):
+        """Collect all type variable contained in types in order of
+        first appearance (lexicographic order). For example::
+
+            _collect_type_vars((T, List[S, T])) == (T, S)
+        """
+        if typevar_types is None:
+            typevar_types = typing.TypeVar
+        tvars = []
+
+        # A required TypeVarLike cannot appear after a TypeVarLike with a default
+        # if it was a direct call to `Generic[]` or `Protocol[]`
+        enforce_default_ordering = _has_generic_or_protocol_as_origin()
+        default_encountered = False
+
+        # Also, a TypeVarLike with a default cannot appear after a TypeVarTuple
+        type_var_tuple_encountered = False
+
+        for t in types:
+            if _is_unpacked_typevartuple(t):
+                type_var_tuple_encountered = True
+            elif isinstance(t, typevar_types) and t not in tvars:
+                if enforce_default_ordering:
+                    has_default = getattr(t, '__default__', NoDefault) is not NoDefault
+                    if has_default:
+                        if type_var_tuple_encountered:
+                            raise TypeError('Type parameter with a default'
+                                            ' follows TypeVarTuple')
+                        default_encountered = True
+                    elif default_encountered:
+                        raise TypeError(f'Type parameter {t!r} without a default'
+                                        ' follows type parameter with a default')
+
+                tvars.append(t)
+            if _should_collect_from_parameters(t):
+                tvars.extend([t for t in t.__parameters__ if t not in tvars])
+        return tuple(tvars)
+
+    typing._collect_type_vars = _collect_type_vars
+else:
+    def _collect_parameters(args):
+        """Collect all type variables and parameter specifications in args
+        in order of first appearance (lexicographic order).
+
+        For example::
+
+            assert _collect_parameters((T, Callable[P, T])) == (T, P)
+        """
+        parameters = []
+
+        # A required TypeVarLike cannot appear after a TypeVarLike with default
+        # if it was a direct call to `Generic[]` or `Protocol[]`
+        enforce_default_ordering = _has_generic_or_protocol_as_origin()
+        default_encountered = False
+
+        # Also, a TypeVarLike with a default cannot appear after a TypeVarTuple
+        type_var_tuple_encountered = False
+
+        for t in args:
+            if isinstance(t, type):
+                # We don't want __parameters__ descriptor of a bare Python class.
+                pass
+            elif isinstance(t, tuple):
+                # `t` might be a tuple, when `ParamSpec` is substituted with
+                # `[T, int]`, or `[int, *Ts]`, etc.
+                for x in t:
+                    for collected in _collect_parameters([x]):
+                        if collected not in parameters:
+                            parameters.append(collected)
+            elif hasattr(t, '__typing_subst__'):
+                if t not in parameters:
+                    if enforce_default_ordering:
+                        has_default = (
+                            getattr(t, '__default__', NoDefault) is not NoDefault
+                        )
+
+                        if type_var_tuple_encountered and has_default:
+                            raise TypeError('Type parameter with a default'
+                                            ' follows TypeVarTuple')
+
+                        if has_default:
+                            default_encountered = True
+                        elif default_encountered:
+                            raise TypeError(f'Type parameter {t!r} without a default'
+                                            ' follows type parameter with a default')
+
+                    parameters.append(t)
+            else:
+                if _is_unpacked_typevartuple(t):
+                    type_var_tuple_encountered = True
+                for x in getattr(t, '__parameters__', ()):
+                    if x not in parameters:
+                        parameters.append(x)
+
+        return tuple(parameters)
+
+    if not _PEP_696_IMPLEMENTED:
+        typing._collect_parameters = _collect_parameters
+
+# Backport typing.NamedTuple as it exists in Python 3.13.
+# In 3.11, the ability to define generic `NamedTuple`s was supported.
+# This was explicitly disallowed in 3.9-3.10, and only half-worked in <=3.8.
+# On 3.12, we added __orig_bases__ to call-based NamedTuples
+# On 3.13, we deprecated kwargs-based NamedTuples
+if sys.version_info >= (3, 13):
+    NamedTuple = typing.NamedTuple
+else:
+    def _make_nmtuple(name, types, module, defaults=()):
+        fields = [n for n, t in types]
+        annotations = {n: typing._type_check(t, f"field {n} annotation must be a type")
+                       for n, t in types}
+        nm_tpl = collections.namedtuple(name, fields,
+                                        defaults=defaults, module=module)
+        nm_tpl.__annotations__ = nm_tpl.__new__.__annotations__ = annotations
+        # The `_field_types` attribute was removed in 3.9;
+        # in earlier versions, it is the same as the `__annotations__` attribute
+        if sys.version_info < (3, 9):
+            nm_tpl._field_types = annotations
+        return nm_tpl
+
+    _prohibited_namedtuple_fields = typing._prohibited
+    _special_namedtuple_fields = frozenset({'__module__', '__name__', '__annotations__'})
+
+    class _NamedTupleMeta(type):
+        def __new__(cls, typename, bases, ns):
+            assert _NamedTuple in bases
+            for base in bases:
+                if base is not _NamedTuple and base is not typing.Generic:
+                    raise TypeError(
+                        'can only inherit from a NamedTuple type and Generic')
+            bases = tuple(tuple if base is _NamedTuple else base for base in bases)
+            if "__annotations__" in ns:
+                types = ns["__annotations__"]
+            elif "__annotate__" in ns:
+                # TODO: Use inspect.VALUE here, and make the annotations lazily evaluated
+                types = ns["__annotate__"](1)
+            else:
+                types = {}
+            default_names = []
+            for field_name in types:
+                if field_name in ns:
+                    default_names.append(field_name)
+                elif default_names:
+                    raise TypeError(f"Non-default namedtuple field {field_name} "
+                                    f"cannot follow default field"
+                                    f"{'s' if len(default_names) > 1 else ''} "
+                                    f"{', '.join(default_names)}")
+            nm_tpl = _make_nmtuple(
+                typename, types.items(),
+                defaults=[ns[n] for n in default_names],
+                module=ns['__module__']
+            )
+            nm_tpl.__bases__ = bases
+            if typing.Generic in bases:
+                if hasattr(typing, '_generic_class_getitem'):  # 3.12+
+                    nm_tpl.__class_getitem__ = classmethod(typing._generic_class_getitem)
+                else:
+                    class_getitem = typing.Generic.__class_getitem__.__func__
+                    nm_tpl.__class_getitem__ = classmethod(class_getitem)
+            # update from user namespace without overriding special namedtuple attributes
+            for key, val in ns.items():
+                if key in _prohibited_namedtuple_fields:
+                    raise AttributeError("Cannot overwrite NamedTuple attribute " + key)
+                elif key not in _special_namedtuple_fields:
+                    if key not in nm_tpl._fields:
+                        setattr(nm_tpl, key, ns[key])
+                    try:
+                        set_name = type(val).__set_name__
+                    except AttributeError:
+                        pass
+                    else:
+                        try:
+                            set_name(val, nm_tpl, key)
+                        except BaseException as e:
+                            msg = (
+                                f"Error calling __set_name__ on {type(val).__name__!r} "
+                                f"instance {key!r} in {typename!r}"
+                            )
+                            # BaseException.add_note() existed on py311,
+                            # but the __set_name__ machinery didn't start
+                            # using add_note() until py312.
+                            # Making sure exceptions are raised in the same way
+                            # as in "normal" classes seems most important here.
+                            if sys.version_info >= (3, 12):
+                                e.add_note(msg)
+                                raise
+                            else:
+                                raise RuntimeError(msg) from e
+
+            if typing.Generic in bases:
+                nm_tpl.__init_subclass__()
+            return nm_tpl
+
+    _NamedTuple = type.__new__(_NamedTupleMeta, 'NamedTuple', (), {})
+
+    def _namedtuple_mro_entries(bases):
+        assert NamedTuple in bases
+        return (_NamedTuple,)
+
+    @_ensure_subclassable(_namedtuple_mro_entries)
+    def NamedTuple(typename, fields=_marker, /, **kwargs):
+        """Typed version of namedtuple.
+
+        Usage::
+
+            class Employee(NamedTuple):
+                name: str
+                id: int
+
+        This is equivalent to::
+
+            Employee = collections.namedtuple('Employee', ['name', 'id'])
+
+        The resulting class has an extra __annotations__ attribute, giving a
+        dict that maps field names to types.  (The field names are also in
+        the _fields attribute, which is part of the namedtuple API.)
+        An alternative equivalent functional syntax is also accepted::
+
+            Employee = NamedTuple('Employee', [('name', str), ('id', int)])
+        """
+        if fields is _marker:
+            if kwargs:
+                deprecated_thing = "Creating NamedTuple classes using keyword arguments"
+                deprecation_msg = (
+                    "{name} is deprecated and will be disallowed in Python {remove}. "
+                    "Use the class-based or functional syntax instead."
+                )
+            else:
+                deprecated_thing = "Failing to pass a value for the 'fields' parameter"
+                example = f"`{typename} = NamedTuple({typename!r}, [])`"
+                deprecation_msg = (
+                    "{name} is deprecated and will be disallowed in Python {remove}. "
+                    "To create a NamedTuple class with 0 fields "
+                    "using the functional syntax, "
+                    "pass an empty list, e.g. "
+                ) + example + "."
+        elif fields is None:
+            if kwargs:
+                raise TypeError(
+                    "Cannot pass `None` as the 'fields' parameter "
+                    "and also specify fields using keyword arguments"
+                )
+            else:
+                deprecated_thing = "Passing `None` as the 'fields' parameter"
+                example = f"`{typename} = NamedTuple({typename!r}, [])`"
+                deprecation_msg = (
+                    "{name} is deprecated and will be disallowed in Python {remove}. "
+                    "To create a NamedTuple class with 0 fields "
+                    "using the functional syntax, "
+                    "pass an empty list, e.g. "
+                ) + example + "."
+        elif kwargs:
+            raise TypeError("Either list of fields or keywords"
+                            " can be provided to NamedTuple, not both")
+        if fields is _marker or fields is None:
+            warnings.warn(
+                deprecation_msg.format(name=deprecated_thing, remove="3.15"),
+                DeprecationWarning,
+                stacklevel=2,
+            )
+            fields = kwargs.items()
+        nt = _make_nmtuple(typename, fields, module=_caller())
+        nt.__orig_bases__ = (NamedTuple,)
+        return nt
+
+
+if hasattr(collections.abc, "Buffer"):
+    Buffer = collections.abc.Buffer
+else:
+    class Buffer(abc.ABC):  # noqa: B024
+        """Base class for classes that implement the buffer protocol.
+
+        The buffer protocol allows Python objects to expose a low-level
+        memory buffer interface. Before Python 3.12, it is not possible
+        to implement the buffer protocol in pure Python code, or even
+        to check whether a class implements the buffer protocol. In
+        Python 3.12 and higher, the ``__buffer__`` method allows access
+        to the buffer protocol from Python code, and the
+        ``collections.abc.Buffer`` ABC allows checking whether a class
+        implements the buffer protocol.
+
+        To indicate support for the buffer protocol in earlier versions,
+        inherit from this ABC, either in a stub file or at runtime,
+        or use ABC registration. This ABC provides no methods, because
+        there is no Python-accessible methods shared by pre-3.12 buffer
+        classes. It is useful primarily for static checks.
+
+        """
+
+    # As a courtesy, register the most common stdlib buffer classes.
+    Buffer.register(memoryview)
+    Buffer.register(bytearray)
+    Buffer.register(bytes)
+
+
+# Backport of types.get_original_bases, available on 3.12+ in CPython
+if hasattr(_types, "get_original_bases"):
+    get_original_bases = _types.get_original_bases
+else:
+    def get_original_bases(cls, /):
+        """Return the class's "original" bases prior to modification by `__mro_entries__`.
+
+        Examples::
+
+            from typing import TypeVar, Generic
+            from metaflow._vendor.typing_extensions import NamedTuple, TypedDict
+
+            T = TypeVar("T")
+            class Foo(Generic[T]): ...
+            class Bar(Foo[int], float): ...
+            class Baz(list[str]): ...
+            Eggs = NamedTuple("Eggs", [("a", int), ("b", str)])
+            Spam = TypedDict("Spam", {"a": int, "b": str})
+
+            assert get_original_bases(Bar) == (Foo[int], float)
+            assert get_original_bases(Baz) == (list[str],)
+            assert get_original_bases(Eggs) == (NamedTuple,)
+            assert get_original_bases(Spam) == (TypedDict,)
+            assert get_original_bases(int) == (object,)
+        """
+        try:
+            return cls.__dict__.get("__orig_bases__", cls.__bases__)
+        except AttributeError:
+            raise TypeError(
+                f'Expected an instance of type, not {type(cls).__name__!r}'
+            ) from None
+
+
+# NewType is a class on Python 3.10+, making it pickleable
+# The error message for subclassing instances of NewType was improved on 3.11+
+if sys.version_info >= (3, 11):
+    NewType = typing.NewType
+else:
+    class NewType:
+        """NewType creates simple unique types with almost zero
+        runtime overhead. NewType(name, tp) is considered a subtype of tp
+        by static type checkers. At runtime, NewType(name, tp) returns
+        a dummy callable that simply returns its argument. Usage::
+            UserId = NewType('UserId', int)
+            def name_by_id(user_id: UserId) -> str:
+                ...
+            UserId('user')          # Fails type check
+            name_by_id(42)          # Fails type check
+            name_by_id(UserId(42))  # OK
+            num = UserId(5) + 1     # type: int
+        """
+
+        def __call__(self, obj, /):
+            return obj
+
+        def __init__(self, name, tp):
+            self.__qualname__ = name
+            if '.' in name:
+                name = name.rpartition('.')[-1]
+            self.__name__ = name
+            self.__supertype__ = tp
+            def_mod = _caller()
+            if def_mod != 'typing_extensions':
+                self.__module__ = def_mod
+
+        def __mro_entries__(self, bases):
+            # We defined __mro_entries__ to get a better error message
+            # if a user attempts to subclass a NewType instance. bpo-46170
+            supercls_name = self.__name__
+
+            class Dummy:
+                def __init_subclass__(cls):
+                    subcls_name = cls.__name__
+                    raise TypeError(
+                        f"Cannot subclass an instance of NewType. "
+                        f"Perhaps you were looking for: "
+                        f"`{subcls_name} = NewType({subcls_name!r}, {supercls_name})`"
+                    )
+
+            return (Dummy,)
+
+        def __repr__(self):
+            return f'{self.__module__}.{self.__qualname__}'
+
+        def __reduce__(self):
+            return self.__qualname__
+
+        if sys.version_info >= (3, 10):
+            # PEP 604 methods
+            # It doesn't make sense to have these methods on Python <3.10
+
+            def __or__(self, other):
+                return typing.Union[self, other]
+
+            def __ror__(self, other):
+                return typing.Union[other, self]
+
+
+if hasattr(typing, "TypeAliasType"):
+    TypeAliasType = typing.TypeAliasType
+else:
+    def _is_unionable(obj):
+        """Corresponds to is_unionable() in unionobject.c in CPython."""
+        return obj is None or isinstance(obj, (
+            type,
+            _types.GenericAlias,
+            _types.UnionType,
+            TypeAliasType,
+        ))
+
+    class TypeAliasType:
+        """Create named, parameterized type aliases.
+
+        This provides a backport of the new `type` statement in Python 3.12:
+
+            type ListOrSet[T] = list[T] | set[T]
+
+        is equivalent to:
+
+            T = TypeVar("T")
+            ListOrSet = TypeAliasType("ListOrSet", list[T] | set[T], type_params=(T,))
+
+        The name ListOrSet can then be used as an alias for the type it refers to.
+
+        The type_params argument should contain all the type parameters used
+        in the value of the type alias. If the alias is not generic, this
+        argument is omitted.
+
+        Static type checkers should only support type aliases declared using
+        TypeAliasType that follow these rules:
+
+        - The first argument (the name) must be a string literal.
+        - The TypeAliasType instance must be immediately assigned to a variable
+          of the same name. (For example, 'X = TypeAliasType("Y", int)' is invalid,
+          as is 'X, Y = TypeAliasType("X", int), TypeAliasType("Y", int)').
+
+        """
+
+        def __init__(self, name: str, value, *, type_params=()):
+            if not isinstance(name, str):
+                raise TypeError("TypeAliasType name must be a string")
+            self.__value__ = value
+            self.__type_params__ = type_params
+
+            parameters = []
+            for type_param in type_params:
+                if isinstance(type_param, TypeVarTuple):
+                    parameters.extend(type_param)
+                else:
+                    parameters.append(type_param)
+            self.__parameters__ = tuple(parameters)
+            def_mod = _caller()
+            if def_mod != 'typing_extensions':
+                self.__module__ = def_mod
+            # Setting this attribute closes the TypeAliasType from further modification
+            self.__name__ = name
+
+        def __setattr__(self, name: str, value: object, /) -> None:
+            if hasattr(self, "__name__"):
+                self._raise_attribute_error(name)
+            super().__setattr__(name, value)
+
+        def __delattr__(self, name: str, /) -> Never:
+            self._raise_attribute_error(name)
+
+        def _raise_attribute_error(self, name: str) -> Never:
+            # Match the Python 3.12 error messages exactly
+            if name == "__name__":
+                raise AttributeError("readonly attribute")
+            elif name in {"__value__", "__type_params__", "__parameters__", "__module__"}:
+                raise AttributeError(
+                    f"attribute '{name}' of 'typing.TypeAliasType' objects "
+                    "is not writable"
+                )
+            else:
+                raise AttributeError(
+                    f"'typing.TypeAliasType' object has no attribute '{name}'"
+                )
+
+        def __repr__(self) -> str:
+            return self.__name__
+
+        def __getitem__(self, parameters):
+            if not isinstance(parameters, tuple):
+                parameters = (parameters,)
+            parameters = [
+                typing._type_check(
+                    item, f'Subscripting {self.__name__} requires a type.'
+                )
+                for item in parameters
+            ]
+            return typing._GenericAlias(self, tuple(parameters))
+
+        def __reduce__(self):
+            return self.__name__
+
+        def __init_subclass__(cls, *args, **kwargs):
+            raise TypeError(
+                "type 'typing_extensions.TypeAliasType' is not an acceptable base type"
+            )
+
+        # The presence of this method convinces typing._type_check
+        # that TypeAliasTypes are types.
+        def __call__(self):
+            raise TypeError("Type alias is not callable")
+
+        if sys.version_info >= (3, 10):
+            def __or__(self, right):
+                # For forward compatibility with 3.12, reject Unions
+                # that are not accepted by the built-in Union.
+                if not _is_unionable(right):
+                    return NotImplemented
+                return typing.Union[self, right]
+
+            def __ror__(self, left):
+                if not _is_unionable(left):
+                    return NotImplemented
+                return typing.Union[left, self]
+
+
+if hasattr(typing, "is_protocol"):
+    is_protocol = typing.is_protocol
+    get_protocol_members = typing.get_protocol_members
+else:
+    def is_protocol(tp: type, /) -> bool:
+        """Return True if the given type is a Protocol.
+
+        Example::
+
+            >>> from typing_extensions import Protocol, is_protocol
+            >>> class P(Protocol):
+            ...     def a(self) -> str: ...
+            ...     b: int
+            >>> is_protocol(P)
+            True
+            >>> is_protocol(int)
+            False
+        """
+        return (
+            isinstance(tp, type)
+            and getattr(tp, '_is_protocol', False)
+            and tp is not Protocol
+            and tp is not typing.Protocol
+        )
+
+    def get_protocol_members(tp: type, /) -> typing.FrozenSet[str]:
+        """Return the set of members defined in a Protocol.
+
+        Example::
+
+            >>> from typing_extensions import Protocol, get_protocol_members
+            >>> class P(Protocol):
+            ...     def a(self) -> str: ...
+            ...     b: int
+            >>> get_protocol_members(P)
+            frozenset({'a', 'b'})
+
+        Raise a TypeError for arguments that are not Protocols.
+        """
+        if not is_protocol(tp):
+            raise TypeError(f'{tp!r} is not a Protocol')
+        if hasattr(tp, '__protocol_attrs__'):
+            return frozenset(tp.__protocol_attrs__)
+        return frozenset(_get_protocol_attrs(tp))
+
+
+if hasattr(typing, "Doc"):
+    Doc = typing.Doc
+else:
+    class Doc:
+        """Define the documentation of a type annotation using ``Annotated``, to be
+         used in class attributes, function and method parameters, return values,
+         and variables.
+
+        The value should be a positional-only string literal to allow static tools
+        like editors and documentation generators to use it.
+
+        This complements docstrings.
+
+        The string value passed is available in the attribute ``documentation``.
+
+        Example::
+
+            >>> from typing_extensions import Annotated, Doc
+            >>> def hi(to: Annotated[str, Doc("Who to say hi to")]) -> None: ...
+        """
+        def __init__(self, documentation: str, /) -> None:
+            self.documentation = documentation
+
+        def __repr__(self) -> str:
+            return f"Doc({self.documentation!r})"
+
+        def __hash__(self) -> int:
+            return hash(self.documentation)
+
+        def __eq__(self, other: object) -> bool:
+            if not isinstance(other, Doc):
+                return NotImplemented
+            return self.documentation == other.documentation
+
+
+_CapsuleType = getattr(_types, "CapsuleType", None)
+
+if _CapsuleType is None:
+    try:
+        import _socket
+    except ImportError:
+        pass
+    else:
+        _CAPI = getattr(_socket, "CAPI", None)
+        if _CAPI is not None:
+            _CapsuleType = type(_CAPI)
+
+if _CapsuleType is not None:
+    CapsuleType = _CapsuleType
+    __all__.append("CapsuleType")
+
+
+# Aliases for items that have always been in typing.
+# Explicitly assign these (rather than using `from typing import *` at the top),
+# so that we get a CI error if one of these is deleted from typing.py
+# in a future version of Python
+AbstractSet = typing.AbstractSet
+AnyStr = typing.AnyStr
+BinaryIO = typing.BinaryIO
+Callable = typing.Callable
+Collection = typing.Collection
+Container = typing.Container
+Dict = typing.Dict
+ForwardRef = typing.ForwardRef
+FrozenSet = typing.FrozenSet
+Generic = typing.Generic
+Hashable = typing.Hashable
+IO = typing.IO
+ItemsView = typing.ItemsView
+Iterable = typing.Iterable
+Iterator = typing.Iterator
+KeysView = typing.KeysView
+List = typing.List
+Mapping = typing.Mapping
+MappingView = typing.MappingView
+Match = typing.Match
+MutableMapping = typing.MutableMapping
+MutableSequence = typing.MutableSequence
+MutableSet = typing.MutableSet
+Optional = typing.Optional
+Pattern = typing.Pattern
+Reversible = typing.Reversible
+Sequence = typing.Sequence
+Set = typing.Set
+Sized = typing.Sized
+TextIO = typing.TextIO
+Tuple = typing.Tuple
+Union = typing.Union
+ValuesView = typing.ValuesView
+cast = typing.cast
+no_type_check = typing.no_type_check
+no_type_check_decorator = typing.no_type_check_decorator
diff --git a/metaflow/_vendor/v3_5/importlib_metadata/__init__.py b/metaflow/_vendor/v3_5/importlib_metadata/__init__.py
deleted file mode 100644
index 429bfa66c4f..00000000000
--- a/metaflow/_vendor/v3_5/importlib_metadata/__init__.py
+++ /dev/null
@@ -1,644 +0,0 @@
-from __future__ import unicode_literals, absolute_import
-
-import io
-import os
-import re
-import abc
-import csv
-import sys
-from metaflow._vendor.v3_5 import zipp
-import operator
-import functools
-import itertools
-import posixpath
-import collections
-
-from ._compat import (
-    install,
-    NullFinder,
-    ConfigParser,
-    suppress,
-    map,
-    FileNotFoundError,
-    IsADirectoryError,
-    NotADirectoryError,
-    PermissionError,
-    pathlib,
-    ModuleNotFoundError,
-    MetaPathFinder,
-    email_message_from_string,
-    PyPy_repr,
-    unique_ordered,
-    str,
-    )
-from importlib import import_module
-from itertools import starmap
-
-
-__metaclass__ = type
-
-
-__all__ = [
-    'Distribution',
-    'DistributionFinder',
-    'PackageNotFoundError',
-    'distribution',
-    'distributions',
-    'entry_points',
-    'files',
-    'metadata',
-    'requires',
-    'version',
-    ]
-
-
-class PackageNotFoundError(ModuleNotFoundError):
-    """The package was not found."""
-
-    def __str__(self):
-        tmpl = "No package metadata was found for {self.name}"
-        return tmpl.format(**locals())
-
-    @property
-    def name(self):
-        name, = self.args
-        return name
-
-
-class EntryPoint(
-        PyPy_repr,
-        collections.namedtuple('EntryPointBase', 'name value group')):
-    """An entry point as defined by Python packaging conventions.
-
-    See `the packaging docs on entry points
-    `_
-    for more information.
-    """
-
-    pattern = re.compile(
-        r'(?P[\w.]+)\s*'
-        r'(:\s*(?P[\w.]+)\s*)?'
-        r'((?P\[.*\])\s*)?$'
-        )
-    """
-    A regular expression describing the syntax for an entry point,
-    which might look like:
-
-        - module
-        - package.module
-        - package.module:attribute
-        - package.module:object.attribute
-        - package.module:attr [extra1, extra2]
-
-    Other combinations are possible as well.
-
-    The expression is lenient about whitespace around the ':',
-    following the attr, and following any extras.
-    """
-
-    def load(self):
-        """Load the entry point from its definition. If only a module
-        is indicated by the value, return that module. Otherwise,
-        return the named object.
-        """
-        match = self.pattern.match(self.value)
-        module = import_module(match.group('module'))
-        attrs = filter(None, (match.group('attr') or '').split('.'))
-        return functools.reduce(getattr, attrs, module)
-
-    @property
-    def module(self):
-        match = self.pattern.match(self.value)
-        return match.group('module')
-
-    @property
-    def attr(self):
-        match = self.pattern.match(self.value)
-        return match.group('attr')
-
-    @property
-    def extras(self):
-        match = self.pattern.match(self.value)
-        return list(re.finditer(r'\w+', match.group('extras') or ''))
-
-    @classmethod
-    def _from_config(cls, config):
-        return [
-            cls(name, value, group)
-            for group in config.sections()
-            for name, value in config.items(group)
-            ]
-
-    @classmethod
-    def _from_text(cls, text):
-        config = ConfigParser(delimiters='=')
-        # case sensitive: https://stackoverflow.com/q/1611799/812183
-        config.optionxform = str
-        try:
-            config.read_string(text)
-        except AttributeError:  # pragma: nocover
-            # Python 2 has no read_string
-            config.readfp(io.StringIO(text))
-        return EntryPoint._from_config(config)
-
-    def __iter__(self):
-        """
-        Supply iter so one may construct dicts of EntryPoints easily.
-        """
-        return iter((self.name, self))
-
-    def __reduce__(self):
-        return (
-            self.__class__,
-            (self.name, self.value, self.group),
-            )
-
-
-class PackagePath(pathlib.PurePosixPath):
-    """A reference to a path in a package"""
-
-    def read_text(self, encoding='utf-8'):
-        with self.locate().open(encoding=encoding) as stream:
-            return stream.read()
-
-    def read_binary(self):
-        with self.locate().open('rb') as stream:
-            return stream.read()
-
-    def locate(self):
-        """Return a path-like object for this path"""
-        return self.dist.locate_file(self)
-
-
-class FileHash:
-    def __init__(self, spec):
-        self.mode, _, self.value = spec.partition('=')
-
-    def __repr__(self):
-        return ''.format(self.mode, self.value)
-
-
-class Distribution:
-    """A Python distribution package."""
-
-    @abc.abstractmethod
-    def read_text(self, filename):
-        """Attempt to load metadata file given by the name.
-
-        :param filename: The name of the file in the distribution info.
-        :return: The text if found, otherwise None.
-        """
-
-    @abc.abstractmethod
-    def locate_file(self, path):
-        """
-        Given a path to a file in this distribution, return a path
-        to it.
-        """
-
-    @classmethod
-    def from_name(cls, name):
-        """Return the Distribution for the given package name.
-
-        :param name: The name of the distribution package to search for.
-        :return: The Distribution instance (or subclass thereof) for the named
-            package, if found.
-        :raises PackageNotFoundError: When the named package's distribution
-            metadata cannot be found.
-        """
-        for resolver in cls._discover_resolvers():
-            dists = resolver(DistributionFinder.Context(name=name))
-            dist = next(iter(dists), None)
-            if dist is not None:
-                return dist
-        else:
-            raise PackageNotFoundError(name)
-
-    @classmethod
-    def discover(cls, **kwargs):
-        """Return an iterable of Distribution objects for all packages.
-
-        Pass a ``context`` or pass keyword arguments for constructing
-        a context.
-
-        :context: A ``DistributionFinder.Context`` object.
-        :return: Iterable of Distribution objects for all packages.
-        """
-        context = kwargs.pop('context', None)
-        if context and kwargs:
-            raise ValueError("cannot accept context and kwargs")
-        context = context or DistributionFinder.Context(**kwargs)
-        return itertools.chain.from_iterable(
-            resolver(context)
-            for resolver in cls._discover_resolvers()
-            )
-
-    @staticmethod
-    def at(path):
-        """Return a Distribution for the indicated metadata path
-
-        :param path: a string or path-like object
-        :return: a concrete Distribution instance for the path
-        """
-        return PathDistribution(pathlib.Path(path))
-
-    @staticmethod
-    def _discover_resolvers():
-        """Search the meta_path for resolvers."""
-        declared = (
-            getattr(finder, 'find_distributions', None)
-            for finder in sys.meta_path
-            )
-        return filter(None, declared)
-
-    @classmethod
-    def _local(cls, root='.'):
-        from pep517 import build, meta
-        system = build.compat_system(root)
-        builder = functools.partial(
-            meta.build,
-            source_dir=root,
-            system=system,
-            )
-        return PathDistribution(zipp.Path(meta.build_as_zip(builder)))
-
-    @property
-    def metadata(self):
-        """Return the parsed metadata for this Distribution.
-
-        The returned object will have keys that name the various bits of
-        metadata.  See PEP 566 for details.
-        """
-        text = (
-            self.read_text('METADATA')
-            or self.read_text('PKG-INFO')
-            # This last clause is here to support old egg-info files.  Its
-            # effect is to just end up using the PathDistribution's self._path
-            # (which points to the egg-info file) attribute unchanged.
-            or self.read_text('')
-            )
-        return email_message_from_string(text)
-
-    @property
-    def version(self):
-        """Return the 'Version' metadata for the distribution package."""
-        return self.metadata['Version']
-
-    @property
-    def entry_points(self):
-        return EntryPoint._from_text(self.read_text('entry_points.txt'))
-
-    @property
-    def files(self):
-        """Files in this distribution.
-
-        :return: List of PackagePath for this distribution or None
-
-        Result is `None` if the metadata file that enumerates files
-        (i.e. RECORD for dist-info or SOURCES.txt for egg-info) is
-        missing.
-        Result may be empty if the metadata exists but is empty.
-        """
-        file_lines = self._read_files_distinfo() or self._read_files_egginfo()
-
-        def make_file(name, hash=None, size_str=None):
-            result = PackagePath(name)
-            result.hash = FileHash(hash) if hash else None
-            result.size = int(size_str) if size_str else None
-            result.dist = self
-            return result
-
-        return file_lines and list(starmap(make_file, csv.reader(file_lines)))
-
-    def _read_files_distinfo(self):
-        """
-        Read the lines of RECORD
-        """
-        text = self.read_text('RECORD')
-        return text and text.splitlines()
-
-    def _read_files_egginfo(self):
-        """
-        SOURCES.txt might contain literal commas, so wrap each line
-        in quotes.
-        """
-        text = self.read_text('SOURCES.txt')
-        return text and map('"{}"'.format, text.splitlines())
-
-    @property
-    def requires(self):
-        """Generated requirements specified for this Distribution"""
-        reqs = self._read_dist_info_reqs() or self._read_egg_info_reqs()
-        return reqs and list(reqs)
-
-    def _read_dist_info_reqs(self):
-        return self.metadata.get_all('Requires-Dist')
-
-    def _read_egg_info_reqs(self):
-        source = self.read_text('requires.txt')
-        return source and self._deps_from_requires_text(source)
-
-    @classmethod
-    def _deps_from_requires_text(cls, source):
-        section_pairs = cls._read_sections(source.splitlines())
-        sections = {
-            section: list(map(operator.itemgetter('line'), results))
-            for section, results in
-            itertools.groupby(section_pairs, operator.itemgetter('section'))
-            }
-        return cls._convert_egg_info_reqs_to_simple_reqs(sections)
-
-    @staticmethod
-    def _read_sections(lines):
-        section = None
-        for line in filter(None, lines):
-            section_match = re.match(r'\[(.*)\]$', line)
-            if section_match:
-                section = section_match.group(1)
-                continue
-            yield locals()
-
-    @staticmethod
-    def _convert_egg_info_reqs_to_simple_reqs(sections):
-        """
-        Historically, setuptools would solicit and store 'extra'
-        requirements, including those with environment markers,
-        in separate sections. More modern tools expect each
-        dependency to be defined separately, with any relevant
-        extras and environment markers attached directly to that
-        requirement. This method converts the former to the
-        latter. See _test_deps_from_requires_text for an example.
-        """
-        def make_condition(name):
-            return name and 'extra == "{name}"'.format(name=name)
-
-        def parse_condition(section):
-            section = section or ''
-            extra, sep, markers = section.partition(':')
-            if extra and markers:
-                markers = '({markers})'.format(markers=markers)
-            conditions = list(filter(None, [markers, make_condition(extra)]))
-            return '; ' + ' and '.join(conditions) if conditions else ''
-
-        for section, deps in sections.items():
-            for dep in deps:
-                yield dep + parse_condition(section)
-
-
-class DistributionFinder(MetaPathFinder):
-    """
-    A MetaPathFinder capable of discovering installed distributions.
-    """
-
-    class Context:
-        """
-        Keyword arguments presented by the caller to
-        ``distributions()`` or ``Distribution.discover()``
-        to narrow the scope of a search for distributions
-        in all DistributionFinders.
-
-        Each DistributionFinder may expect any parameters
-        and should attempt to honor the canonical
-        parameters defined below when appropriate.
-        """
-
-        name = None
-        """
-        Specific name for which a distribution finder should match.
-        A name of ``None`` matches all distributions.
-        """
-
-        def __init__(self, **kwargs):
-            vars(self).update(kwargs)
-
-        @property
-        def path(self):
-            """
-            The path that a distribution finder should search.
-
-            Typically refers to Python package paths and defaults
-            to ``sys.path``.
-            """
-            return vars(self).get('path', sys.path)
-
-    @abc.abstractmethod
-    def find_distributions(self, context=Context()):
-        """
-        Find distributions.
-
-        Return an iterable of all Distribution instances capable of
-        loading the metadata for packages matching the ``context``,
-        a DistributionFinder.Context instance.
-        """
-
-
-class FastPath:
-    """
-    Micro-optimized class for searching a path for
-    children.
-    """
-
-    def __init__(self, root):
-        self.root = str(root)
-        self.base = os.path.basename(self.root).lower()
-
-    def joinpath(self, child):
-        return pathlib.Path(self.root, child)
-
-    def children(self):
-        with suppress(Exception):
-            return os.listdir(self.root or '.')
-        with suppress(Exception):
-            return self.zip_children()
-        return []
-
-    def zip_children(self):
-        zip_path = zipp.Path(self.root)
-        names = zip_path.root.namelist()
-        self.joinpath = zip_path.joinpath
-
-        return unique_ordered(
-            child.split(posixpath.sep, 1)[0]
-            for child in names
-            )
-
-    def search(self, name):
-        return (
-            self.joinpath(child)
-            for child in self.children()
-            if name.matches(child, self.base)
-            )
-
-
-class Prepared:
-    """
-    A prepared search for metadata on a possibly-named package.
-    """
-    normalized = None
-    suffixes = '.dist-info', '.egg-info'
-    exact_matches = [''][:0]
-
-    def __init__(self, name):
-        self.name = name
-        if name is None:
-            return
-        self.normalized = self.normalize(name)
-        self.exact_matches = [
-            self.normalized + suffix for suffix in self.suffixes]
-
-    @staticmethod
-    def normalize(name):
-        """
-        PEP 503 normalization plus dashes as underscores.
-        """
-        return re.sub(r"[-_.]+", "-", name).lower().replace('-', '_')
-
-    @staticmethod
-    def legacy_normalize(name):
-        """
-        Normalize the package name as found in the convention in
-        older packaging tools versions and specs.
-        """
-        return name.lower().replace('-', '_')
-
-    def matches(self, cand, base):
-        low = cand.lower()
-        pre, ext = os.path.splitext(low)
-        name, sep, rest = pre.partition('-')
-        return (
-            low in self.exact_matches
-            or ext in self.suffixes and (
-                not self.normalized or
-                name.replace('.', '_') == self.normalized
-                )
-            # legacy case:
-            or self.is_egg(base) and low == 'egg-info'
-            )
-
-    def is_egg(self, base):
-        normalized = self.legacy_normalize(self.name or '')
-        prefix = normalized + '-' if normalized else ''
-        versionless_egg_name = normalized + '.egg' if self.name else ''
-        return (
-            base == versionless_egg_name
-            or base.startswith(prefix)
-            and base.endswith('.egg'))
-
-
-@install
-class MetadataPathFinder(NullFinder, DistributionFinder):
-    """A degenerate finder for distribution packages on the file system.
-
-    This finder supplies only a find_distributions() method for versions
-    of Python that do not have a PathFinder find_distributions().
-    """
-
-    def find_distributions(self, context=DistributionFinder.Context()):
-        """
-        Find distributions.
-
-        Return an iterable of all Distribution instances capable of
-        loading the metadata for packages matching ``context.name``
-        (or all names if ``None`` indicated) along the paths in the list
-        of directories ``context.path``.
-        """
-        found = self._search_paths(context.name, context.path)
-        return map(PathDistribution, found)
-
-    @classmethod
-    def _search_paths(cls, name, paths):
-        """Find metadata directories in paths heuristically."""
-        return itertools.chain.from_iterable(
-            path.search(Prepared(name))
-            for path in map(FastPath, paths)
-            )
-
-
-class PathDistribution(Distribution):
-    def __init__(self, path):
-        """Construct a distribution from a path to the metadata directory.
-
-        :param path: A pathlib.Path or similar object supporting
-                     .joinpath(), __div__, .parent, and .read_text().
-        """
-        self._path = path
-
-    def read_text(self, filename):
-        with suppress(FileNotFoundError, IsADirectoryError, KeyError,
-                      NotADirectoryError, PermissionError):
-            return self._path.joinpath(filename).read_text(encoding='utf-8')
-    read_text.__doc__ = Distribution.read_text.__doc__
-
-    def locate_file(self, path):
-        return self._path.parent / path
-
-
-def distribution(distribution_name):
-    """Get the ``Distribution`` instance for the named package.
-
-    :param distribution_name: The name of the distribution package as a string.
-    :return: A ``Distribution`` instance (or subclass thereof).
-    """
-    return Distribution.from_name(distribution_name)
-
-
-def distributions(**kwargs):
-    """Get all ``Distribution`` instances in the current environment.
-
-    :return: An iterable of ``Distribution`` instances.
-    """
-    return Distribution.discover(**kwargs)
-
-
-def metadata(distribution_name):
-    """Get the metadata for the named package.
-
-    :param distribution_name: The name of the distribution package to query.
-    :return: An email.Message containing the parsed metadata.
-    """
-    return Distribution.from_name(distribution_name).metadata
-
-
-def version(distribution_name):
-    """Get the version string for the named package.
-
-    :param distribution_name: The name of the distribution package to query.
-    :return: The version string for the package as defined in the package's
-        "Version" metadata key.
-    """
-    return distribution(distribution_name).version
-
-
-def entry_points():
-    """Return EntryPoint objects for all installed packages.
-
-    :return: EntryPoint objects for all installed packages.
-    """
-    eps = itertools.chain.from_iterable(
-        dist.entry_points for dist in distributions())
-    by_group = operator.attrgetter('group')
-    ordered = sorted(eps, key=by_group)
-    grouped = itertools.groupby(ordered, by_group)
-    return {
-        group: tuple(eps)
-        for group, eps in grouped
-        }
-
-
-def files(distribution_name):
-    """Return a list of files for the named package.
-
-    :param distribution_name: The name of the distribution package to query.
-    :return: List of files composing the distribution.
-    """
-    return distribution(distribution_name).files
-
-
-def requires(distribution_name):
-    """
-    Return a list of requirements for the named package.
-
-    :return: An iterator of requirements, suitable for
-    packaging.requirement.Requirement.
-    """
-    return distribution(distribution_name).requires
diff --git a/metaflow/_vendor/v3_5/importlib_metadata/_compat.py b/metaflow/_vendor/v3_5/importlib_metadata/_compat.py
deleted file mode 100644
index 303d4a22e85..00000000000
--- a/metaflow/_vendor/v3_5/importlib_metadata/_compat.py
+++ /dev/null
@@ -1,152 +0,0 @@
-from __future__ import absolute_import, unicode_literals
-
-import io
-import abc
-import sys
-import email
-
-
-if sys.version_info > (3,):  # pragma: nocover
-    import builtins
-    from configparser import ConfigParser
-    import contextlib
-    FileNotFoundError = builtins.FileNotFoundError
-    IsADirectoryError = builtins.IsADirectoryError
-    NotADirectoryError = builtins.NotADirectoryError
-    PermissionError = builtins.PermissionError
-    map = builtins.map
-    from itertools import filterfalse
-else:  # pragma: nocover
-    from backports.configparser import ConfigParser
-    from itertools import imap as map  # type: ignore
-    from itertools import ifilterfalse as filterfalse
-    import contextlib2 as contextlib
-    FileNotFoundError = IOError, OSError
-    IsADirectoryError = IOError, OSError
-    NotADirectoryError = IOError, OSError
-    PermissionError = IOError, OSError
-
-str = type('')
-
-suppress = contextlib.suppress
-
-if sys.version_info > (3, 5):  # pragma: nocover
-    import pathlib
-else:  # pragma: nocover
-    import pathlib2 as pathlib
-
-try:
-    ModuleNotFoundError = builtins.FileNotFoundError
-except (NameError, AttributeError):  # pragma: nocover
-    ModuleNotFoundError = ImportError  # type: ignore
-
-
-if sys.version_info >= (3,):  # pragma: nocover
-    from importlib.abc import MetaPathFinder
-else:  # pragma: nocover
-    class MetaPathFinder(object):
-        __metaclass__ = abc.ABCMeta
-
-
-__metaclass__ = type
-__all__ = [
-    'install', 'NullFinder', 'MetaPathFinder', 'ModuleNotFoundError',
-    'pathlib', 'ConfigParser', 'map', 'suppress', 'FileNotFoundError',
-    'NotADirectoryError', 'email_message_from_string',
-    ]
-
-
-def install(cls):
-    """
-    Class decorator for installation on sys.meta_path.
-
-    Adds the backport DistributionFinder to sys.meta_path and
-    attempts to disable the finder functionality of the stdlib
-    DistributionFinder.
-    """
-    sys.meta_path.append(cls())
-    disable_stdlib_finder()
-    return cls
-
-
-def disable_stdlib_finder():
-    """
-    Give the backport primacy for discovering path-based distributions
-    by monkey-patching the stdlib O_O.
-
-    See #91 for more background for rationale on this sketchy
-    behavior.
-    """
-    def matches(finder):
-        return (
-            getattr(finder, '__module__', None) == '_frozen_importlib_external'
-            and hasattr(finder, 'find_distributions')
-            )
-    for finder in filter(matches, sys.meta_path):  # pragma: nocover
-        del finder.find_distributions
-
-
-class NullFinder:
-    """
-    A "Finder" (aka "MetaClassFinder") that never finds any modules,
-    but may find distributions.
-    """
-    @staticmethod
-    def find_spec(*args, **kwargs):
-        return None
-
-    # In Python 2, the import system requires finders
-    # to have a find_module() method, but this usage
-    # is deprecated in Python 3 in favor of find_spec().
-    # For the purposes of this finder (i.e. being present
-    # on sys.meta_path but having no other import
-    # system functionality), the two methods are identical.
-    find_module = find_spec
-
-
-def py2_message_from_string(text):  # nocoverpy3
-    # Work around https://bugs.python.org/issue25545 where
-    # email.message_from_string cannot handle Unicode on Python 2.
-    io_buffer = io.StringIO(text)
-    return email.message_from_file(io_buffer)
-
-
-email_message_from_string = (
-    py2_message_from_string
-    if sys.version_info < (3,) else
-    email.message_from_string
-    )
-
-
-class PyPy_repr:
-    """
-    Override repr for EntryPoint objects on PyPy to avoid __iter__ access.
-    Ref #97, #102.
-    """
-    affected = hasattr(sys, 'pypy_version_info')
-
-    def __compat_repr__(self):  # pragma: nocover
-        def make_param(name):
-            value = getattr(self, name)
-            return '{name}={value!r}'.format(**locals())
-        params = ', '.join(map(make_param, self._fields))
-        return 'EntryPoint({params})'.format(**locals())
-
-    if affected:  # pragma: nocover
-        __repr__ = __compat_repr__
-    del affected
-
-
-# from itertools recipes
-def unique_everseen(iterable):  # pragma: nocover
-    "List unique elements, preserving order. Remember all elements ever seen."
-    seen = set()
-    seen_add = seen.add
-
-    for element in filterfalse(seen.__contains__, iterable):
-        seen_add(element)
-        yield element
-
-
-unique_ordered = (
-    unique_everseen if sys.version_info < (3, 7) else dict.fromkeys)
diff --git a/metaflow/_vendor/v3_5/__init__.py b/metaflow/_vendor/v3_7/__init__.py
similarity index 100%
rename from metaflow/_vendor/v3_5/__init__.py
rename to metaflow/_vendor/v3_7/__init__.py
diff --git a/metaflow/_vendor/v3_7/importlib_metadata.LICENSE b/metaflow/_vendor/v3_7/importlib_metadata.LICENSE
new file mode 100644
index 00000000000..be7e092b0b0
--- /dev/null
+++ b/metaflow/_vendor/v3_7/importlib_metadata.LICENSE
@@ -0,0 +1,13 @@
+Copyright 2017-2019 Jason R. Coombs, Barry Warsaw
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/metaflow/_vendor/v3_7/importlib_metadata/__init__.py b/metaflow/_vendor/v3_7/importlib_metadata/__init__.py
new file mode 100644
index 00000000000..443f4763c00
--- /dev/null
+++ b/metaflow/_vendor/v3_7/importlib_metadata/__init__.py
@@ -0,0 +1,1063 @@
+import os
+import re
+import abc
+import csv
+import sys
+from metaflow._vendor.v3_7 import zipp
+import email
+import pathlib
+import operator
+import textwrap
+import warnings
+import functools
+import itertools
+import posixpath
+import collections
+
+from . import _adapters, _meta
+from ._collections import FreezableDefaultDict, Pair
+from ._compat import (
+    NullFinder,
+    install,
+    pypy_partial,
+)
+from ._functools import method_cache, pass_none
+from ._itertools import always_iterable, unique_everseen
+from ._meta import PackageMetadata, SimplePath
+
+from contextlib import suppress
+from importlib import import_module
+from importlib.abc import MetaPathFinder
+from itertools import starmap
+from typing import List, Mapping, Optional, Union
+
+
+__all__ = [
+    'Distribution',
+    'DistributionFinder',
+    'PackageMetadata',
+    'PackageNotFoundError',
+    'distribution',
+    'distributions',
+    'entry_points',
+    'files',
+    'metadata',
+    'packages_distributions',
+    'requires',
+    'version',
+]
+
+
+class PackageNotFoundError(ModuleNotFoundError):
+    """The package was not found."""
+
+    def __str__(self):
+        return f"No package metadata was found for {self.name}"
+
+    @property
+    def name(self):
+        (name,) = self.args
+        return name
+
+
+class Sectioned:
+    """
+    A simple entry point config parser for performance
+
+    >>> for item in Sectioned.read(Sectioned._sample):
+    ...     print(item)
+    Pair(name='sec1', value='# comments ignored')
+    Pair(name='sec1', value='a = 1')
+    Pair(name='sec1', value='b = 2')
+    Pair(name='sec2', value='a = 2')
+
+    >>> res = Sectioned.section_pairs(Sectioned._sample)
+    >>> item = next(res)
+    >>> item.name
+    'sec1'
+    >>> item.value
+    Pair(name='a', value='1')
+    >>> item = next(res)
+    >>> item.value
+    Pair(name='b', value='2')
+    >>> item = next(res)
+    >>> item.name
+    'sec2'
+    >>> item.value
+    Pair(name='a', value='2')
+    >>> list(res)
+    []
+    """
+
+    _sample = textwrap.dedent(
+        """
+        [sec1]
+        # comments ignored
+        a = 1
+        b = 2
+
+        [sec2]
+        a = 2
+        """
+    ).lstrip()
+
+    @classmethod
+    def section_pairs(cls, text):
+        return (
+            section._replace(value=Pair.parse(section.value))
+            for section in cls.read(text, filter_=cls.valid)
+            if section.name is not None
+        )
+
+    @staticmethod
+    def read(text, filter_=None):
+        lines = filter(filter_, map(str.strip, text.splitlines()))
+        name = None
+        for value in lines:
+            section_match = value.startswith('[') and value.endswith(']')
+            if section_match:
+                name = value.strip('[]')
+                continue
+            yield Pair(name, value)
+
+    @staticmethod
+    def valid(line):
+        return line and not line.startswith('#')
+
+
+class DeprecatedTuple:
+    """
+    Provide subscript item access for backward compatibility.
+
+    >>> recwarn = getfixture('recwarn')
+    >>> ep = EntryPoint(name='name', value='value', group='group')
+    >>> ep[:]
+    ('name', 'value', 'group')
+    >>> ep[0]
+    'name'
+    >>> len(recwarn)
+    1
+    """
+
+    _warn = functools.partial(
+        warnings.warn,
+        "EntryPoint tuple interface is deprecated. Access members by name.",
+        DeprecationWarning,
+        stacklevel=pypy_partial(2),
+    )
+
+    def __getitem__(self, item):
+        self._warn()
+        return self._key()[item]
+
+
+class EntryPoint(DeprecatedTuple):
+    """An entry point as defined by Python packaging conventions.
+
+    See `the packaging docs on entry points
+    `_
+    for more information.
+    """
+
+    pattern = re.compile(
+        r'(?P[\w.]+)\s*'
+        r'(:\s*(?P[\w.]+))?\s*'
+        r'(?P\[.*\])?\s*$'
+    )
+    """
+    A regular expression describing the syntax for an entry point,
+    which might look like:
+
+        - module
+        - package.module
+        - package.module:attribute
+        - package.module:object.attribute
+        - package.module:attr [extra1, extra2]
+
+    Other combinations are possible as well.
+
+    The expression is lenient about whitespace around the ':',
+    following the attr, and following any extras.
+    """
+
+    dist: Optional['Distribution'] = None
+
+    def __init__(self, name, value, group):
+        vars(self).update(name=name, value=value, group=group)
+
+    def load(self):
+        """Load the entry point from its definition. If only a module
+        is indicated by the value, return that module. Otherwise,
+        return the named object.
+        """
+        match = self.pattern.match(self.value)
+        module = import_module(match.group('module'))
+        attrs = filter(None, (match.group('attr') or '').split('.'))
+        return functools.reduce(getattr, attrs, module)
+
+    @property
+    def module(self):
+        match = self.pattern.match(self.value)
+        return match.group('module')
+
+    @property
+    def attr(self):
+        match = self.pattern.match(self.value)
+        return match.group('attr')
+
+    @property
+    def extras(self):
+        match = self.pattern.match(self.value)
+        return list(re.finditer(r'\w+', match.group('extras') or ''))
+
+    def _for(self, dist):
+        vars(self).update(dist=dist)
+        return self
+
+    def __iter__(self):
+        """
+        Supply iter so one may construct dicts of EntryPoints by name.
+        """
+        msg = (
+            "Construction of dict of EntryPoints is deprecated in "
+            "favor of EntryPoints."
+        )
+        warnings.warn(msg, DeprecationWarning)
+        return iter((self.name, self))
+
+    def matches(self, **params):
+        attrs = (getattr(self, param) for param in params)
+        return all(map(operator.eq, params.values(), attrs))
+
+    def _key(self):
+        return self.name, self.value, self.group
+
+    def __lt__(self, other):
+        return self._key() < other._key()
+
+    def __eq__(self, other):
+        return self._key() == other._key()
+
+    def __setattr__(self, name, value):
+        raise AttributeError("EntryPoint objects are immutable.")
+
+    def __repr__(self):
+        return (
+            f'EntryPoint(name={self.name!r}, value={self.value!r}, '
+            f'group={self.group!r})'
+        )
+
+    def __hash__(self):
+        return hash(self._key())
+
+
+class DeprecatedList(list):
+    """
+    Allow an otherwise immutable object to implement mutability
+    for compatibility.
+
+    >>> recwarn = getfixture('recwarn')
+    >>> dl = DeprecatedList(range(3))
+    >>> dl[0] = 1
+    >>> dl.append(3)
+    >>> del dl[3]
+    >>> dl.reverse()
+    >>> dl.sort()
+    >>> dl.extend([4])
+    >>> dl.pop(-1)
+    4
+    >>> dl.remove(1)
+    >>> dl += [5]
+    >>> dl + [6]
+    [1, 2, 5, 6]
+    >>> dl + (6,)
+    [1, 2, 5, 6]
+    >>> dl.insert(0, 0)
+    >>> dl
+    [0, 1, 2, 5]
+    >>> dl == [0, 1, 2, 5]
+    True
+    >>> dl == (0, 1, 2, 5)
+    True
+    >>> len(recwarn)
+    1
+    """
+
+    _warn = functools.partial(
+        warnings.warn,
+        "EntryPoints list interface is deprecated. Cast to list if needed.",
+        DeprecationWarning,
+        stacklevel=pypy_partial(2),
+    )
+
+    def _wrap_deprecated_method(method_name: str):  # type: ignore
+        def wrapped(self, *args, **kwargs):
+            self._warn()
+            return getattr(super(), method_name)(*args, **kwargs)
+
+        return wrapped
+
+    for method_name in [
+        '__setitem__',
+        '__delitem__',
+        'append',
+        'reverse',
+        'extend',
+        'pop',
+        'remove',
+        '__iadd__',
+        'insert',
+        'sort',
+    ]:
+        locals()[method_name] = _wrap_deprecated_method(method_name)
+
+    def __add__(self, other):
+        if not isinstance(other, tuple):
+            self._warn()
+            other = tuple(other)
+        return self.__class__(tuple(self) + other)
+
+    def __eq__(self, other):
+        if not isinstance(other, tuple):
+            self._warn()
+            other = tuple(other)
+
+        return tuple(self).__eq__(other)
+
+
+class EntryPoints(DeprecatedList):
+    """
+    An immutable collection of selectable EntryPoint objects.
+    """
+
+    __slots__ = ()
+
+    def __getitem__(self, name):  # -> EntryPoint:
+        """
+        Get the EntryPoint in self matching name.
+        """
+        if isinstance(name, int):
+            warnings.warn(
+                "Accessing entry points by index is deprecated. "
+                "Cast to tuple if needed.",
+                DeprecationWarning,
+                stacklevel=2,
+            )
+            return super().__getitem__(name)
+        try:
+            return next(iter(self.select(name=name)))
+        except StopIteration:
+            raise KeyError(name)
+
+    def select(self, **params):
+        """
+        Select entry points from self that match the
+        given parameters (typically group and/or name).
+        """
+        return EntryPoints(ep for ep in self if ep.matches(**params))
+
+    @property
+    def names(self):
+        """
+        Return the set of all names of all entry points.
+        """
+        return {ep.name for ep in self}
+
+    @property
+    def groups(self):
+        """
+        Return the set of all groups of all entry points.
+
+        For coverage while SelectableGroups is present.
+        >>> EntryPoints().groups
+        set()
+        """
+        return {ep.group for ep in self}
+
+    @classmethod
+    def _from_text_for(cls, text, dist):
+        return cls(ep._for(dist) for ep in cls._from_text(text))
+
+    @staticmethod
+    def _from_text(text):
+        return (
+            EntryPoint(name=item.value.name, value=item.value.value, group=item.name)
+            for item in Sectioned.section_pairs(text or '')
+        )
+
+
+class Deprecated:
+    """
+    Compatibility add-in for mapping to indicate that
+    mapping behavior is deprecated.
+
+    >>> recwarn = getfixture('recwarn')
+    >>> class DeprecatedDict(Deprecated, dict): pass
+    >>> dd = DeprecatedDict(foo='bar')
+    >>> dd.get('baz', None)
+    >>> dd['foo']
+    'bar'
+    >>> list(dd)
+    ['foo']
+    >>> list(dd.keys())
+    ['foo']
+    >>> 'foo' in dd
+    True
+    >>> list(dd.values())
+    ['bar']
+    >>> len(recwarn)
+    1
+    """
+
+    _warn = functools.partial(
+        warnings.warn,
+        "SelectableGroups dict interface is deprecated. Use select.",
+        DeprecationWarning,
+        stacklevel=pypy_partial(2),
+    )
+
+    def __getitem__(self, name):
+        self._warn()
+        return super().__getitem__(name)
+
+    def get(self, name, default=None):
+        self._warn()
+        return super().get(name, default)
+
+    def __iter__(self):
+        self._warn()
+        return super().__iter__()
+
+    def __contains__(self, *args):
+        self._warn()
+        return super().__contains__(*args)
+
+    def keys(self):
+        self._warn()
+        return super().keys()
+
+    def values(self):
+        self._warn()
+        return super().values()
+
+
+class SelectableGroups(Deprecated, dict):
+    """
+    A backward- and forward-compatible result from
+    entry_points that fully implements the dict interface.
+    """
+
+    @classmethod
+    def load(cls, eps):
+        by_group = operator.attrgetter('group')
+        ordered = sorted(eps, key=by_group)
+        grouped = itertools.groupby(ordered, by_group)
+        return cls((group, EntryPoints(eps)) for group, eps in grouped)
+
+    @property
+    def _all(self):
+        """
+        Reconstruct a list of all entrypoints from the groups.
+        """
+        groups = super(Deprecated, self).values()
+        return EntryPoints(itertools.chain.from_iterable(groups))
+
+    @property
+    def groups(self):
+        return self._all.groups
+
+    @property
+    def names(self):
+        """
+        for coverage:
+        >>> SelectableGroups().names
+        set()
+        """
+        return self._all.names
+
+    def select(self, **params):
+        if not params:
+            return self
+        return self._all.select(**params)
+
+
+class PackagePath(pathlib.PurePosixPath):
+    """A reference to a path in a package"""
+
+    def read_text(self, encoding='utf-8'):
+        with self.locate().open(encoding=encoding) as stream:
+            return stream.read()
+
+    def read_binary(self):
+        with self.locate().open('rb') as stream:
+            return stream.read()
+
+    def locate(self):
+        """Return a path-like object for this path"""
+        return self.dist.locate_file(self)
+
+
+class FileHash:
+    def __init__(self, spec):
+        self.mode, _, self.value = spec.partition('=')
+
+    def __repr__(self):
+        return f''
+
+
+class Distribution:
+    """A Python distribution package."""
+
+    @abc.abstractmethod
+    def read_text(self, filename):
+        """Attempt to load metadata file given by the name.
+
+        :param filename: The name of the file in the distribution info.
+        :return: The text if found, otherwise None.
+        """
+
+    @abc.abstractmethod
+    def locate_file(self, path):
+        """
+        Given a path to a file in this distribution, return a path
+        to it.
+        """
+
+    @classmethod
+    def from_name(cls, name):
+        """Return the Distribution for the given package name.
+
+        :param name: The name of the distribution package to search for.
+        :return: The Distribution instance (or subclass thereof) for the named
+            package, if found.
+        :raises PackageNotFoundError: When the named package's distribution
+            metadata cannot be found.
+        """
+        for resolver in cls._discover_resolvers():
+            dists = resolver(DistributionFinder.Context(name=name))
+            dist = next(iter(dists), None)
+            if dist is not None:
+                return dist
+        else:
+            raise PackageNotFoundError(name)
+
+    @classmethod
+    def discover(cls, **kwargs):
+        """Return an iterable of Distribution objects for all packages.
+
+        Pass a ``context`` or pass keyword arguments for constructing
+        a context.
+
+        :context: A ``DistributionFinder.Context`` object.
+        :return: Iterable of Distribution objects for all packages.
+        """
+        context = kwargs.pop('context', None)
+        if context and kwargs:
+            raise ValueError("cannot accept context and kwargs")
+        context = context or DistributionFinder.Context(**kwargs)
+        return itertools.chain.from_iterable(
+            resolver(context) for resolver in cls._discover_resolvers()
+        )
+
+    @staticmethod
+    def at(path):
+        """Return a Distribution for the indicated metadata path
+
+        :param path: a string or path-like object
+        :return: a concrete Distribution instance for the path
+        """
+        return PathDistribution(pathlib.Path(path))
+
+    @staticmethod
+    def _discover_resolvers():
+        """Search the meta_path for resolvers."""
+        declared = (
+            getattr(finder, 'find_distributions', None) for finder in sys.meta_path
+        )
+        return filter(None, declared)
+
+    @classmethod
+    def _local(cls, root='.'):
+        from pep517 import build, meta
+
+        system = build.compat_system(root)
+        builder = functools.partial(
+            meta.build,
+            source_dir=root,
+            system=system,
+        )
+        return PathDistribution(zipp.Path(meta.build_as_zip(builder)))
+
+    @property
+    def metadata(self) -> _meta.PackageMetadata:
+        """Return the parsed metadata for this Distribution.
+
+        The returned object will have keys that name the various bits of
+        metadata.  See PEP 566 for details.
+        """
+        text = (
+            self.read_text('METADATA')
+            or self.read_text('PKG-INFO')
+            # This last clause is here to support old egg-info files.  Its
+            # effect is to just end up using the PathDistribution's self._path
+            # (which points to the egg-info file) attribute unchanged.
+            or self.read_text('')
+        )
+        return _adapters.Message(email.message_from_string(text))
+
+    @property
+    def name(self):
+        """Return the 'Name' metadata for the distribution package."""
+        return self.metadata['Name']
+
+    @property
+    def _normalized_name(self):
+        """Return a normalized version of the name."""
+        return Prepared.normalize(self.name)
+
+    @property
+    def version(self):
+        """Return the 'Version' metadata for the distribution package."""
+        return self.metadata['Version']
+
+    @property
+    def entry_points(self):
+        return EntryPoints._from_text_for(self.read_text('entry_points.txt'), self)
+
+    @property
+    def files(self):
+        """Files in this distribution.
+
+        :return: List of PackagePath for this distribution or None
+
+        Result is `None` if the metadata file that enumerates files
+        (i.e. RECORD for dist-info or SOURCES.txt for egg-info) is
+        missing.
+        Result may be empty if the metadata exists but is empty.
+        """
+
+        def make_file(name, hash=None, size_str=None):
+            result = PackagePath(name)
+            result.hash = FileHash(hash) if hash else None
+            result.size = int(size_str) if size_str else None
+            result.dist = self
+            return result
+
+        @pass_none
+        def make_files(lines):
+            return list(starmap(make_file, csv.reader(lines)))
+
+        return make_files(self._read_files_distinfo() or self._read_files_egginfo())
+
+    def _read_files_distinfo(self):
+        """
+        Read the lines of RECORD
+        """
+        text = self.read_text('RECORD')
+        return text and text.splitlines()
+
+    def _read_files_egginfo(self):
+        """
+        SOURCES.txt might contain literal commas, so wrap each line
+        in quotes.
+        """
+        text = self.read_text('SOURCES.txt')
+        return text and map('"{}"'.format, text.splitlines())
+
+    @property
+    def requires(self):
+        """Generated requirements specified for this Distribution"""
+        reqs = self._read_dist_info_reqs() or self._read_egg_info_reqs()
+        return reqs and list(reqs)
+
+    def _read_dist_info_reqs(self):
+        return self.metadata.get_all('Requires-Dist')
+
+    def _read_egg_info_reqs(self):
+        source = self.read_text('requires.txt')
+        return source and self._deps_from_requires_text(source)
+
+    @classmethod
+    def _deps_from_requires_text(cls, source):
+        return cls._convert_egg_info_reqs_to_simple_reqs(Sectioned.read(source))
+
+    @staticmethod
+    def _convert_egg_info_reqs_to_simple_reqs(sections):
+        """
+        Historically, setuptools would solicit and store 'extra'
+        requirements, including those with environment markers,
+        in separate sections. More modern tools expect each
+        dependency to be defined separately, with any relevant
+        extras and environment markers attached directly to that
+        requirement. This method converts the former to the
+        latter. See _test_deps_from_requires_text for an example.
+        """
+
+        def make_condition(name):
+            return name and f'extra == "{name}"'
+
+        def quoted_marker(section):
+            section = section or ''
+            extra, sep, markers = section.partition(':')
+            if extra and markers:
+                markers = f'({markers})'
+            conditions = list(filter(None, [markers, make_condition(extra)]))
+            return '; ' + ' and '.join(conditions) if conditions else ''
+
+        def url_req_space(req):
+            """
+            PEP 508 requires a space between the url_spec and the quoted_marker.
+            Ref python/importlib_metadata#357.
+            """
+            # '@' is uniquely indicative of a url_req.
+            return ' ' * ('@' in req)
+
+        for section in sections:
+            space = url_req_space(section.value)
+            yield section.value + space + quoted_marker(section.name)
+
+
+class DistributionFinder(MetaPathFinder):
+    """
+    A MetaPathFinder capable of discovering installed distributions.
+    """
+
+    class Context:
+        """
+        Keyword arguments presented by the caller to
+        ``distributions()`` or ``Distribution.discover()``
+        to narrow the scope of a search for distributions
+        in all DistributionFinders.
+
+        Each DistributionFinder may expect any parameters
+        and should attempt to honor the canonical
+        parameters defined below when appropriate.
+        """
+
+        name = None
+        """
+        Specific name for which a distribution finder should match.
+        A name of ``None`` matches all distributions.
+        """
+
+        def __init__(self, **kwargs):
+            vars(self).update(kwargs)
+
+        @property
+        def path(self):
+            """
+            The sequence of directory path that a distribution finder
+            should search.
+
+            Typically refers to Python installed package paths such as
+            "site-packages" directories and defaults to ``sys.path``.
+            """
+            return vars(self).get('path', sys.path)
+
+    @abc.abstractmethod
+    def find_distributions(self, context=Context()):
+        """
+        Find distributions.
+
+        Return an iterable of all Distribution instances capable of
+        loading the metadata for packages matching the ``context``,
+        a DistributionFinder.Context instance.
+        """
+
+
+class FastPath:
+    """
+    Micro-optimized class for searching a path for
+    children.
+
+    >>> FastPath('').children()
+    ['...']
+    """
+
+    @functools.lru_cache()  # type: ignore
+    def __new__(cls, root):
+        return super().__new__(cls)
+
+    def __init__(self, root):
+        self.root = str(root)
+
+    def joinpath(self, child):
+        return pathlib.Path(self.root, child)
+
+    def children(self):
+        with suppress(Exception):
+            return os.listdir(self.root or '.')
+        with suppress(Exception):
+            return self.zip_children()
+        return []
+
+    def zip_children(self):
+        zip_path = zipp.Path(self.root)
+        names = zip_path.root.namelist()
+        self.joinpath = zip_path.joinpath
+
+        return dict.fromkeys(child.split(posixpath.sep, 1)[0] for child in names)
+
+    def search(self, name):
+        return self.lookup(self.mtime).search(name)
+
+    @property
+    def mtime(self):
+        with suppress(OSError):
+            return os.stat(self.root).st_mtime
+        self.lookup.cache_clear()
+
+    @method_cache
+    def lookup(self, mtime):
+        return Lookup(self)
+
+
+class Lookup:
+    def __init__(self, path: FastPath):
+        base = os.path.basename(path.root).lower()
+        base_is_egg = base.endswith(".egg")
+        self.infos = FreezableDefaultDict(list)
+        self.eggs = FreezableDefaultDict(list)
+
+        for child in path.children():
+            low = child.lower()
+            if low.endswith((".dist-info", ".egg-info")):
+                # rpartition is faster than splitext and suitable for this purpose.
+                name = low.rpartition(".")[0].partition("-")[0]
+                normalized = Prepared.normalize(name)
+                self.infos[normalized].append(path.joinpath(child))
+            elif base_is_egg and low == "egg-info":
+                name = base.rpartition(".")[0].partition("-")[0]
+                legacy_normalized = Prepared.legacy_normalize(name)
+                self.eggs[legacy_normalized].append(path.joinpath(child))
+
+        self.infos.freeze()
+        self.eggs.freeze()
+
+    def search(self, prepared):
+        infos = (
+            self.infos[prepared.normalized]
+            if prepared
+            else itertools.chain.from_iterable(self.infos.values())
+        )
+        eggs = (
+            self.eggs[prepared.legacy_normalized]
+            if prepared
+            else itertools.chain.from_iterable(self.eggs.values())
+        )
+        return itertools.chain(infos, eggs)
+
+
+class Prepared:
+    """
+    A prepared search for metadata on a possibly-named package.
+    """
+
+    normalized = None
+    legacy_normalized = None
+
+    def __init__(self, name):
+        self.name = name
+        if name is None:
+            return
+        self.normalized = self.normalize(name)
+        self.legacy_normalized = self.legacy_normalize(name)
+
+    @staticmethod
+    def normalize(name):
+        """
+        PEP 503 normalization plus dashes as underscores.
+        """
+        return re.sub(r"[-_.]+", "-", name).lower().replace('-', '_')
+
+    @staticmethod
+    def legacy_normalize(name):
+        """
+        Normalize the package name as found in the convention in
+        older packaging tools versions and specs.
+        """
+        return name.lower().replace('-', '_')
+
+    def __bool__(self):
+        return bool(self.name)
+
+
+@install
+class MetadataPathFinder(NullFinder, DistributionFinder):
+    """A degenerate finder for distribution packages on the file system.
+
+    This finder supplies only a find_distributions() method for versions
+    of Python that do not have a PathFinder find_distributions().
+    """
+
+    def find_distributions(self, context=DistributionFinder.Context()):
+        """
+        Find distributions.
+
+        Return an iterable of all Distribution instances capable of
+        loading the metadata for packages matching ``context.name``
+        (or all names if ``None`` indicated) along the paths in the list
+        of directories ``context.path``.
+        """
+        found = self._search_paths(context.name, context.path)
+        return map(PathDistribution, found)
+
+    @classmethod
+    def _search_paths(cls, name, paths):
+        """Find metadata directories in paths heuristically."""
+        prepared = Prepared(name)
+        return itertools.chain.from_iterable(
+            path.search(prepared) for path in map(FastPath, paths)
+        )
+
+    def invalidate_caches(cls):
+        FastPath.__new__.cache_clear()
+
+
+class PathDistribution(Distribution):
+    def __init__(self, path: SimplePath):
+        """Construct a distribution.
+
+        :param path: SimplePath indicating the metadata directory.
+        """
+        self._path = path
+
+    def read_text(self, filename):
+        with suppress(
+            FileNotFoundError,
+            IsADirectoryError,
+            KeyError,
+            NotADirectoryError,
+            PermissionError,
+        ):
+            return self._path.joinpath(filename).read_text(encoding='utf-8')
+
+    read_text.__doc__ = Distribution.read_text.__doc__
+
+    def locate_file(self, path):
+        return self._path.parent / path
+
+    @property
+    def _normalized_name(self):
+        """
+        Performance optimization: where possible, resolve the
+        normalized name from the file system path.
+        """
+        stem = os.path.basename(str(self._path))
+        return self._name_from_stem(stem) or super()._normalized_name
+
+    def _name_from_stem(self, stem):
+        name, ext = os.path.splitext(stem)
+        if ext not in ('.dist-info', '.egg-info'):
+            return
+        name, sep, rest = stem.partition('-')
+        return name
+
+
+def distribution(distribution_name):
+    """Get the ``Distribution`` instance for the named package.
+
+    :param distribution_name: The name of the distribution package as a string.
+    :return: A ``Distribution`` instance (or subclass thereof).
+    """
+    return Distribution.from_name(distribution_name)
+
+
+def distributions(**kwargs):
+    """Get all ``Distribution`` instances in the current environment.
+
+    :return: An iterable of ``Distribution`` instances.
+    """
+    return Distribution.discover(**kwargs)
+
+
+def metadata(distribution_name) -> _meta.PackageMetadata:
+    """Get the metadata for the named package.
+
+    :param distribution_name: The name of the distribution package to query.
+    :return: A PackageMetadata containing the parsed metadata.
+    """
+    return Distribution.from_name(distribution_name).metadata
+
+
+def version(distribution_name):
+    """Get the version string for the named package.
+
+    :param distribution_name: The name of the distribution package to query.
+    :return: The version string for the package as defined in the package's
+        "Version" metadata key.
+    """
+    return distribution(distribution_name).version
+
+
+def entry_points(**params) -> Union[EntryPoints, SelectableGroups]:
+    """Return EntryPoint objects for all installed packages.
+
+    Pass selection parameters (group or name) to filter the
+    result to entry points matching those properties (see
+    EntryPoints.select()).
+
+    For compatibility, returns ``SelectableGroups`` object unless
+    selection parameters are supplied. In the future, this function
+    will return ``EntryPoints`` instead of ``SelectableGroups``
+    even when no selection parameters are supplied.
+
+    For maximum future compatibility, pass selection parameters
+    or invoke ``.select`` with parameters on the result.
+
+    :return: EntryPoints or SelectableGroups for all installed packages.
+    """
+    norm_name = operator.attrgetter('_normalized_name')
+    unique = functools.partial(unique_everseen, key=norm_name)
+    eps = itertools.chain.from_iterable(
+        dist.entry_points for dist in unique(distributions())
+    )
+    return SelectableGroups.load(eps).select(**params)
+
+
+def files(distribution_name):
+    """Return a list of files for the named package.
+
+    :param distribution_name: The name of the distribution package to query.
+    :return: List of files composing the distribution.
+    """
+    return distribution(distribution_name).files
+
+
+def requires(distribution_name):
+    """
+    Return a list of requirements for the named package.
+
+    :return: An iterator of requirements, suitable for
+        packaging.requirement.Requirement.
+    """
+    return distribution(distribution_name).requires
+
+
+def packages_distributions() -> Mapping[str, List[str]]:
+    """
+    Return a mapping of top-level packages to their
+    distributions.
+
+    >>> import collections.abc
+    >>> pkgs = packages_distributions()
+    >>> all(isinstance(dist, collections.abc.Sequence) for dist in pkgs.values())
+    True
+    """
+    pkg_to_dist = collections.defaultdict(list)
+    for dist in distributions():
+        for pkg in _top_level_declared(dist) or _top_level_inferred(dist):
+            pkg_to_dist[pkg].append(dist.metadata['Name'])
+    return dict(pkg_to_dist)
+
+
+def _top_level_declared(dist):
+    return (dist.read_text('top_level.txt') or '').split()
+
+
+def _top_level_inferred(dist):
+    return {
+        f.parts[0] if len(f.parts) > 1 else f.with_suffix('').name
+        for f in always_iterable(dist.files)
+        if f.suffix == ".py"
+    }
diff --git a/metaflow/_vendor/v3_7/importlib_metadata/_adapters.py b/metaflow/_vendor/v3_7/importlib_metadata/_adapters.py
new file mode 100644
index 00000000000..aa460d3eda5
--- /dev/null
+++ b/metaflow/_vendor/v3_7/importlib_metadata/_adapters.py
@@ -0,0 +1,68 @@
+import re
+import textwrap
+import email.message
+
+from ._text import FoldedCase
+
+
+class Message(email.message.Message):
+    multiple_use_keys = set(
+        map(
+            FoldedCase,
+            [
+                'Classifier',
+                'Obsoletes-Dist',
+                'Platform',
+                'Project-URL',
+                'Provides-Dist',
+                'Provides-Extra',
+                'Requires-Dist',
+                'Requires-External',
+                'Supported-Platform',
+                'Dynamic',
+            ],
+        )
+    )
+    """
+    Keys that may be indicated multiple times per PEP 566.
+    """
+
+    def __new__(cls, orig: email.message.Message):
+        res = super().__new__(cls)
+        vars(res).update(vars(orig))
+        return res
+
+    def __init__(self, *args, **kwargs):
+        self._headers = self._repair_headers()
+
+    # suppress spurious error from mypy
+    def __iter__(self):
+        return super().__iter__()
+
+    def _repair_headers(self):
+        def redent(value):
+            "Correct for RFC822 indentation"
+            if not value or '\n' not in value:
+                return value
+            return textwrap.dedent(' ' * 8 + value)
+
+        headers = [(key, redent(value)) for key, value in vars(self)['_headers']]
+        if self._payload:
+            headers.append(('Description', self.get_payload()))
+        return headers
+
+    @property
+    def json(self):
+        """
+        Convert PackageMetadata to a JSON-compatible format
+        per PEP 0566.
+        """
+
+        def transform(key):
+            value = self.get_all(key) if key in self.multiple_use_keys else self[key]
+            if key == 'Keywords':
+                value = re.split(r'\s+', value)
+            tk = key.lower().replace('-', '_')
+            return tk, value
+
+        return dict(map(transform, map(FoldedCase, self)))
diff --git a/metaflow/_vendor/v3_7/importlib_metadata/_collections.py b/metaflow/_vendor/v3_7/importlib_metadata/_collections.py
new file mode 100644
index 00000000000..cf0954e1a30
--- /dev/null
+++ b/metaflow/_vendor/v3_7/importlib_metadata/_collections.py
@@ -0,0 +1,30 @@
+import collections
+
+
+# from jaraco.collections 3.3
+class FreezableDefaultDict(collections.defaultdict):
+    """
+    Often it is desirable to prevent the mutation of
+    a default dict after its initial construction, such
+    as to prevent mutation during iteration.
+
+    >>> dd = FreezableDefaultDict(list)
+    >>> dd[0].append('1')
+    >>> dd.freeze()
+    >>> dd[1]
+    []
+    >>> len(dd)
+    1
+    """
+
+    def __missing__(self, key):
+        return getattr(self, '_frozen', super().__missing__)(key)
+
+    def freeze(self):
+        self._frozen = lambda key: self.default_factory()
+
+
+class Pair(collections.namedtuple('Pair', 'name value')):
+    @classmethod
+    def parse(cls, text):
+        return cls(*map(str.strip, text.split("=", 1)))
diff --git a/metaflow/_vendor/v3_7/importlib_metadata/_compat.py b/metaflow/_vendor/v3_7/importlib_metadata/_compat.py
new file mode 100644
index 00000000000..173eebe017c
--- /dev/null
+++ b/metaflow/_vendor/v3_7/importlib_metadata/_compat.py
@@ -0,0 +1,71 @@
+import sys
+import platform
+
+
+__all__ = ['install', 'NullFinder', 'Protocol']
+
+
+try:
+    from typing import Protocol
+except ImportError:  # pragma: no cover
+    from metaflow._vendor.v3_7.typing_extensions import Protocol  # type: ignore
+
+
+def install(cls):
+    """
+    Class decorator for installation on sys.meta_path.
+
+    Adds the backport DistributionFinder to sys.meta_path and
+    attempts to disable the finder functionality of the stdlib
+    DistributionFinder.
+    """
+    sys.meta_path.append(cls())
+    disable_stdlib_finder()
+    return cls
+
+
+def disable_stdlib_finder():
+    """
+    Give the backport primacy for discovering path-based distributions
+    by monkey-patching the stdlib O_O.
+
+    See #91 for more background for rationale on this sketchy
+    behavior.
+    """
+
+    def matches(finder):
+        return getattr(
+            finder, '__module__', None
+        ) == '_frozen_importlib_external' and hasattr(finder, 'find_distributions')
+
+    for finder in filter(matches, sys.meta_path):  # pragma: nocover
+        del finder.find_distributions
+
+
+class NullFinder:
+    """
+    A "Finder" (aka "MetaClassFinder") that never finds any modules,
+    but may find distributions.
+    """
+
+    @staticmethod
+    def find_spec(*args, **kwargs):
+        return None
+
+    # In Python 2, the import system requires finders
+    # to have a find_module() method, but this usage
+    # is deprecated in Python 3 in favor of find_spec().
+    # For the purposes of this finder (i.e. being present
+    # on sys.meta_path but having no other import
+    # system functionality), the two methods are identical.
+    find_module = find_spec
+
+
+def pypy_partial(val):
+    """
+    Adjust for variable stacklevel on partial under PyPy.
+
+    Workaround for #327.
+    """
+    is_pypy = platform.python_implementation() == 'PyPy'
+    return val + is_pypy
diff --git a/metaflow/_vendor/v3_7/importlib_metadata/_functools.py b/metaflow/_vendor/v3_7/importlib_metadata/_functools.py
new file mode 100644
index 00000000000..71f66bd03cb
--- /dev/null
+++ b/metaflow/_vendor/v3_7/importlib_metadata/_functools.py
@@ -0,0 +1,104 @@
+import types
+import functools
+
+
+# from jaraco.functools 3.3
+def method_cache(method, cache_wrapper=None):
+    """
+    Wrap lru_cache to support storing the cache data in the object instances.
+
+    Abstracts the common paradigm where the method explicitly saves an
+    underscore-prefixed protected property on first call and returns that
+    subsequently.
+
+    >>> class MyClass:
+    ...     calls = 0
+    ...
+    ...     @method_cache
+    ...     def method(self, value):
+    ...         self.calls += 1
+    ...         return value
+
+    >>> a = MyClass()
+    >>> a.method(3)
+    3
+    >>> for x in range(75):
+    ...     res = a.method(x)
+    >>> a.calls
+    75
+
+    Note that the apparent behavior will be exactly like that of lru_cache
+    except that the cache is stored on each instance, so values in one
+    instance will not flush values from another, and when an instance is
+    deleted, so are the cached values for that instance.
+
+    >>> b = MyClass()
+    >>> for x in range(35):
+    ...     res = b.method(x)
+    >>> b.calls
+    35
+    >>> a.method(0)
+    0
+    >>> a.calls
+    75
+
+    Note that if method had been decorated with ``functools.lru_cache()``,
+    a.calls would have been 76 (due to the cached value of 0 having been
+    flushed by the 'b' instance).
+
+    Clear the cache with ``.cache_clear()``
+
+    >>> a.method.cache_clear()
+
+    Same for a method that hasn't yet been called.
+
+    >>> c = MyClass()
+    >>> c.method.cache_clear()
+
+    Another cache wrapper may be supplied:
+
+    >>> cache = functools.lru_cache(maxsize=2)
+    >>> MyClass.method2 = method_cache(lambda self: 3, cache_wrapper=cache)
+    >>> a = MyClass()
+    >>> a.method2()
+    3
+
+    Caution - do not subsequently wrap the method with another decorator, such
+    as ``@property``, which changes the semantics of the function.
+
+    See also
+    http://code.activestate.com/recipes/577452-a-memoize-decorator-for-instance-methods/
+    for another implementation and additional justification.
+    """
+    cache_wrapper = cache_wrapper or functools.lru_cache()
+
+    def wrapper(self, *args, **kwargs):
+        # it's the first call, replace the method with a cached, bound method
+        bound_method = types.MethodType(method, self)
+        cached_method = cache_wrapper(bound_method)
+        setattr(self, method.__name__, cached_method)
+        return cached_method(*args, **kwargs)
+
+    # Support cache clear even before cache has been created.
+    wrapper.cache_clear = lambda: None
+
+    return wrapper
+
+
+# From jaraco.functools 3.3
+def pass_none(func):
+    """
+    Wrap func so it's not called if its first param is None
+
+    >>> print_text = pass_none(print)
+    >>> print_text('text')
+    text
+    >>> print_text(None)
+    """
+
+    @functools.wraps(func)
+    def wrapper(param, *args, **kwargs):
+        if param is not None:
+            return func(param, *args, **kwargs)
+
+    return wrapper
diff --git a/metaflow/_vendor/v3_7/importlib_metadata/_itertools.py b/metaflow/_vendor/v3_7/importlib_metadata/_itertools.py
new file mode 100644
index 00000000000..d4ca9b9140e
--- /dev/null
+++ b/metaflow/_vendor/v3_7/importlib_metadata/_itertools.py
@@ -0,0 +1,73 @@
+from itertools import filterfalse
+
+
+def unique_everseen(iterable, key=None):
+    "List unique elements, preserving order. Remember all elements ever seen."
+    # unique_everseen('AAAABBBCCDAABBB') --> A B C D
+    # unique_everseen('ABBCcAD', str.lower) --> A B C D
+    seen = set()
+    seen_add = seen.add
+    if key is None:
+        for element in filterfalse(seen.__contains__, iterable):
+            seen_add(element)
+            yield element
+    else:
+        for element in iterable:
+            k = key(element)
+            if k not in seen:
+                seen_add(k)
+                yield element
+
+
+# copied from more_itertools 8.8
+def always_iterable(obj, base_type=(str, bytes)):
+    """If *obj* is iterable, return an iterator over its items::
+
+        >>> obj = (1, 2, 3)
+        >>> list(always_iterable(obj))
+        [1, 2, 3]
+
+    If *obj* is not iterable, return a one-item iterable containing *obj*::
+
+        >>> obj = 1
+        >>> list(always_iterable(obj))
+        [1]
+
+    If *obj* is ``None``, return an empty iterable:
+
+        >>> obj = None
+        >>> list(always_iterable(None))
+        []
+
+    By default, binary and text strings are not considered iterable::
+
+        >>> obj = 'foo'
+        >>> list(always_iterable(obj))
+        ['foo']
+
+    If *base_type* is set, objects for which ``isinstance(obj, base_type)``
+    returns ``True`` won't be considered iterable.
+
+        >>> obj = {'a': 1}
+        >>> list(always_iterable(obj))  # Iterate over the dict's keys
+        ['a']
+        >>> list(always_iterable(obj, base_type=dict))  # Treat dicts as a unit
+        [{'a': 1}]
+
+    Set *base_type* to ``None`` to avoid any special handling and treat objects
+    Python considers iterable as iterable:
+
+        >>> obj = 'foo'
+        >>> list(always_iterable(obj, base_type=None))
+        ['f', 'o', 'o']
+    """
+    if obj is None:
+        return iter(())
+
+    if (base_type is not None) and isinstance(obj, base_type):
+        return iter((obj,))
+
+    try:
+        return iter(obj)
+    except TypeError:
+        return iter((obj,))
diff --git a/metaflow/_vendor/v3_7/importlib_metadata/_meta.py b/metaflow/_vendor/v3_7/importlib_metadata/_meta.py
new file mode 100644
index 00000000000..37ee43e6ef4
--- /dev/null
+++ b/metaflow/_vendor/v3_7/importlib_metadata/_meta.py
@@ -0,0 +1,48 @@
+from ._compat import Protocol
+from typing import Any, Dict, Iterator, List, TypeVar, Union
+
+
+_T = TypeVar("_T")
+
+
+class PackageMetadata(Protocol):
+    def __len__(self) -> int:
+        ...  # pragma: no cover
+
+    def __contains__(self, item: str) -> bool:
+        ...  # pragma: no cover
+
+    def __getitem__(self, key: str) -> str:
+        ...  # pragma: no cover
+
+    def __iter__(self) -> Iterator[str]:
+        ...  # pragma: no cover
+
+    def get_all(self, name: str, failobj: _T = ...) -> Union[List[Any], _T]:
+        """
+        Return all values associated with a possibly multi-valued key.
+        """
+
+    @property
+    def json(self) -> Dict[str, Union[str, List[str]]]:
+        """
+        A JSON-compatible form of the metadata.
+        """
+
+
+class SimplePath(Protocol):
+    """
+    A minimal subset of pathlib.Path required by PathDistribution.
+    """
+
+    def joinpath(self) -> 'SimplePath':
+        ...  # pragma: no cover
+
+    def __truediv__(self) -> 'SimplePath':
+        ...  # pragma: no cover
+
+    def parent(self) -> 'SimplePath':
+        ...  # pragma: no cover
+
+    def read_text(self) -> str:
+        ...  # pragma: no cover
diff --git a/metaflow/_vendor/v3_7/importlib_metadata/_text.py b/metaflow/_vendor/v3_7/importlib_metadata/_text.py
new file mode 100644
index 00000000000..c88cfbb2349
--- /dev/null
+++ b/metaflow/_vendor/v3_7/importlib_metadata/_text.py
@@ -0,0 +1,99 @@
+import re
+
+from ._functools import method_cache
+
+
+# from jaraco.text 3.5
+class FoldedCase(str):
+    """
+    A case insensitive string class; behaves just like str
+    except compares equal when the only variation is case.
+
+    >>> s = FoldedCase('hello world')
+
+    >>> s == 'Hello World'
+    True
+
+    >>> 'Hello World' == s
+    True
+
+    >>> s != 'Hello World'
+    False
+
+    >>> s.index('O')
+    4
+
+    >>> s.split('O')
+    ['hell', ' w', 'rld']
+
+    >>> sorted(map(FoldedCase, ['GAMMA', 'alpha', 'Beta']))
+    ['alpha', 'Beta', 'GAMMA']
+
+    Sequence membership is straightforward.
+
+    >>> "Hello World" in [s]
+    True
+    >>> s in ["Hello World"]
+    True
+
+    You may test for set inclusion, but candidate and elements
+    must both be folded.
+
+    >>> FoldedCase("Hello World") in {s}
+    True
+    >>> s in {FoldedCase("Hello World")}
+    True
+
+    String inclusion works as long as the FoldedCase object
+    is on the right.
+
+    >>> "hello" in FoldedCase("Hello World")
+    True
+
+    But not if the FoldedCase object is on the left:
+
+    >>> FoldedCase('hello') in 'Hello World'
+    False
+
+    In that case, use in_:
+
+    >>> FoldedCase('hello').in_('Hello World')
+    True
+
+    >>> FoldedCase('hello') > FoldedCase('Hello')
+    False
+    """
+
+    def __lt__(self, other):
+        return self.lower() < other.lower()
+
+    def __gt__(self, other):
+        return self.lower() > other.lower()
+
+    def __eq__(self, other):
+        return self.lower() == other.lower()
+
+    def __ne__(self, other):
+        return self.lower() != other.lower()
+
+    def __hash__(self):
+        return hash(self.lower())
+
+    def __contains__(self, other):
+        return super().lower().__contains__(other.lower())
+
+    def in_(self, other):
+        "Does self appear in other?"
+        return self in FoldedCase(other)
+
+    # cache lower since it's likely to be called frequently.
+    @method_cache
+    def lower(self):
+        return super().lower()
+
+    def index(self, sub):
+        return self.lower().index(sub.lower())
+
+    def split(self, splitter=' ', maxsplit=0):
+        pattern = re.compile(re.escape(splitter), re.I)
+        return pattern.split(self, maxsplit)
diff --git a/metaflow/_vendor/v3_7/importlib_metadata/py.typed b/metaflow/_vendor/v3_7/importlib_metadata/py.typed
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/metaflow/_vendor/v3_7/typeguard.LICENSE b/metaflow/_vendor/v3_7/typeguard.LICENSE
new file mode 100644
index 00000000000..07806f8af9d
--- /dev/null
+++ b/metaflow/_vendor/v3_7/typeguard.LICENSE
@@ -0,0 +1,19 @@
+This is the MIT license: http://www.opensource.org/licenses/mit-license.php
+
+Copyright (c) Alex GrΓΆnholm
+
+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/metaflow/_vendor/v3_7/typeguard/__init__.py b/metaflow/_vendor/v3_7/typeguard/__init__.py
new file mode 100644
index 00000000000..6781cad094b
--- /dev/null
+++ b/metaflow/_vendor/v3_7/typeguard/__init__.py
@@ -0,0 +1,48 @@
+import os
+from typing import Any
+
+from ._checkers import TypeCheckerCallable as TypeCheckerCallable
+from ._checkers import TypeCheckLookupCallback as TypeCheckLookupCallback
+from ._checkers import check_type_internal as check_type_internal
+from ._checkers import checker_lookup_functions as checker_lookup_functions
+from ._checkers import load_plugins as load_plugins
+from ._config import CollectionCheckStrategy as CollectionCheckStrategy
+from ._config import ForwardRefPolicy as ForwardRefPolicy
+from ._config import TypeCheckConfiguration as TypeCheckConfiguration
+from ._decorators import typechecked as typechecked
+from ._decorators import typeguard_ignore as typeguard_ignore
+from ._exceptions import InstrumentationWarning as InstrumentationWarning
+from ._exceptions import TypeCheckError as TypeCheckError
+from ._exceptions import TypeCheckWarning as TypeCheckWarning
+from ._exceptions import TypeHintWarning as TypeHintWarning
+from ._functions import TypeCheckFailCallback as TypeCheckFailCallback
+from ._functions import check_type as check_type
+from ._functions import warn_on_error as warn_on_error
+from ._importhook import ImportHookManager as ImportHookManager
+from ._importhook import TypeguardFinder as TypeguardFinder
+from ._importhook import install_import_hook as install_import_hook
+from ._memo import TypeCheckMemo as TypeCheckMemo
+from ._suppression import suppress_type_checks as suppress_type_checks
+from ._utils import Unset as Unset
+
+# Re-export imports so they look like they live directly in this package
+for value in list(locals().values()):
+    if getattr(value, "__module__", "").startswith(f"{__name__}."):
+        value.__module__ = __name__
+
+
+config: TypeCheckConfiguration
+
+
+def __getattr__(name: str) -> Any:
+    if name == "config":
+        from ._config import global_config
+
+        return global_config
+
+    raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+
+
+# Automatically load checker lookup functions unless explicitly disabled
+if "TYPEGUARD_DISABLE_PLUGIN_AUTOLOAD" not in os.environ:
+    load_plugins()
diff --git a/metaflow/_vendor/v3_7/typeguard/_checkers.py b/metaflow/_vendor/v3_7/typeguard/_checkers.py
new file mode 100644
index 00000000000..d329b59f1a1
--- /dev/null
+++ b/metaflow/_vendor/v3_7/typeguard/_checkers.py
@@ -0,0 +1,906 @@
+from __future__ import annotations
+
+import collections.abc
+import inspect
+import sys
+import types
+import typing
+import warnings
+from enum import Enum
+from inspect import Parameter, isclass, isfunction
+from io import BufferedIOBase, IOBase, RawIOBase, TextIOBase
+from textwrap import indent
+from typing import (
+    IO,
+    AbstractSet,
+    Any,
+    BinaryIO,
+    Callable,
+    Dict,
+    ForwardRef,
+    List,
+    Mapping,
+    MutableMapping,
+    NewType,
+    Optional,
+    Sequence,
+    Set,
+    TextIO,
+    Tuple,
+    Type,
+    TypeVar,
+    Union,
+)
+from unittest.mock import Mock
+
+try:
+    from metaflow._vendor.v3_7 import typing_extensions
+except ImportError:
+    typing_extensions = None  # type: ignore[assignment]
+
+from ._config import ForwardRefPolicy
+from ._exceptions import TypeCheckError, TypeHintWarning
+from ._memo import TypeCheckMemo
+from ._utils import evaluate_forwardref, get_stacklevel, get_type_name, qualified_name
+
+if sys.version_info >= (3, 11):
+    from typing import (
+        Annotated,
+        TypeAlias,
+        get_args,
+        get_origin,
+        get_type_hints,
+        is_typeddict,
+    )
+
+    SubclassableAny = Any
+else:
+    from metaflow._vendor.v3_7.typing_extensions import (
+        Annotated,
+        TypeAlias,
+        get_args,
+        get_origin,
+        get_type_hints,
+        is_typeddict,
+    )
+    from metaflow._vendor.v3_7.typing_extensions import Any as SubclassableAny
+
+if sys.version_info >= (3, 10):
+    from importlib.metadata import entry_points
+    from typing import ParamSpec
+else:
+    from metaflow._vendor.v3_7.importlib_metadata import entry_points
+    from metaflow._vendor.v3_7.typing_extensions import ParamSpec
+
+TypeCheckerCallable: TypeAlias = Callable[
+    [Any, Any, Tuple[Any, ...], TypeCheckMemo], Any
+]
+TypeCheckLookupCallback: TypeAlias = Callable[
+    [Any, Tuple[Any, ...], Tuple[Any, ...]], Optional[TypeCheckerCallable]
+]
+
+checker_lookup_functions: list[TypeCheckLookupCallback] = []
+
+
+# Sentinel
+_missing = object()
+
+# Lifted from mypy.sharedparse
+BINARY_MAGIC_METHODS = {
+    "__add__",
+    "__and__",
+    "__cmp__",
+    "__divmod__",
+    "__div__",
+    "__eq__",
+    "__floordiv__",
+    "__ge__",
+    "__gt__",
+    "__iadd__",
+    "__iand__",
+    "__idiv__",
+    "__ifloordiv__",
+    "__ilshift__",
+    "__imatmul__",
+    "__imod__",
+    "__imul__",
+    "__ior__",
+    "__ipow__",
+    "__irshift__",
+    "__isub__",
+    "__itruediv__",
+    "__ixor__",
+    "__le__",
+    "__lshift__",
+    "__lt__",
+    "__matmul__",
+    "__mod__",
+    "__mul__",
+    "__ne__",
+    "__or__",
+    "__pow__",
+    "__radd__",
+    "__rand__",
+    "__rdiv__",
+    "__rfloordiv__",
+    "__rlshift__",
+    "__rmatmul__",
+    "__rmod__",
+    "__rmul__",
+    "__ror__",
+    "__rpow__",
+    "__rrshift__",
+    "__rshift__",
+    "__rsub__",
+    "__rtruediv__",
+    "__rxor__",
+    "__sub__",
+    "__truediv__",
+    "__xor__",
+}
+
+
+def check_callable(
+    value: Any,
+    origin_type: Any,
+    args: tuple[Any, ...],
+    memo: TypeCheckMemo,
+) -> None:
+    if not callable(value):
+        raise TypeCheckError("is not callable")
+
+    if args:
+        try:
+            signature = inspect.signature(value)
+        except (TypeError, ValueError):
+            return
+
+        argument_types = args[0]
+        if isinstance(argument_types, list) and not any(
+            type(item) is ParamSpec for item in argument_types
+        ):
+            # The callable must not have keyword-only arguments without defaults
+            unfulfilled_kwonlyargs = [
+                param.name
+                for param in signature.parameters.values()
+                if param.kind == Parameter.KEYWORD_ONLY
+                and param.default == Parameter.empty
+            ]
+            if unfulfilled_kwonlyargs:
+                raise TypeCheckError(
+                    f"has mandatory keyword-only arguments in its declaration: "
+                    f'{", ".join(unfulfilled_kwonlyargs)}'
+                )
+
+            num_mandatory_args = len(
+                [
+                    param.name
+                    for param in signature.parameters.values()
+                    if param.kind
+                    in (Parameter.POSITIONAL_ONLY, Parameter.POSITIONAL_OR_KEYWORD)
+                    and param.default is Parameter.empty
+                ]
+            )
+            has_varargs = any(
+                param
+                for param in signature.parameters.values()
+                if param.kind == Parameter.VAR_POSITIONAL
+            )
+
+            if num_mandatory_args > len(argument_types):
+                raise TypeCheckError(
+                    f"has too many arguments in its declaration; expected "
+                    f"{len(argument_types)} but {num_mandatory_args} argument(s) "
+                    f"declared"
+                )
+            elif not has_varargs and num_mandatory_args < len(argument_types):
+                raise TypeCheckError(
+                    f"has too few arguments in its declaration; expected "
+                    f"{len(argument_types)} but {num_mandatory_args} argument(s) "
+                    f"declared"
+                )
+
+
+def check_mapping(
+    value: Any,
+    origin_type: Any,
+    args: tuple[Any, ...],
+    memo: TypeCheckMemo,
+) -> None:
+    if origin_type is Dict or origin_type is dict:
+        if not isinstance(value, dict):
+            raise TypeCheckError("is not a dict")
+    if origin_type is MutableMapping or origin_type is collections.abc.MutableMapping:
+        if not isinstance(value, collections.abc.MutableMapping):
+            raise TypeCheckError("is not a mutable mapping")
+    elif not isinstance(value, collections.abc.Mapping):
+        raise TypeCheckError("is not a mapping")
+
+    if args:
+        key_type, value_type = args
+        if key_type is not Any or value_type is not Any:
+            samples = memo.config.collection_check_strategy.iterate_samples(
+                value.items()
+            )
+            for k, v in samples:
+                try:
+                    check_type_internal(k, key_type, memo)
+                except TypeCheckError as exc:
+                    exc.append_path_element(f"key {k!r}")
+                    raise
+
+                try:
+                    check_type_internal(v, value_type, memo)
+                except TypeCheckError as exc:
+                    exc.append_path_element(f"value of key {k!r}")
+                    raise
+
+
+def check_typed_dict(
+    value: Any,
+    origin_type: Any,
+    args: tuple[Any, ...],
+    memo: TypeCheckMemo,
+) -> None:
+    if not isinstance(value, dict):
+        raise TypeCheckError("is not a dict")
+
+    declared_keys = frozenset(origin_type.__annotations__)
+    if hasattr(origin_type, "__required_keys__"):
+        required_keys = origin_type.__required_keys__
+    else:  # py3.8 and lower
+        required_keys = declared_keys if origin_type.__total__ else frozenset()
+
+    existing_keys = frozenset(value)
+    extra_keys = existing_keys - declared_keys
+    if extra_keys:
+        keys_formatted = ", ".join(f'"{key}"' for key in sorted(extra_keys, key=repr))
+        raise TypeCheckError(f"has unexpected extra key(s): {keys_formatted}")
+
+    missing_keys = required_keys - existing_keys
+    if missing_keys:
+        keys_formatted = ", ".join(f'"{key}"' for key in sorted(missing_keys, key=repr))
+        raise TypeCheckError(f"is missing required key(s): {keys_formatted}")
+
+    for key, argtype in get_type_hints(origin_type).items():
+        argvalue = value.get(key, _missing)
+        if argvalue is not _missing:
+            try:
+                check_type_internal(argvalue, argtype, memo)
+            except TypeCheckError as exc:
+                exc.append_path_element(f"value of key {key!r}")
+                raise
+
+
+def check_list(
+    value: Any,
+    origin_type: Any,
+    args: tuple[Any, ...],
+    memo: TypeCheckMemo,
+) -> None:
+    if not isinstance(value, list):
+        raise TypeCheckError("is not a list")
+
+    if args and args != (Any,):
+        samples = memo.config.collection_check_strategy.iterate_samples(value)
+        for i, v in enumerate(samples):
+            try:
+                check_type_internal(v, args[0], memo)
+            except TypeCheckError as exc:
+                exc.append_path_element(f"item {i}")
+                raise
+
+
+def check_sequence(
+    value: Any,
+    origin_type: Any,
+    args: tuple[Any, ...],
+    memo: TypeCheckMemo,
+) -> None:
+    if not isinstance(value, collections.abc.Sequence):
+        raise TypeCheckError("is not a sequence")
+
+    if args and args != (Any,):
+        samples = memo.config.collection_check_strategy.iterate_samples(value)
+        for i, v in enumerate(samples):
+            try:
+                check_type_internal(v, args[0], memo)
+            except TypeCheckError as exc:
+                exc.append_path_element(f"item {i}")
+                raise
+
+
+def check_set(
+    value: Any,
+    origin_type: Any,
+    args: tuple[Any, ...],
+    memo: TypeCheckMemo,
+) -> None:
+    if origin_type is frozenset:
+        if not isinstance(value, frozenset):
+            raise TypeCheckError("is not a frozenset")
+    elif not isinstance(value, AbstractSet):
+        raise TypeCheckError("is not a set")
+
+    if args and args != (Any,):
+        samples = memo.config.collection_check_strategy.iterate_samples(value)
+        for v in samples:
+            try:
+                check_type_internal(v, args[0], memo)
+            except TypeCheckError as exc:
+                exc.append_path_element(f"[{v}]")
+                raise
+
+
+def check_tuple(
+    value: Any,
+    origin_type: Any,
+    args: tuple[Any, ...],
+    memo: TypeCheckMemo,
+) -> None:
+    # Specialized check for NamedTuples
+    field_types = getattr(origin_type, "__annotations__", None)
+    if field_types is None and sys.version_info < (3, 8):
+        field_types = getattr(origin_type, "_field_types", None)
+
+    if field_types:
+        if not isinstance(value, origin_type):
+            raise TypeCheckError(
+                f"is not a named tuple of type {qualified_name(origin_type)}"
+            )
+
+        for name, field_type in field_types.items():
+            try:
+                check_type_internal(getattr(value, name), field_type, memo)
+            except TypeCheckError as exc:
+                exc.append_path_element(f"attribute {name!r}")
+                raise
+
+        return
+    elif not isinstance(value, tuple):
+        raise TypeCheckError("is not a tuple")
+
+    if args:
+        # Python 3.6+
+        use_ellipsis = args[-1] is Ellipsis
+        tuple_params = args[: -1 if use_ellipsis else None]
+    else:
+        # Unparametrized Tuple or plain tuple
+        return
+
+    if use_ellipsis:
+        element_type = tuple_params[0]
+        samples = memo.config.collection_check_strategy.iterate_samples(value)
+        for i, element in enumerate(samples):
+            try:
+                check_type_internal(element, element_type, memo)
+            except TypeCheckError as exc:
+                exc.append_path_element(f"item {i}")
+                raise
+    elif tuple_params == ((),):
+        if value != ():
+            raise TypeCheckError("is not an empty tuple")
+    else:
+        if len(value) != len(tuple_params):
+            raise TypeCheckError(
+                f"has wrong number of elements (expected {len(tuple_params)}, got "
+                f"{len(value)} instead)"
+            )
+
+        for i, (element, element_type) in enumerate(zip(value, tuple_params)):
+            try:
+                check_type_internal(element, element_type, memo)
+            except TypeCheckError as exc:
+                exc.append_path_element(f"item {i}")
+                raise
+
+
+def check_union(
+    value: Any,
+    origin_type: Any,
+    args: tuple[Any, ...],
+    memo: TypeCheckMemo,
+) -> None:
+    errors: dict[str, TypeCheckError] = {}
+    for type_ in args:
+        try:
+            check_type_internal(value, type_, memo)
+            return
+        except TypeCheckError as exc:
+            errors[get_type_name(type_)] = exc
+
+    formatted_errors = indent(
+        "\n".join(f"{key}: {error}" for key, error in errors.items()), "  "
+    )
+    raise TypeCheckError(f"did not match any element in the union:\n{formatted_errors}")
+
+
+def check_uniontype(
+    value: Any,
+    origin_type: Any,
+    args: tuple[Any, ...],
+    memo: TypeCheckMemo,
+) -> None:
+    errors: dict[str, TypeCheckError] = {}
+    for type_ in args:
+        try:
+            check_type_internal(value, type_, memo)
+            return
+        except TypeCheckError as exc:
+            errors[get_type_name(type_)] = exc
+
+    formatted_errors = indent(
+        "\n".join(f"{key}: {error}" for key, error in errors.items()), "  "
+    )
+    raise TypeCheckError(f"did not match any element in the union:\n{formatted_errors}")
+
+
+def check_class(
+    value: Any,
+    origin_type: Any,
+    args: tuple[Any, ...],
+    memo: TypeCheckMemo,
+) -> None:
+    if not isclass(value):
+        raise TypeCheckError("is not a class")
+
+    # Needed on Python 3.7+
+    if not args:
+        return
+
+    if isinstance(args[0], ForwardRef):
+        expected_class = evaluate_forwardref(args[0], memo)
+    else:
+        expected_class = args[0]
+
+    if expected_class is Any:
+        return
+    elif getattr(expected_class, "_is_protocol", False):
+        check_protocol(value, expected_class, (), memo)
+    elif isinstance(expected_class, TypeVar):
+        check_typevar(value, expected_class, (), memo, subclass_check=True)
+    elif get_origin(expected_class) is Union:
+        errors: dict[str, TypeCheckError] = {}
+        for arg in get_args(expected_class):
+            if arg is Any:
+                return
+
+            try:
+                check_class(value, type, (arg,), memo)
+                return
+            except TypeCheckError as exc:
+                errors[get_type_name(arg)] = exc
+        else:
+            formatted_errors = indent(
+                "\n".join(f"{key}: {error}" for key, error in errors.items()), "  "
+            )
+            raise TypeCheckError(
+                f"did not match any element in the union:\n{formatted_errors}"
+            )
+    elif not issubclass(value, expected_class):
+        raise TypeCheckError(f"is not a subclass of {qualified_name(expected_class)}")
+
+
+def check_newtype(
+    value: Any,
+    origin_type: Any,
+    args: tuple[Any, ...],
+    memo: TypeCheckMemo,
+) -> None:
+    check_type_internal(value, origin_type.__supertype__, memo)
+
+
+def check_instance(
+    value: Any,
+    origin_type: Any,
+    args: tuple[Any, ...],
+    memo: TypeCheckMemo,
+) -> None:
+    if not isinstance(value, origin_type):
+        raise TypeCheckError(f"is not an instance of {qualified_name(origin_type)}")
+
+
+def check_typevar(
+    value: Any,
+    origin_type: TypeVar,
+    args: tuple[Any, ...],
+    memo: TypeCheckMemo,
+    *,
+    subclass_check: bool = False,
+) -> None:
+    if origin_type.__bound__ is not None:
+        annotation = (
+            Type[origin_type.__bound__] if subclass_check else origin_type.__bound__
+        )
+        check_type_internal(value, annotation, memo)
+    elif origin_type.__constraints__:
+        for constraint in origin_type.__constraints__:
+            annotation = Type[constraint] if subclass_check else constraint
+            try:
+                check_type_internal(value, annotation, memo)
+            except TypeCheckError:
+                pass
+            else:
+                break
+        else:
+            formatted_constraints = ", ".join(
+                get_type_name(constraint) for constraint in origin_type.__constraints__
+            )
+            raise TypeCheckError(
+                f"does not match any of the constraints " f"({formatted_constraints})"
+            )
+
+
+if sys.version_info >= (3, 8):
+    if typing_extensions is None:
+
+        def _is_literal_type(typ: object) -> bool:
+            return typ is typing.Literal
+
+    else:
+
+        def _is_literal_type(typ: object) -> bool:
+            return typ is typing.Literal or typ is typing_extensions.Literal
+
+else:
+
+    def _is_literal_type(typ: object) -> bool:
+        return typ is typing_extensions.Literal
+
+
+def check_literal(
+    value: Any,
+    origin_type: Any,
+    args: tuple[Any, ...],
+    memo: TypeCheckMemo,
+) -> None:
+    def get_literal_args(literal_args: tuple[Any, ...]) -> tuple[Any, ...]:
+        retval: list[Any] = []
+        for arg in literal_args:
+            if _is_literal_type(get_origin(arg)):
+                # The first check works on py3.6 and lower, the second one on py3.7+
+                retval.extend(get_literal_args(arg.__args__))
+            elif arg is None or isinstance(arg, (int, str, bytes, bool, Enum)):
+                retval.append(arg)
+            else:
+                raise TypeError(
+                    f"Illegal literal value: {arg}"
+                )  # TypeError here is deliberate
+
+        return tuple(retval)
+
+    final_args = tuple(get_literal_args(args))
+    try:
+        index = final_args.index(value)
+    except ValueError:
+        pass
+    else:
+        if type(final_args[index]) is type(value):
+            return
+
+    formatted_args = ", ".join(repr(arg) for arg in final_args)
+    raise TypeCheckError(f"is not any of ({formatted_args})") from None
+
+
+def check_literal_string(
+    value: Any,
+    origin_type: Any,
+    args: tuple[Any, ...],
+    memo: TypeCheckMemo,
+) -> None:
+    check_type_internal(value, str, memo)
+
+
+def check_typeguard(
+    value: Any,
+    origin_type: Any,
+    args: tuple[Any, ...],
+    memo: TypeCheckMemo,
+) -> None:
+    check_type_internal(value, bool, memo)
+
+
+def check_none(
+    value: Any,
+    origin_type: Any,
+    args: tuple[Any, ...],
+    memo: TypeCheckMemo,
+) -> None:
+    if value is not None:
+        raise TypeCheckError("is not None")
+
+
+def check_number(
+    value: Any,
+    origin_type: Any,
+    args: tuple[Any, ...],
+    memo: TypeCheckMemo,
+) -> None:
+    if origin_type is complex and not isinstance(value, (complex, float, int)):
+        raise TypeCheckError("is neither complex, float or int")
+    elif origin_type is float and not isinstance(value, (float, int)):
+        raise TypeCheckError("is neither float or int")
+
+
+def check_io(
+    value: Any,
+    origin_type: Any,
+    args: tuple[Any, ...],
+    memo: TypeCheckMemo,
+) -> None:
+    if origin_type is TextIO or (origin_type is IO and args == (str,)):
+        if not isinstance(value, TextIOBase):
+            raise TypeCheckError("is not a text based I/O object")
+    elif origin_type is BinaryIO or (origin_type is IO and args == (bytes,)):
+        if not isinstance(value, (RawIOBase, BufferedIOBase)):
+            raise TypeCheckError("is not a binary I/O object")
+    elif not isinstance(value, IOBase):
+        raise TypeCheckError("is not an I/O object")
+
+
+def check_protocol(
+    value: Any,
+    origin_type: Any,
+    args: tuple[Any, ...],
+    memo: TypeCheckMemo,
+) -> None:
+    # TODO: implement proper compatibility checking and support non-runtime protocols
+    if getattr(origin_type, "_is_runtime_protocol", False):
+        if not isinstance(value, origin_type):
+            raise TypeCheckError(
+                f"is not compatible with the {origin_type.__qualname__} protocol"
+            )
+    else:
+        warnings.warn(
+            f"Typeguard cannot check the {origin_type.__qualname__} protocol because "
+            f"it is a non-runtime protocol. If you would like to type check this "
+            f"protocol, please use @typing.runtime_checkable",
+            stacklevel=get_stacklevel(),
+        )
+
+
+def check_byteslike(
+    value: Any,
+    origin_type: Any,
+    args: tuple[Any, ...],
+    memo: TypeCheckMemo,
+) -> None:
+    if not isinstance(value, (bytearray, bytes, memoryview)):
+        raise TypeCheckError("is not bytes-like")
+
+
+def check_self(
+    value: Any,
+    origin_type: Any,
+    args: tuple[Any, ...],
+    memo: TypeCheckMemo,
+) -> None:
+    if memo.self_type is None:
+        raise TypeCheckError("cannot be checked against Self outside of a method call")
+
+    if isclass(value):
+        if not issubclass(value, memo.self_type):
+            raise TypeCheckError(
+                f"is not an instance of the self type "
+                f"({qualified_name(memo.self_type)})"
+            )
+    elif not isinstance(value, memo.self_type):
+        raise TypeCheckError(
+            f"is not an instance of the self type ({qualified_name(memo.self_type)})"
+        )
+
+
+def check_paramspec(
+    value: Any,
+    origin_type: Any,
+    args: tuple[Any, ...],
+    memo: TypeCheckMemo,
+) -> None:
+    pass  # No-op for now
+
+
+def check_instanceof(
+    value: Any,
+    origin_type: Any,
+    args: tuple[Any, ...],
+    memo: TypeCheckMemo,
+) -> None:
+    if not isinstance(value, origin_type):
+        raise TypeCheckError(f"is not an instance of {qualified_name(origin_type)}")
+
+
+def check_type_internal(
+    value: Any,
+    annotation: Any,
+    memo: TypeCheckMemo,
+) -> None:
+    """
+    Check that the given object is compatible with the given type annotation.
+
+    This function should only be used by type checker callables. Applications should use
+    :func:`~.check_type` instead.
+
+    :param value: the value to check
+    :param annotation: the type annotation to check against
+    :param memo: a memo object containing configuration and information necessary for
+        looking up forward references
+    """
+
+    if isinstance(annotation, ForwardRef):
+        try:
+            annotation = evaluate_forwardref(annotation, memo)
+        except NameError:
+            if memo.config.forward_ref_policy is ForwardRefPolicy.ERROR:
+                raise
+            elif memo.config.forward_ref_policy is ForwardRefPolicy.WARN:
+                warnings.warn(
+                    f"Cannot resolve forward reference {annotation.__forward_arg__!r}",
+                    TypeHintWarning,
+                    stacklevel=get_stacklevel(),
+                )
+
+            return
+
+    if annotation is Any or annotation is SubclassableAny or isinstance(value, Mock):
+        return
+
+    # Skip type checks if value is an instance of a class that inherits from Any
+    if not isclass(value) and SubclassableAny in type(value).__bases__:
+        return
+
+    extras: tuple[Any, ...]
+    origin_type = get_origin(annotation)
+    if origin_type is Annotated:
+        annotation, *extras_ = get_args(annotation)
+        extras = tuple(extras_)
+        origin_type = get_origin(annotation)
+    else:
+        extras = ()
+
+    if origin_type is not None:
+        args = get_args(annotation)
+
+        # Compatibility hack to distinguish between unparametrized and empty tuple
+        # (tuple[()]), necessary due to https://github.com/python/cpython/issues/91137
+        if origin_type in (tuple, Tuple) and annotation is not Tuple and not args:
+            args = ((),)
+    else:
+        origin_type = annotation
+        args = ()
+
+    for lookup_func in checker_lookup_functions:
+        checker = lookup_func(origin_type, args, extras)
+        if checker:
+            checker(value, origin_type, args, memo)
+            return
+
+    if isclass(origin_type):
+        if not isinstance(value, origin_type):
+            raise TypeCheckError(f"is not an instance of {qualified_name(origin_type)}")
+    elif type(origin_type) is str:  # noqa: E721
+        warnings.warn(
+            f"Skipping type check against {origin_type!r}; this looks like a "
+            f"string-form forward reference imported from another module",
+            TypeHintWarning,
+            stacklevel=get_stacklevel(),
+        )
+
+
+# Equality checks are applied to these
+origin_type_checkers = {
+    bytes: check_byteslike,
+    AbstractSet: check_set,
+    BinaryIO: check_io,
+    Callable: check_callable,
+    collections.abc.Callable: check_callable,
+    complex: check_number,
+    dict: check_mapping,
+    Dict: check_mapping,
+    float: check_number,
+    frozenset: check_set,
+    IO: check_io,
+    list: check_list,
+    List: check_list,
+    Mapping: check_mapping,
+    MutableMapping: check_mapping,
+    None: check_none,
+    collections.abc.Mapping: check_mapping,
+    collections.abc.MutableMapping: check_mapping,
+    Sequence: check_sequence,
+    collections.abc.Sequence: check_sequence,
+    collections.abc.Set: check_set,
+    set: check_set,
+    Set: check_set,
+    TextIO: check_io,
+    tuple: check_tuple,
+    Tuple: check_tuple,
+    type: check_class,
+    Type: check_class,
+    Union: check_union,
+}
+if sys.version_info >= (3, 8):
+    origin_type_checkers[typing.Literal] = check_literal
+if sys.version_info >= (3, 10):
+    origin_type_checkers[types.UnionType] = check_uniontype
+    origin_type_checkers[typing.TypeGuard] = check_typeguard
+if sys.version_info >= (3, 11):
+    origin_type_checkers.update(
+        {typing.LiteralString: check_literal_string, typing.Self: check_self}
+    )
+if typing_extensions is not None:
+    # On some Python versions, these may simply be re-exports from typing,
+    # but exactly which Python versions is subject to change,
+    # so it's best to err on the safe side
+    # and update the dictionary on all Python versions
+    # if typing_extensions is installed
+    origin_type_checkers[typing_extensions.Literal] = check_literal
+    origin_type_checkers[typing_extensions.LiteralString] = check_literal_string
+    origin_type_checkers[typing_extensions.Self] = check_self
+    origin_type_checkers[typing_extensions.TypeGuard] = check_typeguard
+
+
+def builtin_checker_lookup(
+    origin_type: Any, args: tuple[Any, ...], extras: tuple[Any, ...]
+) -> TypeCheckerCallable | None:
+    checker = origin_type_checkers.get(origin_type)
+    if checker is not None:
+        return checker
+    elif is_typeddict(origin_type):
+        return check_typed_dict
+    elif isclass(origin_type) and issubclass(
+        origin_type, Tuple  # type: ignore[arg-type]
+    ):
+        # NamedTuple
+        return check_tuple
+    elif getattr(origin_type, "_is_protocol", False):
+        return check_protocol
+    elif isinstance(origin_type, ParamSpec):
+        return check_paramspec
+    elif isinstance(origin_type, TypeVar):
+        return check_typevar
+    elif origin_type.__class__ is NewType:
+        # typing.NewType on Python 3.10+
+        return check_newtype
+    elif (
+        isfunction(origin_type)
+        and getattr(origin_type, "__module__", None) == "typing"
+        and getattr(origin_type, "__qualname__", "").startswith("NewType.")
+        and hasattr(origin_type, "__supertype__")
+    ):
+        # typing.NewType on Python 3.9 and below
+        return check_newtype
+
+    return None
+
+
+checker_lookup_functions.append(builtin_checker_lookup)
+
+
+def load_plugins() -> None:
+    """
+    Load all type checker lookup functions from entry points.
+
+    All entry points from the ``typeguard.checker_lookup`` group are loaded, and the
+    returned lookup functions are added to :data:`typeguard.checker_lookup_functions`.
+
+    .. note:: This function is called implicitly on import, unless the
+        ``TYPEGUARD_DISABLE_PLUGIN_AUTOLOAD`` environment variable is present.
+    """
+
+    for ep in entry_points(group="typeguard.checker_lookup"):
+        try:
+            plugin = ep.load()
+        except Exception as exc:
+            warnings.warn(
+                f"Failed to load plugin {ep.name!r}: " f"{qualified_name(exc)}: {exc}",
+                stacklevel=2,
+            )
+            continue
+
+        if not callable(plugin):
+            warnings.warn(
+                f"Plugin {ep} returned a non-callable object: {plugin!r}", stacklevel=2
+            )
+            continue
+
+        checker_lookup_functions.insert(0, plugin)
diff --git a/metaflow/_vendor/v3_7/typeguard/_config.py b/metaflow/_vendor/v3_7/typeguard/_config.py
new file mode 100644
index 00000000000..04cecf84b3e
--- /dev/null
+++ b/metaflow/_vendor/v3_7/typeguard/_config.py
@@ -0,0 +1,108 @@
+from __future__ import annotations
+
+from collections.abc import Collection
+from dataclasses import dataclass
+from enum import Enum, auto
+from typing import TYPE_CHECKING, TypeVar
+
+if TYPE_CHECKING:
+    from ._functions import TypeCheckFailCallback
+
+T = TypeVar("T")
+
+
+class ForwardRefPolicy(Enum):
+    """
+    Defines how unresolved forward references are handled.
+
+    Members:
+
+    * ``ERROR``: propagate the :exc:`NameError` when the forward reference lookup fails
+    * ``WARN``: emit a :class:`~.TypeHintWarning` if the forward reference lookup fails
+    * ``IGNORE``: silently skip checks for unresolveable forward references
+    """
+
+    ERROR = auto()
+    WARN = auto()
+    IGNORE = auto()
+
+
+class CollectionCheckStrategy(Enum):
+    """
+    Specifies how thoroughly the contents of collections are type checked.
+
+    This has an effect on the following built-in checkers:
+
+    * ``AbstractSet``
+    * ``Dict``
+    * ``List``
+    * ``Mapping``
+    * ``Set``
+    * ``Tuple[, ...]`` (arbitrarily sized tuples)
+
+    Members:
+
+    * ``FIRST_ITEM``: check only the first item
+    * ``ALL_ITEMS``: check all items
+    """
+
+    FIRST_ITEM = auto()
+    ALL_ITEMS = auto()
+
+    def iterate_samples(self, collection: Collection[T]) -> Collection[T]:
+        if self is CollectionCheckStrategy.FIRST_ITEM:
+            if len(collection):
+                return [next(iter(collection))]
+            else:
+                return ()
+        else:
+            return collection
+
+
+@dataclass
+class TypeCheckConfiguration:
+    """
+     You can change Typeguard's behavior with these settings.
+
+    .. attribute:: typecheck_fail_callback
+       :type: Callable[[TypeCheckError, TypeCheckMemo], Any]
+
+         Callable that is called when type checking fails.
+
+         Default: ``None`` (the :exc:`~.TypeCheckError` is raised directly)
+
+    .. attribute:: forward_ref_policy
+       :type: ForwardRefPolicy
+
+         Specifies what to do when a forward reference fails to resolve.
+
+         Default: ``WARN``
+
+    .. attribute:: collection_check_strategy
+       :type: CollectionCheckStrategy
+
+         Specifies how thoroughly the contents of collections (list, dict, etc.) are
+         type checked.
+
+         Default: ``FIRST_ITEM``
+
+    .. attribute:: debug_instrumentation
+       :type: bool
+
+         If set to ``True``, the code of modules or functions instrumented by typeguard
+         is printed to ``sys.stderr`` after the instrumentation is done
+
+         Requires Python 3.9 or newer.
+
+         Default: ``False``
+    """
+
+    forward_ref_policy: ForwardRefPolicy = ForwardRefPolicy.WARN
+    typecheck_fail_callback: TypeCheckFailCallback | None = None
+    collection_check_strategy: CollectionCheckStrategy = (
+        CollectionCheckStrategy.FIRST_ITEM
+    )
+    debug_instrumentation: bool = False
+
+
+global_config = TypeCheckConfiguration()
diff --git a/metaflow/_vendor/v3_7/typeguard/_decorators.py b/metaflow/_vendor/v3_7/typeguard/_decorators.py
new file mode 100644
index 00000000000..53f254f7080
--- /dev/null
+++ b/metaflow/_vendor/v3_7/typeguard/_decorators.py
@@ -0,0 +1,237 @@
+from __future__ import annotations
+
+import ast
+import inspect
+import sys
+from collections.abc import Sequence
+from functools import partial
+from inspect import isclass, isfunction
+from types import CodeType, FrameType, FunctionType
+from typing import TYPE_CHECKING, Any, Callable, ForwardRef, TypeVar, cast, overload
+from warnings import warn
+
+from ._config import CollectionCheckStrategy, ForwardRefPolicy, global_config
+from ._exceptions import InstrumentationWarning
+from ._functions import TypeCheckFailCallback
+from ._transformer import TypeguardTransformer
+from ._utils import Unset, function_name, get_stacklevel, is_method_of, unset
+
+if TYPE_CHECKING:
+    from typeshed.stdlib.types import _Cell
+
+    _F = TypeVar("_F")
+
+    def typeguard_ignore(f: _F) -> _F:
+        """This decorator is a noop during static type-checking."""
+        return f
+
+else:
+    from typing import no_type_check as typeguard_ignore  # noqa: F401
+
+T_CallableOrType = TypeVar("T_CallableOrType", bound=Callable[..., Any])
+
+
+def make_cell(value: object) -> _Cell:
+    return (lambda: value).__closure__[0]  # type: ignore[index]
+
+
+def find_target_function(
+    new_code: CodeType, target_path: Sequence[str], firstlineno: int
+) -> CodeType | None:
+    target_name = target_path[0]
+    for const in new_code.co_consts:
+        if isinstance(const, CodeType):
+            if const.co_name == target_name:
+                if const.co_firstlineno == firstlineno:
+                    return const
+                elif len(target_path) > 1:
+                    target_code = find_target_function(
+                        const, target_path[1:], firstlineno
+                    )
+                    if target_code:
+                        return target_code
+
+    return None
+
+
+def instrument(f: T_CallableOrType) -> FunctionType | str:
+    if not getattr(f, "__code__", None):
+        return "no code associated"
+    elif not getattr(f, "__module__", None):
+        return "__module__ attribute is not set"
+    elif f.__code__.co_filename == "":
+        return "cannot instrument functions defined in a REPL"
+    elif hasattr(f, "__wrapped__"):
+        return (
+            "@typechecked only supports instrumenting functions wrapped with "
+            "@classmethod, @staticmethod or @property"
+        )
+
+    target_path = [item for item in f.__qualname__.split(".") if item != ""]
+    module_source = inspect.getsource(sys.modules[f.__module__])
+    module_ast = ast.parse(module_source)
+    instrumentor = TypeguardTransformer(target_path, f.__code__.co_firstlineno)
+    instrumentor.visit(module_ast)
+
+    if not instrumentor.target_node or instrumentor.target_lineno is None:
+        return "instrumentor did not find the target function"
+
+    module_code = compile(module_ast, f.__code__.co_filename, "exec", dont_inherit=True)
+    new_code = find_target_function(
+        module_code, target_path, instrumentor.target_lineno
+    )
+    if not new_code:
+        return "cannot find the target function in the AST"
+
+    if global_config.debug_instrumentation and sys.version_info >= (3, 9):
+        # Find the matching AST node, then unparse it to source and print to stdout
+        print(
+            f"Source code of {f.__qualname__}() after instrumentation:"
+            "\n----------------------------------------------",
+            file=sys.stderr,
+        )
+        print(ast.unparse(instrumentor.target_node), file=sys.stderr)
+        print(
+            "----------------------------------------------",
+            file=sys.stderr,
+        )
+
+    closure = f.__closure__
+    if new_code.co_freevars != f.__code__.co_freevars:
+        # Create a new closure and find values for the new free variables
+        frame = cast(FrameType, inspect.currentframe())
+        frame = cast(FrameType, frame.f_back)
+        frame_locals = cast(FrameType, frame.f_back).f_locals
+        cells: list[_Cell] = []
+        for key in new_code.co_freevars:
+            if key in instrumentor.names_used_in_annotations:
+                # Find the value and make a new cell from it
+                value = frame_locals.get(key) or ForwardRef(key)
+                cells.append(make_cell(value))
+            else:
+                # Reuse the cell from the existing closure
+                assert f.__closure__
+                cells.append(f.__closure__[f.__code__.co_freevars.index(key)])
+
+        closure = tuple(cells)
+
+    new_function = FunctionType(new_code, f.__globals__, f.__name__, closure=closure)
+    new_function.__module__ = f.__module__
+    new_function.__name__ = f.__name__
+    new_function.__qualname__ = f.__qualname__
+    new_function.__annotations__ = f.__annotations__
+    new_function.__doc__ = f.__doc__
+    new_function.__defaults__ = f.__defaults__
+    new_function.__kwdefaults__ = f.__kwdefaults__
+    return new_function
+
+
+@overload
+def typechecked(
+    *,
+    forward_ref_policy: ForwardRefPolicy | Unset = unset,
+    typecheck_fail_callback: TypeCheckFailCallback | Unset = unset,
+    collection_check_strategy: CollectionCheckStrategy | Unset = unset,
+    debug_instrumentation: bool | Unset = unset,
+) -> Callable[[T_CallableOrType], T_CallableOrType]:
+    ...
+
+
+@overload
+def typechecked(target: T_CallableOrType) -> T_CallableOrType:
+    ...
+
+
+def typechecked(
+    target: T_CallableOrType | None = None,
+    *,
+    forward_ref_policy: ForwardRefPolicy | Unset = unset,
+    typecheck_fail_callback: TypeCheckFailCallback | Unset = unset,
+    collection_check_strategy: CollectionCheckStrategy | Unset = unset,
+    debug_instrumentation: bool | Unset = unset,
+) -> Any:
+    """
+    Instrument the target function to perform run-time type checking.
+
+    This decorator recompiles the target function, injecting code to type check
+    arguments, return values, yield values (excluding ``yield from``) and assignments to
+    annotated local variables.
+
+    This can also be used as a class decorator. This will instrument all type annotated
+    methods, including :func:`@classmethod `,
+    :func:`@staticmethod `,  and :class:`@property ` decorated
+    methods in the class.
+
+    .. note:: When Python is run in optimized mode (``-O`` or ``-OO``, this decorator
+        is a no-op). This is a feature meant for selectively introducing type checking
+        into a code base where the checks aren't meant to be run in production.
+
+    :param target: the function or class to enable type checking for
+    :param forward_ref_policy: override for
+        :attr:`.TypeCheckConfiguration.forward_ref_policy`
+    :param typecheck_fail_callback: override for
+        :attr:`.TypeCheckConfiguration.typecheck_fail_callback`
+    :param collection_check_strategy: override for
+        :attr:`.TypeCheckConfiguration.collection_check_strategy`
+    :param debug_instrumentation: override for
+        :attr:`.TypeCheckConfiguration.debug_instrumentation`
+
+    """
+    if target is None:
+        return partial(
+            typechecked,
+            forward_ref_policy=forward_ref_policy,
+            typecheck_fail_callback=typecheck_fail_callback,
+            collection_check_strategy=collection_check_strategy,
+            debug_instrumentation=debug_instrumentation,
+        )
+
+    if not __debug__:
+        return target
+
+    if isclass(target):
+        for key, attr in target.__dict__.items():
+            if is_method_of(attr, target):
+                retval = instrument(attr)
+                if isfunction(retval):
+                    setattr(target, key, retval)
+            elif isinstance(attr, (classmethod, staticmethod)):
+                if is_method_of(attr.__func__, target):
+                    retval = instrument(attr.__func__)
+                    if isfunction(retval):
+                        wrapper = attr.__class__(retval)
+                        setattr(target, key, wrapper)
+            elif isinstance(attr, property):
+                kwargs: dict[str, Any] = dict(doc=attr.__doc__)
+                for name in ("fset", "fget", "fdel"):
+                    property_func = kwargs[name] = getattr(attr, name)
+                    if is_method_of(property_func, target):
+                        retval = instrument(property_func)
+                        if isfunction(retval):
+                            kwargs[name] = retval
+
+                setattr(target, key, attr.__class__(**kwargs))
+
+        return target
+
+    # Find either the first Python wrapper or the actual function
+    wrapper_class: type[classmethod[Any, Any, Any]] | type[
+        staticmethod[Any, Any]
+    ] | None = None
+    if isinstance(target, (classmethod, staticmethod)):
+        wrapper_class = target.__class__
+        target = target.__func__
+
+    retval = instrument(target)
+    if isinstance(retval, str):
+        warn(
+            f"{retval} -- not typechecking {function_name(target)}",
+            InstrumentationWarning,
+            stacklevel=get_stacklevel(),
+        )
+        return target
+
+    if wrapper_class is None:
+        return retval
+    else:
+        return wrapper_class(retval)
diff --git a/metaflow/_vendor/v3_7/typeguard/_exceptions.py b/metaflow/_vendor/v3_7/typeguard/_exceptions.py
new file mode 100644
index 00000000000..625437a6499
--- /dev/null
+++ b/metaflow/_vendor/v3_7/typeguard/_exceptions.py
@@ -0,0 +1,42 @@
+from collections import deque
+from typing import Deque
+
+
+class TypeHintWarning(UserWarning):
+    """
+    A warning that is emitted when a type hint in string form could not be resolved to
+    an actual type.
+    """
+
+
+class TypeCheckWarning(UserWarning):
+    """Emitted by typeguard's type checkers when a type mismatch is detected."""
+
+    def __init__(self, message: str):
+        super().__init__(message)
+
+
+class InstrumentationWarning(UserWarning):
+    """Emitted when there's a problem with instrumenting a function for type checks."""
+
+    def __init__(self, message: str):
+        super().__init__(message)
+
+
+class TypeCheckError(Exception):
+    """
+    Raised by typeguard's type checkers when a type mismatch is detected.
+    """
+
+    def __init__(self, message: str):
+        super().__init__(message)
+        self._path: Deque[str] = deque()
+
+    def append_path_element(self, element: str) -> None:
+        self._path.append(element)
+
+    def __str__(self) -> str:
+        if self._path:
+            return " of ".join(self._path) + " " + str(self.args[0])
+        else:
+            return str(self.args[0])
diff --git a/metaflow/_vendor/v3_7/typeguard/_functions.py b/metaflow/_vendor/v3_7/typeguard/_functions.py
new file mode 100644
index 00000000000..6c64bd19c42
--- /dev/null
+++ b/metaflow/_vendor/v3_7/typeguard/_functions.py
@@ -0,0 +1,310 @@
+from __future__ import annotations
+
+import sys
+import warnings
+from typing import Any, Callable, NoReturn, TypeVar, Union, overload
+
+from . import _suppression
+from ._checkers import BINARY_MAGIC_METHODS, check_type_internal
+from ._config import (
+    CollectionCheckStrategy,
+    ForwardRefPolicy,
+    TypeCheckConfiguration,
+)
+from ._exceptions import TypeCheckError, TypeCheckWarning
+from ._memo import TypeCheckMemo
+from ._utils import get_stacklevel, qualified_name
+
+if sys.version_info >= (3, 11):
+    from typing import Literal, Never, TypeAlias
+else:
+    from metaflow._vendor.v3_7.typing_extensions import Literal, Never, TypeAlias
+
+T = TypeVar("T")
+TypeCheckFailCallback: TypeAlias = Callable[[TypeCheckError, TypeCheckMemo], Any]
+
+
+@overload
+def check_type(
+    value: object,
+    expected_type: type[T],
+    *,
+    forward_ref_policy: ForwardRefPolicy = ...,
+    typecheck_fail_callback: TypeCheckFailCallback | None = ...,
+    collection_check_strategy: CollectionCheckStrategy = ...,
+) -> T:
+    ...
+
+
+@overload
+def check_type(
+    value: object,
+    expected_type: Any,
+    *,
+    forward_ref_policy: ForwardRefPolicy = ...,
+    typecheck_fail_callback: TypeCheckFailCallback | None = ...,
+    collection_check_strategy: CollectionCheckStrategy = ...,
+) -> Any:
+    ...
+
+
+def check_type(
+    value: object,
+    expected_type: Any,
+    *,
+    forward_ref_policy: ForwardRefPolicy = TypeCheckConfiguration().forward_ref_policy,
+    typecheck_fail_callback: (TypeCheckFailCallback | None) = (
+        TypeCheckConfiguration().typecheck_fail_callback
+    ),
+    collection_check_strategy: CollectionCheckStrategy = (
+        TypeCheckConfiguration().collection_check_strategy
+    ),
+) -> Any:
+    """
+    Ensure that ``value`` matches ``expected_type``.
+
+    The types from the :mod:`typing` module do not support :func:`isinstance` or
+    :func:`issubclass` so a number of type specific checks are required. This function
+    knows which checker to call for which type.
+
+    This function wraps :func:`~.check_type_internal` in the following ways:
+
+    * Respects type checking suppression (:func:`~.suppress_type_checks`)
+    * Forms a :class:`~.TypeCheckMemo` from the current stack frame
+    * Calls the configured type check fail callback if the check fails
+
+    Note that this function is independent of the globally shared configuration in
+    :data:`typeguard.config`. This means that usage within libraries is safe from being
+    affected configuration changes made by other libraries or by the integrating
+    application. Instead, configuration options have the same default values as their
+    corresponding fields in :class:`TypeCheckConfiguration`.
+
+    :param value: value to be checked against ``expected_type``
+    :param expected_type: a class or generic type instance, or a tuple of such things
+    :param forward_ref_policy: see :attr:`TypeCheckConfiguration.forward_ref_policy`
+    :param typecheck_fail_callback:
+        see :attr`TypeCheckConfiguration.typecheck_fail_callback`
+    :param collection_check_strategy:
+        see :attr:`TypeCheckConfiguration.collection_check_strategy`
+    :return: ``value``, unmodified
+    :raises TypeCheckError: if there is a type mismatch
+
+    """
+    if type(expected_type) is tuple:
+        expected_type = Union[expected_type]
+
+    config = TypeCheckConfiguration(
+        forward_ref_policy=forward_ref_policy,
+        typecheck_fail_callback=typecheck_fail_callback,
+        collection_check_strategy=collection_check_strategy,
+    )
+
+    if _suppression.type_checks_suppressed or expected_type is Any:
+        return value
+
+    frame = sys._getframe(1)
+    memo = TypeCheckMemo(frame.f_globals, frame.f_locals, config=config)
+    try:
+        check_type_internal(value, expected_type, memo)
+    except TypeCheckError as exc:
+        exc.append_path_element(qualified_name(value, add_class_prefix=True))
+        if config.typecheck_fail_callback:
+            config.typecheck_fail_callback(exc, memo)
+        else:
+            raise
+
+    return value
+
+
+def check_argument_types(
+    func_name: str,
+    arguments: dict[str, tuple[Any, Any]],
+    memo: TypeCheckMemo,
+) -> Literal[True]:
+    if _suppression.type_checks_suppressed:
+        return True
+
+    for argname, (value, annotation) in arguments.items():
+        if annotation is NoReturn or annotation is Never:
+            exc = TypeCheckError(
+                f"{func_name}() was declared never to be called but it was"
+            )
+            if memo.config.typecheck_fail_callback:
+                memo.config.typecheck_fail_callback(exc, memo)
+            else:
+                raise exc
+
+        try:
+            check_type_internal(value, annotation, memo)
+        except TypeCheckError as exc:
+            qualname = qualified_name(value, add_class_prefix=True)
+            exc.append_path_element(f'argument "{argname}" ({qualname})')
+            if memo.config.typecheck_fail_callback:
+                memo.config.typecheck_fail_callback(exc, memo)
+            else:
+                raise
+
+    return True
+
+
+def check_return_type(
+    func_name: str,
+    retval: T,
+    annotation: Any,
+    memo: TypeCheckMemo,
+) -> T:
+    if _suppression.type_checks_suppressed:
+        return retval
+
+    if annotation is NoReturn or annotation is Never:
+        exc = TypeCheckError(f"{func_name}() was declared never to return but it did")
+        if memo.config.typecheck_fail_callback:
+            memo.config.typecheck_fail_callback(exc, memo)
+        else:
+            raise exc
+
+    try:
+        check_type_internal(retval, annotation, memo)
+    except TypeCheckError as exc:
+        # Allow NotImplemented if this is a binary magic method (__eq__() et al)
+        if retval is NotImplemented and annotation is bool:
+            # This does (and cannot) not check if it's actually a method
+            func_name = func_name.rsplit(".", 1)[-1]
+            if func_name in BINARY_MAGIC_METHODS:
+                return retval
+
+        qualname = qualified_name(retval, add_class_prefix=True)
+        exc.append_path_element(f"the return value ({qualname})")
+        if memo.config.typecheck_fail_callback:
+            memo.config.typecheck_fail_callback(exc, memo)
+        else:
+            raise
+
+    return retval
+
+
+def check_send_type(
+    func_name: str,
+    sendval: T,
+    annotation: Any,
+    memo: TypeCheckMemo,
+) -> T:
+    if _suppression.type_checks_suppressed:
+        return sendval
+
+    if annotation is NoReturn or annotation is Never:
+        exc = TypeCheckError(
+            f"{func_name}() was declared never to be sent a value to but it was"
+        )
+        if memo.config.typecheck_fail_callback:
+            memo.config.typecheck_fail_callback(exc, memo)
+        else:
+            raise exc
+
+    try:
+        check_type_internal(sendval, annotation, memo)
+    except TypeCheckError as exc:
+        qualname = qualified_name(sendval, add_class_prefix=True)
+        exc.append_path_element(f"the value sent to generator ({qualname})")
+        if memo.config.typecheck_fail_callback:
+            memo.config.typecheck_fail_callback(exc, memo)
+        else:
+            raise
+
+    return sendval
+
+
+def check_yield_type(
+    func_name: str,
+    yieldval: T,
+    annotation: Any,
+    memo: TypeCheckMemo,
+) -> T:
+    if _suppression.type_checks_suppressed:
+        return yieldval
+
+    if annotation is NoReturn or annotation is Never:
+        exc = TypeCheckError(f"{func_name}() was declared never to yield but it did")
+        if memo.config.typecheck_fail_callback:
+            memo.config.typecheck_fail_callback(exc, memo)
+        else:
+            raise exc
+
+    try:
+        check_type_internal(yieldval, annotation, memo)
+    except TypeCheckError as exc:
+        qualname = qualified_name(yieldval, add_class_prefix=True)
+        exc.append_path_element(f"the yielded value ({qualname})")
+        if memo.config.typecheck_fail_callback:
+            memo.config.typecheck_fail_callback(exc, memo)
+        else:
+            raise
+
+    return yieldval
+
+
+def check_variable_assignment(
+    value: object, varname: str, annotation: Any, memo: TypeCheckMemo
+) -> Any:
+    if _suppression.type_checks_suppressed:
+        return value
+
+    try:
+        check_type_internal(value, annotation, memo)
+    except TypeCheckError as exc:
+        qualname = qualified_name(value, add_class_prefix=True)
+        exc.append_path_element(f"value assigned to {varname} ({qualname})")
+        if memo.config.typecheck_fail_callback:
+            memo.config.typecheck_fail_callback(exc, memo)
+        else:
+            raise
+
+    return value
+
+
+def check_multi_variable_assignment(
+    value: Any, targets: list[dict[str, Any]], memo: TypeCheckMemo
+) -> Any:
+    if max(len(target) for target in targets) == 1:
+        iterated_values = [value]
+    else:
+        iterated_values = list(value)
+
+    if not _suppression.type_checks_suppressed:
+        for expected_types in targets:
+            value_index = 0
+            for ann_index, (varname, expected_type) in enumerate(
+                expected_types.items()
+            ):
+                if varname.startswith("*"):
+                    varname = varname[1:]
+                    keys_left = len(expected_types) - 1 - ann_index
+                    next_value_index = len(iterated_values) - keys_left
+                    obj: object = iterated_values[value_index:next_value_index]
+                    value_index = next_value_index
+                else:
+                    obj = iterated_values[value_index]
+                    value_index += 1
+
+                try:
+                    check_type_internal(obj, expected_type, memo)
+                except TypeCheckError as exc:
+                    qualname = qualified_name(obj, add_class_prefix=True)
+                    exc.append_path_element(f"value assigned to {varname} ({qualname})")
+                    if memo.config.typecheck_fail_callback:
+                        memo.config.typecheck_fail_callback(exc, memo)
+                    else:
+                        raise
+
+    return iterated_values[0] if len(iterated_values) == 1 else iterated_values
+
+
+def warn_on_error(exc: TypeCheckError, memo: TypeCheckMemo) -> None:
+    """
+    Emit a warning on a type mismatch.
+
+    This is intended to be used as an error handler in
+    :attr:`TypeCheckConfiguration.typecheck_fail_callback`.
+
+    """
+    warnings.warn(TypeCheckWarning(str(exc)), stacklevel=get_stacklevel())
diff --git a/metaflow/_vendor/v3_7/typeguard/_importhook.py b/metaflow/_vendor/v3_7/typeguard/_importhook.py
new file mode 100644
index 00000000000..5b8b8f60e69
--- /dev/null
+++ b/metaflow/_vendor/v3_7/typeguard/_importhook.py
@@ -0,0 +1,213 @@
+from __future__ import annotations
+
+import ast
+import sys
+import types
+from collections.abc import Callable, Iterable
+from importlib.abc import MetaPathFinder
+from importlib.machinery import ModuleSpec, SourceFileLoader
+from importlib.util import cache_from_source, decode_source
+from inspect import isclass
+from os import PathLike
+from types import CodeType, ModuleType, TracebackType
+from typing import Sequence, TypeVar
+from unittest.mock import patch
+
+from ._config import global_config
+from ._transformer import TypeguardTransformer
+
+if sys.version_info >= (3, 12):
+    from collections.abc import Buffer
+else:
+    from metaflow._vendor.v3_7.typing_extensions import Buffer
+
+if sys.version_info >= (3, 11):
+    from typing import ParamSpec
+else:
+    from metaflow._vendor.v3_7.typing_extensions import ParamSpec
+
+if sys.version_info >= (3, 10):
+    from importlib.metadata import PackageNotFoundError, version
+else:
+    from metaflow._vendor.v3_7.importlib_metadata import PackageNotFoundError, version
+
+try:
+    OPTIMIZATION = "typeguard" + "".join(version("typeguard").split(".")[:3])
+except PackageNotFoundError:
+    OPTIMIZATION = "typeguard"
+
+P = ParamSpec("P")
+T = TypeVar("T")
+
+
+# The name of this function is magical
+def _call_with_frames_removed(
+    f: Callable[P, T], *args: P.args, **kwargs: P.kwargs
+) -> T:
+    return f(*args, **kwargs)
+
+
+def optimized_cache_from_source(path: str, debug_override: bool | None = None) -> str:
+    return cache_from_source(path, debug_override, optimization=OPTIMIZATION)
+
+
+class TypeguardLoader(SourceFileLoader):
+    @staticmethod
+    def source_to_code(
+        data: Buffer | str | ast.Module | ast.Expression | ast.Interactive,
+        path: Buffer | str | PathLike[str] = "",
+    ) -> CodeType:
+        if isinstance(data, (ast.Module, ast.Expression, ast.Interactive)):
+            tree = data
+        else:
+            if isinstance(data, str):
+                source = data
+            else:
+                source = decode_source(data)
+
+            tree = _call_with_frames_removed(
+                ast.parse,
+                source,
+                path,
+                "exec",
+            )
+
+        tree = TypeguardTransformer().visit(tree)
+        ast.fix_missing_locations(tree)
+
+        if global_config.debug_instrumentation and sys.version_info >= (3, 9):
+            print(
+                f"Source code of {path!r} after instrumentation:\n"
+                "----------------------------------------------",
+                file=sys.stderr,
+            )
+            print(ast.unparse(tree), file=sys.stderr)
+            print("----------------------------------------------", file=sys.stderr)
+
+        return _call_with_frames_removed(
+            compile, tree, path, "exec", 0, dont_inherit=True
+        )
+
+    def exec_module(self, module: ModuleType) -> None:
+        # Use a custom optimization marker – the import lock should make this monkey
+        # patch safe
+        with patch(
+            "importlib._bootstrap_external.cache_from_source",
+            optimized_cache_from_source,
+        ):
+            super().exec_module(module)
+
+
+class TypeguardFinder(MetaPathFinder):
+    """
+    Wraps another path finder and instruments the module with
+    :func:`@typechecked ` if :meth:`should_instrument` returns
+    ``True``.
+
+    Should not be used directly, but rather via :func:`~.install_import_hook`.
+
+    .. versionadded:: 2.6
+    """
+
+    def __init__(self, packages: list[str] | None, original_pathfinder: MetaPathFinder):
+        self.packages = packages
+        self._original_pathfinder = original_pathfinder
+
+    def find_spec(
+        self,
+        fullname: str,
+        path: Sequence[str] | None,
+        target: types.ModuleType | None = None,
+    ) -> ModuleSpec | None:
+        if self.should_instrument(fullname):
+            spec = self._original_pathfinder.find_spec(fullname, path, target)
+            if spec is not None and isinstance(spec.loader, SourceFileLoader):
+                spec.loader = TypeguardLoader(spec.loader.name, spec.loader.path)
+                return spec
+
+        return None
+
+    def should_instrument(self, module_name: str) -> bool:
+        """
+        Determine whether the module with the given name should be instrumented.
+
+        :param module_name: full name of the module that is about to be imported (e.g.
+            ``xyz.abc``)
+
+        """
+        if self.packages is None:
+            return True
+
+        for package in self.packages:
+            if module_name == package or module_name.startswith(package + "."):
+                return True
+
+        return False
+
+
+class ImportHookManager:
+    """
+    A handle that can be used to uninstall the Typeguard import hook.
+    """
+
+    def __init__(self, hook: MetaPathFinder):
+        self.hook = hook
+
+    def __enter__(self) -> None:
+        pass
+
+    def __exit__(
+        self,
+        exc_type: type[BaseException],
+        exc_val: BaseException,
+        exc_tb: TracebackType,
+    ) -> None:
+        self.uninstall()
+
+    def uninstall(self) -> None:
+        """Uninstall the import hook."""
+        try:
+            sys.meta_path.remove(self.hook)
+        except ValueError:
+            pass  # already removed
+
+
+def install_import_hook(
+    packages: Iterable[str] | None = None,
+    *,
+    cls: type[TypeguardFinder] = TypeguardFinder,
+) -> ImportHookManager:
+    """
+    Install an import hook that instruments functions for automatic type checking.
+
+    This only affects modules loaded **after** this hook has been installed.
+
+    :param packages: an iterable of package names to instrument, or ``None`` to
+        instrument all packages
+    :param cls: a custom meta path finder class
+    :return: a context manager that uninstalls the hook on exit (or when you call
+        ``.uninstall()``)
+
+    .. versionadded:: 2.6
+
+    """
+    if packages is None:
+        target_packages: list[str] | None = None
+    elif isinstance(packages, str):
+        target_packages = [packages]
+    else:
+        target_packages = list(packages)
+
+    for finder in sys.meta_path:
+        if (
+            isclass(finder)
+            and finder.__name__ == "PathFinder"
+            and hasattr(finder, "find_spec")
+        ):
+            break
+    else:
+        raise RuntimeError("Cannot find a PathFinder in sys.meta_path")
+
+    hook = cls(target_packages, finder)
+    sys.meta_path.insert(0, hook)
+    return ImportHookManager(hook)
diff --git a/metaflow/_vendor/v3_7/typeguard/_memo.py b/metaflow/_vendor/v3_7/typeguard/_memo.py
new file mode 100644
index 00000000000..2eb8e62efae
--- /dev/null
+++ b/metaflow/_vendor/v3_7/typeguard/_memo.py
@@ -0,0 +1,48 @@
+from __future__ import annotations
+
+from typing import Any
+
+from metaflow._vendor.v3_7.typeguard._config import TypeCheckConfiguration, global_config
+
+
+class TypeCheckMemo:
+    """
+    Contains information necessary for type checkers to do their work.
+
+    .. attribute:: globals
+       :type: dict[str, Any]
+
+        Dictionary of global variables to use for resolving forward references.
+
+    .. attribute:: locals
+       :type: dict[str, Any]
+
+        Dictionary of local variables to use for resolving forward references.
+
+    .. attribute:: self_type
+       :type: type | None
+
+        When running type checks within an instance method or class method, this is the
+        class object that the first argument (usually named ``self`` or ``cls``) refers
+        to.
+
+    .. attribute:: config
+       :type: TypeCheckConfiguration
+
+         Contains the configuration for a particular set of type checking operations.
+    """
+
+    __slots__ = "globals", "locals", "self_type", "config"
+
+    def __init__(
+        self,
+        globals: dict[str, Any],
+        locals: dict[str, Any],
+        *,
+        self_type: type | None = None,
+        config: TypeCheckConfiguration = global_config,
+    ):
+        self.globals = globals
+        self.locals = locals
+        self.self_type = self_type
+        self.config = config
diff --git a/metaflow/_vendor/v3_7/typeguard/_pytest_plugin.py b/metaflow/_vendor/v3_7/typeguard/_pytest_plugin.py
new file mode 100644
index 00000000000..fc7650bc9a9
--- /dev/null
+++ b/metaflow/_vendor/v3_7/typeguard/_pytest_plugin.py
@@ -0,0 +1,100 @@
+from __future__ import annotations
+
+import sys
+import warnings
+
+from pytest import Config, Parser
+
+from metaflow._vendor.v3_7.typeguard._config import CollectionCheckStrategy, ForwardRefPolicy, global_config
+from metaflow._vendor.v3_7.typeguard._exceptions import InstrumentationWarning
+from metaflow._vendor.v3_7.typeguard._importhook import install_import_hook
+from metaflow._vendor.v3_7.typeguard._utils import qualified_name, resolve_reference
+
+
+def pytest_addoption(parser: Parser) -> None:
+    group = parser.getgroup("typeguard")
+    group.addoption(
+        "--typeguard-packages",
+        action="store",
+        help="comma separated name list of packages and modules to instrument for "
+        "type checking, or :all: to instrument all modules loaded after typeguard",
+    )
+    group.addoption(
+        "--typeguard-debug-instrumentation",
+        action="store_true",
+        help="print all instrumented code to stderr",
+    )
+    group.addoption(
+        "--typeguard-typecheck-fail-callback",
+        action="store",
+        help=(
+            "a module:varname (e.g. typeguard:warn_on_error) reference to a function "
+            "that is called (with the exception, and memo object as arguments) to "
+            "handle a TypeCheckError"
+        ),
+    )
+    group.addoption(
+        "--typeguard-forward-ref-policy",
+        action="store",
+        choices=list(ForwardRefPolicy.__members__),
+        help=(
+            "determines how to deal with unresolveable forward references in type "
+            "annotations"
+        ),
+    )
+    group.addoption(
+        "--typeguard-collection-check-strategy",
+        action="store",
+        choices=list(CollectionCheckStrategy.__members__),
+        help="determines how thoroughly to check collections (list, dict, etc)",
+    )
+
+
+def pytest_configure(config: Config) -> None:
+    packages_option = config.getoption("typeguard_packages")
+    if packages_option:
+        if packages_option == ":all:":
+            packages: list[str] | None = None
+        else:
+            packages = [pkg.strip() for pkg in packages_option.split(",")]
+            already_imported_packages = sorted(
+                package for package in packages if package in sys.modules
+            )
+            if already_imported_packages:
+                warnings.warn(
+                    f"typeguard cannot check these packages because they are already "
+                    f"imported: {', '.join(already_imported_packages)}",
+                    InstrumentationWarning,
+                    stacklevel=1,
+                )
+
+        install_import_hook(packages=packages)
+
+    debug_option = config.getoption("typeguard_debug_instrumentation")
+    if debug_option:
+        global_config.debug_instrumentation = True
+
+    fail_callback_option = config.getoption("typeguard_typecheck_fail_callback")
+    if fail_callback_option:
+        callback = resolve_reference(fail_callback_option)
+        if not callable(callback):
+            raise TypeError(
+                f"{fail_callback_option} ({qualified_name(callback.__class__)}) is not "
+                f"a callable"
+            )
+
+        global_config.typecheck_fail_callback = callback
+
+    forward_ref_policy_option = config.getoption("typeguard_forward_ref_policy")
+    if forward_ref_policy_option:
+        forward_ref_policy = ForwardRefPolicy.__members__[forward_ref_policy_option]
+        global_config.forward_ref_policy = forward_ref_policy
+
+    collection_check_strategy_option = config.getoption(
+        "typeguard_collection_check_strategy"
+    )
+    if collection_check_strategy_option:
+        collection_check_strategy = CollectionCheckStrategy.__members__[
+            collection_check_strategy_option
+        ]
+        global_config.collection_check_strategy = collection_check_strategy
diff --git a/metaflow/_vendor/v3_7/typeguard/_suppression.py b/metaflow/_vendor/v3_7/typeguard/_suppression.py
new file mode 100644
index 00000000000..44f5c4088c8
--- /dev/null
+++ b/metaflow/_vendor/v3_7/typeguard/_suppression.py
@@ -0,0 +1,88 @@
+from __future__ import annotations
+
+import sys
+from collections.abc import Callable, Generator
+from contextlib import contextmanager
+from functools import update_wrapper
+from threading import Lock
+from typing import ContextManager, TypeVar, overload
+
+if sys.version_info >= (3, 10):
+    from typing import ParamSpec
+else:
+    from metaflow._vendor.v3_7.typing_extensions import ParamSpec
+
+P = ParamSpec("P")
+T = TypeVar("T")
+
+type_checks_suppressed = 0
+type_checks_suppress_lock = Lock()
+
+
+@overload
+def suppress_type_checks(func: Callable[P, T]) -> Callable[P, T]:
+    ...
+
+
+@overload
+def suppress_type_checks() -> ContextManager[None]:
+    ...
+
+
+def suppress_type_checks(
+    func: Callable[P, T] | None = None
+) -> Callable[P, T] | ContextManager[None]:
+    """
+    Temporarily suppress all type checking.
+
+    This function has two operating modes, based on how it's used:
+
+    #. as a context manager (``with suppress_type_checks(): ...``)
+    #. as a decorator (``@suppress_type_checks``)
+
+    When used as a context manager, :func:`check_type` and any automatically
+    instrumented functions skip the actual type checking. These context managers can be
+    nested.
+
+    When used as a decorator, all type checking is suppressed while the function is
+    running.
+
+    Type checking will resume once no more context managers are active and no decorated
+    functions are running.
+
+    Both operating modes are thread-safe.
+
+    """
+
+    def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
+        global type_checks_suppressed
+
+        with type_checks_suppress_lock:
+            type_checks_suppressed += 1
+
+        assert func is not None
+        try:
+            return func(*args, **kwargs)
+        finally:
+            with type_checks_suppress_lock:
+                type_checks_suppressed -= 1
+
+    def cm() -> Generator[None, None, None]:
+        global type_checks_suppressed
+
+        with type_checks_suppress_lock:
+            type_checks_suppressed += 1
+
+        try:
+            yield
+        finally:
+            with type_checks_suppress_lock:
+                type_checks_suppressed -= 1
+
+    if func is None:
+        # Context manager mode
+        return contextmanager(cm)()
+    else:
+        # Decorator mode
+        update_wrapper(wrapper, func)
+        return wrapper
diff --git a/metaflow/_vendor/v3_7/typeguard/_transformer.py b/metaflow/_vendor/v3_7/typeguard/_transformer.py
new file mode 100644
index 00000000000..24090b19b00
--- /dev/null
+++ b/metaflow/_vendor/v3_7/typeguard/_transformer.py
@@ -0,0 +1,1207 @@
+from __future__ import annotations
+
+import ast
+import builtins
+import sys
+import typing
+from ast import (
+    AST,
+    Add,
+    AnnAssign,
+    Assign,
+    AsyncFunctionDef,
+    Attribute,
+    AugAssign,
+    BinOp,
+    BitAnd,
+    BitOr,
+    BitXor,
+    Call,
+    ClassDef,
+    Constant,
+    Dict,
+    Div,
+    Expr,
+    Expression,
+    FloorDiv,
+    FunctionDef,
+    If,
+    Import,
+    ImportFrom,
+    Index,
+    List,
+    Load,
+    LShift,
+    MatMult,
+    Mod,
+    Module,
+    Mult,
+    Name,
+    NodeTransformer,
+    NodeVisitor,
+    Pass,
+    Pow,
+    Return,
+    RShift,
+    Starred,
+    Store,
+    Str,
+    Sub,
+    Subscript,
+    Tuple,
+    Yield,
+    YieldFrom,
+    alias,
+    copy_location,
+    expr,
+    fix_missing_locations,
+    keyword,
+    walk,
+)
+from collections import defaultdict
+from collections.abc import Generator, Sequence
+from contextlib import contextmanager
+from copy import deepcopy
+from dataclasses import dataclass, field
+from typing import Any, ClassVar, cast, overload
+
+if sys.version_info >= (3, 8):
+    from ast import NamedExpr
+
+generator_names = (
+    "typing.Generator",
+    "collections.abc.Generator",
+    "typing.Iterator",
+    "collections.abc.Iterator",
+    "typing.Iterable",
+    "collections.abc.Iterable",
+    "typing.AsyncIterator",
+    "collections.abc.AsyncIterator",
+    "typing.AsyncIterable",
+    "collections.abc.AsyncIterable",
+    "typing.AsyncGenerator",
+    "collections.abc.AsyncGenerator",
+)
+anytype_names = (
+    "typing.Any",
+    "typing_extensions.Any",
+)
+literal_names = (
+    "typing.Literal",
+    "typing_extensions.Literal",
+)
+annotated_names = (
+    "typing.Annotated",
+    "typing_extensions.Annotated",
+)
+ignore_decorators = (
+    "typing.no_type_check",
+    "typeguard.typeguard_ignore",
+)
+aug_assign_functions = {
+    Add: "iadd",
+    Sub: "isub",
+    Mult: "imul",
+    MatMult: "imatmul",
+    Div: "itruediv",
+    FloorDiv: "ifloordiv",
+    Mod: "imod",
+    Pow: "ipow",
+    LShift: "ilshift",
+    RShift: "irshift",
+    BitAnd: "iand",
+    BitXor: "ixor",
+    BitOr: "ior",
+}
+
+
+@dataclass
+class TransformMemo:
+    node: Module | ClassDef | FunctionDef | AsyncFunctionDef | None
+    parent: TransformMemo | None
+    path: tuple[str, ...]
+    joined_path: Constant = field(init=False)
+    return_annotation: expr | None = None
+    yield_annotation: expr | None = None
+    send_annotation: expr | None = None
+    is_async: bool = False
+    local_names: set[str] = field(init=False, default_factory=set)
+    imported_names: dict[str, str] = field(init=False, default_factory=dict)
+    ignored_names: set[str] = field(init=False, default_factory=set)
+    load_names: defaultdict[str, dict[str, Name]] = field(
+        init=False, default_factory=lambda: defaultdict(dict)
+    )
+    has_yield_expressions: bool = field(init=False, default=False)
+    has_return_expressions: bool = field(init=False, default=False)
+    memo_var_name: Name | None = field(init=False, default=None)
+    should_instrument: bool = field(init=False, default=True)
+    variable_annotations: dict[str, expr] = field(init=False, default_factory=dict)
+    configuration_overrides: dict[str, Any] = field(init=False, default_factory=dict)
+    code_inject_index: int = field(init=False, default=0)
+
+    def __post_init__(self) -> None:
+        elements: list[str] = []
+        memo = self
+        while isinstance(memo.node, (ClassDef, FunctionDef, AsyncFunctionDef)):
+            elements.insert(0, memo.node.name)
+            if not memo.parent:
+                break
+
+            memo = memo.parent
+            if isinstance(memo.node, (FunctionDef, AsyncFunctionDef)):
+                elements.insert(0, "")
+
+        self.joined_path = Constant(".".join(elements))
+
+        # Figure out where to insert instrumentation code
+        if self.node:
+            for index, child in enumerate(self.node.body):
+                if isinstance(child, ImportFrom) and child.module == "__future__":
+                    # (module only) __future__ imports must come first
+                    continue
+                elif isinstance(child, Expr):
+                    if isinstance(child.value, Constant) and isinstance(
+                        child.value.value, str
+                    ):
+                        continue  # docstring
+                    elif sys.version_info < (3, 8) and isinstance(child.value, Str):
+                        continue  # docstring
+
+                self.code_inject_index = index
+                break
+
+    def get_unused_name(self, name: str) -> str:
+        memo: TransformMemo | None = self
+        while memo is not None:
+            if name in memo.local_names:
+                memo = self
+                name += "_"
+            else:
+                memo = memo.parent
+
+        self.local_names.add(name)
+        return name
+
+    def is_ignored_name(self, expression: expr | Expr | None) -> bool:
+        top_expression = (
+            expression.value if isinstance(expression, Expr) else expression
+        )
+
+        if isinstance(top_expression, Attribute) and isinstance(
+            top_expression.value, Name
+        ):
+            name = top_expression.value.id
+        elif isinstance(top_expression, Name):
+            name = top_expression.id
+        else:
+            return False
+
+        memo: TransformMemo | None = self
+        while memo is not None:
+            if name in memo.ignored_names:
+                return True
+
+            memo = memo.parent
+
+        return False
+
+    def get_memo_name(self) -> Name:
+        if not self.memo_var_name:
+            self.memo_var_name = Name(id="memo", ctx=Load())
+
+        return self.memo_var_name
+
+    def get_import(self, module: str, name: str) -> Name:
+        if module in self.load_names and name in self.load_names[module]:
+            return self.load_names[module][name]
+
+        qualified_name = f"{module}.{name}"
+        if name in self.imported_names and self.imported_names[name] == qualified_name:
+            return Name(id=name, ctx=Load())
+
+        alias = self.get_unused_name(name)
+        node = self.load_names[module][name] = Name(id=alias, ctx=Load())
+        self.imported_names[name] = qualified_name
+        return node
+
+    def insert_imports(self, node: Module | FunctionDef | AsyncFunctionDef) -> None:
+        """Insert imports needed by injected code."""
+        if not self.load_names:
+            return
+
+        # Insert imports after any "from __future__ ..." imports and any docstring
+        for modulename, names in self.load_names.items():
+            aliases = [
+                alias(orig_name, new_name.id if orig_name != new_name.id else None)
+                for orig_name, new_name in sorted(names.items())
+            ]
+            node.body.insert(self.code_inject_index, ImportFrom(modulename, aliases, 0))
+
+    def name_matches(self, expression: expr | Expr | None, *names: str) -> bool:
+        if expression is None:
+            return False
+
+        path: list[str] = []
+        top_expression = (
+            expression.value if isinstance(expression, Expr) else expression
+        )
+
+        if isinstance(top_expression, Subscript):
+            top_expression = top_expression.value
+        elif isinstance(top_expression, Call):
+            top_expression = top_expression.func
+
+        while isinstance(top_expression, Attribute):
+            path.insert(0, top_expression.attr)
+            top_expression = top_expression.value
+
+        if not isinstance(top_expression, Name):
+            return False
+
+        if top_expression.id in self.imported_names:
+            translated = self.imported_names[top_expression.id]
+        elif hasattr(builtins, top_expression.id):
+            translated = "builtins." + top_expression.id
+        else:
+            translated = top_expression.id
+
+        path.insert(0, translated)
+        joined_path = ".".join(path)
+        if joined_path in names:
+            return True
+        elif self.parent:
+            return self.parent.name_matches(expression, *names)
+        else:
+            return False
+
+    def get_config_keywords(self) -> list[keyword]:
+        if self.parent and isinstance(self.parent.node, ClassDef):
+            overrides = self.parent.configuration_overrides.copy()
+        else:
+            overrides = {}
+
+        overrides.update(self.configuration_overrides)
+        return [keyword(key, value) for key, value in overrides.items()]
+
+
+class NameCollector(NodeVisitor):
+    def __init__(self) -> None:
+        self.names: set[str] = set()
+
+    def visit_Import(self, node: Import) -> None:
+        for name in node.names:
+            self.names.add(name.asname or name.name)
+
+    def visit_ImportFrom(self, node: ImportFrom) -> None:
+        for name in node.names:
+            self.names.add(name.asname or name.name)
+
+    def visit_Assign(self, node: Assign) -> None:
+        for target in node.targets:
+            if isinstance(target, Name):
+                self.names.add(target.id)
+
+    def visit_NamedExpr(self, node: NamedExpr) -> Any:
+        if isinstance(node.target, Name):
+            self.names.add(node.target.id)
+
+    def visit_FunctionDef(self, node: FunctionDef) -> None:
+        pass
+
+    def visit_ClassDef(self, node: ClassDef) -> None:
+        pass
+
+
+class GeneratorDetector(NodeVisitor):
+    """Detects if a function node is a generator function."""
+
+    contains_yields: bool = False
+    in_root_function: bool = False
+
+    def visit_Yield(self, node: Yield) -> Any:
+        self.contains_yields = True
+
+    def visit_YieldFrom(self, node: YieldFrom) -> Any:
+        self.contains_yields = True
+
+    def visit_ClassDef(self, node: ClassDef) -> Any:
+        pass
+
+    def visit_FunctionDef(self, node: FunctionDef | AsyncFunctionDef) -> Any:
+        if not self.in_root_function:
+            self.in_root_function = True
+            self.generic_visit(node)
+            self.in_root_function = False
+
+    def visit_AsyncFunctionDef(self, node: AsyncFunctionDef) -> Any:
+        self.visit_FunctionDef(node)
+
+
+class AnnotationTransformer(NodeTransformer):
+    type_substitutions: ClassVar[dict[str, tuple[str, str]]] = {
+        "builtins.dict": ("typing", "Dict"),
+        "builtins.list": ("typing", "List"),
+        "builtins.tuple": ("typing", "Tuple"),
+        "builtins.set": ("typing", "Set"),
+        "builtins.frozenset": ("typing", "FrozenSet"),
+    }
+
+    def __init__(self, transformer: TypeguardTransformer):
+        self.transformer = transformer
+        self._memo = transformer._memo
+        self._level = 0
+
+    def visit(self, node: AST) -> Any:
+        self._level += 1
+        new_node = super().visit(node)
+        self._level -= 1
+
+        if isinstance(new_node, Expression) and not hasattr(new_node, "body"):
+            return None
+
+        # Return None if this new node matches a variation of typing.Any
+        if (
+            self._level == 0
+            and isinstance(new_node, expr)
+            and self._memo.name_matches(new_node, *anytype_names)
+        ):
+            return None
+
+        return new_node
+
+    def generic_visit(self, node: AST) -> AST:
+        if isinstance(node, expr) and self._memo.name_matches(node, *literal_names):
+            return node
+
+        return super().generic_visit(node)
+
+    def visit_BinOp(self, node: BinOp) -> Any:
+        self.generic_visit(node)
+
+        if isinstance(node.op, BitOr):
+            # Return Any if either side is Any
+            if self._memo.name_matches(node.left, *anytype_names):
+                return node.left
+            elif self._memo.name_matches(node.right, *anytype_names):
+                return node.right
+
+            if sys.version_info < (3, 10):
+                union_name = self.transformer._get_import("typing", "Union")
+                return Subscript(
+                    value=union_name,
+                    slice=Index(
+                        Tuple(elts=[node.left, node.right], ctx=Load()), ctx=Load()
+                    ),
+                    ctx=Load(),
+                )
+
+        return node
+
+    def visit_Attribute(self, node: Attribute) -> Any:
+        if self._memo.is_ignored_name(node):
+            return None
+
+        return node
+
+    def visit_Subscript(self, node: Subscript) -> Any:
+        if self._memo.is_ignored_name(node.value):
+            return None
+
+        # The subscript of typing(_extensions).Literal can be any arbitrary string, so
+        # don't try to evaluate it as code
+        if node.slice:
+            if isinstance(node.slice, Index):
+                # Python 3.7 and 3.8
+                slice_value = node.slice.value  # type: ignore[attr-defined]
+            else:
+                slice_value = node.slice
+
+            if isinstance(slice_value, Tuple):
+                if self._memo.name_matches(node.value, *annotated_names):
+                    # Only treat the first argument to typing.Annotated as a potential
+                    # forward reference
+                    items = cast(
+                        typing.List[expr],
+                        [self.generic_visit(slice_value.elts[0])]
+                        + slice_value.elts[1:],
+                    )
+                else:
+                    items = cast(
+                        typing.List[expr],
+                        [self.generic_visit(item) for item in slice_value.elts],
+                    )
+
+                # If this is a Union and any of the items is Any, erase the entire
+                # annotation
+                if self._memo.name_matches(node.value, "typing.Union") and any(
+                    isinstance(item, expr)
+                    and self._memo.name_matches(item, *anytype_names)
+                    for item in items
+                ):
+                    return None
+
+                # If all items in the subscript were Any, erase the subscript entirely
+                if all(item is None for item in items):
+                    return node.value
+
+                for index, item in enumerate(items):
+                    if item is None:
+                        items[index] = self.transformer._get_import("typing", "Any")
+
+                slice_value.elts = items
+            else:
+                self.generic_visit(node)
+
+                # If the transformer erased the slice entirely, just return the node
+                # value without the subscript (unless it's Optional, in which case erase
+                # the node entirely
+                if self._memo.name_matches(node.value, "typing.Optional"):
+                    return None
+                elif sys.version_info >= (3, 9) and not hasattr(node, "slice"):
+                    return node.value
+                elif sys.version_info < (3, 9) and not hasattr(node.slice, "value"):
+                    return node.value
+
+        return node
+
+    def visit_Name(self, node: Name) -> Any:
+        if self._memo.is_ignored_name(node):
+            return None
+
+        if sys.version_info < (3, 9):
+            for typename, substitute in self.type_substitutions.items():
+                if self._memo.name_matches(node, typename):
+                    new_node = self.transformer._get_import(*substitute)
+                    return copy_location(new_node, node)
+
+        return node
+
+    def visit_Call(self, node: Call) -> Any:
+        # Don't recurse into calls
+        return node
+
+    def visit_Constant(self, node: Constant) -> Any:
+        if isinstance(node.value, str):
+            expression = ast.parse(node.value, mode="eval")
+            new_node = self.visit(expression)
+            if new_node:
+                return copy_location(new_node.body, node)
+            else:
+                return None
+
+        return node
+
+    def visit_Str(self, node: Str) -> Any:
+        # Only used on Python 3.7
+        expression = ast.parse(node.s, mode="eval")
+        new_node = self.visit(expression)
+        if new_node:
+            return copy_location(new_node.body, node)
+        else:
+            return None
+
+
+class TypeguardTransformer(NodeTransformer):
+    def __init__(
+        self, target_path: Sequence[str] | None = None, target_lineno: int | None = None
+    ) -> None:
+        self._target_path = tuple(target_path) if target_path else None
+        self._memo = self._module_memo = TransformMemo(None, None, ())
+        self.names_used_in_annotations: set[str] = set()
+        self.target_node: FunctionDef | AsyncFunctionDef | None = None
+        self.target_lineno = target_lineno
+
+    @contextmanager
+    def _use_memo(
+        self, node: ClassDef | FunctionDef | AsyncFunctionDef
+    ) -> Generator[None, Any, None]:
+        new_memo = TransformMemo(node, self._memo, self._memo.path + (node.name,))
+        if isinstance(node, (FunctionDef, AsyncFunctionDef)):
+            new_memo.should_instrument = (
+                self._target_path is None or new_memo.path == self._target_path
+            )
+            if new_memo.should_instrument:
+                # Check if the function is a generator function
+                detector = GeneratorDetector()
+                detector.visit(node)
+
+                # Extract yield, send and return types where possible from a subscripted
+                # annotation like Generator[int, str, bool]
+                return_annotation = deepcopy(node.returns)
+                if detector.contains_yields and new_memo.name_matches(
+                    return_annotation, *generator_names
+                ):
+                    if isinstance(return_annotation, Subscript):
+                        annotation_slice = return_annotation.slice
+
+                        # Python < 3.9
+                        if isinstance(annotation_slice, Index):
+                            annotation_slice = (
+                                annotation_slice.value  # type: ignore[attr-defined]
+                            )
+
+                        if isinstance(annotation_slice, Tuple):
+                            items = annotation_slice.elts
+                        else:
+                            items = [annotation_slice]
+
+                        if len(items) > 0:
+                            new_memo.yield_annotation = self._convert_annotation(
+                                items[0]
+                            )
+
+                        if len(items) > 1:
+                            new_memo.send_annotation = self._convert_annotation(
+                                items[1]
+                            )
+
+                        if len(items) > 2:
+                            new_memo.return_annotation = self._convert_annotation(
+                                items[2]
+                            )
+                else:
+                    new_memo.return_annotation = self._convert_annotation(
+                        return_annotation
+                    )
+
+        if isinstance(node, AsyncFunctionDef):
+            new_memo.is_async = True
+
+        old_memo = self._memo
+        self._memo = new_memo
+        yield
+        self._memo = old_memo
+
+    def _get_import(self, module: str, name: str) -> Name:
+        memo = self._memo if self._target_path else self._module_memo
+        return memo.get_import(module, name)
+
+    @overload
+    def _convert_annotation(self, annotation: None) -> None:
+        ...
+
+    @overload
+    def _convert_annotation(self, annotation: expr) -> expr:
+        ...
+
+    def _convert_annotation(self, annotation: expr | None) -> expr | None:
+        if annotation is None:
+            return None
+
+        # Convert PEP 604 unions (x | y) and generic built-in collections where
+        # necessary, and undo forward references
+        new_annotation = cast(expr, AnnotationTransformer(self).visit(annotation))
+        if isinstance(new_annotation, expr):
+            new_annotation = ast.copy_location(new_annotation, annotation)
+
+            # Store names used in the annotation
+            names = {node.id for node in walk(new_annotation) if isinstance(node, Name)}
+            self.names_used_in_annotations.update(names)
+
+        return new_annotation
+
+    def visit_Name(self, node: Name) -> Name:
+        self._memo.local_names.add(node.id)
+        return node
+
+    def visit_Module(self, node: Module) -> Module:
+        self.generic_visit(node)
+        self._memo.insert_imports(node)
+
+        fix_missing_locations(node)
+        return node
+
+    def visit_Import(self, node: Import) -> Import:
+        for name in node.names:
+            self._memo.local_names.add(name.asname or name.name)
+            self._memo.imported_names[name.asname or name.name] = name.name
+
+        return node
+
+    def visit_ImportFrom(self, node: ImportFrom) -> ImportFrom:
+        for name in node.names:
+            if name.name != "*":
+                alias = name.asname or name.name
+                self._memo.local_names.add(alias)
+                self._memo.imported_names[alias] = f"{node.module}.{name.name}"
+
+        return node
+
+    def visit_ClassDef(self, node: ClassDef) -> ClassDef | None:
+        self._memo.local_names.add(node.name)
+
+        # Eliminate top level classes not belonging to the target path
+        if (
+            self._target_path is not None
+            and not self._memo.path
+            and node.name != self._target_path[0]
+        ):
+            return None
+
+        with self._use_memo(node):
+            for decorator in node.decorator_list.copy():
+                if self._memo.name_matches(decorator, "typeguard.typechecked"):
+                    # Remove the decorator to prevent duplicate instrumentation
+                    node.decorator_list.remove(decorator)
+
+                    # Store any configuration overrides
+                    if isinstance(decorator, Call) and decorator.keywords:
+                        self._memo.configuration_overrides.update(
+                            {kw.arg: kw.value for kw in decorator.keywords if kw.arg}
+                        )
+
+            self.generic_visit(node)
+            return node
+
+    def visit_FunctionDef(
+        self, node: FunctionDef | AsyncFunctionDef
+    ) -> FunctionDef | AsyncFunctionDef | None:
+        """
+        Injects type checks for function arguments, and for a return of None if the
+        function is annotated to return something else than Any or None, and the body
+        ends without an explicit "return".
+
+        """
+        self._memo.local_names.add(node.name)
+
+        # Eliminate top level functions not belonging to the target path
+        if (
+            self._target_path is not None
+            and not self._memo.path
+            and node.name != self._target_path[0]
+        ):
+            return None
+
+        # Skip instrumentation if we're instrumenting the whole module and the function
+        # contains either @no_type_check or @typeguard_ignore
+        if self._target_path is None:
+            for decorator in node.decorator_list:
+                if self._memo.name_matches(decorator, *ignore_decorators):
+                    return node
+
+        with self._use_memo(node):
+            arg_annotations: dict[str, Any] = {}
+            if self._target_path is None or self._memo.path == self._target_path:
+                # Find line number we're supposed to match against
+                if node.decorator_list:
+                    first_lineno = node.decorator_list[0].lineno
+                else:
+                    first_lineno = node.lineno
+
+                for decorator in node.decorator_list.copy():
+                    if self._memo.name_matches(decorator, "typing.overload"):
+                        # Remove overloads entirely
+                        return None
+                    elif self._memo.name_matches(decorator, "typeguard.typechecked"):
+                        # Remove the decorator to prevent duplicate instrumentation
+                        node.decorator_list.remove(decorator)
+
+                        # Store any configuration overrides
+                        if isinstance(decorator, Call) and decorator.keywords:
+                            self._memo.configuration_overrides = {
+                                kw.arg: kw.value for kw in decorator.keywords if kw.arg
+                            }
+
+                if self.target_lineno == first_lineno:
+                    assert self.target_node is None
+                    self.target_node = node
+                    if node.decorator_list and sys.version_info >= (3, 8):
+                        self.target_lineno = node.decorator_list[0].lineno
+                    else:
+                        self.target_lineno = node.lineno
+
+                all_args = node.args.args + node.args.kwonlyargs
+                if sys.version_info >= (3, 8):
+                    all_args.extend(node.args.posonlyargs)
+
+                # Ensure that any type shadowed by the positional or keyword-only
+                # argument names are ignored in this function
+                for arg in all_args:
+                    self._memo.ignored_names.add(arg.arg)
+
+                # Ensure that any type shadowed by the variable positional argument name
+                # (e.g. "args" in *args) is ignored this function
+                if node.args.vararg:
+                    self._memo.ignored_names.add(node.args.vararg.arg)
+
+                # Ensure that any type shadowed by the variable keywrod argument name
+                # (e.g. "kwargs" in *kwargs) is ignored this function
+                if node.args.kwarg:
+                    self._memo.ignored_names.add(node.args.kwarg.arg)
+
+                for arg in all_args:
+                    annotation = self._convert_annotation(deepcopy(arg.annotation))
+                    if annotation:
+                        arg_annotations[arg.arg] = annotation
+
+                if node.args.vararg:
+                    annotation_ = self._convert_annotation(node.args.vararg.annotation)
+                    if annotation_:
+                        if sys.version_info >= (3, 9):
+                            container = Name("tuple", ctx=Load())
+                        else:
+                            container = self._get_import("typing", "Tuple")
+
+                        subscript_slice: Tuple | Index = Tuple(
+                            [
+                                annotation_,
+                                Constant(Ellipsis),
+                            ],
+                            ctx=Load(),
+                        )
+                        if sys.version_info < (3, 9):
+                            subscript_slice = Index(subscript_slice, ctx=Load())
+
+                        arg_annotations[node.args.vararg.arg] = Subscript(
+                            container, subscript_slice, ctx=Load()
+                        )
+
+                if node.args.kwarg:
+                    annotation_ = self._convert_annotation(node.args.kwarg.annotation)
+                    if annotation_:
+                        if sys.version_info >= (3, 9):
+                            container = Name("dict", ctx=Load())
+                        else:
+                            container = self._get_import("typing", "Dict")
+
+                        subscript_slice = Tuple(
+                            [
+                                Name("str", ctx=Load()),
+                                annotation_,
+                            ],
+                            ctx=Load(),
+                        )
+                        if sys.version_info < (3, 9):
+                            subscript_slice = Index(subscript_slice, ctx=Load())
+
+                        arg_annotations[node.args.kwarg.arg] = Subscript(
+                            container, subscript_slice, ctx=Load()
+                        )
+
+                if arg_annotations:
+                    self._memo.variable_annotations.update(arg_annotations)
+
+            self.generic_visit(node)
+
+            if arg_annotations:
+                annotations_dict = Dict(
+                    keys=[Constant(key) for key in arg_annotations.keys()],
+                    values=[
+                        Tuple([Name(key, ctx=Load()), annotation], ctx=Load())
+                        for key, annotation in arg_annotations.items()
+                    ],
+                )
+                func_name = self._get_import(
+                    "typeguard._functions", "check_argument_types"
+                )
+                args = [
+                    self._memo.joined_path,
+                    annotations_dict,
+                    self._memo.get_memo_name(),
+                ]
+                node.body.insert(
+                    self._memo.code_inject_index, Expr(Call(func_name, args, []))
+                )
+
+            # Add a checked "return None" to the end if there's no explicit return
+            # Skip if the return annotation is None or Any
+            if (
+                self._memo.return_annotation
+                and (not self._memo.is_async or not self._memo.has_yield_expressions)
+                and not isinstance(node.body[-1], Return)
+                and (
+                    not isinstance(self._memo.return_annotation, Constant)
+                    or self._memo.return_annotation.value is not None
+                )
+            ):
+                func_name = self._get_import(
+                    "typeguard._functions", "check_return_type"
+                )
+                return_node = Return(
+                    Call(
+                        func_name,
+                        [
+                            self._memo.joined_path,
+                            Constant(None),
+                            self._memo.return_annotation,
+                            self._memo.get_memo_name(),
+                        ],
+                        [],
+                    )
+                )
+
+                # Replace a placeholder "pass" at the end
+                if isinstance(node.body[-1], Pass):
+                    copy_location(return_node, node.body[-1])
+                    del node.body[-1]
+
+                node.body.append(return_node)
+
+            # Insert code to create the call memo, if it was ever needed for this
+            # function
+            if self._memo.memo_var_name:
+                memo_kwargs: dict[str, Any] = {}
+                if self._memo.parent and isinstance(self._memo.parent.node, ClassDef):
+                    for decorator in node.decorator_list:
+                        if (
+                            isinstance(decorator, Name)
+                            and decorator.id == "staticmethod"
+                        ):
+                            break
+                        elif (
+                            isinstance(decorator, Name)
+                            and decorator.id == "classmethod"
+                        ):
+                            memo_kwargs["self_type"] = Name(
+                                id=node.args.args[0].arg, ctx=Load()
+                            )
+                            break
+                    else:
+                        if node.args.args:
+                            if node.name == "__new__":
+                                memo_kwargs["self_type"] = Name(
+                                    id=node.args.args[0].arg, ctx=Load()
+                                )
+                            else:
+                                memo_kwargs["self_type"] = Attribute(
+                                    Name(id=node.args.args[0].arg, ctx=Load()),
+                                    "__class__",
+                                    ctx=Load(),
+                                )
+
+                # Construct the function reference
+                # Nested functions get special treatment: the function name is added
+                # to free variables (and the closure of the resulting function)
+                names: list[str] = [node.name]
+                memo = self._memo.parent
+                while memo:
+                    if isinstance(memo.node, (FunctionDef, AsyncFunctionDef)):
+                        # This is a nested function. Use the function name as-is.
+                        del names[:-1]
+                        break
+                    elif not isinstance(memo.node, ClassDef):
+                        break
+
+                    names.insert(0, memo.node.name)
+                    memo = memo.parent
+
+                config_keywords = self._memo.get_config_keywords()
+                if config_keywords:
+                    memo_kwargs["config"] = Call(
+                        self._get_import("dataclasses", "replace"),
+                        [self._get_import("typeguard._config", "global_config")],
+                        config_keywords,
+                    )
+
+                self._memo.memo_var_name.id = self._memo.get_unused_name("memo")
+                memo_store_name = Name(id=self._memo.memo_var_name.id, ctx=Store())
+                globals_call = Call(Name(id="globals", ctx=Load()), [], [])
+                locals_call = Call(Name(id="locals", ctx=Load()), [], [])
+                memo_expr = Call(
+                    self._get_import("typeguard", "TypeCheckMemo"),
+                    [globals_call, locals_call],
+                    [keyword(key, value) for key, value in memo_kwargs.items()],
+                )
+                node.body.insert(
+                    self._memo.code_inject_index,
+                    Assign([memo_store_name], memo_expr),
+                )
+
+                self._memo.insert_imports(node)
+
+                # Rmove any placeholder "pass" at the end
+                if isinstance(node.body[-1], Pass):
+                    del node.body[-1]
+
+        return node
+
+    def visit_AsyncFunctionDef(
+        self, node: AsyncFunctionDef
+    ) -> FunctionDef | AsyncFunctionDef | None:
+        return self.visit_FunctionDef(node)
+
+    def visit_Return(self, node: Return) -> Return:
+        """This injects type checks into "return" statements."""
+        self.generic_visit(node)
+        if (
+            self._memo.return_annotation
+            and self._memo.should_instrument
+            and not self._memo.is_ignored_name(self._memo.return_annotation)
+        ):
+            func_name = self._get_import("typeguard._functions", "check_return_type")
+            old_node = node
+            retval = old_node.value or Constant(None)
+            node = Return(
+                Call(
+                    func_name,
+                    [
+                        self._memo.joined_path,
+                        retval,
+                        self._memo.return_annotation,
+                        self._memo.get_memo_name(),
+                    ],
+                    [],
+                )
+            )
+            copy_location(node, old_node)
+
+        return node
+
+    def visit_Yield(self, node: Yield) -> Yield | Call:
+        """
+        This injects type checks into "yield" expressions, checking both the yielded
+        value and the value sent back to the generator, when appropriate.
+
+        """
+        self._memo.has_yield_expressions = True
+        self.generic_visit(node)
+
+        if (
+            self._memo.yield_annotation
+            and self._memo.should_instrument
+            and not self._memo.is_ignored_name(self._memo.yield_annotation)
+        ):
+            func_name = self._get_import("typeguard._functions", "check_yield_type")
+            yieldval = node.value or Constant(None)
+            node.value = Call(
+                func_name,
+                [
+                    self._memo.joined_path,
+                    yieldval,
+                    self._memo.yield_annotation,
+                    self._memo.get_memo_name(),
+                ],
+                [],
+            )
+
+        if (
+            self._memo.send_annotation
+            and self._memo.should_instrument
+            and not self._memo.is_ignored_name(self._memo.send_annotation)
+        ):
+            func_name = self._get_import("typeguard._functions", "check_send_type")
+            old_node = node
+            call_node = Call(
+                func_name,
+                [
+                    self._memo.joined_path,
+                    old_node,
+                    self._memo.send_annotation,
+                    self._memo.get_memo_name(),
+                ],
+                [],
+            )
+            copy_location(call_node, old_node)
+            return call_node
+
+        return node
+
+    def visit_AnnAssign(self, node: AnnAssign) -> Any:
+        """
+        This injects a type check into a local variable annotation-assignment within a
+        function body.
+
+        """
+        self.generic_visit(node)
+
+        if (
+            isinstance(self._memo.node, (FunctionDef, AsyncFunctionDef))
+            and node.annotation
+            and isinstance(node.target, Name)
+        ):
+            self._memo.ignored_names.add(node.target.id)
+            annotation = self._convert_annotation(deepcopy(node.annotation))
+            if annotation:
+                self._memo.variable_annotations[node.target.id] = annotation
+                if node.value:
+                    func_name = self._get_import(
+                        "typeguard._functions", "check_variable_assignment"
+                    )
+                    node.value = Call(
+                        func_name,
+                        [
+                            node.value,
+                            Constant(node.target.id),
+                            annotation,
+                            self._memo.get_memo_name(),
+                        ],
+                        [],
+                    )
+
+        return node
+
+    def visit_Assign(self, node: Assign) -> Any:
+        """
+        This injects a type check into a local variable assignment within a function
+        body. The variable must have been annotated earlier in the function body.
+
+        """
+        self.generic_visit(node)
+
+        # Only instrument function-local assignments
+        if isinstance(self._memo.node, (FunctionDef, AsyncFunctionDef)):
+            targets: list[dict[Constant, expr | None]] = []
+            check_required = False
+            for target in node.targets:
+                elts: Sequence[expr]
+                if isinstance(target, Name):
+                    elts = [target]
+                elif isinstance(target, Tuple):
+                    elts = target.elts
+                else:
+                    continue
+
+                annotations_: dict[Constant, expr | None] = {}
+                for exp in elts:
+                    prefix = ""
+                    if isinstance(exp, Starred):
+                        exp = exp.value
+                        prefix = "*"
+
+                    if isinstance(exp, Name):
+                        self._memo.ignored_names.add(exp.id)
+                        name = prefix + exp.id
+                        annotation = self._memo.variable_annotations.get(exp.id)
+                        if annotation:
+                            annotations_[Constant(name)] = annotation
+                            check_required = True
+                        else:
+                            annotations_[Constant(name)] = None
+
+                targets.append(annotations_)
+
+            if check_required:
+                # Replace missing annotations with typing.Any
+                for item in targets:
+                    for key, expression in item.items():
+                        if expression is None:
+                            item[key] = self._get_import("typing", "Any")
+
+                if len(targets) == 1 and len(targets[0]) == 1:
+                    func_name = self._get_import(
+                        "typeguard._functions", "check_variable_assignment"
+                    )
+                    target_varname = next(iter(targets[0]))
+                    node.value = Call(
+                        func_name,
+                        [
+                            node.value,
+                            target_varname,
+                            targets[0][target_varname],
+                            self._memo.get_memo_name(),
+                        ],
+                        [],
+                    )
+                elif targets:
+                    func_name = self._get_import(
+                        "typeguard._functions", "check_multi_variable_assignment"
+                    )
+                    targets_arg = List(
+                        [
+                            Dict(keys=list(target), values=list(target.values()))
+                            for target in targets
+                        ],
+                        ctx=Load(),
+                    )
+                    node.value = Call(
+                        func_name,
+                        [node.value, targets_arg, self._memo.get_memo_name()],
+                        [],
+                    )
+
+        return node
+
+    def visit_NamedExpr(self, node: NamedExpr) -> Any:
+        """This injects a type check into an assignment expression (a := foo())."""
+        self.generic_visit(node)
+
+        # Only instrument function-local assignments
+        if isinstance(self._memo.node, (FunctionDef, AsyncFunctionDef)) and isinstance(
+            node.target, Name
+        ):
+            self._memo.ignored_names.add(node.target.id)
+
+            # Bail out if no matching annotation is found
+            annotation = self._memo.variable_annotations.get(node.target.id)
+            if annotation is None:
+                return node
+
+            func_name = self._get_import(
+                "typeguard._functions", "check_variable_assignment"
+            )
+            node.value = Call(
+                func_name,
+                [
+                    node.value,
+                    Constant(node.target.id),
+                    annotation,
+                    self._memo.get_memo_name(),
+                ],
+                [],
+            )
+
+        return node
+
+    def visit_AugAssign(self, node: AugAssign) -> Any:
+        """
+        This injects a type check into an augmented assignment expression (a += 1).
+
+        """
+        self.generic_visit(node)
+
+        # Only instrument function-local assignments
+        if isinstance(self._memo.node, (FunctionDef, AsyncFunctionDef)) and isinstance(
+            node.target, Name
+        ):
+            # Bail out if no matching annotation is found
+            annotation = self._memo.variable_annotations.get(node.target.id)
+            if annotation is None:
+                return node
+
+            # Bail out if the operator is not found (newer Python version?)
+            try:
+                operator_func_name = aug_assign_functions[node.op.__class__]
+            except KeyError:
+                return node
+
+            operator_func = self._get_import("operator", operator_func_name)
+            operator_call = Call(
+                operator_func, [Name(node.target.id, ctx=Load()), node.value], []
+            )
+            check_call = Call(
+                self._get_import("typeguard._functions", "check_variable_assignment"),
+                [
+                    operator_call,
+                    Constant(node.target.id),
+                    annotation,
+                    self._memo.get_memo_name(),
+                ],
+                [],
+            )
+            return Assign(targets=[node.target], value=check_call)
+
+        return node
+
+    def visit_If(self, node: If) -> Any:
+        """
+        This blocks names from being collected from a module-level
+        "if typing.TYPE_CHECKING:" block, so that they won't be type checked.
+
+        """
+        self.generic_visit(node)
+
+        # Fix empty node body (caused by removal of classes/functions not on the target
+        # path)
+        if not node.body:
+            node.body.append(Pass())
+
+        if (
+            self._memo is self._module_memo
+            and isinstance(node.test, Name)
+            and self._memo.name_matches(node.test, "typing.TYPE_CHECKING")
+        ):
+            collector = NameCollector()
+            collector.visit(node)
+            self._memo.ignored_names.update(collector.names)
+
+        return node
diff --git a/metaflow/_vendor/v3_7/typeguard/_union_transformer.py b/metaflow/_vendor/v3_7/typeguard/_union_transformer.py
new file mode 100644
index 00000000000..fcd6349d35a
--- /dev/null
+++ b/metaflow/_vendor/v3_7/typeguard/_union_transformer.py
@@ -0,0 +1,54 @@
+"""
+Transforms lazily evaluated PEP 604 unions into typing.Unions, for compatibility with
+Python versions older than 3.10.
+"""
+from __future__ import annotations
+
+from ast import (
+    BinOp,
+    BitOr,
+    Index,
+    Load,
+    Name,
+    NodeTransformer,
+    Subscript,
+    fix_missing_locations,
+    parse,
+)
+from ast import Tuple as ASTTuple
+from types import CodeType
+from typing import Any, Dict, FrozenSet, List, Set, Tuple, Union
+
+type_substitutions = {
+    "dict": Dict,
+    "list": List,
+    "tuple": Tuple,
+    "set": Set,
+    "frozenset": FrozenSet,
+    "Union": Union,
+}
+
+
+class UnionTransformer(NodeTransformer):
+    def __init__(self, union_name: Name | None = None):
+        self.union_name = union_name or Name(id="Union", ctx=Load())
+
+    def visit_BinOp(self, node: BinOp) -> Any:
+        self.generic_visit(node)
+        if isinstance(node.op, BitOr):
+            return Subscript(
+                value=self.union_name,
+                slice=Index(
+                    ASTTuple(elts=[node.left, node.right], ctx=Load()), ctx=Load()
+                ),
+                ctx=Load(),
+            )
+
+        return node
+
+
+def compile_type_hint(hint: str) -> CodeType:
+    parsed = parse(hint, "", "eval")
+    UnionTransformer().visit(parsed)
+    fix_missing_locations(parsed)
+    return compile(parsed, "", "eval", flags=0)
diff --git a/metaflow/_vendor/v3_7/typeguard/_utils.py b/metaflow/_vendor/v3_7/typeguard/_utils.py
new file mode 100644
index 00000000000..9999ad7edca
--- /dev/null
+++ b/metaflow/_vendor/v3_7/typeguard/_utils.py
@@ -0,0 +1,169 @@
+from __future__ import annotations
+
+import inspect
+import sys
+from importlib import import_module
+from inspect import currentframe
+from types import CodeType, FrameType, FunctionType
+from typing import TYPE_CHECKING, Any, Callable, ForwardRef, Union, cast
+from weakref import WeakValueDictionary
+
+if TYPE_CHECKING:
+    from ._memo import TypeCheckMemo
+
+if sys.version_info >= (3, 10):
+    from typing import get_args, get_origin
+
+    def evaluate_forwardref(forwardref: ForwardRef, memo: TypeCheckMemo) -> Any:
+        return forwardref._evaluate(memo.globals, memo.locals, frozenset())
+
+else:
+    from metaflow._vendor.v3_7.typing_extensions import get_args, get_origin
+
+    evaluate_extra_args: tuple[frozenset[Any], ...] = (
+        (frozenset(),) if sys.version_info >= (3, 9) else ()
+    )
+
+    def evaluate_forwardref(forwardref: ForwardRef, memo: TypeCheckMemo) -> Any:
+        from ._union_transformer import compile_type_hint, type_substitutions
+
+        if not forwardref.__forward_evaluated__:
+            forwardref.__forward_code__ = compile_type_hint(forwardref.__forward_arg__)
+
+        try:
+            return forwardref._evaluate(memo.globals, memo.locals, *evaluate_extra_args)
+        except NameError:
+            if sys.version_info < (3, 10):
+                # Try again, with the type substitutions (list -> List etc.) in place
+                new_globals = memo.globals.copy()
+                new_globals.setdefault("Union", Union)
+                if sys.version_info < (3, 9):
+                    new_globals.update(type_substitutions)
+
+                return forwardref._evaluate(
+                    new_globals, memo.locals or new_globals, *evaluate_extra_args
+                )
+
+            raise
+
+
+if sys.version_info >= (3, 8):
+    from typing import final
+else:
+    from metaflow._vendor.v3_7.typing_extensions import final
+
+
+_functions_map: WeakValueDictionary[CodeType, FunctionType] = WeakValueDictionary()
+
+
+def get_type_name(type_: Any) -> str:
+    name: str
+    for attrname in "__name__", "_name", "__forward_arg__":
+        candidate = getattr(type_, attrname, None)
+        if isinstance(candidate, str):
+            name = candidate
+            break
+    else:
+        origin = get_origin(type_)
+        candidate = getattr(origin, "_name", None)
+        if candidate is None:
+            candidate = type_.__class__.__name__.strip("_")
+
+        if isinstance(candidate, str):
+            name = candidate
+        else:
+            return "(unknown)"
+
+    args = get_args(type_)
+    if args:
+        if name == "Literal":
+            formatted_args = ", ".join(repr(arg) for arg in args)
+        else:
+            formatted_args = ", ".join(get_type_name(arg) for arg in args)
+
+        name += f"[{formatted_args}]"
+
+    module = getattr(type_, "__module__", None)
+    if module and module not in (None, "typing", "typing_extensions", "builtins"):
+        name = module + "." + name
+
+    return name
+
+
+def qualified_name(obj: Any, *, add_class_prefix: bool = False) -> str:
+    """
+    Return the qualified name (e.g. package.module.Type) for the given object.
+
+    Builtins and types from the :mod:`typing` package get special treatment by having
+    the module name stripped from the generated name.
+
+    """
+    if obj is None:
+        return "None"
+    elif inspect.isclass(obj):
+        prefix = "class " if add_class_prefix else ""
+        type_ = obj
+    else:
+        prefix = ""
+        type_ = type(obj)
+
+    module = type_.__module__
+    qualname = type_.__qualname__
+    name = qualname if module in ("typing", "builtins") else f"{module}.{qualname}"
+    return prefix + name
+
+
+def function_name(func: Callable[..., Any]) -> str:
+    """
+    Return the qualified name of the given function.
+
+    Builtins and types from the :mod:`typing` package get special treatment by having
+    the module name stripped from the generated name.
+
+    """
+    # For partial functions and objects with __call__ defined, __qualname__ does not
+    # exist
+    module = getattr(func, "__module__", "")
+    qualname = (module + ".") if module not in ("builtins", "") else ""
+    return qualname + getattr(func, "__qualname__", repr(func))
+
+
+def resolve_reference(reference: str) -> Any:
+    modulename, varname = reference.partition(":")[::2]
+    if not modulename or not varname:
+        raise ValueError(f"{reference!r} is not a module:varname reference")
+
+    obj = import_module(modulename)
+    for attr in varname.split("."):
+        obj = getattr(obj, attr)
+
+    return obj
+
+
+def is_method_of(obj: object, cls: type) -> bool:
+    return (
+        inspect.isfunction(obj)
+        and obj.__module__ == cls.__module__
+        and obj.__qualname__.startswith(cls.__qualname__ + ".")
+    )
+
+
+def get_stacklevel() -> int:
+    level = 1
+    frame = cast(FrameType, currentframe()).f_back
+    while frame and frame.f_globals.get("__name__", "").startswith("typeguard."):
+        level += 1
+        frame = frame.f_back
+
+    return level
+
+
+@final
+class Unset:
+    __slots__ = ()
+
+    def __repr__(self) -> str:
+        return ""
+
+
+unset = Unset()
diff --git a/metaflow/_vendor/v3_7/typeguard/py.typed b/metaflow/_vendor/v3_7/typeguard/py.typed
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/metaflow/_vendor/v3_7/typing_extensions.LICENSE b/metaflow/_vendor/v3_7/typing_extensions.LICENSE
new file mode 100644
index 00000000000..f26bcf4d2de
--- /dev/null
+++ b/metaflow/_vendor/v3_7/typing_extensions.LICENSE
@@ -0,0 +1,279 @@
+A. HISTORY OF THE SOFTWARE
+==========================
+
+Python was created in the early 1990s by Guido van Rossum at Stichting
+Mathematisch Centrum (CWI, see https://www.cwi.nl) in the Netherlands
+as a successor of a language called ABC.  Guido remains Python's
+principal author, although it includes many contributions from others.
+
+In 1995, Guido continued his work on Python at the Corporation for
+National Research Initiatives (CNRI, see https://www.cnri.reston.va.us)
+in Reston, Virginia where he released several versions of the
+software.
+
+In May 2000, Guido and the Python core development team moved to
+BeOpen.com to form the BeOpen PythonLabs team.  In October of the same
+year, the PythonLabs team moved to Digital Creations, which became
+Zope Corporation.  In 2001, the Python Software Foundation (PSF, see
+https://www.python.org/psf/) was formed, a non-profit organization
+created specifically to own Python-related Intellectual Property.
+Zope Corporation was a sponsoring member of the PSF.
+
+All Python releases are Open Source (see https://opensource.org for
+the Open Source Definition).  Historically, most, but not all, Python
+releases have also been GPL-compatible; the table below summarizes
+the various releases.
+
+    Release         Derived     Year        Owner       GPL-
+                    from                                compatible? (1)
+
+    0.9.0 thru 1.2              1991-1995   CWI         yes
+    1.3 thru 1.5.2  1.2         1995-1999   CNRI        yes
+    1.6             1.5.2       2000        CNRI        no
+    2.0             1.6         2000        BeOpen.com  no
+    1.6.1           1.6         2001        CNRI        yes (2)
+    2.1             2.0+1.6.1   2001        PSF         no
+    2.0.1           2.0+1.6.1   2001        PSF         yes
+    2.1.1           2.1+2.0.1   2001        PSF         yes
+    2.1.2           2.1.1       2002        PSF         yes
+    2.1.3           2.1.2       2002        PSF         yes
+    2.2 and above   2.1.1       2001-now    PSF         yes
+
+Footnotes:
+
+(1) GPL-compatible doesn't mean that we're distributing Python under
+    the GPL.  All Python licenses, unlike the GPL, let you distribute
+    a modified version without making your changes open source.  The
+    GPL-compatible licenses make it possible to combine Python with
+    other software that is released under the GPL; the others don't.
+
+(2) According to Richard Stallman, 1.6.1 is not GPL-compatible,
+    because its license has a choice of law clause.  According to
+    CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1
+    is "not incompatible" with the GPL.
+
+Thanks to the many outside volunteers who have worked under Guido's
+direction to make these releases possible.
+
+
+B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON
+===============================================================
+
+Python software and documentation are licensed under the
+Python Software Foundation License Version 2.
+
+Starting with Python 3.8.6, examples, recipes, and other code in
+the documentation are dual licensed under the PSF License Version 2
+and the Zero-Clause BSD license.
+
+Some software incorporated into Python is under different licenses.
+The licenses are listed with code falling under that license.
+
+
+PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
+--------------------------------------------
+
+1. This LICENSE AGREEMENT is between the Python Software Foundation
+("PSF"), and the Individual or Organization ("Licensee") accessing and
+otherwise using this software ("Python") in source or binary form and
+its associated documentation.
+
+2. Subject to the terms and conditions of this License Agreement, PSF hereby
+grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
+analyze, test, perform and/or display publicly, prepare derivative works,
+distribute, and otherwise use Python alone or in any derivative version,
+provided, however, that PSF's License Agreement and PSF's notice of copyright,
+i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 Python Software Foundation;
+All Rights Reserved" are retained in Python alone or in any derivative version
+prepared by Licensee.
+
+3. In the event Licensee prepares a derivative work that is based on
+or incorporates Python or any part thereof, and wants to make
+the derivative work available to others as provided herein, then
+Licensee hereby agrees to include in any such work a brief summary of
+the changes made to Python.
+
+4. PSF is making Python available to Licensee on an "AS IS"
+basis.  PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
+IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
+DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
+FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
+INFRINGE ANY THIRD PARTY RIGHTS.
+
+5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
+FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
+A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
+OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
+
+6. This License Agreement will automatically terminate upon a material
+breach of its terms and conditions.
+
+7. Nothing in this License Agreement shall be deemed to create any
+relationship of agency, partnership, or joint venture between PSF and
+Licensee.  This License Agreement does not grant permission to use PSF
+trademarks or trade name in a trademark sense to endorse or promote
+products or services of Licensee, or any third party.
+
+8. By copying, installing or otherwise using Python, Licensee
+agrees to be bound by the terms and conditions of this License
+Agreement.
+
+
+BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0
+-------------------------------------------
+
+BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1
+
+1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an
+office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the
+Individual or Organization ("Licensee") accessing and otherwise using
+this software in source or binary form and its associated
+documentation ("the Software").
+
+2. Subject to the terms and conditions of this BeOpen Python License
+Agreement, BeOpen hereby grants Licensee a non-exclusive,
+royalty-free, world-wide license to reproduce, analyze, test, perform
+and/or display publicly, prepare derivative works, distribute, and
+otherwise use the Software alone or in any derivative version,
+provided, however, that the BeOpen Python License is retained in the
+Software, alone or in any derivative version prepared by Licensee.
+
+3. BeOpen is making the Software available to Licensee on an "AS IS"
+basis.  BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
+IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND
+DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
+FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT
+INFRINGE ANY THIRD PARTY RIGHTS.
+
+4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE
+SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS
+AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY
+DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
+
+5. This License Agreement will automatically terminate upon a material
+breach of its terms and conditions.
+
+6. This License Agreement shall be governed by and interpreted in all
+respects by the law of the State of California, excluding conflict of
+law provisions.  Nothing in this License Agreement shall be deemed to
+create any relationship of agency, partnership, or joint venture
+between BeOpen and Licensee.  This License Agreement does not grant
+permission to use BeOpen trademarks or trade names in a trademark
+sense to endorse or promote products or services of Licensee, or any
+third party.  As an exception, the "BeOpen Python" logos available at
+http://www.pythonlabs.com/logos.html may be used according to the
+permissions granted on that web page.
+
+7. By copying, installing or otherwise using the software, Licensee
+agrees to be bound by the terms and conditions of this License
+Agreement.
+
+
+CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1
+---------------------------------------
+
+1. This LICENSE AGREEMENT is between the Corporation for National
+Research Initiatives, having an office at 1895 Preston White Drive,
+Reston, VA 20191 ("CNRI"), and the Individual or Organization
+("Licensee") accessing and otherwise using Python 1.6.1 software in
+source or binary form and its associated documentation.
+
+2. Subject to the terms and conditions of this License Agreement, CNRI
+hereby grants Licensee a nonexclusive, royalty-free, world-wide
+license to reproduce, analyze, test, perform and/or display publicly,
+prepare derivative works, distribute, and otherwise use Python 1.6.1
+alone or in any derivative version, provided, however, that CNRI's
+License Agreement and CNRI's notice of copyright, i.e., "Copyright (c)
+1995-2001 Corporation for National Research Initiatives; All Rights
+Reserved" are retained in Python 1.6.1 alone or in any derivative
+version prepared by Licensee.  Alternately, in lieu of CNRI's License
+Agreement, Licensee may substitute the following text (omitting the
+quotes): "Python 1.6.1 is made available subject to the terms and
+conditions in CNRI's License Agreement.  This Agreement together with
+Python 1.6.1 may be located on the internet using the following
+unique, persistent identifier (known as a handle): 1895.22/1013.  This
+Agreement may also be obtained from a proxy server on the internet
+using the following URL: http://hdl.handle.net/1895.22/1013".
+
+3. In the event Licensee prepares a derivative work that is based on
+or incorporates Python 1.6.1 or any part thereof, and wants to make
+the derivative work available to others as provided herein, then
+Licensee hereby agrees to include in any such work a brief summary of
+the changes made to Python 1.6.1.
+
+4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS"
+basis.  CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
+IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND
+DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
+FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT
+INFRINGE ANY THIRD PARTY RIGHTS.
+
+5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
+1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
+A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1,
+OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
+
+6. This License Agreement will automatically terminate upon a material
+breach of its terms and conditions.
+
+7. This License Agreement shall be governed by the federal
+intellectual property law of the United States, including without
+limitation the federal copyright law, and, to the extent such
+U.S. federal law does not apply, by the law of the Commonwealth of
+Virginia, excluding Virginia's conflict of law provisions.
+Notwithstanding the foregoing, with regard to derivative works based
+on Python 1.6.1 that incorporate non-separable material that was
+previously distributed under the GNU General Public License (GPL), the
+law of the Commonwealth of Virginia shall govern this License
+Agreement only as to issues arising under or with respect to
+Paragraphs 4, 5, and 7 of this License Agreement.  Nothing in this
+License Agreement shall be deemed to create any relationship of
+agency, partnership, or joint venture between CNRI and Licensee.  This
+License Agreement does not grant permission to use CNRI trademarks or
+trade name in a trademark sense to endorse or promote products or
+services of Licensee, or any third party.
+
+8. By clicking on the "ACCEPT" button where indicated, or by copying,
+installing or otherwise using Python 1.6.1, Licensee agrees to be
+bound by the terms and conditions of this License Agreement.
+
+        ACCEPT
+
+
+CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2
+--------------------------------------------------
+
+Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam,
+The Netherlands.  All rights reserved.
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Stichting Mathematisch
+Centrum or CWI not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.
+
+STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
+FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+ZERO-CLAUSE BSD LICENSE FOR CODE IN THE PYTHON DOCUMENTATION
+----------------------------------------------------------------------
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
diff --git a/metaflow/_vendor/v3_7/typing_extensions.py b/metaflow/_vendor/v3_7/typing_extensions.py
new file mode 100644
index 00000000000..6b7dc6cc103
--- /dev/null
+++ b/metaflow/_vendor/v3_7/typing_extensions.py
@@ -0,0 +1,3072 @@
+import abc
+import collections
+import collections.abc
+import functools
+import inspect
+import operator
+import sys
+import types as _types
+import typing
+import warnings
+
+__all__ = [
+    # Super-special typing primitives.
+    'Any',
+    'ClassVar',
+    'Concatenate',
+    'Final',
+    'LiteralString',
+    'ParamSpec',
+    'ParamSpecArgs',
+    'ParamSpecKwargs',
+    'Self',
+    'Type',
+    'TypeVar',
+    'TypeVarTuple',
+    'Unpack',
+
+    # ABCs (from collections.abc).
+    'Awaitable',
+    'AsyncIterator',
+    'AsyncIterable',
+    'Coroutine',
+    'AsyncGenerator',
+    'AsyncContextManager',
+    'Buffer',
+    'ChainMap',
+
+    # Concrete collection types.
+    'ContextManager',
+    'Counter',
+    'Deque',
+    'DefaultDict',
+    'NamedTuple',
+    'OrderedDict',
+    'TypedDict',
+
+    # Structural checks, a.k.a. protocols.
+    'SupportsAbs',
+    'SupportsBytes',
+    'SupportsComplex',
+    'SupportsFloat',
+    'SupportsIndex',
+    'SupportsInt',
+    'SupportsRound',
+
+    # One-off things.
+    'Annotated',
+    'assert_never',
+    'assert_type',
+    'clear_overloads',
+    'dataclass_transform',
+    'deprecated',
+    'get_overloads',
+    'final',
+    'get_args',
+    'get_origin',
+    'get_original_bases',
+    'get_protocol_members',
+    'get_type_hints',
+    'IntVar',
+    'is_protocol',
+    'is_typeddict',
+    'Literal',
+    'NewType',
+    'overload',
+    'override',
+    'Protocol',
+    'reveal_type',
+    'runtime',
+    'runtime_checkable',
+    'Text',
+    'TypeAlias',
+    'TypeAliasType',
+    'TypeGuard',
+    'TYPE_CHECKING',
+    'Never',
+    'NoReturn',
+    'Required',
+    'NotRequired',
+
+    # Pure aliases, have always been in typing
+    'AbstractSet',
+    'AnyStr',
+    'BinaryIO',
+    'Callable',
+    'Collection',
+    'Container',
+    'Dict',
+    'ForwardRef',
+    'FrozenSet',
+    'Generator',
+    'Generic',
+    'Hashable',
+    'IO',
+    'ItemsView',
+    'Iterable',
+    'Iterator',
+    'KeysView',
+    'List',
+    'Mapping',
+    'MappingView',
+    'Match',
+    'MutableMapping',
+    'MutableSequence',
+    'MutableSet',
+    'Optional',
+    'Pattern',
+    'Reversible',
+    'Sequence',
+    'Set',
+    'Sized',
+    'TextIO',
+    'Tuple',
+    'Union',
+    'ValuesView',
+    'cast',
+    'no_type_check',
+    'no_type_check_decorator',
+]
+
+# for backward compatibility
+PEP_560 = True
+GenericMeta = type
+
+# The functions below are modified copies of typing internal helpers.
+# They are needed by _ProtocolMeta and they provide support for PEP 646.
+
+
+class _Sentinel:
+    def __repr__(self):
+        return ""
+
+
+_marker = _Sentinel()
+
+
+def _check_generic(cls, parameters, elen=_marker):
+    """Check correct count for parameters of a generic cls (internal helper).
+    This gives a nice error message in case of count mismatch.
+    """
+    if not elen:
+        raise TypeError(f"{cls} is not a generic class")
+    if elen is _marker:
+        if not hasattr(cls, "__parameters__") or not cls.__parameters__:
+            raise TypeError(f"{cls} is not a generic class")
+        elen = len(cls.__parameters__)
+    alen = len(parameters)
+    if alen != elen:
+        if hasattr(cls, "__parameters__"):
+            parameters = [p for p in cls.__parameters__ if not _is_unpack(p)]
+            num_tv_tuples = sum(isinstance(p, TypeVarTuple) for p in parameters)
+            if (num_tv_tuples > 0) and (alen >= elen - num_tv_tuples):
+                return
+        raise TypeError(f"Too {'many' if alen > elen else 'few'} parameters for {cls};"
+                        f" actual {alen}, expected {elen}")
+
+
+if sys.version_info >= (3, 10):
+    def _should_collect_from_parameters(t):
+        return isinstance(
+            t, (typing._GenericAlias, _types.GenericAlias, _types.UnionType)
+        )
+elif sys.version_info >= (3, 9):
+    def _should_collect_from_parameters(t):
+        return isinstance(t, (typing._GenericAlias, _types.GenericAlias))
+else:
+    def _should_collect_from_parameters(t):
+        return isinstance(t, typing._GenericAlias) and not t._special
+
+
+def _collect_type_vars(types, typevar_types=None):
+    """Collect all type variable contained in types in order of
+    first appearance (lexicographic order). For example::
+
+        _collect_type_vars((T, List[S, T])) == (T, S)
+    """
+    if typevar_types is None:
+        typevar_types = typing.TypeVar
+    tvars = []
+    for t in types:
+        if (
+            isinstance(t, typevar_types) and
+            t not in tvars and
+            not _is_unpack(t)
+        ):
+            tvars.append(t)
+        if _should_collect_from_parameters(t):
+            tvars.extend([t for t in t.__parameters__ if t not in tvars])
+    return tuple(tvars)
+
+
+NoReturn = typing.NoReturn
+
+# Some unconstrained type variables.  These are used by the container types.
+# (These are not for export.)
+T = typing.TypeVar('T')  # Any type.
+KT = typing.TypeVar('KT')  # Key type.
+VT = typing.TypeVar('VT')  # Value type.
+T_co = typing.TypeVar('T_co', covariant=True)  # Any type covariant containers.
+T_contra = typing.TypeVar('T_contra', contravariant=True)  # Ditto contravariant.
+
+
+if sys.version_info >= (3, 11):
+    from typing import Any
+else:
+
+    class _AnyMeta(type):
+        def __instancecheck__(self, obj):
+            if self is Any:
+                raise TypeError("typing_extensions.Any cannot be used with isinstance()")
+            return super().__instancecheck__(obj)
+
+        def __repr__(self):
+            if self is Any:
+                return "typing_extensions.Any"
+            return super().__repr__()
+
+    class Any(metaclass=_AnyMeta):
+        """Special type indicating an unconstrained type.
+        - Any is compatible with every type.
+        - Any assumed to have all methods.
+        - All values assumed to be instances of Any.
+        Note that all the above statements are true from the point of view of
+        static type checkers. At runtime, Any should not be used with instance
+        checks.
+        """
+        def __new__(cls, *args, **kwargs):
+            if cls is Any:
+                raise TypeError("Any cannot be instantiated")
+            return super().__new__(cls, *args, **kwargs)
+
+
+ClassVar = typing.ClassVar
+
+
+class _ExtensionsSpecialForm(typing._SpecialForm, _root=True):
+    def __repr__(self):
+        return 'typing_extensions.' + self._name
+
+
+# On older versions of typing there is an internal class named "Final".
+# 3.8+
+if hasattr(typing, 'Final') and sys.version_info[:2] >= (3, 7):
+    Final = typing.Final
+# 3.7
+else:
+    class _FinalForm(_ExtensionsSpecialForm, _root=True):
+        def __getitem__(self, parameters):
+            item = typing._type_check(parameters,
+                                      f'{self._name} accepts only a single type.')
+            return typing._GenericAlias(self, (item,))
+
+    Final = _FinalForm('Final',
+                       doc="""A special typing construct to indicate that a name
+                       cannot be re-assigned or overridden in a subclass.
+                       For example:
+
+                           MAX_SIZE: Final = 9000
+                           MAX_SIZE += 1  # Error reported by type checker
+
+                           class Connection:
+                               TIMEOUT: Final[int] = 10
+                           class FastConnector(Connection):
+                               TIMEOUT = 1  # Error reported by type checker
+
+                       There is no runtime checking of these properties.""")
+
+if sys.version_info >= (3, 11):
+    final = typing.final
+else:
+    # @final exists in 3.8+, but we backport it for all versions
+    # before 3.11 to keep support for the __final__ attribute.
+    # See https://bugs.python.org/issue46342
+    def final(f):
+        """This decorator can be used to indicate to type checkers that
+        the decorated method cannot be overridden, and decorated class
+        cannot be subclassed. For example:
+
+            class Base:
+                @final
+                def done(self) -> None:
+                    ...
+            class Sub(Base):
+                def done(self) -> None:  # Error reported by type checker
+                    ...
+            @final
+            class Leaf:
+                ...
+            class Other(Leaf):  # Error reported by type checker
+                ...
+
+        There is no runtime checking of these properties. The decorator
+        sets the ``__final__`` attribute to ``True`` on the decorated object
+        to allow runtime introspection.
+        """
+        try:
+            f.__final__ = True
+        except (AttributeError, TypeError):
+            # Skip the attribute silently if it is not writable.
+            # AttributeError happens if the object has __slots__ or a
+            # read-only property, TypeError if it's a builtin class.
+            pass
+        return f
+
+
+def IntVar(name):
+    return typing.TypeVar(name)
+
+
+# A Literal bug was fixed in 3.11.0, 3.10.1 and 3.9.8
+if sys.version_info >= (3, 10, 1):
+    Literal = typing.Literal
+else:
+    def _flatten_literal_params(parameters):
+        """An internal helper for Literal creation: flatten Literals among parameters"""
+        params = []
+        for p in parameters:
+            if isinstance(p, _LiteralGenericAlias):
+                params.extend(p.__args__)
+            else:
+                params.append(p)
+        return tuple(params)
+
+    def _value_and_type_iter(params):
+        for p in params:
+            yield p, type(p)
+
+    class _LiteralGenericAlias(typing._GenericAlias, _root=True):
+        def __eq__(self, other):
+            if not isinstance(other, _LiteralGenericAlias):
+                return NotImplemented
+            these_args_deduped = set(_value_and_type_iter(self.__args__))
+            other_args_deduped = set(_value_and_type_iter(other.__args__))
+            return these_args_deduped == other_args_deduped
+
+        def __hash__(self):
+            return hash(frozenset(_value_and_type_iter(self.__args__)))
+
+    class _LiteralForm(_ExtensionsSpecialForm, _root=True):
+        def __init__(self, doc: str):
+            self._name = 'Literal'
+            self._doc = self.__doc__ = doc
+
+        def __getitem__(self, parameters):
+            if not isinstance(parameters, tuple):
+                parameters = (parameters,)
+
+            parameters = _flatten_literal_params(parameters)
+
+            val_type_pairs = list(_value_and_type_iter(parameters))
+            try:
+                deduped_pairs = set(val_type_pairs)
+            except TypeError:
+                # unhashable parameters
+                pass
+            else:
+                # similar logic to typing._deduplicate on Python 3.9+
+                if len(deduped_pairs) < len(val_type_pairs):
+                    new_parameters = []
+                    for pair in val_type_pairs:
+                        if pair in deduped_pairs:
+                            new_parameters.append(pair[0])
+                            deduped_pairs.remove(pair)
+                    assert not deduped_pairs, deduped_pairs
+                    parameters = tuple(new_parameters)
+
+            return _LiteralGenericAlias(self, parameters)
+
+    Literal = _LiteralForm(doc="""\
+                           A type that can be used to indicate to type checkers
+                           that the corresponding value has a value literally equivalent
+                           to the provided parameter. For example:
+
+                               var: Literal[4] = 4
+
+                           The type checker understands that 'var' is literally equal to
+                           the value 4 and no other value.
+
+                           Literal[...] cannot be subclassed. There is no runtime
+                           checking verifying that the parameter is actually a value
+                           instead of a type.""")
+
+
+_overload_dummy = typing._overload_dummy
+
+
+if hasattr(typing, "get_overloads"):  # 3.11+
+    overload = typing.overload
+    get_overloads = typing.get_overloads
+    clear_overloads = typing.clear_overloads
+else:
+    # {module: {qualname: {firstlineno: func}}}
+    _overload_registry = collections.defaultdict(
+        functools.partial(collections.defaultdict, dict)
+    )
+
+    def overload(func):
+        """Decorator for overloaded functions/methods.
+
+        In a stub file, place two or more stub definitions for the same
+        function in a row, each decorated with @overload.  For example:
+
+        @overload
+        def utf8(value: None) -> None: ...
+        @overload
+        def utf8(value: bytes) -> bytes: ...
+        @overload
+        def utf8(value: str) -> bytes: ...
+
+        In a non-stub file (i.e. a regular .py file), do the same but
+        follow it with an implementation.  The implementation should *not*
+        be decorated with @overload.  For example:
+
+        @overload
+        def utf8(value: None) -> None: ...
+        @overload
+        def utf8(value: bytes) -> bytes: ...
+        @overload
+        def utf8(value: str) -> bytes: ...
+        def utf8(value):
+            # implementation goes here
+
+        The overloads for a function can be retrieved at runtime using the
+        get_overloads() function.
+        """
+        # classmethod and staticmethod
+        f = getattr(func, "__func__", func)
+        try:
+            _overload_registry[f.__module__][f.__qualname__][
+                f.__code__.co_firstlineno
+            ] = func
+        except AttributeError:
+            # Not a normal function; ignore.
+            pass
+        return _overload_dummy
+
+    def get_overloads(func):
+        """Return all defined overloads for *func* as a sequence."""
+        # classmethod and staticmethod
+        f = getattr(func, "__func__", func)
+        if f.__module__ not in _overload_registry:
+            return []
+        mod_dict = _overload_registry[f.__module__]
+        if f.__qualname__ not in mod_dict:
+            return []
+        return list(mod_dict[f.__qualname__].values())
+
+    def clear_overloads():
+        """Clear all overloads in the registry."""
+        _overload_registry.clear()
+
+
+# This is not a real generic class.  Don't use outside annotations.
+Type = typing.Type
+
+# Various ABCs mimicking those in collections.abc.
+# A few are simply re-exported for completeness.
+
+
+Awaitable = typing.Awaitable
+Coroutine = typing.Coroutine
+AsyncIterable = typing.AsyncIterable
+AsyncIterator = typing.AsyncIterator
+Deque = typing.Deque
+ContextManager = typing.ContextManager
+AsyncContextManager = typing.AsyncContextManager
+DefaultDict = typing.DefaultDict
+
+# 3.7.2+
+if hasattr(typing, 'OrderedDict'):
+    OrderedDict = typing.OrderedDict
+# 3.7.0-3.7.2
+else:
+    OrderedDict = typing._alias(collections.OrderedDict, (KT, VT))
+
+Counter = typing.Counter
+ChainMap = typing.ChainMap
+AsyncGenerator = typing.AsyncGenerator
+Text = typing.Text
+TYPE_CHECKING = typing.TYPE_CHECKING
+
+
+_PROTO_ALLOWLIST = {
+    'collections.abc': [
+        'Callable', 'Awaitable', 'Iterable', 'Iterator', 'AsyncIterable',
+        'Hashable', 'Sized', 'Container', 'Collection', 'Reversible', 'Buffer',
+    ],
+    'contextlib': ['AbstractContextManager', 'AbstractAsyncContextManager'],
+    'typing_extensions': ['Buffer'],
+}
+
+
+_EXCLUDED_ATTRS = {
+    "__abstractmethods__", "__annotations__", "__weakref__", "_is_protocol",
+    "_is_runtime_protocol", "__dict__", "__slots__", "__parameters__",
+    "__orig_bases__", "__module__", "_MutableMapping__marker", "__doc__",
+    "__subclasshook__", "__orig_class__", "__init__", "__new__",
+    "__protocol_attrs__", "__callable_proto_members_only__",
+}
+
+if sys.version_info < (3, 8):
+    _EXCLUDED_ATTRS |= {
+        "_gorg", "__next_in_mro__", "__extra__", "__tree_hash__", "__args__",
+        "__origin__"
+    }
+
+if sys.version_info >= (3, 9):
+    _EXCLUDED_ATTRS.add("__class_getitem__")
+
+if sys.version_info >= (3, 12):
+    _EXCLUDED_ATTRS.add("__type_params__")
+
+_EXCLUDED_ATTRS = frozenset(_EXCLUDED_ATTRS)
+
+
+def _get_protocol_attrs(cls):
+    attrs = set()
+    for base in cls.__mro__[:-1]:  # without object
+        if base.__name__ in {'Protocol', 'Generic'}:
+            continue
+        annotations = getattr(base, '__annotations__', {})
+        for attr in (*base.__dict__, *annotations):
+            if (not attr.startswith('_abc_') and attr not in _EXCLUDED_ATTRS):
+                attrs.add(attr)
+    return attrs
+
+
+def _maybe_adjust_parameters(cls):
+    """Helper function used in Protocol.__init_subclass__ and _TypedDictMeta.__new__.
+
+    The contents of this function are very similar
+    to logic found in typing.Generic.__init_subclass__
+    on the CPython main branch.
+    """
+    tvars = []
+    if '__orig_bases__' in cls.__dict__:
+        tvars = _collect_type_vars(cls.__orig_bases__)
+        # Look for Generic[T1, ..., Tn] or Protocol[T1, ..., Tn].
+        # If found, tvars must be a subset of it.
+        # If not found, tvars is it.
+        # Also check for and reject plain Generic,
+        # and reject multiple Generic[...] and/or Protocol[...].
+        gvars = None
+        for base in cls.__orig_bases__:
+            if (isinstance(base, typing._GenericAlias) and
+                    base.__origin__ in (typing.Generic, Protocol)):
+                # for error messages
+                the_base = base.__origin__.__name__
+                if gvars is not None:
+                    raise TypeError(
+                        "Cannot inherit from Generic[...]"
+                        " and/or Protocol[...] multiple types.")
+                gvars = base.__parameters__
+        if gvars is None:
+            gvars = tvars
+        else:
+            tvarset = set(tvars)
+            gvarset = set(gvars)
+            if not tvarset <= gvarset:
+                s_vars = ', '.join(str(t) for t in tvars if t not in gvarset)
+                s_args = ', '.join(str(g) for g in gvars)
+                raise TypeError(f"Some type variables ({s_vars}) are"
+                                f" not listed in {the_base}[{s_args}]")
+            tvars = gvars
+    cls.__parameters__ = tuple(tvars)
+
+
+def _caller(depth=2):
+    try:
+        return sys._getframe(depth).f_globals.get('__name__', '__main__')
+    except (AttributeError, ValueError):  # For platforms without _getframe()
+        return None
+
+
+# The performance of runtime-checkable protocols is significantly improved on Python 3.12,
+# so we backport the 3.12 version of Protocol to Python <=3.11
+if sys.version_info >= (3, 12):
+    Protocol = typing.Protocol
+else:
+    def _allow_reckless_class_checks(depth=3):
+        """Allow instance and class checks for special stdlib modules.
+        The abc and functools modules indiscriminately call isinstance() and
+        issubclass() on the whole MRO of a user class, which may contain protocols.
+        """
+        return _caller(depth) in {'abc', 'functools', None}
+
+    def _no_init(self, *args, **kwargs):
+        if type(self)._is_protocol:
+            raise TypeError('Protocols cannot be instantiated')
+
+    if sys.version_info >= (3, 8):
+        # Inheriting from typing._ProtocolMeta isn't actually desirable,
+        # but is necessary to allow typing.Protocol and typing_extensions.Protocol
+        # to mix without getting TypeErrors about "metaclass conflict"
+        _typing_Protocol = typing.Protocol
+        _ProtocolMetaBase = type(_typing_Protocol)
+    else:
+        _typing_Protocol = _marker
+        _ProtocolMetaBase = abc.ABCMeta
+
+    class _ProtocolMeta(_ProtocolMetaBase):
+        # This metaclass is somewhat unfortunate,
+        # but is necessary for several reasons...
+        #
+        # NOTE: DO NOT call super() in any methods in this class
+        # That would call the methods on typing._ProtocolMeta on Python 3.8-3.11
+        # and those are slow
+        def __new__(mcls, name, bases, namespace, **kwargs):
+            if name == "Protocol" and len(bases) < 2:
+                pass
+            elif {Protocol, _typing_Protocol} & set(bases):
+                for base in bases:
+                    if not (
+                        base in {object, typing.Generic, Protocol, _typing_Protocol}
+                        or base.__name__ in _PROTO_ALLOWLIST.get(base.__module__, [])
+                        or is_protocol(base)
+                    ):
+                        raise TypeError(
+                            f"Protocols can only inherit from other protocols, "
+                            f"got {base!r}"
+                        )
+            return abc.ABCMeta.__new__(mcls, name, bases, namespace, **kwargs)
+
+        def __init__(cls, *args, **kwargs):
+            abc.ABCMeta.__init__(cls, *args, **kwargs)
+            if getattr(cls, "_is_protocol", False):
+                cls.__protocol_attrs__ = _get_protocol_attrs(cls)
+                # PEP 544 prohibits using issubclass()
+                # with protocols that have non-method members.
+                cls.__callable_proto_members_only__ = all(
+                    callable(getattr(cls, attr, None)) for attr in cls.__protocol_attrs__
+                )
+
+        def __subclasscheck__(cls, other):
+            if cls is Protocol:
+                return type.__subclasscheck__(cls, other)
+            if (
+                getattr(cls, '_is_protocol', False)
+                and not _allow_reckless_class_checks()
+            ):
+                if not isinstance(other, type):
+                    # Same error message as for issubclass(1, int).
+                    raise TypeError('issubclass() arg 1 must be a class')
+                if (
+                    not cls.__callable_proto_members_only__
+                    and cls.__dict__.get("__subclasshook__") is _proto_hook
+                ):
+                    raise TypeError(
+                        "Protocols with non-method members don't support issubclass()"
+                    )
+                if not getattr(cls, '_is_runtime_protocol', False):
+                    raise TypeError(
+                        "Instance and class checks can only be used with "
+                        "@runtime_checkable protocols"
+                    )
+            return abc.ABCMeta.__subclasscheck__(cls, other)
+
+        def __instancecheck__(cls, instance):
+            # We need this method for situations where attributes are
+            # assigned in __init__.
+            if cls is Protocol:
+                return type.__instancecheck__(cls, instance)
+            if not getattr(cls, "_is_protocol", False):
+                # i.e., it's a concrete subclass of a protocol
+                return abc.ABCMeta.__instancecheck__(cls, instance)
+
+            if (
+                not getattr(cls, '_is_runtime_protocol', False) and
+                not _allow_reckless_class_checks()
+            ):
+                raise TypeError("Instance and class checks can only be used with"
+                                " @runtime_checkable protocols")
+
+            if abc.ABCMeta.__instancecheck__(cls, instance):
+                return True
+
+            for attr in cls.__protocol_attrs__:
+                try:
+                    val = inspect.getattr_static(instance, attr)
+                except AttributeError:
+                    break
+                if val is None and callable(getattr(cls, attr, None)):
+                    break
+            else:
+                return True
+
+            return False
+
+        def __eq__(cls, other):
+            # Hack so that typing.Generic.__class_getitem__
+            # treats typing_extensions.Protocol
+            # as equivalent to typing.Protocol on Python 3.8+
+            if abc.ABCMeta.__eq__(cls, other) is True:
+                return True
+            return (
+                cls is Protocol and other is getattr(typing, "Protocol", object())
+            )
+
+        # This has to be defined, or the abc-module cache
+        # complains about classes with this metaclass being unhashable,
+        # if we define only __eq__!
+        def __hash__(cls) -> int:
+            return type.__hash__(cls)
+
+    @classmethod
+    def _proto_hook(cls, other):
+        if not cls.__dict__.get('_is_protocol', False):
+            return NotImplemented
+
+        for attr in cls.__protocol_attrs__:
+            for base in other.__mro__:
+                # Check if the members appears in the class dictionary...
+                if attr in base.__dict__:
+                    if base.__dict__[attr] is None:
+                        return NotImplemented
+                    break
+
+                # ...or in annotations, if it is a sub-protocol.
+                annotations = getattr(base, '__annotations__', {})
+                if (
+                    isinstance(annotations, collections.abc.Mapping)
+                    and attr in annotations
+                    and is_protocol(other)
+                ):
+                    break
+            else:
+                return NotImplemented
+        return True
+
+    if sys.version_info >= (3, 8):
+        class Protocol(typing.Generic, metaclass=_ProtocolMeta):
+            __doc__ = typing.Protocol.__doc__
+            __slots__ = ()
+            _is_protocol = True
+            _is_runtime_protocol = False
+
+            def __init_subclass__(cls, *args, **kwargs):
+                super().__init_subclass__(*args, **kwargs)
+
+                # Determine if this is a protocol or a concrete subclass.
+                if not cls.__dict__.get('_is_protocol', False):
+                    cls._is_protocol = any(b is Protocol for b in cls.__bases__)
+
+                # Set (or override) the protocol subclass hook.
+                if '__subclasshook__' not in cls.__dict__:
+                    cls.__subclasshook__ = _proto_hook
+
+                # Prohibit instantiation for protocol classes
+                if cls._is_protocol and cls.__init__ is Protocol.__init__:
+                    cls.__init__ = _no_init
+
+    else:
+        class Protocol(metaclass=_ProtocolMeta):
+            # There is quite a lot of overlapping code with typing.Generic.
+            # Unfortunately it is hard to avoid this on Python <3.8,
+            # as the typing module on Python 3.7 doesn't let us subclass typing.Generic!
+            """Base class for protocol classes. Protocol classes are defined as::
+
+                class Proto(Protocol):
+                    def meth(self) -> int:
+                        ...
+
+            Such classes are primarily used with static type checkers that recognize
+            structural subtyping (static duck-typing), for example::
+
+                class C:
+                    def meth(self) -> int:
+                        return 0
+
+                def func(x: Proto) -> int:
+                    return x.meth()
+
+                func(C())  # Passes static type check
+
+            See PEP 544 for details. Protocol classes decorated with
+            @typing_extensions.runtime_checkable act
+            as simple-minded runtime-checkable protocols that check
+            only the presence of given attributes, ignoring their type signatures.
+
+            Protocol classes can be generic, they are defined as::
+
+                class GenProto(Protocol[T]):
+                    def meth(self) -> T:
+                        ...
+            """
+            __slots__ = ()
+            _is_protocol = True
+            _is_runtime_protocol = False
+
+            def __new__(cls, *args, **kwds):
+                if cls is Protocol:
+                    raise TypeError("Type Protocol cannot be instantiated; "
+                                    "it can only be used as a base class")
+                return super().__new__(cls)
+
+            @typing._tp_cache
+            def __class_getitem__(cls, params):
+                if not isinstance(params, tuple):
+                    params = (params,)
+                if not params and cls is not typing.Tuple:
+                    raise TypeError(
+                        f"Parameter list to {cls.__qualname__}[...] cannot be empty")
+                msg = "Parameters to generic types must be types."
+                params = tuple(typing._type_check(p, msg) for p in params)
+                if cls is Protocol:
+                    # Generic can only be subscripted with unique type variables.
+                    if not all(isinstance(p, typing.TypeVar) for p in params):
+                        i = 0
+                        while isinstance(params[i], typing.TypeVar):
+                            i += 1
+                        raise TypeError(
+                            "Parameters to Protocol[...] must all be type variables."
+                            f" Parameter {i + 1} is {params[i]}")
+                    if len(set(params)) != len(params):
+                        raise TypeError(
+                            "Parameters to Protocol[...] must all be unique")
+                else:
+                    # Subscripting a regular Generic subclass.
+                    _check_generic(cls, params, len(cls.__parameters__))
+                return typing._GenericAlias(cls, params)
+
+            def __init_subclass__(cls, *args, **kwargs):
+                if '__orig_bases__' in cls.__dict__:
+                    error = typing.Generic in cls.__orig_bases__
+                else:
+                    error = typing.Generic in cls.__bases__
+                if error:
+                    raise TypeError("Cannot inherit from plain Generic")
+                _maybe_adjust_parameters(cls)
+
+                # Determine if this is a protocol or a concrete subclass.
+                if not cls.__dict__.get('_is_protocol', None):
+                    cls._is_protocol = any(b is Protocol for b in cls.__bases__)
+
+                # Set (or override) the protocol subclass hook.
+                if '__subclasshook__' not in cls.__dict__:
+                    cls.__subclasshook__ = _proto_hook
+
+                # Prohibit instantiation for protocol classes
+                if cls._is_protocol and cls.__init__ is Protocol.__init__:
+                    cls.__init__ = _no_init
+
+
+if sys.version_info >= (3, 8):
+    runtime_checkable = typing.runtime_checkable
+else:
+    def runtime_checkable(cls):
+        """Mark a protocol class as a runtime protocol, so that it
+        can be used with isinstance() and issubclass(). Raise TypeError
+        if applied to a non-protocol class.
+
+        This allows a simple-minded structural check very similar to the
+        one-offs in collections.abc such as Hashable.
+        """
+        if not (
+            (isinstance(cls, _ProtocolMeta) or issubclass(cls, typing.Generic))
+            and getattr(cls, "_is_protocol", False)
+        ):
+            raise TypeError('@runtime_checkable can be only applied to protocol classes,'
+                            f' got {cls!r}')
+        cls._is_runtime_protocol = True
+        return cls
+
+
+# Exists for backwards compatibility.
+runtime = runtime_checkable
+
+
+# Our version of runtime-checkable protocols is faster on Python 3.7-3.11
+if sys.version_info >= (3, 12):
+    SupportsInt = typing.SupportsInt
+    SupportsFloat = typing.SupportsFloat
+    SupportsComplex = typing.SupportsComplex
+    SupportsBytes = typing.SupportsBytes
+    SupportsIndex = typing.SupportsIndex
+    SupportsAbs = typing.SupportsAbs
+    SupportsRound = typing.SupportsRound
+else:
+    @runtime_checkable
+    class SupportsInt(Protocol):
+        """An ABC with one abstract method __int__."""
+        __slots__ = ()
+
+        @abc.abstractmethod
+        def __int__(self) -> int:
+            pass
+
+    @runtime_checkable
+    class SupportsFloat(Protocol):
+        """An ABC with one abstract method __float__."""
+        __slots__ = ()
+
+        @abc.abstractmethod
+        def __float__(self) -> float:
+            pass
+
+    @runtime_checkable
+    class SupportsComplex(Protocol):
+        """An ABC with one abstract method __complex__."""
+        __slots__ = ()
+
+        @abc.abstractmethod
+        def __complex__(self) -> complex:
+            pass
+
+    @runtime_checkable
+    class SupportsBytes(Protocol):
+        """An ABC with one abstract method __bytes__."""
+        __slots__ = ()
+
+        @abc.abstractmethod
+        def __bytes__(self) -> bytes:
+            pass
+
+    @runtime_checkable
+    class SupportsIndex(Protocol):
+        __slots__ = ()
+
+        @abc.abstractmethod
+        def __index__(self) -> int:
+            pass
+
+    @runtime_checkable
+    class SupportsAbs(Protocol[T_co]):
+        """
+        An ABC with one abstract method __abs__ that is covariant in its return type.
+        """
+        __slots__ = ()
+
+        @abc.abstractmethod
+        def __abs__(self) -> T_co:
+            pass
+
+    @runtime_checkable
+    class SupportsRound(Protocol[T_co]):
+        """
+        An ABC with one abstract method __round__ that is covariant in its return type.
+        """
+        __slots__ = ()
+
+        @abc.abstractmethod
+        def __round__(self, ndigits: int = 0) -> T_co:
+            pass
+
+
+def _ensure_subclassable(mro_entries):
+    def inner(func):
+        if sys.implementation.name == "pypy" and sys.version_info < (3, 9):
+            cls_dict = {
+                "__call__": staticmethod(func),
+                "__mro_entries__": staticmethod(mro_entries)
+            }
+            t = type(func.__name__, (), cls_dict)
+            return functools.update_wrapper(t(), func)
+        else:
+            func.__mro_entries__ = mro_entries
+            return func
+    return inner
+
+
+if sys.version_info >= (3, 13):
+    # The standard library TypedDict in Python 3.8 does not store runtime information
+    # about which (if any) keys are optional.  See https://bugs.python.org/issue38834
+    # The standard library TypedDict in Python 3.9.0/1 does not honour the "total"
+    # keyword with old-style TypedDict().  See https://bugs.python.org/issue42059
+    # The standard library TypedDict below Python 3.11 does not store runtime
+    # information about optional and required keys when using Required or NotRequired.
+    # Generic TypedDicts are also impossible using typing.TypedDict on Python <3.11.
+    # Aaaand on 3.12 we add __orig_bases__ to TypedDict
+    # to enable better runtime introspection.
+    # On 3.13 we deprecate some odd ways of creating TypedDicts.
+    TypedDict = typing.TypedDict
+    _TypedDictMeta = typing._TypedDictMeta
+    is_typeddict = typing.is_typeddict
+else:
+    # 3.10.0 and later
+    _TAKES_MODULE = "module" in inspect.signature(typing._type_check).parameters
+
+    if sys.version_info >= (3, 8):
+        _fake_name = "Protocol"
+    else:
+        _fake_name = "_Protocol"
+
+    class _TypedDictMeta(type):
+        def __new__(cls, name, bases, ns, total=True):
+            """Create new typed dict class object.
+
+            This method is called when TypedDict is subclassed,
+            or when TypedDict is instantiated. This way
+            TypedDict supports all three syntax forms described in its docstring.
+            Subclasses and instances of TypedDict return actual dictionaries.
+            """
+            for base in bases:
+                if type(base) is not _TypedDictMeta and base is not typing.Generic:
+                    raise TypeError('cannot inherit from both a TypedDict type '
+                                    'and a non-TypedDict base class')
+
+            if any(issubclass(b, typing.Generic) for b in bases):
+                generic_base = (typing.Generic,)
+            else:
+                generic_base = ()
+
+            # typing.py generally doesn't let you inherit from plain Generic, unless
+            # the name of the class happens to be "Protocol" (or "_Protocol" on 3.7).
+            tp_dict = type.__new__(_TypedDictMeta, _fake_name, (*generic_base, dict), ns)
+            tp_dict.__name__ = name
+            if tp_dict.__qualname__ == _fake_name:
+                tp_dict.__qualname__ = name
+
+            if not hasattr(tp_dict, '__orig_bases__'):
+                tp_dict.__orig_bases__ = bases
+
+            annotations = {}
+            own_annotations = ns.get('__annotations__', {})
+            msg = "TypedDict('Name', {f0: t0, f1: t1, ...}); each t must be a type"
+            if _TAKES_MODULE:
+                own_annotations = {
+                    n: typing._type_check(tp, msg, module=tp_dict.__module__)
+                    for n, tp in own_annotations.items()
+                }
+            else:
+                own_annotations = {
+                    n: typing._type_check(tp, msg)
+                    for n, tp in own_annotations.items()
+                }
+            required_keys = set()
+            optional_keys = set()
+
+            for base in bases:
+                annotations.update(base.__dict__.get('__annotations__', {}))
+                required_keys.update(base.__dict__.get('__required_keys__', ()))
+                optional_keys.update(base.__dict__.get('__optional_keys__', ()))
+
+            annotations.update(own_annotations)
+            for annotation_key, annotation_type in own_annotations.items():
+                annotation_origin = get_origin(annotation_type)
+                if annotation_origin is Annotated:
+                    annotation_args = get_args(annotation_type)
+                    if annotation_args:
+                        annotation_type = annotation_args[0]
+                        annotation_origin = get_origin(annotation_type)
+
+                if annotation_origin is Required:
+                    required_keys.add(annotation_key)
+                elif annotation_origin is NotRequired:
+                    optional_keys.add(annotation_key)
+                elif total:
+                    required_keys.add(annotation_key)
+                else:
+                    optional_keys.add(annotation_key)
+
+            tp_dict.__annotations__ = annotations
+            tp_dict.__required_keys__ = frozenset(required_keys)
+            tp_dict.__optional_keys__ = frozenset(optional_keys)
+            if not hasattr(tp_dict, '__total__'):
+                tp_dict.__total__ = total
+            return tp_dict
+
+        __call__ = dict  # static method
+
+        def __subclasscheck__(cls, other):
+            # Typed dicts are only for static structural subtyping.
+            raise TypeError('TypedDict does not support instance and class checks')
+
+        __instancecheck__ = __subclasscheck__
+
+    _TypedDict = type.__new__(_TypedDictMeta, 'TypedDict', (), {})
+
+    @_ensure_subclassable(lambda bases: (_TypedDict,))
+    def TypedDict(__typename, __fields=_marker, *, total=True, **kwargs):
+        """A simple typed namespace. At runtime it is equivalent to a plain dict.
+
+        TypedDict creates a dictionary type such that a type checker will expect all
+        instances to have a certain set of keys, where each key is
+        associated with a value of a consistent type. This expectation
+        is not checked at runtime.
+
+        Usage::
+
+            class Point2D(TypedDict):
+                x: int
+                y: int
+                label: str
+
+            a: Point2D = {'x': 1, 'y': 2, 'label': 'good'}  # OK
+            b: Point2D = {'z': 3, 'label': 'bad'}           # Fails type check
+
+            assert Point2D(x=1, y=2, label='first') == dict(x=1, y=2, label='first')
+
+        The type info can be accessed via the Point2D.__annotations__ dict, and
+        the Point2D.__required_keys__ and Point2D.__optional_keys__ frozensets.
+        TypedDict supports an additional equivalent form::
+
+            Point2D = TypedDict('Point2D', {'x': int, 'y': int, 'label': str})
+
+        By default, all keys must be present in a TypedDict. It is possible
+        to override this by specifying totality::
+
+            class Point2D(TypedDict, total=False):
+                x: int
+                y: int
+
+        This means that a Point2D TypedDict can have any of the keys omitted. A type
+        checker is only expected to support a literal False or True as the value of
+        the total argument. True is the default, and makes all items defined in the
+        class body be required.
+
+        The Required and NotRequired special forms can also be used to mark
+        individual keys as being required or not required::
+
+            class Point2D(TypedDict):
+                x: int  # the "x" key must always be present (Required is the default)
+                y: NotRequired[int]  # the "y" key can be omitted
+
+        See PEP 655 for more details on Required and NotRequired.
+        """
+        if __fields is _marker or __fields is None:
+            if __fields is _marker:
+                deprecated_thing = "Failing to pass a value for the 'fields' parameter"
+            else:
+                deprecated_thing = "Passing `None` as the 'fields' parameter"
+
+            example = f"`{__typename} = TypedDict({__typename!r}, {{}})`"
+            deprecation_msg = (
+                f"{deprecated_thing} is deprecated and will be disallowed in "
+                "Python 3.15. To create a TypedDict class with 0 fields "
+                "using the functional syntax, pass an empty dictionary, e.g. "
+            ) + example + "."
+            warnings.warn(deprecation_msg, DeprecationWarning, stacklevel=2)
+            __fields = kwargs
+        elif kwargs:
+            raise TypeError("TypedDict takes either a dict or keyword arguments,"
+                            " but not both")
+        if kwargs:
+            warnings.warn(
+                "The kwargs-based syntax for TypedDict definitions is deprecated "
+                "in Python 3.11, will be removed in Python 3.13, and may not be "
+                "understood by third-party type checkers.",
+                DeprecationWarning,
+                stacklevel=2,
+            )
+
+        ns = {'__annotations__': dict(__fields)}
+        module = _caller()
+        if module is not None:
+            # Setting correct module is necessary to make typed dict classes pickleable.
+            ns['__module__'] = module
+
+        td = _TypedDictMeta(__typename, (), ns, total=total)
+        td.__orig_bases__ = (TypedDict,)
+        return td
+
+    if hasattr(typing, "_TypedDictMeta"):
+        _TYPEDDICT_TYPES = (typing._TypedDictMeta, _TypedDictMeta)
+    else:
+        _TYPEDDICT_TYPES = (_TypedDictMeta,)
+
+    def is_typeddict(tp):
+        """Check if an annotation is a TypedDict class
+
+        For example::
+            class Film(TypedDict):
+                title: str
+                year: int
+
+            is_typeddict(Film)  # => True
+            is_typeddict(Union[list, str])  # => False
+        """
+        # On 3.8, this would otherwise return True
+        if hasattr(typing, "TypedDict") and tp is typing.TypedDict:
+            return False
+        return isinstance(tp, _TYPEDDICT_TYPES)
+
+
+if hasattr(typing, "assert_type"):
+    assert_type = typing.assert_type
+
+else:
+    def assert_type(__val, __typ):
+        """Assert (to the type checker) that the value is of the given type.
+
+        When the type checker encounters a call to assert_type(), it
+        emits an error if the value is not of the specified type::
+
+            def greet(name: str) -> None:
+                assert_type(name, str)  # ok
+                assert_type(name, int)  # type checker error
+
+        At runtime this returns the first argument unchanged and otherwise
+        does nothing.
+        """
+        return __val
+
+
+if hasattr(typing, "Required"):
+    get_type_hints = typing.get_type_hints
+else:
+    # replaces _strip_annotations()
+    def _strip_extras(t):
+        """Strips Annotated, Required and NotRequired from a given type."""
+        if isinstance(t, _AnnotatedAlias):
+            return _strip_extras(t.__origin__)
+        if hasattr(t, "__origin__") and t.__origin__ in (Required, NotRequired):
+            return _strip_extras(t.__args__[0])
+        if isinstance(t, typing._GenericAlias):
+            stripped_args = tuple(_strip_extras(a) for a in t.__args__)
+            if stripped_args == t.__args__:
+                return t
+            return t.copy_with(stripped_args)
+        if hasattr(_types, "GenericAlias") and isinstance(t, _types.GenericAlias):
+            stripped_args = tuple(_strip_extras(a) for a in t.__args__)
+            if stripped_args == t.__args__:
+                return t
+            return _types.GenericAlias(t.__origin__, stripped_args)
+        if hasattr(_types, "UnionType") and isinstance(t, _types.UnionType):
+            stripped_args = tuple(_strip_extras(a) for a in t.__args__)
+            if stripped_args == t.__args__:
+                return t
+            return functools.reduce(operator.or_, stripped_args)
+
+        return t
+
+    def get_type_hints(obj, globalns=None, localns=None, include_extras=False):
+        """Return type hints for an object.
+
+        This is often the same as obj.__annotations__, but it handles
+        forward references encoded as string literals, adds Optional[t] if a
+        default value equal to None is set and recursively replaces all
+        'Annotated[T, ...]', 'Required[T]' or 'NotRequired[T]' with 'T'
+        (unless 'include_extras=True').
+
+        The argument may be a module, class, method, or function. The annotations
+        are returned as a dictionary. For classes, annotations include also
+        inherited members.
+
+        TypeError is raised if the argument is not of a type that can contain
+        annotations, and an empty dictionary is returned if no annotations are
+        present.
+
+        BEWARE -- the behavior of globalns and localns is counterintuitive
+        (unless you are familiar with how eval() and exec() work).  The
+        search order is locals first, then globals.
+
+        - If no dict arguments are passed, an attempt is made to use the
+          globals from obj (or the respective module's globals for classes),
+          and these are also used as the locals.  If the object does not appear
+          to have globals, an empty dictionary is used.
+
+        - If one dict argument is passed, it is used for both globals and
+          locals.
+
+        - If two dict arguments are passed, they specify globals and
+          locals, respectively.
+        """
+        if hasattr(typing, "Annotated"):
+            hint = typing.get_type_hints(
+                obj, globalns=globalns, localns=localns, include_extras=True
+            )
+        else:
+            hint = typing.get_type_hints(obj, globalns=globalns, localns=localns)
+        if include_extras:
+            return hint
+        return {k: _strip_extras(t) for k, t in hint.items()}
+
+
+# Python 3.9+ has PEP 593 (Annotated)
+if hasattr(typing, 'Annotated'):
+    Annotated = typing.Annotated
+    # Not exported and not a public API, but needed for get_origin() and get_args()
+    # to work.
+    _AnnotatedAlias = typing._AnnotatedAlias
+# 3.7-3.8
+else:
+    class _AnnotatedAlias(typing._GenericAlias, _root=True):
+        """Runtime representation of an annotated type.
+
+        At its core 'Annotated[t, dec1, dec2, ...]' is an alias for the type 't'
+        with extra annotations. The alias behaves like a normal typing alias,
+        instantiating is the same as instantiating the underlying type, binding
+        it to types is also the same.
+        """
+        def __init__(self, origin, metadata):
+            if isinstance(origin, _AnnotatedAlias):
+                metadata = origin.__metadata__ + metadata
+                origin = origin.__origin__
+            super().__init__(origin, origin)
+            self.__metadata__ = metadata
+
+        def copy_with(self, params):
+            assert len(params) == 1
+            new_type = params[0]
+            return _AnnotatedAlias(new_type, self.__metadata__)
+
+        def __repr__(self):
+            return (f"typing_extensions.Annotated[{typing._type_repr(self.__origin__)}, "
+                    f"{', '.join(repr(a) for a in self.__metadata__)}]")
+
+        def __reduce__(self):
+            return operator.getitem, (
+                Annotated, (self.__origin__,) + self.__metadata__
+            )
+
+        def __eq__(self, other):
+            if not isinstance(other, _AnnotatedAlias):
+                return NotImplemented
+            if self.__origin__ != other.__origin__:
+                return False
+            return self.__metadata__ == other.__metadata__
+
+        def __hash__(self):
+            return hash((self.__origin__, self.__metadata__))
+
+    class Annotated:
+        """Add context specific metadata to a type.
+
+        Example: Annotated[int, runtime_check.Unsigned] indicates to the
+        hypothetical runtime_check module that this type is an unsigned int.
+        Every other consumer of this type can ignore this metadata and treat
+        this type as int.
+
+        The first argument to Annotated must be a valid type (and will be in
+        the __origin__ field), the remaining arguments are kept as a tuple in
+        the __extra__ field.
+
+        Details:
+
+        - It's an error to call `Annotated` with less than two arguments.
+        - Nested Annotated are flattened::
+
+            Annotated[Annotated[T, Ann1, Ann2], Ann3] == Annotated[T, Ann1, Ann2, Ann3]
+
+        - Instantiating an annotated type is equivalent to instantiating the
+        underlying type::
+
+            Annotated[C, Ann1](5) == C(5)
+
+        - Annotated can be used as a generic type alias::
+
+            Optimized = Annotated[T, runtime.Optimize()]
+            Optimized[int] == Annotated[int, runtime.Optimize()]
+
+            OptimizedList = Annotated[List[T], runtime.Optimize()]
+            OptimizedList[int] == Annotated[List[int], runtime.Optimize()]
+        """
+
+        __slots__ = ()
+
+        def __new__(cls, *args, **kwargs):
+            raise TypeError("Type Annotated cannot be instantiated.")
+
+        @typing._tp_cache
+        def __class_getitem__(cls, params):
+            if not isinstance(params, tuple) or len(params) < 2:
+                raise TypeError("Annotated[...] should be used "
+                                "with at least two arguments (a type and an "
+                                "annotation).")
+            allowed_special_forms = (ClassVar, Final)
+            if get_origin(params[0]) in allowed_special_forms:
+                origin = params[0]
+            else:
+                msg = "Annotated[t, ...]: t must be a type."
+                origin = typing._type_check(params[0], msg)
+            metadata = tuple(params[1:])
+            return _AnnotatedAlias(origin, metadata)
+
+        def __init_subclass__(cls, *args, **kwargs):
+            raise TypeError(
+                f"Cannot subclass {cls.__module__}.Annotated"
+            )
+
+# Python 3.8 has get_origin() and get_args() but those implementations aren't
+# Annotated-aware, so we can't use those. Python 3.9's versions don't support
+# ParamSpecArgs and ParamSpecKwargs, so only Python 3.10's versions will do.
+if sys.version_info[:2] >= (3, 10):
+    get_origin = typing.get_origin
+    get_args = typing.get_args
+# 3.7-3.9
+else:
+    try:
+        # 3.9+
+        from typing import _BaseGenericAlias
+    except ImportError:
+        _BaseGenericAlias = typing._GenericAlias
+    try:
+        # 3.9+
+        from typing import GenericAlias as _typing_GenericAlias
+    except ImportError:
+        _typing_GenericAlias = typing._GenericAlias
+
+    def get_origin(tp):
+        """Get the unsubscripted version of a type.
+
+        This supports generic types, Callable, Tuple, Union, Literal, Final, ClassVar
+        and Annotated. Return None for unsupported types. Examples::
+
+            get_origin(Literal[42]) is Literal
+            get_origin(int) is None
+            get_origin(ClassVar[int]) is ClassVar
+            get_origin(Generic) is Generic
+            get_origin(Generic[T]) is Generic
+            get_origin(Union[T, int]) is Union
+            get_origin(List[Tuple[T, T]][int]) == list
+            get_origin(P.args) is P
+        """
+        if isinstance(tp, _AnnotatedAlias):
+            return Annotated
+        if isinstance(tp, (typing._GenericAlias, _typing_GenericAlias, _BaseGenericAlias,
+                           ParamSpecArgs, ParamSpecKwargs)):
+            return tp.__origin__
+        if tp is typing.Generic:
+            return typing.Generic
+        return None
+
+    def get_args(tp):
+        """Get type arguments with all substitutions performed.
+
+        For unions, basic simplifications used by Union constructor are performed.
+        Examples::
+            get_args(Dict[str, int]) == (str, int)
+            get_args(int) == ()
+            get_args(Union[int, Union[T, int], str][int]) == (int, str)
+            get_args(Union[int, Tuple[T, int]][str]) == (int, Tuple[str, int])
+            get_args(Callable[[], T][int]) == ([], int)
+        """
+        if isinstance(tp, _AnnotatedAlias):
+            return (tp.__origin__,) + tp.__metadata__
+        if isinstance(tp, (typing._GenericAlias, _typing_GenericAlias)):
+            if getattr(tp, "_special", False):
+                return ()
+            res = tp.__args__
+            if get_origin(tp) is collections.abc.Callable and res[0] is not Ellipsis:
+                res = (list(res[:-1]), res[-1])
+            return res
+        return ()
+
+
+# 3.10+
+if hasattr(typing, 'TypeAlias'):
+    TypeAlias = typing.TypeAlias
+# 3.9
+elif sys.version_info[:2] >= (3, 9):
+    @_ExtensionsSpecialForm
+    def TypeAlias(self, parameters):
+        """Special marker indicating that an assignment should
+        be recognized as a proper type alias definition by type
+        checkers.
+
+        For example::
+
+            Predicate: TypeAlias = Callable[..., bool]
+
+        It's invalid when used anywhere except as in the example above.
+        """
+        raise TypeError(f"{self} is not subscriptable")
+# 3.7-3.8
+else:
+    TypeAlias = _ExtensionsSpecialForm(
+        'TypeAlias',
+        doc="""Special marker indicating that an assignment should
+        be recognized as a proper type alias definition by type
+        checkers.
+
+        For example::
+
+            Predicate: TypeAlias = Callable[..., bool]
+
+        It's invalid when used anywhere except as in the example
+        above."""
+    )
+
+
+def _set_default(type_param, default):
+    if isinstance(default, (tuple, list)):
+        type_param.__default__ = tuple((typing._type_check(d, "Default must be a type")
+                                        for d in default))
+    elif default != _marker:
+        type_param.__default__ = typing._type_check(default, "Default must be a type")
+    else:
+        type_param.__default__ = None
+
+
+def _set_module(typevarlike):
+    # for pickling:
+    def_mod = _caller(depth=3)
+    if def_mod != 'typing_extensions':
+        typevarlike.__module__ = def_mod
+
+
+class _DefaultMixin:
+    """Mixin for TypeVarLike defaults."""
+
+    __slots__ = ()
+    __init__ = _set_default
+
+
+# Classes using this metaclass must provide a _backported_typevarlike ClassVar
+class _TypeVarLikeMeta(type):
+    def __instancecheck__(cls, __instance: Any) -> bool:
+        return isinstance(__instance, cls._backported_typevarlike)
+
+
+# Add default and infer_variance parameters from PEP 696 and 695
+class TypeVar(metaclass=_TypeVarLikeMeta):
+    """Type variable."""
+
+    _backported_typevarlike = typing.TypeVar
+
+    def __new__(cls, name, *constraints, bound=None,
+                covariant=False, contravariant=False,
+                default=_marker, infer_variance=False):
+        if hasattr(typing, "TypeAliasType"):
+            # PEP 695 implemented, can pass infer_variance to typing.TypeVar
+            typevar = typing.TypeVar(name, *constraints, bound=bound,
+                                     covariant=covariant, contravariant=contravariant,
+                                     infer_variance=infer_variance)
+        else:
+            typevar = typing.TypeVar(name, *constraints, bound=bound,
+                                     covariant=covariant, contravariant=contravariant)
+            if infer_variance and (covariant or contravariant):
+                raise ValueError("Variance cannot be specified with infer_variance.")
+            typevar.__infer_variance__ = infer_variance
+        _set_default(typevar, default)
+        _set_module(typevar)
+        return typevar
+
+    def __init_subclass__(cls) -> None:
+        raise TypeError(f"type '{__name__}.TypeVar' is not an acceptable base type")
+
+
+# Python 3.10+ has PEP 612
+if hasattr(typing, 'ParamSpecArgs'):
+    ParamSpecArgs = typing.ParamSpecArgs
+    ParamSpecKwargs = typing.ParamSpecKwargs
+# 3.7-3.9
+else:
+    class _Immutable:
+        """Mixin to indicate that object should not be copied."""
+        __slots__ = ()
+
+        def __copy__(self):
+            return self
+
+        def __deepcopy__(self, memo):
+            return self
+
+    class ParamSpecArgs(_Immutable):
+        """The args for a ParamSpec object.
+
+        Given a ParamSpec object P, P.args is an instance of ParamSpecArgs.
+
+        ParamSpecArgs objects have a reference back to their ParamSpec:
+
+        P.args.__origin__ is P
+
+        This type is meant for runtime introspection and has no special meaning to
+        static type checkers.
+        """
+        def __init__(self, origin):
+            self.__origin__ = origin
+
+        def __repr__(self):
+            return f"{self.__origin__.__name__}.args"
+
+        def __eq__(self, other):
+            if not isinstance(other, ParamSpecArgs):
+                return NotImplemented
+            return self.__origin__ == other.__origin__
+
+    class ParamSpecKwargs(_Immutable):
+        """The kwargs for a ParamSpec object.
+
+        Given a ParamSpec object P, P.kwargs is an instance of ParamSpecKwargs.
+
+        ParamSpecKwargs objects have a reference back to their ParamSpec:
+
+        P.kwargs.__origin__ is P
+
+        This type is meant for runtime introspection and has no special meaning to
+        static type checkers.
+        """
+        def __init__(self, origin):
+            self.__origin__ = origin
+
+        def __repr__(self):
+            return f"{self.__origin__.__name__}.kwargs"
+
+        def __eq__(self, other):
+            if not isinstance(other, ParamSpecKwargs):
+                return NotImplemented
+            return self.__origin__ == other.__origin__
+
+# 3.10+
+if hasattr(typing, 'ParamSpec'):
+
+    # Add default parameter - PEP 696
+    class ParamSpec(metaclass=_TypeVarLikeMeta):
+        """Parameter specification."""
+
+        _backported_typevarlike = typing.ParamSpec
+
+        def __new__(cls, name, *, bound=None,
+                    covariant=False, contravariant=False,
+                    infer_variance=False, default=_marker):
+            if hasattr(typing, "TypeAliasType"):
+                # PEP 695 implemented, can pass infer_variance to typing.TypeVar
+                paramspec = typing.ParamSpec(name, bound=bound,
+                                             covariant=covariant,
+                                             contravariant=contravariant,
+                                             infer_variance=infer_variance)
+            else:
+                paramspec = typing.ParamSpec(name, bound=bound,
+                                             covariant=covariant,
+                                             contravariant=contravariant)
+                paramspec.__infer_variance__ = infer_variance
+
+            _set_default(paramspec, default)
+            _set_module(paramspec)
+            return paramspec
+
+        def __init_subclass__(cls) -> None:
+            raise TypeError(f"type '{__name__}.ParamSpec' is not an acceptable base type")
+
+# 3.7-3.9
+else:
+
+    # Inherits from list as a workaround for Callable checks in Python < 3.9.2.
+    class ParamSpec(list, _DefaultMixin):
+        """Parameter specification variable.
+
+        Usage::
+
+           P = ParamSpec('P')
+
+        Parameter specification variables exist primarily for the benefit of static
+        type checkers.  They are used to forward the parameter types of one
+        callable to another callable, a pattern commonly found in higher order
+        functions and decorators.  They are only valid when used in ``Concatenate``,
+        or s the first argument to ``Callable``. In Python 3.10 and higher,
+        they are also supported in user-defined Generics at runtime.
+        See class Generic for more information on generic types.  An
+        example for annotating a decorator::
+
+           T = TypeVar('T')
+           P = ParamSpec('P')
+
+           def add_logging(f: Callable[P, T]) -> Callable[P, T]:
+               '''A type-safe decorator to add logging to a function.'''
+               def inner(*args: P.args, **kwargs: P.kwargs) -> T:
+                   logging.info(f'{f.__name__} was called')
+                   return f(*args, **kwargs)
+               return inner
+
+           @add_logging
+           def add_two(x: float, y: float) -> float:
+               '''Add two numbers together.'''
+               return x + y
+
+        Parameter specification variables defined with covariant=True or
+        contravariant=True can be used to declare covariant or contravariant
+        generic types.  These keyword arguments are valid, but their actual semantics
+        are yet to be decided.  See PEP 612 for details.
+
+        Parameter specification variables can be introspected. e.g.:
+
+           P.__name__ == 'T'
+           P.__bound__ == None
+           P.__covariant__ == False
+           P.__contravariant__ == False
+
+        Note that only parameter specification variables defined in global scope can
+        be pickled.
+        """
+
+        # Trick Generic __parameters__.
+        __class__ = typing.TypeVar
+
+        @property
+        def args(self):
+            return ParamSpecArgs(self)
+
+        @property
+        def kwargs(self):
+            return ParamSpecKwargs(self)
+
+        def __init__(self, name, *, bound=None, covariant=False, contravariant=False,
+                     infer_variance=False, default=_marker):
+            super().__init__([self])
+            self.__name__ = name
+            self.__covariant__ = bool(covariant)
+            self.__contravariant__ = bool(contravariant)
+            self.__infer_variance__ = bool(infer_variance)
+            if bound:
+                self.__bound__ = typing._type_check(bound, 'Bound must be a type.')
+            else:
+                self.__bound__ = None
+            _DefaultMixin.__init__(self, default)
+
+            # for pickling:
+            def_mod = _caller()
+            if def_mod != 'typing_extensions':
+                self.__module__ = def_mod
+
+        def __repr__(self):
+            if self.__infer_variance__:
+                prefix = ''
+            elif self.__covariant__:
+                prefix = '+'
+            elif self.__contravariant__:
+                prefix = '-'
+            else:
+                prefix = '~'
+            return prefix + self.__name__
+
+        def __hash__(self):
+            return object.__hash__(self)
+
+        def __eq__(self, other):
+            return self is other
+
+        def __reduce__(self):
+            return self.__name__
+
+        # Hack to get typing._type_check to pass.
+        def __call__(self, *args, **kwargs):
+            pass
+
+
+# 3.7-3.9
+if not hasattr(typing, 'Concatenate'):
+    # Inherits from list as a workaround for Callable checks in Python < 3.9.2.
+    class _ConcatenateGenericAlias(list):
+
+        # Trick Generic into looking into this for __parameters__.
+        __class__ = typing._GenericAlias
+
+        # Flag in 3.8.
+        _special = False
+
+        def __init__(self, origin, args):
+            super().__init__(args)
+            self.__origin__ = origin
+            self.__args__ = args
+
+        def __repr__(self):
+            _type_repr = typing._type_repr
+            return (f'{_type_repr(self.__origin__)}'
+                    f'[{", ".join(_type_repr(arg) for arg in self.__args__)}]')
+
+        def __hash__(self):
+            return hash((self.__origin__, self.__args__))
+
+        # Hack to get typing._type_check to pass in Generic.
+        def __call__(self, *args, **kwargs):
+            pass
+
+        @property
+        def __parameters__(self):
+            return tuple(
+                tp for tp in self.__args__ if isinstance(tp, (typing.TypeVar, ParamSpec))
+            )
+
+
+# 3.7-3.9
+@typing._tp_cache
+def _concatenate_getitem(self, parameters):
+    if parameters == ():
+        raise TypeError("Cannot take a Concatenate of no types.")
+    if not isinstance(parameters, tuple):
+        parameters = (parameters,)
+    if not isinstance(parameters[-1], ParamSpec):
+        raise TypeError("The last parameter to Concatenate should be a "
+                        "ParamSpec variable.")
+    msg = "Concatenate[arg, ...]: each arg must be a type."
+    parameters = tuple(typing._type_check(p, msg) for p in parameters)
+    return _ConcatenateGenericAlias(self, parameters)
+
+
+# 3.10+
+if hasattr(typing, 'Concatenate'):
+    Concatenate = typing.Concatenate
+    _ConcatenateGenericAlias = typing._ConcatenateGenericAlias  # noqa: F811
+# 3.9
+elif sys.version_info[:2] >= (3, 9):
+    @_ExtensionsSpecialForm
+    def Concatenate(self, parameters):
+        """Used in conjunction with ``ParamSpec`` and ``Callable`` to represent a
+        higher order function which adds, removes or transforms parameters of a
+        callable.
+
+        For example::
+
+           Callable[Concatenate[int, P], int]
+
+        See PEP 612 for detailed information.
+        """
+        return _concatenate_getitem(self, parameters)
+# 3.7-8
+else:
+    class _ConcatenateForm(_ExtensionsSpecialForm, _root=True):
+        def __getitem__(self, parameters):
+            return _concatenate_getitem(self, parameters)
+
+    Concatenate = _ConcatenateForm(
+        'Concatenate',
+        doc="""Used in conjunction with ``ParamSpec`` and ``Callable`` to represent a
+        higher order function which adds, removes or transforms parameters of a
+        callable.
+
+        For example::
+
+           Callable[Concatenate[int, P], int]
+
+        See PEP 612 for detailed information.
+        """)
+
+# 3.10+
+if hasattr(typing, 'TypeGuard'):
+    TypeGuard = typing.TypeGuard
+# 3.9
+elif sys.version_info[:2] >= (3, 9):
+    @_ExtensionsSpecialForm
+    def TypeGuard(self, parameters):
+        """Special typing form used to annotate the return type of a user-defined
+        type guard function.  ``TypeGuard`` only accepts a single type argument.
+        At runtime, functions marked this way should return a boolean.
+
+        ``TypeGuard`` aims to benefit *type narrowing* -- a technique used by static
+        type checkers to determine a more precise type of an expression within a
+        program's code flow.  Usually type narrowing is done by analyzing
+        conditional code flow and applying the narrowing to a block of code.  The
+        conditional expression here is sometimes referred to as a "type guard".
+
+        Sometimes it would be convenient to use a user-defined boolean function
+        as a type guard.  Such a function should use ``TypeGuard[...]`` as its
+        return type to alert static type checkers to this intention.
+
+        Using  ``-> TypeGuard`` tells the static type checker that for a given
+        function:
+
+        1. The return value is a boolean.
+        2. If the return value is ``True``, the type of its argument
+        is the type inside ``TypeGuard``.
+
+        For example::
+
+            def is_str(val: Union[str, float]):
+                # "isinstance" type guard
+                if isinstance(val, str):
+                    # Type of ``val`` is narrowed to ``str``
+                    ...
+                else:
+                    # Else, type of ``val`` is narrowed to ``float``.
+                    ...
+
+        Strict type narrowing is not enforced -- ``TypeB`` need not be a narrower
+        form of ``TypeA`` (it can even be a wider form) and this may lead to
+        type-unsafe results.  The main reason is to allow for things like
+        narrowing ``List[object]`` to ``List[str]`` even though the latter is not
+        a subtype of the former, since ``List`` is invariant.  The responsibility of
+        writing type-safe type guards is left to the user.
+
+        ``TypeGuard`` also works with type variables.  For more information, see
+        PEP 647 (User-Defined Type Guards).
+        """
+        item = typing._type_check(parameters, f'{self} accepts only a single type.')
+        return typing._GenericAlias(self, (item,))
+# 3.7-3.8
+else:
+    class _TypeGuardForm(_ExtensionsSpecialForm, _root=True):
+        def __getitem__(self, parameters):
+            item = typing._type_check(parameters,
+                                      f'{self._name} accepts only a single type')
+            return typing._GenericAlias(self, (item,))
+
+    TypeGuard = _TypeGuardForm(
+        'TypeGuard',
+        doc="""Special typing form used to annotate the return type of a user-defined
+        type guard function.  ``TypeGuard`` only accepts a single type argument.
+        At runtime, functions marked this way should return a boolean.
+
+        ``TypeGuard`` aims to benefit *type narrowing* -- a technique used by static
+        type checkers to determine a more precise type of an expression within a
+        program's code flow.  Usually type narrowing is done by analyzing
+        conditional code flow and applying the narrowing to a block of code.  The
+        conditional expression here is sometimes referred to as a "type guard".
+
+        Sometimes it would be convenient to use a user-defined boolean function
+        as a type guard.  Such a function should use ``TypeGuard[...]`` as its
+        return type to alert static type checkers to this intention.
+
+        Using  ``-> TypeGuard`` tells the static type checker that for a given
+        function:
+
+        1. The return value is a boolean.
+        2. If the return value is ``True``, the type of its argument
+        is the type inside ``TypeGuard``.
+
+        For example::
+
+            def is_str(val: Union[str, float]):
+                # "isinstance" type guard
+                if isinstance(val, str):
+                    # Type of ``val`` is narrowed to ``str``
+                    ...
+                else:
+                    # Else, type of ``val`` is narrowed to ``float``.
+                    ...
+
+        Strict type narrowing is not enforced -- ``TypeB`` need not be a narrower
+        form of ``TypeA`` (it can even be a wider form) and this may lead to
+        type-unsafe results.  The main reason is to allow for things like
+        narrowing ``List[object]`` to ``List[str]`` even though the latter is not
+        a subtype of the former, since ``List`` is invariant.  The responsibility of
+        writing type-safe type guards is left to the user.
+
+        ``TypeGuard`` also works with type variables.  For more information, see
+        PEP 647 (User-Defined Type Guards).
+        """)
+
+
+# Vendored from cpython typing._SpecialFrom
+class _SpecialForm(typing._Final, _root=True):
+    __slots__ = ('_name', '__doc__', '_getitem')
+
+    def __init__(self, getitem):
+        self._getitem = getitem
+        self._name = getitem.__name__
+        self.__doc__ = getitem.__doc__
+
+    def __getattr__(self, item):
+        if item in {'__name__', '__qualname__'}:
+            return self._name
+
+        raise AttributeError(item)
+
+    def __mro_entries__(self, bases):
+        raise TypeError(f"Cannot subclass {self!r}")
+
+    def __repr__(self):
+        return f'typing_extensions.{self._name}'
+
+    def __reduce__(self):
+        return self._name
+
+    def __call__(self, *args, **kwds):
+        raise TypeError(f"Cannot instantiate {self!r}")
+
+    def __or__(self, other):
+        return typing.Union[self, other]
+
+    def __ror__(self, other):
+        return typing.Union[other, self]
+
+    def __instancecheck__(self, obj):
+        raise TypeError(f"{self} cannot be used with isinstance()")
+
+    def __subclasscheck__(self, cls):
+        raise TypeError(f"{self} cannot be used with issubclass()")
+
+    @typing._tp_cache
+    def __getitem__(self, parameters):
+        return self._getitem(self, parameters)
+
+
+if hasattr(typing, "LiteralString"):
+    LiteralString = typing.LiteralString
+else:
+    @_SpecialForm
+    def LiteralString(self, params):
+        """Represents an arbitrary literal string.
+
+        Example::
+
+          from metaflow._vendor.v3_7.typing_extensions import LiteralString
+
+          def query(sql: LiteralString) -> ...:
+              ...
+
+          query("SELECT * FROM table")  # ok
+          query(f"SELECT * FROM {input()}")  # not ok
+
+        See PEP 675 for details.
+
+        """
+        raise TypeError(f"{self} is not subscriptable")
+
+
+if hasattr(typing, "Self"):
+    Self = typing.Self
+else:
+    @_SpecialForm
+    def Self(self, params):
+        """Used to spell the type of "self" in classes.
+
+        Example::
+
+          from typing import Self
+
+          class ReturnsSelf:
+              def parse(self, data: bytes) -> Self:
+                  ...
+                  return self
+
+        """
+
+        raise TypeError(f"{self} is not subscriptable")
+
+
+if hasattr(typing, "Never"):
+    Never = typing.Never
+else:
+    @_SpecialForm
+    def Never(self, params):
+        """The bottom type, a type that has no members.
+
+        This can be used to define a function that should never be
+        called, or a function that never returns::
+
+            from metaflow._vendor.v3_7.typing_extensions import Never
+
+            def never_call_me(arg: Never) -> None:
+                pass
+
+            def int_or_str(arg: int | str) -> None:
+                never_call_me(arg)  # type checker error
+                match arg:
+                    case int():
+                        print("It's an int")
+                    case str():
+                        print("It's a str")
+                    case _:
+                        never_call_me(arg)  # ok, arg is of type Never
+
+        """
+
+        raise TypeError(f"{self} is not subscriptable")
+
+
+if hasattr(typing, 'Required'):
+    Required = typing.Required
+    NotRequired = typing.NotRequired
+elif sys.version_info[:2] >= (3, 9):
+    @_ExtensionsSpecialForm
+    def Required(self, parameters):
+        """A special typing construct to mark a key of a total=False TypedDict
+        as required. For example:
+
+            class Movie(TypedDict, total=False):
+                title: Required[str]
+                year: int
+
+            m = Movie(
+                title='The Matrix',  # typechecker error if key is omitted
+                year=1999,
+            )
+
+        There is no runtime checking that a required key is actually provided
+        when instantiating a related TypedDict.
+        """
+        item = typing._type_check(parameters, f'{self._name} accepts only a single type.')
+        return typing._GenericAlias(self, (item,))
+
+    @_ExtensionsSpecialForm
+    def NotRequired(self, parameters):
+        """A special typing construct to mark a key of a TypedDict as
+        potentially missing. For example:
+
+            class Movie(TypedDict):
+                title: str
+                year: NotRequired[int]
+
+            m = Movie(
+                title='The Matrix',  # typechecker error if key is omitted
+                year=1999,
+            )
+        """
+        item = typing._type_check(parameters, f'{self._name} accepts only a single type.')
+        return typing._GenericAlias(self, (item,))
+
+else:
+    class _RequiredForm(_ExtensionsSpecialForm, _root=True):
+        def __getitem__(self, parameters):
+            item = typing._type_check(parameters,
+                                      f'{self._name} accepts only a single type.')
+            return typing._GenericAlias(self, (item,))
+
+    Required = _RequiredForm(
+        'Required',
+        doc="""A special typing construct to mark a key of a total=False TypedDict
+        as required. For example:
+
+            class Movie(TypedDict, total=False):
+                title: Required[str]
+                year: int
+
+            m = Movie(
+                title='The Matrix',  # typechecker error if key is omitted
+                year=1999,
+            )
+
+        There is no runtime checking that a required key is actually provided
+        when instantiating a related TypedDict.
+        """)
+    NotRequired = _RequiredForm(
+        'NotRequired',
+        doc="""A special typing construct to mark a key of a TypedDict as
+        potentially missing. For example:
+
+            class Movie(TypedDict):
+                title: str
+                year: NotRequired[int]
+
+            m = Movie(
+                title='The Matrix',  # typechecker error if key is omitted
+                year=1999,
+            )
+        """)
+
+
+_UNPACK_DOC = """\
+Type unpack operator.
+
+The type unpack operator takes the child types from some container type,
+such as `tuple[int, str]` or a `TypeVarTuple`, and 'pulls them out'. For
+example:
+
+  # For some generic class `Foo`:
+  Foo[Unpack[tuple[int, str]]]  # Equivalent to Foo[int, str]
+
+  Ts = TypeVarTuple('Ts')
+  # Specifies that `Bar` is generic in an arbitrary number of types.
+  # (Think of `Ts` as a tuple of an arbitrary number of individual
+  #  `TypeVar`s, which the `Unpack` is 'pulling out' directly into the
+  #  `Generic[]`.)
+  class Bar(Generic[Unpack[Ts]]): ...
+  Bar[int]  # Valid
+  Bar[int, str]  # Also valid
+
+From Python 3.11, this can also be done using the `*` operator:
+
+    Foo[*tuple[int, str]]
+    class Bar(Generic[*Ts]): ...
+
+The operator can also be used along with a `TypedDict` to annotate
+`**kwargs` in a function signature. For instance:
+
+  class Movie(TypedDict):
+    name: str
+    year: int
+
+  # This function expects two keyword arguments - *name* of type `str` and
+  # *year* of type `int`.
+  def foo(**kwargs: Unpack[Movie]): ...
+
+Note that there is only some runtime checking of this operator. Not
+everything the runtime allows may be accepted by static type checkers.
+
+For more information, see PEP 646 and PEP 692.
+"""
+
+
+if sys.version_info >= (3, 12):  # PEP 692 changed the repr of Unpack[]
+    Unpack = typing.Unpack
+
+    def _is_unpack(obj):
+        return get_origin(obj) is Unpack
+
+elif sys.version_info[:2] >= (3, 9):
+    class _UnpackSpecialForm(_ExtensionsSpecialForm, _root=True):
+        def __init__(self, getitem):
+            super().__init__(getitem)
+            self.__doc__ = _UNPACK_DOC
+
+    class _UnpackAlias(typing._GenericAlias, _root=True):
+        __class__ = typing.TypeVar
+
+    @_UnpackSpecialForm
+    def Unpack(self, parameters):
+        item = typing._type_check(parameters, f'{self._name} accepts only a single type.')
+        return _UnpackAlias(self, (item,))
+
+    def _is_unpack(obj):
+        return isinstance(obj, _UnpackAlias)
+
+else:
+    class _UnpackAlias(typing._GenericAlias, _root=True):
+        __class__ = typing.TypeVar
+
+    class _UnpackForm(_ExtensionsSpecialForm, _root=True):
+        def __getitem__(self, parameters):
+            item = typing._type_check(parameters,
+                                      f'{self._name} accepts only a single type.')
+            return _UnpackAlias(self, (item,))
+
+    Unpack = _UnpackForm('Unpack', doc=_UNPACK_DOC)
+
+    def _is_unpack(obj):
+        return isinstance(obj, _UnpackAlias)
+
+
+if hasattr(typing, "TypeVarTuple"):  # 3.11+
+
+    # Add default parameter - PEP 696
+    class TypeVarTuple(metaclass=_TypeVarLikeMeta):
+        """Type variable tuple."""
+
+        _backported_typevarlike = typing.TypeVarTuple
+
+        def __new__(cls, name, *, default=_marker):
+            tvt = typing.TypeVarTuple(name)
+            _set_default(tvt, default)
+            _set_module(tvt)
+            return tvt
+
+        def __init_subclass__(self, *args, **kwds):
+            raise TypeError("Cannot subclass special typing classes")
+
+else:
+    class TypeVarTuple(_DefaultMixin):
+        """Type variable tuple.
+
+        Usage::
+
+            Ts = TypeVarTuple('Ts')
+
+        In the same way that a normal type variable is a stand-in for a single
+        type such as ``int``, a type variable *tuple* is a stand-in for a *tuple*
+        type such as ``Tuple[int, str]``.
+
+        Type variable tuples can be used in ``Generic`` declarations.
+        Consider the following example::
+
+            class Array(Generic[*Ts]): ...
+
+        The ``Ts`` type variable tuple here behaves like ``tuple[T1, T2]``,
+        where ``T1`` and ``T2`` are type variables. To use these type variables
+        as type parameters of ``Array``, we must *unpack* the type variable tuple using
+        the star operator: ``*Ts``. The signature of ``Array`` then behaves
+        as if we had simply written ``class Array(Generic[T1, T2]): ...``.
+        In contrast to ``Generic[T1, T2]``, however, ``Generic[*Shape]`` allows
+        us to parameterise the class with an *arbitrary* number of type parameters.
+
+        Type variable tuples can be used anywhere a normal ``TypeVar`` can.
+        This includes class definitions, as shown above, as well as function
+        signatures and variable annotations::
+
+            class Array(Generic[*Ts]):
+
+                def __init__(self, shape: Tuple[*Ts]):
+                    self._shape: Tuple[*Ts] = shape
+
+                def get_shape(self) -> Tuple[*Ts]:
+                    return self._shape
+
+            shape = (Height(480), Width(640))
+            x: Array[Height, Width] = Array(shape)
+            y = abs(x)  # Inferred type is Array[Height, Width]
+            z = x + x   #        ...    is Array[Height, Width]
+            x.get_shape()  #     ...    is tuple[Height, Width]
+
+        """
+
+        # Trick Generic __parameters__.
+        __class__ = typing.TypeVar
+
+        def __iter__(self):
+            yield self.__unpacked__
+
+        def __init__(self, name, *, default=_marker):
+            self.__name__ = name
+            _DefaultMixin.__init__(self, default)
+
+            # for pickling:
+            def_mod = _caller()
+            if def_mod != 'typing_extensions':
+                self.__module__ = def_mod
+
+            self.__unpacked__ = Unpack[self]
+
+        def __repr__(self):
+            return self.__name__
+
+        def __hash__(self):
+            return object.__hash__(self)
+
+        def __eq__(self, other):
+            return self is other
+
+        def __reduce__(self):
+            return self.__name__
+
+        def __init_subclass__(self, *args, **kwds):
+            if '_root' not in kwds:
+                raise TypeError("Cannot subclass special typing classes")
+
+
+if hasattr(typing, "reveal_type"):
+    reveal_type = typing.reveal_type
+else:
+    def reveal_type(__obj: T) -> T:
+        """Reveal the inferred type of a variable.
+
+        When a static type checker encounters a call to ``reveal_type()``,
+        it will emit the inferred type of the argument::
+
+            x: int = 1
+            reveal_type(x)
+
+        Running a static type checker (e.g., ``mypy``) on this example
+        will produce output similar to 'Revealed type is "builtins.int"'.
+
+        At runtime, the function prints the runtime type of the
+        argument and returns it unchanged.
+
+        """
+        print(f"Runtime type is {type(__obj).__name__!r}", file=sys.stderr)
+        return __obj
+
+
+if hasattr(typing, "assert_never"):
+    assert_never = typing.assert_never
+else:
+    def assert_never(__arg: Never) -> Never:
+        """Assert to the type checker that a line of code is unreachable.
+
+        Example::
+
+            def int_or_str(arg: int | str) -> None:
+                match arg:
+                    case int():
+                        print("It's an int")
+                    case str():
+                        print("It's a str")
+                    case _:
+                        assert_never(arg)
+
+        If a type checker finds that a call to assert_never() is
+        reachable, it will emit an error.
+
+        At runtime, this throws an exception when called.
+
+        """
+        raise AssertionError("Expected code to be unreachable")
+
+
+if sys.version_info >= (3, 12):
+    # dataclass_transform exists in 3.11 but lacks the frozen_default parameter
+    dataclass_transform = typing.dataclass_transform
+else:
+    def dataclass_transform(
+        *,
+        eq_default: bool = True,
+        order_default: bool = False,
+        kw_only_default: bool = False,
+        frozen_default: bool = False,
+        field_specifiers: typing.Tuple[
+            typing.Union[typing.Type[typing.Any], typing.Callable[..., typing.Any]],
+            ...
+        ] = (),
+        **kwargs: typing.Any,
+    ) -> typing.Callable[[T], T]:
+        """Decorator that marks a function, class, or metaclass as providing
+        dataclass-like behavior.
+
+        Example:
+
+            from metaflow._vendor.v3_7.typing_extensions import dataclass_transform
+
+            _T = TypeVar("_T")
+
+            # Used on a decorator function
+            @dataclass_transform()
+            def create_model(cls: type[_T]) -> type[_T]:
+                ...
+                return cls
+
+            @create_model
+            class CustomerModel:
+                id: int
+                name: str
+
+            # Used on a base class
+            @dataclass_transform()
+            class ModelBase: ...
+
+            class CustomerModel(ModelBase):
+                id: int
+                name: str
+
+            # Used on a metaclass
+            @dataclass_transform()
+            class ModelMeta(type): ...
+
+            class ModelBase(metaclass=ModelMeta): ...
+
+            class CustomerModel(ModelBase):
+                id: int
+                name: str
+
+        Each of the ``CustomerModel`` classes defined in this example will now
+        behave similarly to a dataclass created with the ``@dataclasses.dataclass``
+        decorator. For example, the type checker will synthesize an ``__init__``
+        method.
+
+        The arguments to this decorator can be used to customize this behavior:
+        - ``eq_default`` indicates whether the ``eq`` parameter is assumed to be
+          True or False if it is omitted by the caller.
+        - ``order_default`` indicates whether the ``order`` parameter is
+          assumed to be True or False if it is omitted by the caller.
+        - ``kw_only_default`` indicates whether the ``kw_only`` parameter is
+          assumed to be True or False if it is omitted by the caller.
+        - ``frozen_default`` indicates whether the ``frozen`` parameter is
+          assumed to be True or False if it is omitted by the caller.
+        - ``field_specifiers`` specifies a static list of supported classes
+          or functions that describe fields, similar to ``dataclasses.field()``.
+
+        At runtime, this decorator records its arguments in the
+        ``__dataclass_transform__`` attribute on the decorated object.
+
+        See PEP 681 for details.
+
+        """
+        def decorator(cls_or_fn):
+            cls_or_fn.__dataclass_transform__ = {
+                "eq_default": eq_default,
+                "order_default": order_default,
+                "kw_only_default": kw_only_default,
+                "frozen_default": frozen_default,
+                "field_specifiers": field_specifiers,
+                "kwargs": kwargs,
+            }
+            return cls_or_fn
+        return decorator
+
+
+if hasattr(typing, "override"):
+    override = typing.override
+else:
+    _F = typing.TypeVar("_F", bound=typing.Callable[..., typing.Any])
+
+    def override(__arg: _F) -> _F:
+        """Indicate that a method is intended to override a method in a base class.
+
+        Usage:
+
+            class Base:
+                def method(self) -> None: ...
+                    pass
+
+            class Child(Base):
+                @override
+                def method(self) -> None:
+                    super().method()
+
+        When this decorator is applied to a method, the type checker will
+        validate that it overrides a method with the same name on a base class.
+        This helps prevent bugs that may occur when a base class is changed
+        without an equivalent change to a child class.
+
+        There is no runtime checking of these properties. The decorator
+        sets the ``__override__`` attribute to ``True`` on the decorated object
+        to allow runtime introspection.
+
+        See PEP 698 for details.
+
+        """
+        try:
+            __arg.__override__ = True
+        except (AttributeError, TypeError):
+            # Skip the attribute silently if it is not writable.
+            # AttributeError happens if the object has __slots__ or a
+            # read-only property, TypeError if it's a builtin class.
+            pass
+        return __arg
+
+
+if hasattr(typing, "deprecated"):
+    deprecated = typing.deprecated
+else:
+    _T = typing.TypeVar("_T")
+
+    def deprecated(
+        __msg: str,
+        *,
+        category: typing.Optional[typing.Type[Warning]] = DeprecationWarning,
+        stacklevel: int = 1,
+    ) -> typing.Callable[[_T], _T]:
+        """Indicate that a class, function or overload is deprecated.
+
+        Usage:
+
+            @deprecated("Use B instead")
+            class A:
+                pass
+
+            @deprecated("Use g instead")
+            def f():
+                pass
+
+            @overload
+            @deprecated("int support is deprecated")
+            def g(x: int) -> int: ...
+            @overload
+            def g(x: str) -> int: ...
+
+        When this decorator is applied to an object, the type checker
+        will generate a diagnostic on usage of the deprecated object.
+
+        The warning specified by ``category`` will be emitted on use
+        of deprecated objects. For functions, that happens on calls;
+        for classes, on instantiation. If the ``category`` is ``None``,
+        no warning is emitted. The ``stacklevel`` determines where the
+        warning is emitted. If it is ``1`` (the default), the warning
+        is emitted at the direct caller of the deprecated object; if it
+        is higher, it is emitted further up the stack.
+
+        The decorator sets the ``__deprecated__``
+        attribute on the decorated object to the deprecation message
+        passed to the decorator. If applied to an overload, the decorator
+        must be after the ``@overload`` decorator for the attribute to
+        exist on the overload as returned by ``get_overloads()``.
+
+        See PEP 702 for details.
+
+        """
+        def decorator(__arg: _T) -> _T:
+            if category is None:
+                __arg.__deprecated__ = __msg
+                return __arg
+            elif isinstance(__arg, type):
+                original_new = __arg.__new__
+                has_init = __arg.__init__ is not object.__init__
+
+                @functools.wraps(original_new)
+                def __new__(cls, *args, **kwargs):
+                    warnings.warn(__msg, category=category, stacklevel=stacklevel + 1)
+                    if original_new is not object.__new__:
+                        return original_new(cls, *args, **kwargs)
+                    # Mirrors a similar check in object.__new__.
+                    elif not has_init and (args or kwargs):
+                        raise TypeError(f"{cls.__name__}() takes no arguments")
+                    else:
+                        return original_new(cls)
+
+                __arg.__new__ = staticmethod(__new__)
+                __arg.__deprecated__ = __new__.__deprecated__ = __msg
+                return __arg
+            elif callable(__arg):
+                @functools.wraps(__arg)
+                def wrapper(*args, **kwargs):
+                    warnings.warn(__msg, category=category, stacklevel=stacklevel + 1)
+                    return __arg(*args, **kwargs)
+
+                __arg.__deprecated__ = wrapper.__deprecated__ = __msg
+                return wrapper
+            else:
+                raise TypeError(
+                    "@deprecated decorator with non-None category must be applied to "
+                    f"a class or callable, not {__arg!r}"
+                )
+
+        return decorator
+
+
+# We have to do some monkey patching to deal with the dual nature of
+# Unpack/TypeVarTuple:
+# - We want Unpack to be a kind of TypeVar so it gets accepted in
+#   Generic[Unpack[Ts]]
+# - We want it to *not* be treated as a TypeVar for the purposes of
+#   counting generic parameters, so that when we subscript a generic,
+#   the runtime doesn't try to substitute the Unpack with the subscripted type.
+if not hasattr(typing, "TypeVarTuple"):
+    typing._collect_type_vars = _collect_type_vars
+    typing._check_generic = _check_generic
+
+
+# Backport typing.NamedTuple as it exists in Python 3.12.
+# In 3.11, the ability to define generic `NamedTuple`s was supported.
+# This was explicitly disallowed in 3.9-3.10, and only half-worked in <=3.8.
+# On 3.12, we added __orig_bases__ to call-based NamedTuples
+# On 3.13, we deprecated kwargs-based NamedTuples
+if sys.version_info >= (3, 13):
+    NamedTuple = typing.NamedTuple
+else:
+    def _make_nmtuple(name, types, module, defaults=()):
+        fields = [n for n, t in types]
+        annotations = {n: typing._type_check(t, f"field {n} annotation must be a type")
+                       for n, t in types}
+        nm_tpl = collections.namedtuple(name, fields,
+                                        defaults=defaults, module=module)
+        nm_tpl.__annotations__ = nm_tpl.__new__.__annotations__ = annotations
+        # The `_field_types` attribute was removed in 3.9;
+        # in earlier versions, it is the same as the `__annotations__` attribute
+        if sys.version_info < (3, 9):
+            nm_tpl._field_types = annotations
+        return nm_tpl
+
+    _prohibited_namedtuple_fields = typing._prohibited
+    _special_namedtuple_fields = frozenset({'__module__', '__name__', '__annotations__'})
+
+    class _NamedTupleMeta(type):
+        def __new__(cls, typename, bases, ns):
+            assert _NamedTuple in bases
+            for base in bases:
+                if base is not _NamedTuple and base is not typing.Generic:
+                    raise TypeError(
+                        'can only inherit from a NamedTuple type and Generic')
+            bases = tuple(tuple if base is _NamedTuple else base for base in bases)
+            types = ns.get('__annotations__', {})
+            default_names = []
+            for field_name in types:
+                if field_name in ns:
+                    default_names.append(field_name)
+                elif default_names:
+                    raise TypeError(f"Non-default namedtuple field {field_name} "
+                                    f"cannot follow default field"
+                                    f"{'s' if len(default_names) > 1 else ''} "
+                                    f"{', '.join(default_names)}")
+            nm_tpl = _make_nmtuple(
+                typename, types.items(),
+                defaults=[ns[n] for n in default_names],
+                module=ns['__module__']
+            )
+            nm_tpl.__bases__ = bases
+            if typing.Generic in bases:
+                if hasattr(typing, '_generic_class_getitem'):  # 3.12+
+                    nm_tpl.__class_getitem__ = classmethod(typing._generic_class_getitem)
+                else:
+                    class_getitem = typing.Generic.__class_getitem__.__func__
+                    nm_tpl.__class_getitem__ = classmethod(class_getitem)
+            # update from user namespace without overriding special namedtuple attributes
+            for key in ns:
+                if key in _prohibited_namedtuple_fields:
+                    raise AttributeError("Cannot overwrite NamedTuple attribute " + key)
+                elif key not in _special_namedtuple_fields and key not in nm_tpl._fields:
+                    setattr(nm_tpl, key, ns[key])
+            if typing.Generic in bases:
+                nm_tpl.__init_subclass__()
+            return nm_tpl
+
+    _NamedTuple = type.__new__(_NamedTupleMeta, 'NamedTuple', (), {})
+
+    def _namedtuple_mro_entries(bases):
+        assert NamedTuple in bases
+        return (_NamedTuple,)
+
+    @_ensure_subclassable(_namedtuple_mro_entries)
+    def NamedTuple(__typename, __fields=_marker, **kwargs):
+        """Typed version of namedtuple.
+
+        Usage::
+
+            class Employee(NamedTuple):
+                name: str
+                id: int
+
+        This is equivalent to::
+
+            Employee = collections.namedtuple('Employee', ['name', 'id'])
+
+        The resulting class has an extra __annotations__ attribute, giving a
+        dict that maps field names to types.  (The field names are also in
+        the _fields attribute, which is part of the namedtuple API.)
+        An alternative equivalent functional syntax is also accepted::
+
+            Employee = NamedTuple('Employee', [('name', str), ('id', int)])
+        """
+        if __fields is _marker:
+            if kwargs:
+                deprecated_thing = "Creating NamedTuple classes using keyword arguments"
+                deprecation_msg = (
+                    "{name} is deprecated and will be disallowed in Python {remove}. "
+                    "Use the class-based or functional syntax instead."
+                )
+            else:
+                deprecated_thing = "Failing to pass a value for the 'fields' parameter"
+                example = f"`{__typename} = NamedTuple({__typename!r}, [])`"
+                deprecation_msg = (
+                    "{name} is deprecated and will be disallowed in Python {remove}. "
+                    "To create a NamedTuple class with 0 fields "
+                    "using the functional syntax, "
+                    "pass an empty list, e.g. "
+                ) + example + "."
+        elif __fields is None:
+            if kwargs:
+                raise TypeError(
+                    "Cannot pass `None` as the 'fields' parameter "
+                    "and also specify fields using keyword arguments"
+                )
+            else:
+                deprecated_thing = "Passing `None` as the 'fields' parameter"
+                example = f"`{__typename} = NamedTuple({__typename!r}, [])`"
+                deprecation_msg = (
+                    "{name} is deprecated and will be disallowed in Python {remove}. "
+                    "To create a NamedTuple class with 0 fields "
+                    "using the functional syntax, "
+                    "pass an empty list, e.g. "
+                ) + example + "."
+        elif kwargs:
+            raise TypeError("Either list of fields or keywords"
+                            " can be provided to NamedTuple, not both")
+        if __fields is _marker or __fields is None:
+            warnings.warn(
+                deprecation_msg.format(name=deprecated_thing, remove="3.15"),
+                DeprecationWarning,
+                stacklevel=2,
+            )
+            __fields = kwargs.items()
+        nt = _make_nmtuple(__typename, __fields, module=_caller())
+        nt.__orig_bases__ = (NamedTuple,)
+        return nt
+
+    # On 3.8+, alter the signature so that it matches typing.NamedTuple.
+    # The signature of typing.NamedTuple on >=3.8 is invalid syntax in Python 3.7,
+    # so just leave the signature as it is on 3.7.
+    if sys.version_info >= (3, 8):
+        _new_signature = '(typename, fields=None, /, **kwargs)'
+        if isinstance(NamedTuple, _types.FunctionType):
+            NamedTuple.__text_signature__ = _new_signature
+        else:
+            NamedTuple.__call__.__text_signature__ = _new_signature
+
+
+if hasattr(collections.abc, "Buffer"):
+    Buffer = collections.abc.Buffer
+else:
+    class Buffer(abc.ABC):
+        """Base class for classes that implement the buffer protocol.
+
+        The buffer protocol allows Python objects to expose a low-level
+        memory buffer interface. Before Python 3.12, it is not possible
+        to implement the buffer protocol in pure Python code, or even
+        to check whether a class implements the buffer protocol. In
+        Python 3.12 and higher, the ``__buffer__`` method allows access
+        to the buffer protocol from Python code, and the
+        ``collections.abc.Buffer`` ABC allows checking whether a class
+        implements the buffer protocol.
+
+        To indicate support for the buffer protocol in earlier versions,
+        inherit from this ABC, either in a stub file or at runtime,
+        or use ABC registration. This ABC provides no methods, because
+        there is no Python-accessible methods shared by pre-3.12 buffer
+        classes. It is useful primarily for static checks.
+
+        """
+
+    # As a courtesy, register the most common stdlib buffer classes.
+    Buffer.register(memoryview)
+    Buffer.register(bytearray)
+    Buffer.register(bytes)
+
+
+# Backport of types.get_original_bases, available on 3.12+ in CPython
+if hasattr(_types, "get_original_bases"):
+    get_original_bases = _types.get_original_bases
+else:
+    def get_original_bases(__cls):
+        """Return the class's "original" bases prior to modification by `__mro_entries__`.
+
+        Examples::
+
+            from typing import TypeVar, Generic
+            from metaflow._vendor.v3_7.typing_extensions import NamedTuple, TypedDict
+
+            T = TypeVar("T")
+            class Foo(Generic[T]): ...
+            class Bar(Foo[int], float): ...
+            class Baz(list[str]): ...
+            Eggs = NamedTuple("Eggs", [("a", int), ("b", str)])
+            Spam = TypedDict("Spam", {"a": int, "b": str})
+
+            assert get_original_bases(Bar) == (Foo[int], float)
+            assert get_original_bases(Baz) == (list[str],)
+            assert get_original_bases(Eggs) == (NamedTuple,)
+            assert get_original_bases(Spam) == (TypedDict,)
+            assert get_original_bases(int) == (object,)
+        """
+        try:
+            return __cls.__orig_bases__
+        except AttributeError:
+            try:
+                return __cls.__bases__
+            except AttributeError:
+                raise TypeError(
+                    f'Expected an instance of type, not {type(__cls).__name__!r}'
+                ) from None
+
+
+# NewType is a class on Python 3.10+, making it pickleable
+# The error message for subclassing instances of NewType was improved on 3.11+
+if sys.version_info >= (3, 11):
+    NewType = typing.NewType
+else:
+    class NewType:
+        """NewType creates simple unique types with almost zero
+        runtime overhead. NewType(name, tp) is considered a subtype of tp
+        by static type checkers. At runtime, NewType(name, tp) returns
+        a dummy callable that simply returns its argument. Usage::
+            UserId = NewType('UserId', int)
+            def name_by_id(user_id: UserId) -> str:
+                ...
+            UserId('user')          # Fails type check
+            name_by_id(42)          # Fails type check
+            name_by_id(UserId(42))  # OK
+            num = UserId(5) + 1     # type: int
+        """
+
+        def __call__(self, obj):
+            return obj
+
+        def __init__(self, name, tp):
+            self.__qualname__ = name
+            if '.' in name:
+                name = name.rpartition('.')[-1]
+            self.__name__ = name
+            self.__supertype__ = tp
+            def_mod = _caller()
+            if def_mod != 'typing_extensions':
+                self.__module__ = def_mod
+
+        def __mro_entries__(self, bases):
+            # We defined __mro_entries__ to get a better error message
+            # if a user attempts to subclass a NewType instance. bpo-46170
+            supercls_name = self.__name__
+
+            class Dummy:
+                def __init_subclass__(cls):
+                    subcls_name = cls.__name__
+                    raise TypeError(
+                        f"Cannot subclass an instance of NewType. "
+                        f"Perhaps you were looking for: "
+                        f"`{subcls_name} = NewType({subcls_name!r}, {supercls_name})`"
+                    )
+
+            return (Dummy,)
+
+        def __repr__(self):
+            return f'{self.__module__}.{self.__qualname__}'
+
+        def __reduce__(self):
+            return self.__qualname__
+
+        if sys.version_info >= (3, 10):
+            # PEP 604 methods
+            # It doesn't make sense to have these methods on Python <3.10
+
+            def __or__(self, other):
+                return typing.Union[self, other]
+
+            def __ror__(self, other):
+                return typing.Union[other, self]
+
+
+if hasattr(typing, "TypeAliasType"):
+    TypeAliasType = typing.TypeAliasType
+else:
+    def _is_unionable(obj):
+        """Corresponds to is_unionable() in unionobject.c in CPython."""
+        return obj is None or isinstance(obj, (
+            type,
+            _types.GenericAlias,
+            _types.UnionType,
+            TypeAliasType,
+        ))
+
+    class TypeAliasType:
+        """Create named, parameterized type aliases.
+
+        This provides a backport of the new `type` statement in Python 3.12:
+
+            type ListOrSet[T] = list[T] | set[T]
+
+        is equivalent to:
+
+            T = TypeVar("T")
+            ListOrSet = TypeAliasType("ListOrSet", list[T] | set[T], type_params=(T,))
+
+        The name ListOrSet can then be used as an alias for the type it refers to.
+
+        The type_params argument should contain all the type parameters used
+        in the value of the type alias. If the alias is not generic, this
+        argument is omitted.
+
+        Static type checkers should only support type aliases declared using
+        TypeAliasType that follow these rules:
+
+        - The first argument (the name) must be a string literal.
+        - The TypeAliasType instance must be immediately assigned to a variable
+          of the same name. (For example, 'X = TypeAliasType("Y", int)' is invalid,
+          as is 'X, Y = TypeAliasType("X", int), TypeAliasType("Y", int)').
+
+        """
+
+        def __init__(self, name: str, value, *, type_params=()):
+            if not isinstance(name, str):
+                raise TypeError("TypeAliasType name must be a string")
+            self.__value__ = value
+            self.__type_params__ = type_params
+
+            parameters = []
+            for type_param in type_params:
+                if isinstance(type_param, TypeVarTuple):
+                    parameters.extend(type_param)
+                else:
+                    parameters.append(type_param)
+            self.__parameters__ = tuple(parameters)
+            def_mod = _caller()
+            if def_mod != 'typing_extensions':
+                self.__module__ = def_mod
+            # Setting this attribute closes the TypeAliasType from further modification
+            self.__name__ = name
+
+        def __setattr__(self, __name: str, __value: object) -> None:
+            if hasattr(self, "__name__"):
+                self._raise_attribute_error(__name)
+            super().__setattr__(__name, __value)
+
+        def __delattr__(self, __name: str) -> Never:
+            self._raise_attribute_error(__name)
+
+        def _raise_attribute_error(self, name: str) -> Never:
+            # Match the Python 3.12 error messages exactly
+            if name == "__name__":
+                raise AttributeError("readonly attribute")
+            elif name in {"__value__", "__type_params__", "__parameters__", "__module__"}:
+                raise AttributeError(
+                    f"attribute '{name}' of 'typing.TypeAliasType' objects "
+                    "is not writable"
+                )
+            else:
+                raise AttributeError(
+                    f"'typing.TypeAliasType' object has no attribute '{name}'"
+                )
+
+        def __repr__(self) -> str:
+            return self.__name__
+
+        def __getitem__(self, parameters):
+            if not isinstance(parameters, tuple):
+                parameters = (parameters,)
+            parameters = [
+                typing._type_check(
+                    item, f'Subscripting {self.__name__} requires a type.'
+                )
+                for item in parameters
+            ]
+            return typing._GenericAlias(self, tuple(parameters))
+
+        def __reduce__(self):
+            return self.__name__
+
+        def __init_subclass__(cls, *args, **kwargs):
+            raise TypeError(
+                "type 'typing_extensions.TypeAliasType' is not an acceptable base type"
+            )
+
+        # The presence of this method convinces typing._type_check
+        # that TypeAliasTypes are types.
+        def __call__(self):
+            raise TypeError("Type alias is not callable")
+
+        if sys.version_info >= (3, 10):
+            def __or__(self, right):
+                # For forward compatibility with 3.12, reject Unions
+                # that are not accepted by the built-in Union.
+                if not _is_unionable(right):
+                    return NotImplemented
+                return typing.Union[self, right]
+
+            def __ror__(self, left):
+                if not _is_unionable(left):
+                    return NotImplemented
+                return typing.Union[left, self]
+
+
+if hasattr(typing, "is_protocol"):
+    is_protocol = typing.is_protocol
+    get_protocol_members = typing.get_protocol_members
+else:
+    def is_protocol(__tp: type) -> bool:
+        """Return True if the given type is a Protocol.
+
+        Example::
+
+            >>> from typing_extensions import Protocol, is_protocol
+            >>> class P(Protocol):
+            ...     def a(self) -> str: ...
+            ...     b: int
+            >>> is_protocol(P)
+            True
+            >>> is_protocol(int)
+            False
+        """
+        return (
+            isinstance(__tp, type)
+            and getattr(__tp, '_is_protocol', False)
+            and __tp is not Protocol
+            and __tp is not getattr(typing, "Protocol", object())
+        )
+
+    def get_protocol_members(__tp: type) -> typing.FrozenSet[str]:
+        """Return the set of members defined in a Protocol.
+
+        Example::
+
+            >>> from typing_extensions import Protocol, get_protocol_members
+            >>> class P(Protocol):
+            ...     def a(self) -> str: ...
+            ...     b: int
+            >>> get_protocol_members(P)
+            frozenset({'a', 'b'})
+
+        Raise a TypeError for arguments that are not Protocols.
+        """
+        if not is_protocol(__tp):
+            raise TypeError(f'{__tp!r} is not a Protocol')
+        if hasattr(__tp, '__protocol_attrs__'):
+            return frozenset(__tp.__protocol_attrs__)
+        return frozenset(_get_protocol_attrs(__tp))
+
+
+# Aliases for items that have always been in typing.
+# Explicitly assign these (rather than using `from typing import *` at the top),
+# so that we get a CI error if one of these is deleted from typing.py
+# in a future version of Python
+AbstractSet = typing.AbstractSet
+AnyStr = typing.AnyStr
+BinaryIO = typing.BinaryIO
+Callable = typing.Callable
+Collection = typing.Collection
+Container = typing.Container
+Dict = typing.Dict
+ForwardRef = typing.ForwardRef
+FrozenSet = typing.FrozenSet
+Generator = typing.Generator
+Generic = typing.Generic
+Hashable = typing.Hashable
+IO = typing.IO
+ItemsView = typing.ItemsView
+Iterable = typing.Iterable
+Iterator = typing.Iterator
+KeysView = typing.KeysView
+List = typing.List
+Mapping = typing.Mapping
+MappingView = typing.MappingView
+Match = typing.Match
+MutableMapping = typing.MutableMapping
+MutableSequence = typing.MutableSequence
+MutableSet = typing.MutableSet
+Optional = typing.Optional
+Pattern = typing.Pattern
+Reversible = typing.Reversible
+Sequence = typing.Sequence
+Set = typing.Set
+Sized = typing.Sized
+TextIO = typing.TextIO
+Tuple = typing.Tuple
+Union = typing.Union
+ValuesView = typing.ValuesView
+cast = typing.cast
+no_type_check = typing.no_type_check
+no_type_check_decorator = typing.no_type_check_decorator
diff --git a/metaflow/_vendor/v3_5/zipp.LICENSE b/metaflow/_vendor/v3_7/zipp.LICENSE
similarity index 100%
rename from metaflow/_vendor/v3_5/zipp.LICENSE
rename to metaflow/_vendor/v3_7/zipp.LICENSE
diff --git a/metaflow/_vendor/v3_5/zipp.py b/metaflow/_vendor/v3_7/zipp.py
similarity index 100%
rename from metaflow/_vendor/v3_5/zipp.py
rename to metaflow/_vendor/v3_7/zipp.py
diff --git a/metaflow/_vendor/vendor_any.txt b/metaflow/_vendor/vendor_any.txt
index f7fc59bf3bb..683e0596ab6 100644
--- a/metaflow/_vendor/vendor_any.txt
+++ b/metaflow/_vendor/vendor_any.txt
@@ -1 +1,6 @@
 click==7.1.2
+packaging==23.0
+importlib_metadata==4.8.3
+typeguard==4.4.0
+typing_extensions==4.12.2
+zipp==3.6.0
diff --git a/metaflow/_vendor/vendor_v3_5.txt b/metaflow/_vendor/vendor_v3_5.txt
deleted file mode 100644
index 43295d035a6..00000000000
--- a/metaflow/_vendor/vendor_v3_5.txt
+++ /dev/null
@@ -1 +0,0 @@
-importlib_metadata==2.1.3
diff --git a/metaflow/_vendor/vendor_v3_6.txt b/metaflow/_vendor/vendor_v3_6.txt
index 7ab36b23bb5..5efb33f9d70 100644
--- a/metaflow/_vendor/vendor_v3_6.txt
+++ b/metaflow/_vendor/vendor_v3_6.txt
@@ -1 +1,3 @@
-importlib_metadata==4.8.3
\ No newline at end of file
+importlib_metadata==4.8.3
+typing_extensions==4.1.1
+zipp==3.6.0
diff --git a/metaflow/_vendor/vendor_v3_7.txt b/metaflow/_vendor/vendor_v3_7.txt
new file mode 100644
index 00000000000..681b2b4007b
--- /dev/null
+++ b/metaflow/_vendor/vendor_v3_7.txt
@@ -0,0 +1,4 @@
+importlib_metadata==4.8.3
+typeguard==4.1.2
+typing_extensions==4.7.1
+zipp==3.6.0
diff --git a/metaflow/_vendor/zipp.LICENSE b/metaflow/_vendor/zipp.LICENSE
new file mode 100644
index 00000000000..353924be0e5
--- /dev/null
+++ b/metaflow/_vendor/zipp.LICENSE
@@ -0,0 +1,19 @@
+Copyright Jason R. Coombs
+
+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/metaflow/_vendor/zipp.py b/metaflow/_vendor/zipp.py
new file mode 100644
index 00000000000..26b723c1fd3
--- /dev/null
+++ b/metaflow/_vendor/zipp.py
@@ -0,0 +1,329 @@
+import io
+import posixpath
+import zipfile
+import itertools
+import contextlib
+import sys
+import pathlib
+
+if sys.version_info < (3, 7):
+    from collections import OrderedDict
+else:
+    OrderedDict = dict
+
+
+__all__ = ['Path']
+
+
+def _parents(path):
+    """
+    Given a path with elements separated by
+    posixpath.sep, generate all parents of that path.
+
+    >>> list(_parents('b/d'))
+    ['b']
+    >>> list(_parents('/b/d/'))
+    ['/b']
+    >>> list(_parents('b/d/f/'))
+    ['b/d', 'b']
+    >>> list(_parents('b'))
+    []
+    >>> list(_parents(''))
+    []
+    """
+    return itertools.islice(_ancestry(path), 1, None)
+
+
+def _ancestry(path):
+    """
+    Given a path with elements separated by
+    posixpath.sep, generate all elements of that path
+
+    >>> list(_ancestry('b/d'))
+    ['b/d', 'b']
+    >>> list(_ancestry('/b/d/'))
+    ['/b/d', '/b']
+    >>> list(_ancestry('b/d/f/'))
+    ['b/d/f', 'b/d', 'b']
+    >>> list(_ancestry('b'))
+    ['b']
+    >>> list(_ancestry(''))
+    []
+    """
+    path = path.rstrip(posixpath.sep)
+    while path and path != posixpath.sep:
+        yield path
+        path, tail = posixpath.split(path)
+
+
+_dedupe = OrderedDict.fromkeys
+"""Deduplicate an iterable in original order"""
+
+
+def _difference(minuend, subtrahend):
+    """
+    Return items in minuend not in subtrahend, retaining order
+    with O(1) lookup.
+    """
+    return itertools.filterfalse(set(subtrahend).__contains__, minuend)
+
+
+class CompleteDirs(zipfile.ZipFile):
+    """
+    A ZipFile subclass that ensures that implied directories
+    are always included in the namelist.
+    """
+
+    @staticmethod
+    def _implied_dirs(names):
+        parents = itertools.chain.from_iterable(map(_parents, names))
+        as_dirs = (p + posixpath.sep for p in parents)
+        return _dedupe(_difference(as_dirs, names))
+
+    def namelist(self):
+        names = super(CompleteDirs, self).namelist()
+        return names + list(self._implied_dirs(names))
+
+    def _name_set(self):
+        return set(self.namelist())
+
+    def resolve_dir(self, name):
+        """
+        If the name represents a directory, return that name
+        as a directory (with the trailing slash).
+        """
+        names = self._name_set()
+        dirname = name + '/'
+        dir_match = name not in names and dirname in names
+        return dirname if dir_match else name
+
+    @classmethod
+    def make(cls, source):
+        """
+        Given a source (filename or zipfile), return an
+        appropriate CompleteDirs subclass.
+        """
+        if isinstance(source, CompleteDirs):
+            return source
+
+        if not isinstance(source, zipfile.ZipFile):
+            return cls(_pathlib_compat(source))
+
+        # Only allow for FastLookup when supplied zipfile is read-only
+        if 'r' not in source.mode:
+            cls = CompleteDirs
+
+        source.__class__ = cls
+        return source
+
+
+class FastLookup(CompleteDirs):
+    """
+    ZipFile subclass to ensure implicit
+    dirs exist and are resolved rapidly.
+    """
+
+    def namelist(self):
+        with contextlib.suppress(AttributeError):
+            return self.__names
+        self.__names = super(FastLookup, self).namelist()
+        return self.__names
+
+    def _name_set(self):
+        with contextlib.suppress(AttributeError):
+            return self.__lookup
+        self.__lookup = super(FastLookup, self)._name_set()
+        return self.__lookup
+
+
+def _pathlib_compat(path):
+    """
+    For path-like objects, convert to a filename for compatibility
+    on Python 3.6.1 and earlier.
+    """
+    try:
+        return path.__fspath__()
+    except AttributeError:
+        return str(path)
+
+
+class Path:
+    """
+    A pathlib-compatible interface for zip files.
+
+    Consider a zip file with this structure::
+
+        .
+        β”œβ”€β”€ a.txt
+        └── b
+            β”œβ”€β”€ c.txt
+            └── d
+                └── e.txt
+
+    >>> data = io.BytesIO()
+    >>> zf = zipfile.ZipFile(data, 'w')
+    >>> zf.writestr('a.txt', 'content of a')
+    >>> zf.writestr('b/c.txt', 'content of c')
+    >>> zf.writestr('b/d/e.txt', 'content of e')
+    >>> zf.filename = 'mem/abcde.zip'
+
+    Path accepts the zipfile object itself or a filename
+
+    >>> root = Path(zf)
+
+    From there, several path operations are available.
+
+    Directory iteration (including the zip file itself):
+
+    >>> a, b = root.iterdir()
+    >>> a
+    Path('mem/abcde.zip', 'a.txt')
+    >>> b
+    Path('mem/abcde.zip', 'b/')
+
+    name property:
+
+    >>> b.name
+    'b'
+
+    join with divide operator:
+
+    >>> c = b / 'c.txt'
+    >>> c
+    Path('mem/abcde.zip', 'b/c.txt')
+    >>> c.name
+    'c.txt'
+
+    Read text:
+
+    >>> c.read_text()
+    'content of c'
+
+    existence:
+
+    >>> c.exists()
+    True
+    >>> (b / 'missing.txt').exists()
+    False
+
+    Coercion to string:
+
+    >>> import os
+    >>> str(c).replace(os.sep, posixpath.sep)
+    'mem/abcde.zip/b/c.txt'
+
+    At the root, ``name``, ``filename``, and ``parent``
+    resolve to the zipfile. Note these attributes are not
+    valid and will raise a ``ValueError`` if the zipfile
+    has no filename.
+
+    >>> root.name
+    'abcde.zip'
+    >>> str(root.filename).replace(os.sep, posixpath.sep)
+    'mem/abcde.zip'
+    >>> str(root.parent)
+    'mem'
+    """
+
+    __repr = "{self.__class__.__name__}({self.root.filename!r}, {self.at!r})"
+
+    def __init__(self, root, at=""):
+        """
+        Construct a Path from a ZipFile or filename.
+
+        Note: When the source is an existing ZipFile object,
+        its type (__class__) will be mutated to a
+        specialized type. If the caller wishes to retain the
+        original type, the caller should either create a
+        separate ZipFile object or pass a filename.
+        """
+        self.root = FastLookup.make(root)
+        self.at = at
+
+    def open(self, mode='r', *args, pwd=None, **kwargs):
+        """
+        Open this entry as text or binary following the semantics
+        of ``pathlib.Path.open()`` by passing arguments through
+        to io.TextIOWrapper().
+        """
+        if self.is_dir():
+            raise IsADirectoryError(self)
+        zip_mode = mode[0]
+        if not self.exists() and zip_mode == 'r':
+            raise FileNotFoundError(self)
+        stream = self.root.open(self.at, zip_mode, pwd=pwd)
+        if 'b' in mode:
+            if args or kwargs:
+                raise ValueError("encoding args invalid for binary operation")
+            return stream
+        return io.TextIOWrapper(stream, *args, **kwargs)
+
+    @property
+    def name(self):
+        return pathlib.Path(self.at).name or self.filename.name
+
+    @property
+    def suffix(self):
+        return pathlib.Path(self.at).suffix or self.filename.suffix
+
+    @property
+    def suffixes(self):
+        return pathlib.Path(self.at).suffixes or self.filename.suffixes
+
+    @property
+    def stem(self):
+        return pathlib.Path(self.at).stem or self.filename.stem
+
+    @property
+    def filename(self):
+        return pathlib.Path(self.root.filename).joinpath(self.at)
+
+    def read_text(self, *args, **kwargs):
+        with self.open('r', *args, **kwargs) as strm:
+            return strm.read()
+
+    def read_bytes(self):
+        with self.open('rb') as strm:
+            return strm.read()
+
+    def _is_child(self, path):
+        return posixpath.dirname(path.at.rstrip("/")) == self.at.rstrip("/")
+
+    def _next(self, at):
+        return self.__class__(self.root, at)
+
+    def is_dir(self):
+        return not self.at or self.at.endswith("/")
+
+    def is_file(self):
+        return self.exists() and not self.is_dir()
+
+    def exists(self):
+        return self.at in self.root._name_set()
+
+    def iterdir(self):
+        if not self.is_dir():
+            raise ValueError("Can't listdir a file")
+        subs = map(self._next, self.root.namelist())
+        return filter(self._is_child, subs)
+
+    def __str__(self):
+        return posixpath.join(self.root.filename, self.at)
+
+    def __repr__(self):
+        return self.__repr.format(self=self)
+
+    def joinpath(self, *other):
+        next = posixpath.join(self.at, *map(_pathlib_compat, other))
+        return self._next(self.root.resolve_dir(next))
+
+    __truediv__ = joinpath
+
+    @property
+    def parent(self):
+        if not self.at:
+            return self.filename.parent
+        parent_at = posixpath.dirname(self.at.rstrip('/'))
+        if parent_at:
+            parent_at += '/'
+        return self._next(parent_at)
diff --git a/metaflow/cards.py b/metaflow/cards.py
index edebaf68968..2497d4dd89c 100644
--- a/metaflow/cards.py
+++ b/metaflow/cards.py
@@ -6,6 +6,9 @@
     Image,
     Error,
     Markdown,
+    VegaChart,
+    ProgressBar,
+    PythonCode,
 )
 from metaflow.plugins.cards.card_modules.basic import (
     DefaultCard,
diff --git a/metaflow/cli.py b/metaflow/cli.py
index ad582043a66..c22ee7a9c59 100644
--- a/metaflow/cli.py
+++ b/metaflow/cli.py
@@ -1,54 +1,46 @@
+import functools
 import inspect
+import os
 import sys
 import traceback
 from datetime import datetime
-from functools import wraps
 
+import metaflow.tracing as tracing
 from metaflow._vendor import click
 
-from . import lint
-from . import plugins
-from . import parameters
-from . import decorators
-from . import metaflow_version
-from . import namespace
-from . import current
+from . import decorators, lint, metaflow_version, parameters, plugins
 from .cli_args import cli_args
-from .tagging_util import validate_tags
-from .util import (
-    resolve_identity,
-    decompress_list,
-    write_latest_run_id,
-    get_latest_run_id,
-)
-from .task import MetaflowTask
+from .cli_components.utils import LazyGroup, LazyPluginCommandCollection
+from .datastore import FlowDataStore, TaskDataStoreSet
+from .debug import debug
 from .exception import CommandException, MetaflowException
+from .flowspec import _FlowState
 from .graph import FlowGraph
-from .datastore import FlowDataStore, TaskDataStoreSet, TaskDataStore
-
-from .runtime import NativeRuntime
-from .package import MetaflowPackage
-from .plugins import (
-    DATASTORES,
-    ENVIRONMENTS,
-    LOGGING_SIDECARS,
-    METADATA_PROVIDERS,
-    MONITOR_SIDECARS,
-)
 from .metaflow_config import (
     DEFAULT_DATASTORE,
+    DEFAULT_DECOSPECS,
     DEFAULT_ENVIRONMENT,
     DEFAULT_EVENT_LOGGER,
     DEFAULT_METADATA,
     DEFAULT_MONITOR,
     DEFAULT_PACKAGE_SUFFIXES,
 )
+from .metaflow_current import current
+from metaflow.system import _system_monitor, _system_logger
 from .metaflow_environment import MetaflowEnvironment
+from .packaging_sys import MetaflowCodeContent
+from .plugins import (
+    DATASTORES,
+    ENVIRONMENTS,
+    LOGGING_SIDECARS,
+    METADATA_PROVIDERS,
+    MONITOR_SIDECARS,
+)
 from .pylint_wrapper import PyLint
-from .R import use_r, metaflow_r_version
-from .mflog import mflog, LOG_SOURCES
-from .unbounded_foreach import UBF_CONTROL, UBF_TASK
-
+from .R import metaflow_r_version, use_r
+from .util import get_latest_run_id, resolve_identity
+from .user_configs.config_options import LocalFileInput, config_options
+from .user_configs.config_parameters import ConfigValue
 
 ERASE_TO_EOL = "\033[K"
 HIGHLIGHT = "red"
@@ -58,13 +50,6 @@
 LOGGER_COLOR = "green"
 LOGGER_BAD_COLOR = "red"
 
-try:
-    # Python 2
-    import cPickle as pickle
-except ImportError:
-    # Python 3
-    import pickle
-
 
 def echo_dev_null(*args, **kwargs):
     pass
@@ -123,7 +108,16 @@ def logger(body="", system_msg=False, head="", bad=False, timestamp=True, nl=Tru
     click.secho(body, bold=system_msg, fg=LOGGER_BAD_COLOR if bad else None, nl=nl)
 
 
-@click.group()
+@click.group(
+    cls=LazyGroup,
+    lazy_subcommands={
+        "init": "metaflow.cli_components.init_cmd.init",
+        "dump": "metaflow.cli_components.dump_cmd.dump",
+        "step": "metaflow.cli_components.step_cmd.step",
+        "run": "metaflow.cli_components.run_cmds.run",
+        "resume": "metaflow.cli_components.run_cmds.resume",
+    },
+)
 def cli(ctx):
     pass
 
@@ -137,7 +131,13 @@ def cli(ctx):
 )
 @click.pass_obj
 def check(obj, warnings=False):
-    _check(obj.graph, obj.flow, obj.environment, pylint=obj.pylint, warnings=warnings)
+    if obj.is_quiet:
+        echo = echo_dev_null
+    else:
+        echo = echo_always
+    _check(
+        echo, obj.graph, obj.flow, obj.environment, pylint=obj.pylint, warnings=warnings
+    )
     fname = inspect.getfile(obj.flow.__class__)
     echo(
         "\n*'{cmd} show'* shows a description of this flow.\n"
@@ -152,8 +152,14 @@ def check(obj, warnings=False):
 @click.pass_obj
 def show(obj):
     echo_always("\n%s" % obj.graph.doc)
-    for _, node in sorted((n.func_lineno, n) for n in obj.graph):
-        echo_always("\nStep *%s*" % node.name, err=False)
+    for node_name in obj.graph.sorted_nodes:
+        echo_always("")
+        node = obj.graph[node_name]
+        for deco in node.decorators:
+            echo_always("@%s" % deco.name, err=False)
+        for deco in node.wrappers:
+            echo_always("@%s" % deco.decorator_name, err=False)
+        echo_always("Step *%s*" % node.name, err=False)
         echo_always(node.doc if node.doc else "?", indent=True, err=False)
         if node.type != "end":
             echo_always(
@@ -187,15 +193,15 @@ def output_raw(obj, json):
     else:
         _graph = str(obj.graph)
         _msg = "Internal representation of the flow:"
-    echo(_msg, fg="magenta", bold=False)
+    echo_always(_msg, fg="magenta", bold=False)
     echo_always(_graph, err=False)
 
 
 @cli.command(help="Visualize the flow with Graphviz.")
 @click.pass_obj
 def output_dot(obj):
-    echo("Visualizing the flow as a GraphViz graph", fg="magenta", bold=False)
-    echo(
+    echo_always("Visualizing the flow as a GraphViz graph", fg="magenta", bold=False)
+    echo_always(
         "Try piping the output to 'dot -Tpng -o graph.png' to produce "
         "an actual image.",
         indent=True,
@@ -203,708 +209,31 @@ def output_dot(obj):
     echo_always(obj.graph.output_dot(), err=False)
 
 
-@cli.command(
-    help="Get data artifacts of a task or all tasks in a step. "
-    "The format for input-path is either / or "
-    "//."
-)
-@click.argument("input-path")
-@click.option(
-    "--private/--no-private",
-    default=False,
-    show_default=True,
-    help="Show also private attributes.",
-)
-@click.option(
-    "--max-value-size",
-    default=1000,
-    show_default=True,
-    type=int,
-    help="Show only values that are smaller than this number. "
-    "Set to 0 to see only keys.",
-)
-@click.option(
-    "--include",
-    type=str,
-    default="",
-    help="Include only artifacts in the given comma-separated list.",
-)
-@click.option(
-    "--file", type=str, default=None, help="Serialize artifacts in the given file."
-)
-@click.pass_obj
-def dump(obj, input_path, private=None, max_value_size=None, include=None, file=None):
-
-    output = {}
-    kwargs = {
-        "show_private": private,
-        "max_value_size": max_value_size,
-        "include": {t for t in include.split(",") if t},
-    }
-
-    # Pathspec can either be run_id/step_name or run_id/step_name/task_id.
-    parts = input_path.split("/")
-    if len(parts) == 2:
-        run_id, step_name = parts
-        task_id = None
-    elif len(parts) == 3:
-        run_id, step_name, task_id = parts
-    else:
-        raise CommandException(
-            "input_path should either be run_id/step_name or run_id/step_name/task_id"
-        )
-
-    datastore_set = TaskDataStoreSet(
-        obj.flow_datastore,
-        run_id,
-        steps=[step_name],
-        prefetch_data_artifacts=kwargs.get("include"),
-    )
-    if task_id:
-        ds_list = [datastore_set.get_with_pathspec(input_path)]
-    else:
-        ds_list = list(datastore_set)  # get all tasks
-
-    for ds in ds_list:
-        echo(
-            "Dumping output of run_id=*{run_id}* "
-            "step=*{step}* task_id=*{task_id}*".format(
-                run_id=ds.run_id, step=ds.step_name, task_id=ds.task_id
-            ),
-            fg="magenta",
-        )
-
-        if file is None:
-            echo_always(
-                ds.format(**kwargs), highlight="green", highlight_bold=False, err=False
-            )
-        else:
-            output[ds.pathspec] = ds.to_dict(**kwargs)
-
-    if file is not None:
-        with open(file, "wb") as f:
-            pickle.dump(output, f, protocol=pickle.HIGHEST_PROTOCOL)
-        echo("Artifacts written to *%s*" % file)
-
-
-@cli.command(
-    help="Show stdout/stderr produced by a task or all tasks in a step. "
-    "The format for input-path is either / or "
-    "//."
-)
-@click.argument("input-path")
-@click.option(
-    "--stdout/--no-stdout",
-    default=False,
-    show_default=True,
-    help="Show stdout of the task.",
-)
-@click.option(
-    "--stderr/--no-stderr",
-    default=False,
-    show_default=True,
-    help="Show stderr of the task.",
-)
-@click.option(
-    "--both/--no-both",
-    default=True,
-    show_default=True,
-    help="Show both stdout and stderr of the task.",
-)
-@click.option(
-    "--timestamps/--no-timestamps",
-    default=False,
-    show_default=True,
-    help="Show timestamps.",
-)
-@click.pass_obj
-def logs(obj, input_path, stdout=None, stderr=None, both=None, timestamps=False):
-    types = set()
-    if stdout:
-        types.add("stdout")
-        both = False
-    if stderr:
-        types.add("stderr")
-        both = False
-    if both:
-        types.update(("stdout", "stderr"))
-
-    streams = list(sorted(types, reverse=True))
-
-    # Pathspec can either be run_id/step_name or run_id/step_name/task_id.
-    parts = input_path.split("/")
-    if len(parts) == 2:
-        run_id, step_name = parts
-        task_id = None
-    elif len(parts) == 3:
-        run_id, step_name, task_id = parts
-    else:
-        raise CommandException(
-            "input_path should either be run_id/step_name "
-            "or run_id/step_name/task_id"
-        )
-
-    datastore_set = TaskDataStoreSet(
-        obj.flow_datastore, run_id, steps=[step_name], allow_not_done=True
-    )
-    if task_id:
-        ds_list = [
-            TaskDataStore(
-                obj.flow_datastore,
-                run_id=run_id,
-                step_name=step_name,
-                task_id=task_id,
-                mode="r",
-                allow_not_done=True,
-            )
-        ]
-    else:
-        ds_list = list(datastore_set)  # get all tasks
-
-    if ds_list:
-
-        def echo_unicode(line, **kwargs):
-            click.secho(line.decode("UTF-8", errors="replace"), **kwargs)
-
-        # old style logs are non mflog-style logs
-        maybe_old_style = True
-        for ds in ds_list:
-            echo(
-                "Dumping logs of run_id=*{run_id}* "
-                "step=*{step}* task_id=*{task_id}*".format(
-                    run_id=ds.run_id, step=ds.step_name, task_id=ds.task_id
-                ),
-                fg="magenta",
-            )
-
-            for stream in streams:
-                echo(stream, bold=True)
-                logs = ds.load_logs(LOG_SOURCES, stream)
-                if any(data for _, data in logs):
-                    # attempt to read new, mflog-style logs
-                    for line in mflog.merge_logs([blob for _, blob in logs]):
-                        if timestamps:
-                            ts = mflog.utc_to_local(line.utc_tstamp)
-                            tstamp = ts.strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
-                            click.secho(tstamp + " ", fg=LOGGER_TIMESTAMP, nl=False)
-                        echo_unicode(line.msg)
-                    maybe_old_style = False
-                elif maybe_old_style:
-                    # if they are not available, we may be looking at
-                    # a legacy run (unless we have seen new-style data already
-                    # for another stream). This return an empty string if
-                    # nothing is found
-                    log = ds.load_log_legacy(stream)
-                    if log and timestamps:
-                        raise CommandException(
-                            "We can't show --timestamps for old runs. Sorry!"
-                        )
-                    echo_unicode(log, nl=False)
-    else:
-        raise CommandException(
-            "No Tasks found at the given path -- "
-            "either none exist or none have started yet"
-        )
-
-
-# TODO - move step and init under a separate 'internal' subcommand
-
-
-@cli.command(help="Internal command to execute a single task.", hidden=True)
-@click.argument("step-name")
-@click.option(
-    "--run-id",
-    default=None,
-    required=True,
-    help="ID for one execution of all steps in the flow.",
-)
-@click.option(
-    "--task-id",
-    default=None,
-    required=True,
-    show_default=True,
-    help="ID for this instance of the step.",
-)
-@click.option(
-    "--input-paths",
-    help="A comma-separated list of pathspecs specifying inputs for this step.",
-)
-@click.option(
-    "--input-paths-filename",
-    type=click.Path(exists=True, readable=True, dir_okay=False, resolve_path=True),
-    help="A filename containing the argument typically passed to `input-paths`",
-    hidden=True,
-)
-@click.option(
-    "--split-index",
-    type=int,
-    default=None,
-    show_default=True,
-    help="Index of this foreach split.",
-)
-@click.option(
-    "--tag",
-    "opt_tag",
-    multiple=True,
-    default=None,
-    help="Annotate this run with the given tag. You can specify "
-    "this option multiple times to attach multiple tags in "
-    "the task.",
-)
-@click.option(
-    "--namespace",
-    "opt_namespace",
-    default=None,
-    help="Change namespace from the default (your username) to the specified tag.",
-)
-@click.option(
-    "--retry-count",
-    default=0,
-    help="How many times we have attempted to run this task.",
-)
-@click.option(
-    "--max-user-code-retries",
-    default=0,
-    help="How many times we should attempt running the user code.",
-)
-@click.option(
-    "--clone-only",
-    default=None,
-    help="Pathspec of the origin task for this task to clone. Do "
-    "not execute anything.",
-)
-@click.option(
-    "--clone-wait-only/--no-clone-wait-only",
-    default=False,
-    show_default=True,
-    help="If specified, waits for an external process to clone the task",
-    hidden=True,
-)
-@click.option(
-    "--clone-run-id",
-    default=None,
-    help="Run id of the origin flow, if this task is part of a flow being resumed.",
-)
-@click.option(
-    "--with",
-    "decospecs",
-    multiple=True,
-    help="Add a decorator to this task. You can specify this "
-    "option multiple times to attach multiple decorators "
-    "to this task.",
-)
-@click.option(
-    "--ubf-context",
-    default="none",
-    type=click.Choice(["none", UBF_CONTROL, UBF_TASK]),
-    help="Provides additional context if this task is of type unbounded foreach.",
-)
-@click.option(
-    "--num-parallel",
-    default=0,
-    type=int,
-    help="Number of parallel instances of a step. Ignored in local mode (see parallel decorator code).",
-)
-@click.pass_context
-def step(
-    ctx,
-    step_name,
-    opt_tag=None,
-    run_id=None,
-    task_id=None,
-    input_paths=None,
-    input_paths_filename=None,
-    split_index=None,
-    opt_namespace=None,
-    retry_count=None,
-    max_user_code_retries=None,
-    clone_only=None,
-    clone_wait_only=False,
-    clone_run_id=None,
-    decospecs=None,
-    ubf_context="none",
-    num_parallel=None,
-):
-    if ubf_context == "none":
-        ubf_context = None
-    if opt_namespace is not None:
-        namespace(opt_namespace or None)
-
-    func = None
-    try:
-        func = getattr(ctx.obj.flow, step_name)
-    except:
-        raise CommandException("Step *%s* doesn't exist." % step_name)
-    if not func.is_step:
-        raise CommandException("Function *%s* is not a step." % step_name)
-    echo("Executing a step, *%s*" % step_name, fg="magenta", bold=False)
-
-    if decospecs:
-        decorators._attach_decorators_to_step(func, decospecs)
-
-    step_kwargs = ctx.params
-    # Remove argument `step_name` from `step_kwargs`.
-    step_kwargs.pop("step_name", None)
-    # Remove `opt_*` prefix from (some) option keys.
-    step_kwargs = dict(
-        [(k[4:], v) if k.startswith("opt_") else (k, v) for k, v in step_kwargs.items()]
-    )
-    cli_args._set_step_kwargs(step_kwargs)
-
-    ctx.obj.metadata.add_sticky_tags(tags=opt_tag)
-    if not input_paths and input_paths_filename:
-        with open(input_paths_filename, mode="r", encoding="utf-8") as f:
-            input_paths = f.read().strip(" \n\"'")
-
-    paths = decompress_list(input_paths) if input_paths else []
-
-    task = MetaflowTask(
-        ctx.obj.flow,
-        ctx.obj.flow_datastore,
-        ctx.obj.metadata,
-        ctx.obj.environment,
-        ctx.obj.echo,
-        ctx.obj.event_logger,
-        ctx.obj.monitor,
-        ubf_context,
-    )
-    if clone_only:
-        task.clone_only(
-            step_name,
-            run_id,
-            task_id,
-            clone_only,
-            retry_count,
-            wait_only=clone_wait_only,
-        )
-    else:
-        task.run_step(
-            step_name,
-            run_id,
-            task_id,
-            clone_run_id,
-            paths,
-            split_index,
-            retry_count,
-            max_user_code_retries,
-        )
-
-    echo("Success", fg="green", bold=True, indent=True)
-
-
-@parameters.add_custom_parameters(deploy_mode=False)
-@cli.command(help="Internal command to initialize a run.", hidden=True)
-@click.option(
-    "--run-id",
-    default=None,
-    required=True,
-    help="ID for one execution of all steps in the flow.",
-)
-@click.option(
-    "--task-id", default=None, required=True, help="ID for this instance of the step."
-)
-@click.option(
-    "--tag",
-    "tags",
-    multiple=True,
-    default=None,
-    help="Tags for this instance of the step.",
-)
-@click.pass_obj
-def init(obj, run_id=None, task_id=None, tags=None, **kwargs):
-    # init is a separate command instead of an option in 'step'
-    # since we need to capture user-specified parameters with
-    # @add_custom_parameters. Adding custom parameters to 'step'
-    # is not desirable due to the possibility of name clashes between
-    # user-specified parameters and our internal options. Note that
-    # user-specified parameters are often defined as environment
-    # variables.
-
-    obj.metadata.add_sticky_tags(tags=tags)
-
-    runtime = NativeRuntime(
-        obj.flow,
-        obj.graph,
-        obj.flow_datastore,
-        obj.metadata,
-        obj.environment,
-        obj.package,
-        obj.logger,
-        obj.entrypoint,
-        obj.event_logger,
-        obj.monitor,
-        run_id=run_id,
-    )
-    obj.flow._set_constants(obj.graph, kwargs)
-    runtime.persist_constants(task_id=task_id)
-
-
-def common_run_options(func):
-    @click.option(
-        "--tag",
-        "tags",
-        multiple=True,
-        default=None,
-        help="Annotate this run with the given tag. You can specify "
-        "this option multiple times to attach multiple tags in "
-        "the run.",
-    )
-    @click.option(
-        "--max-workers",
-        default=16,
-        show_default=True,
-        help="Maximum number of parallel processes.",
-    )
-    @click.option(
-        "--max-num-splits",
-        default=100,
-        show_default=True,
-        help="Maximum number of splits allowed in a foreach. This "
-        "is a safety check preventing bugs from triggering "
-        "thousands of steps inadvertently.",
-    )
-    @click.option(
-        "--max-log-size",
-        default=10,
-        show_default=True,
-        help="Maximum size of stdout and stderr captured in "
-        "megabytes. If a step outputs more than this to "
-        "stdout/stderr, its output will be truncated.",
-    )
-    @click.option(
-        "--with",
-        "decospecs",
-        multiple=True,
-        help="Add a decorator to all steps. You can specify this "
-        "option multiple times to attach multiple decorators "
-        "in steps.",
-    )
-    @click.option(
-        "--run-id-file",
-        default=None,
-        show_default=True,
-        type=str,
-        help="Write the ID of this run to the file specified.",
-    )
-    @wraps(func)
-    def wrapper(*args, **kwargs):
-        return func(*args, **kwargs)
-
-    return wrapper
-
-
-@click.option(
-    "--origin-run-id",
-    default=None,
-    help="ID of the run that should be resumed. By default, the "
-    "last run executed locally.",
-)
-@click.option(
-    "--run-id",
-    default=None,
-    help="Run ID for the new run. By default, a new run-id will be generated",
-    hidden=True,
-)
-@click.option(
-    "--clone-only/--no-clone-only",
-    default=False,
-    show_default=True,
-    help="Only clone tasks without continuing execution",
-    hidden=True,
-)
-@click.option(
-    "--reentrant/--no-reentrant",
-    default=False,
-    show_default=True,
-    hidden=True,
-    help="If specified, allows this call to be called in parallel",
-)
-@click.argument("step-to-rerun", required=False)
-@cli.command(help="Resume execution of a previous run of this flow.")
-@common_run_options
-@click.pass_obj
-def resume(
-    obj,
-    tags=None,
-    step_to_rerun=None,
-    origin_run_id=None,
-    run_id=None,
-    clone_only=False,
-    reentrant=False,
-    max_workers=None,
-    max_num_splits=None,
-    max_log_size=None,
-    decospecs=None,
-    run_id_file=None,
-):
-
-    before_run(obj, tags, decospecs + obj.environment.decospecs())
-
-    if origin_run_id is None:
-        origin_run_id = get_latest_run_id(obj.echo, obj.flow.name)
-        if origin_run_id is None:
-            raise CommandException(
-                "A previous run id was not found. Specify --origin-run-id."
-            )
-
-    if step_to_rerun is None:
-        clone_steps = set()
-    else:
-        # validate step name
-        if step_to_rerun not in obj.graph.nodes:
-            raise CommandException(
-                "invalid step name {0} specified, must be step present in "
-                "current form of execution graph. Valid step names include: {1}".format(
-                    step_to_rerun, ",".join(list(obj.graph.nodes.keys()))
-                )
-            )
-        clone_steps = {step_to_rerun}
-
-    if run_id:
-        # Run-ids that are provided by the metadata service are always integers.
-        # External providers or run-ids (like external schedulers) always need to
-        # be non-integers to avoid any clashes. This condition ensures this.
-        try:
-            int(run_id)
-        except:
-            pass
-        else:
-            raise CommandException("run-id %s cannot be an integer" % run_id)
-
-    runtime = NativeRuntime(
-        obj.flow,
-        obj.graph,
-        obj.flow_datastore,
-        obj.metadata,
-        obj.environment,
-        obj.package,
-        obj.logger,
-        obj.entrypoint,
-        obj.event_logger,
-        obj.monitor,
-        run_id=run_id,
-        clone_run_id=origin_run_id,
-        clone_only=clone_only,
-        reentrant=reentrant,
-        clone_steps=clone_steps,
-        max_workers=max_workers,
-        max_num_splits=max_num_splits,
-        max_log_size=max_log_size * 1024 * 1024,
-    )
-    write_run_id(run_id_file, runtime.run_id)
-    runtime.persist_constants()
-    runtime.execute()
-
-
-@parameters.add_custom_parameters(deploy_mode=True)
-@cli.command(help="Run the workflow locally.")
-@common_run_options
-@click.option(
-    "--namespace",
-    "user_namespace",
-    default=None,
-    help="Change namespace from the default (your username) to "
-    "the specified tag. Note that this option does not alter "
-    "tags assigned to the objects produced by this run, just "
-    "what existing objects are visible in the client API. You "
-    "can enable the global namespace with an empty string."
-    "--namespace=",
-)
-@click.pass_obj
-def run(
-    obj,
-    tags=None,
-    max_workers=None,
-    max_num_splits=None,
-    max_log_size=None,
-    decospecs=None,
-    run_id_file=None,
-    user_namespace=None,
-    **kwargs
-):
-
-    if user_namespace is not None:
-        namespace(user_namespace or None)
-    before_run(obj, tags, decospecs + obj.environment.decospecs())
-
-    runtime = NativeRuntime(
-        obj.flow,
-        obj.graph,
-        obj.flow_datastore,
-        obj.metadata,
-        obj.environment,
-        obj.package,
-        obj.logger,
-        obj.entrypoint,
-        obj.event_logger,
-        obj.monitor,
-        max_workers=max_workers,
-        max_num_splits=max_num_splits,
-        max_log_size=max_log_size * 1024 * 1024,
-    )
-    write_latest_run_id(obj, runtime.run_id)
-    write_run_id(run_id_file, runtime.run_id)
-
-    obj.flow._set_constants(obj.graph, kwargs)
-    runtime.persist_constants()
-    runtime.execute()
-
-
-def write_run_id(run_id_file, run_id):
-    if run_id_file is not None:
-        with open(run_id_file, "w") as f:
-            f.write(str(run_id))
-
-
-def before_run(obj, tags, decospecs):
-    validate_tags(tags)
-
-    # There's a --with option both at the top-level and for the run
-    # subcommand. Why?
-    #
-    # "run --with shoes" looks so much better than "--with shoes run".
-    # This is a very common use case of --with.
-    #
-    # A downside is that we need to have the following decorators handling
-    # in two places in this module and make sure _init_step_decorators
-    # doesn't get called twice.
-    if decospecs:
-        decorators._attach_decorators(obj.flow, decospecs)
-        obj.graph = FlowGraph(obj.flow.__class__)
-    obj.check(obj.graph, obj.flow, obj.environment, pylint=obj.pylint)
-    # obj.environment.init_environment(obj.logger)
-
-    decorators._init_step_decorators(
-        obj.flow, obj.graph, obj.environment, obj.flow_datastore, obj.logger
-    )
-
-    obj.metadata.add_sticky_tags(tags=tags)
-
-    # Package working directory only once per run.
-    # We explicitly avoid doing this in `start` since it is invoked for every
-    # step in the run.
-    obj.package = MetaflowPackage(
-        obj.flow, obj.environment, obj.echo, obj.package_suffixes
-    )
-
-
 @cli.command(help="Print the Metaflow version")
 @click.pass_obj
 def version(obj):
     echo_always(obj.version)
 
 
+# NOTE: add_decorator_options should be TL because it checks to make sure
+# that no option conflict with the ones below
 @decorators.add_decorator_options
+@config_options
 @click.command(
-    cls=click.CommandCollection,
-    sources=[cli] + plugins.get_plugin_cli(),
+    cls=LazyPluginCommandCollection,
+    sources=[cli],
+    lazy_sources=plugins.get_plugin_cli_path(),
     invoke_without_command=True,
 )
+# Quiet is eager to make sure it is available when processing --config options since
+# we need it to construct a context to pass to any DeployTimeField for the default
+# value.
 @click.option(
     "--quiet/--not-quiet",
     show_default=True,
     default=False,
     help="Suppress unnecessary messages",
+    is_eager=True,
 )
 @click.option(
     "--metadata",
@@ -920,12 +249,22 @@ def version(obj):
     type=click.Choice(["local"] + [m.TYPE for m in ENVIRONMENTS]),
     help="Execution environment type",
 )
+@click.option(
+    "--force-rebuild-environments/--no-force-rebuild-environments",
+    is_flag=True,
+    default=False,
+    hidden=True,
+    type=bool,
+    help="Explicitly rebuild the execution environments",
+)
+# See comment for --quiet
 @click.option(
     "--datastore",
     default=DEFAULT_DATASTORE,
     show_default=True,
     type=click.Choice([d.TYPE for d in DATASTORES]),
     help="Data backend type",
+    is_eager=True,
 )
 @click.option("--datastore-root", help="Root path for datastore")
 @click.option(
@@ -961,12 +300,22 @@ def version(obj):
     type=click.Choice(MONITOR_SIDECARS),
     help="Monitoring backend type",
 )
+@click.option(
+    "--local-config-file",
+    type=LocalFileInput(exists=True, readable=True, dir_okay=False, resolve_path=True),
+    required=False,
+    default=None,
+    help="A filename containing the dumped configuration values. Internal use only.",
+    hidden=True,
+    is_eager=True,
+)
 @click.pass_context
 def start(
     ctx,
     quiet=False,
     metadata=None,
     environment=None,
+    force_rebuild_environments=False,
     datastore=None,
     datastore_root=None,
     decospecs=None,
@@ -974,9 +323,11 @@ def start(
     pylint=None,
     event_logger=None,
     monitor=None,
+    local_config_file=None,
+    config=None,
+    config_value=None,
     **deco_options
 ):
-    global echo
     if quiet:
         echo = echo_dev_null
     else:
@@ -991,52 +342,144 @@ def start(
     echo(" executing *%s*" % ctx.obj.flow.name, fg="magenta", nl=False)
     echo(" for *%s*" % resolve_identity(), fg="magenta")
 
+    # Check if we need to setup the distribution finder (if running )
+    dist_info = MetaflowCodeContent.get_distribution_finder()
+    if dist_info:
+        sys.meta_path.append(dist_info)
+
+    # Setup the context
     cli_args._set_top_kwargs(ctx.params)
     ctx.obj.echo = echo
     ctx.obj.echo_always = echo_always
     ctx.obj.is_quiet = quiet
-    ctx.obj.graph = FlowGraph(ctx.obj.flow.__class__)
     ctx.obj.logger = logger
-    ctx.obj.check = _check
     ctx.obj.pylint = pylint
+    ctx.obj.check = functools.partial(_check, echo)
     ctx.obj.top_cli = cli
     ctx.obj.package_suffixes = package_suffixes.split(",")
-    ctx.obj.reconstruct_cli = _reconstruct_cli
+
+    ctx.obj.datastore_impl = [d for d in DATASTORES if d.TYPE == datastore][0]
+
+    if datastore_root is None:
+        datastore_root = ctx.obj.datastore_impl.get_datastore_root_from_config(
+            ctx.obj.echo
+        )
+    if datastore_root is None:
+        raise CommandException(
+            "Could not find the location of the datastore -- did you correctly set the "
+            "METAFLOW_DATASTORE_SYSROOT_%s environment variable?" % datastore.upper()
+        )
+
+    ctx.obj.datastore_impl.datastore_root = datastore_root
+
+    FlowDataStore.default_storage_impl = ctx.obj.datastore_impl
+
+    # At this point, we are able to resolve the user-configuration options so we can
+    # process all those decorators that the user added that will modify the flow based
+    # on those configurations. It is important to do this as early as possible since it
+    # actually modifies the flow itself
+
+    # When we process the options, the first one processed will return None and the
+    # second one processed will return the actual options. The order of processing
+    # depends on what (and in what order) the user specifies on the command line.
+    config_options = config or config_value
+
+    if (
+        hasattr(ctx, "saved_args")
+        and ctx.saved_args
+        and ctx.saved_args[0] == "resume"
+        and getattr(ctx.obj, "has_config_options", False)
+    ):
+        # In the case of resume, we actually need to load the configurations
+        # from the resumed run to process them. This can be slightly onerous so check
+        # if we need to in the first place
+        if getattr(ctx.obj, "has_cl_config_options", False):
+            raise click.UsageError(
+                "Cannot specify --config or --config-value with 'resume'"
+            )
+        # We now load the config artifacts from the original run id
+        run_id = None
+        try:
+            idx = ctx.saved_args.index("--origin-run-id")
+        except ValueError:
+            idx = -1
+        if idx >= 0:
+            run_id = ctx.saved_args[idx + 1]
+        else:
+            run_id = get_latest_run_id(ctx.obj.echo, ctx.obj.flow.name)
+        if run_id is None:
+            raise CommandException(
+                "A previous run id was not found. Specify --origin-run-id."
+            )
+        # We get the name of the parameters we need to load from the datastore -- these
+        # are accessed using the *variable* name and not necessarily the *parameter* name
+        config_var_names = []
+        config_param_names = []
+        for name, param in ctx.obj.flow._get_parameters():
+            if not param.IS_CONFIG_PARAMETER:
+                continue
+            config_var_names.append(name)
+            config_param_names.append(param.name)
+
+        # We just need a task datastore that will be thrown away -- we do this so
+        # we don't have to create the logger, monitor, etc.
+        debug.userconf_exec("Loading config parameters from run %s" % run_id)
+        for d in TaskDataStoreSet(
+            FlowDataStore(ctx.obj.flow.name),
+            run_id,
+            steps=["_parameters"],
+            prefetch_data_artifacts=config_var_names,
+        ):
+            param_ds = d
+
+        # We can now set the the CONFIGS value in the flow properly. This will overwrite
+        # anything that may have been passed in by default and we will use exactly what
+        # the original flow had. Note that these are accessed through the parameter name
+        ctx.obj.flow._flow_state[_FlowState.CONFIGS].clear()
+        d = ctx.obj.flow._flow_state[_FlowState.CONFIGS]
+        for param_name, var_name in zip(config_param_names, config_var_names):
+            val = param_ds[var_name]
+            debug.userconf_exec("Loaded config %s as: %s" % (param_name, val))
+            d[param_name] = val
+
+    elif getattr(ctx.obj, "delayed_config_exception", None):
+        # If we are not doing a resume, any exception we had parsing configs needs to
+        # be raised. For resume, since we ignore those options, we ignore the error.
+        raise ctx.obj.delayed_config_exception
+
+    # Init all values in the flow mutators and then process them
+    for decorator in ctx.obj.flow._flow_state.get(_FlowState.FLOW_MUTATORS, []):
+        decorator.external_init()
+
+    new_cls = ctx.obj.flow._process_config_decorators(config_options)
+    if new_cls:
+        ctx.obj.flow = new_cls(use_cli=False)
+
+    ctx.obj.graph = ctx.obj.flow._graph
 
     ctx.obj.environment = [
         e for e in ENVIRONMENTS + [MetaflowEnvironment] if e.TYPE == environment
     ][0](ctx.obj.flow)
-    ctx.obj.environment.validate_environment(echo, datastore)
+    # set force rebuild flag for environments that support it.
+    ctx.obj.environment._force_rebuild = force_rebuild_environments
+    ctx.obj.environment.validate_environment(ctx.obj.logger, datastore)
 
     ctx.obj.event_logger = LOGGING_SIDECARS[event_logger](
         flow=ctx.obj.flow, env=ctx.obj.environment
     )
     ctx.obj.event_logger.start()
+    _system_logger.init_system_logger(ctx.obj.flow.name, ctx.obj.event_logger)
 
     ctx.obj.monitor = MONITOR_SIDECARS[monitor](
         flow=ctx.obj.flow, env=ctx.obj.environment
     )
     ctx.obj.monitor.start()
+    _system_monitor.init_system_monitor(ctx.obj.flow.name, ctx.obj.monitor)
 
     ctx.obj.metadata = [m for m in METADATA_PROVIDERS if m.TYPE == metadata][0](
         ctx.obj.environment, ctx.obj.flow, ctx.obj.event_logger, ctx.obj.monitor
     )
 
-    ctx.obj.datastore_impl = [d for d in DATASTORES if d.TYPE == datastore][0]
-
-    if datastore_root is None:
-        datastore_root = ctx.obj.datastore_impl.get_datastore_root_from_config(
-            ctx.obj.echo
-        )
-    if datastore_root is None:
-        raise CommandException(
-            "Could not find the location of the datastore -- did you correctly set the "
-            "METAFLOW_DATASTORE_SYSROOT_%s environment variable?" % datastore.upper()
-        )
-
-    ctx.obj.datastore_impl.datastore_root = datastore_root
-
-    FlowDataStore.default_storage_impl = ctx.obj.datastore_impl
     ctx.obj.flow_datastore = FlowDataStore(
         ctx.obj.flow.name,
         ctx.obj.environment,
@@ -1045,6 +488,10 @@ def start(
         ctx.obj.monitor,
     )
 
+    ctx.obj.config_options = config_options
+
+    decorators._init(ctx.obj.flow)
+
     # It is important to initialize flow decorators early as some of the
     # things they provide may be used by some of the objects initialized after.
     decorators._init_flow_decorators(
@@ -1058,19 +505,55 @@ def start(
         deco_options,
     )
 
-    if decospecs:
-        decorators._attach_decorators(ctx.obj.flow, decospecs)
+    # In the case of run/resume, we will want to apply the TL decospecs
+    # *after* the run decospecs so that they don't take precedence. In other
+    # words, for the same decorator, we want `myflow.py run --with foo` to
+    # take precedence over any other `foo` decospec
+
+    # Note that top-level decospecs are used primarily with non run/resume
+    # options as well as with the airflow/argo/sfn integrations which pass
+    # all the decospecs (the ones from top-level but also the ones from the
+    # run/resume level) through the tl decospecs.
+    ctx.obj.tl_decospecs = list(decospecs or [])
 
     # initialize current and parameter context for deploy-time parameters
     current._set_env(flow=ctx.obj.flow, is_running=False)
     parameters.set_parameter_context(
-        ctx.obj.flow.name, ctx.obj.echo, ctx.obj.flow_datastore
+        ctx.obj.flow.name,
+        ctx.obj.echo,
+        ctx.obj.flow_datastore,
+        {
+            k: ConfigValue(v) if v is not None else None
+            for k, v in ctx.obj.flow.__class__._flow_state.get(
+                _FlowState.CONFIGS, {}
+            ).items()
+        },
     )
 
-    if ctx.invoked_subcommand not in ("run", "resume"):
+    if (
+        hasattr(ctx, "saved_args")
+        and ctx.saved_args
+        and ctx.saved_args[0] not in ("run", "resume")
+    ):
         # run/resume are special cases because they can add more decorators with --with,
         # so they have to take care of themselves.
-        decorators._attach_decorators(ctx.obj.flow, ctx.obj.environment.decospecs())
+
+        all_decospecs = ctx.obj.tl_decospecs + list(
+            ctx.obj.environment.decospecs() or []
+        )
+
+        # We add the default decospecs for everything except init and step since in those
+        # cases, the decospecs will already have been handled by either a run/resume
+        # or a scheduler setting them up in their own way.
+        if ctx.saved_args[0] not in ("step", "init"):
+            all_decospecs += DEFAULT_DECOSPECS.split()
+        if all_decospecs:
+            decorators._attach_decorators(ctx.obj.flow, all_decospecs)
+            decorators._init(ctx.obj.flow)
+            # Regenerate graph if we attached more decorators
+            ctx.obj.flow.__class__._init_graph()
+            ctx.obj.graph = ctx.obj.flow._graph
+
         decorators._init_step_decorators(
             ctx.obj.flow,
             ctx.obj.graph,
@@ -1078,27 +561,18 @@ def start(
             ctx.obj.flow_datastore,
             ctx.obj.logger,
         )
+
+        # Check the graph again (mutators may have changed it)
+        ctx.obj.graph = ctx.obj.flow._graph
+
         # TODO (savin): Enable lazy instantiation of package
         ctx.obj.package = None
+
     if ctx.invoked_subcommand is None:
         ctx.invoke(check)
 
 
-def _reconstruct_cli(params):
-    for k, v in params.items():
-        if v:
-            if k == "decospecs":
-                k = "with"
-            k = k.replace("_", "-")
-            if not isinstance(v, tuple):
-                v = [v]
-            for value in v:
-                yield "--%s" % k
-                if not isinstance(value, bool):
-                    yield str(value)
-
-
-def _check(graph, flow, environment, pylint=True, warnings=False, **kwargs):
+def _check(echo, graph, flow, environment, pylint=True, warnings=False, **kwargs):
     echo("Validating your flow...", fg="magenta", bold=False)
     linter = lint.linter
     # TODO set linter settings
@@ -1137,10 +611,13 @@ def _check(graph, flow, environment, pylint=True, warnings=False, **kwargs):
 
 def print_metaflow_exception(ex):
     echo_always(ex.headline, indent=True, nl=False, bold=True)
-    if ex.line_no is None:
-        echo_always(":")
-    else:
-        echo_always(" on line %d:" % ex.line_no, bold=True)
+    location = ""
+    if ex.source_file is not None:
+        location += " in file %s" % ex.source_file
+    if ex.line_no is not None:
+        location += " on line %d" % ex.line_no
+    location += ":"
+    echo_always(location, bold=True)
     echo_always(ex.message, indent=True, bold=False, padding_bottom=True)
 
 
@@ -1172,7 +649,7 @@ def main(flow, args=None, handle_exceptions=True, entrypoint=None):
             start(auto_envvar_prefix="METAFLOW", obj=state)
         else:
             try:
-                start.main(args=args, obj=state, auto_envvar_prefix="METAFLOW")
+                start(args=args, obj=state, auto_envvar_prefix="METAFLOW")
             except SystemExit as e:
                 return e.code
     except MetaflowException as x:
diff --git a/metaflow/cli_args.py b/metaflow/cli_args.py
index 40918f984ff..ae58a2f6991 100644
--- a/metaflow/cli_args.py
+++ b/metaflow/cli_args.py
@@ -12,7 +12,14 @@
 # well as the converting of options in runtime.py. We should make it so that we
 # can properly shlex things and un-shlex when using. Ideally this should all be
 # done in one place.
+#
+# NOTE: There is an important between these two as well:
+#  - this one will include local_config_file whereas the other one WILL NOT.
+#    This is because this is used when constructing the parallel UBF command which
+#    executes locally and therefore needs the local_config_file but the other (remote)
+#    commands do not.
 
+from .user_configs.config_options import ConfigInput
 from .util import to_unicode
 
 
@@ -65,6 +72,16 @@ def _options(mapping):
             # keyword in Python, so we call it 'decospecs' in click args
             if k == "decospecs":
                 k = "with"
+            if k in ("config", "config_value"):
+                # Special handling here since we gather them all in one option but actually
+                # need to send them one at a time using --config-value  kv..
+                # Note it can be either config or config_value depending
+                # on click processing order.
+                for config_name in v.keys():
+                    yield "--config-value"
+                    yield to_unicode(config_name)
+                    yield to_unicode(ConfigInput.make_key_name(config_name))
+                continue
             k = k.replace("_", "-")
             v = v if isinstance(v, (list, tuple, set)) else [v]
             for value in v:
diff --git a/metaflow/cli_components/__init__.py b/metaflow/cli_components/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/metaflow/cli_components/dump_cmd.py b/metaflow/cli_components/dump_cmd.py
new file mode 100644
index 00000000000..2038d21d771
--- /dev/null
+++ b/metaflow/cli_components/dump_cmd.py
@@ -0,0 +1,96 @@
+import pickle
+
+from metaflow._vendor import click
+
+from ..cli import echo_always, echo_dev_null
+from ..datastore import TaskDataStoreSet
+from ..exception import CommandException
+
+
+@click.command(
+    help="Get data artifacts of a task or all tasks in a step. "
+    "The format for input-path is either / or "
+    "//."
+)
+@click.argument("input-path")
+@click.option(
+    "--private/--no-private",
+    default=False,
+    show_default=True,
+    help="Show also private attributes.",
+)
+@click.option(
+    "--max-value-size",
+    default=1000,
+    show_default=True,
+    type=int,
+    help="Show only values that are smaller than this number. "
+    "Set to 0 to see only keys.",
+)
+@click.option(
+    "--include",
+    type=str,
+    default="",
+    help="Include only artifacts in the given comma-separated list.",
+)
+@click.option(
+    "--file", type=str, default=None, help="Serialize artifacts in the given file."
+)
+@click.pass_obj
+def dump(obj, input_path, private=None, max_value_size=None, include=None, file=None):
+
+    if obj.is_quiet:
+        echo = echo_dev_null
+    else:
+        echo = echo_always
+
+    output = {}
+    kwargs = {
+        "show_private": private,
+        "max_value_size": max_value_size,
+        "include": {t for t in include.split(",") if t},
+    }
+
+    # Pathspec can either be run_id/step_name or run_id/step_name/task_id.
+    parts = input_path.split("/")
+    if len(parts) == 2:
+        run_id, step_name = parts
+        task_id = None
+    elif len(parts) == 3:
+        run_id, step_name, task_id = parts
+    else:
+        raise CommandException(
+            "input_path should either be run_id/step_name or run_id/step_name/task_id"
+        )
+
+    datastore_set = TaskDataStoreSet(
+        obj.flow_datastore,
+        run_id,
+        steps=[step_name],
+        prefetch_data_artifacts=kwargs.get("include"),
+    )
+    if task_id:
+        ds_list = [datastore_set.get_with_pathspec(input_path)]
+    else:
+        ds_list = list(datastore_set)  # get all tasks
+
+    for ds in ds_list:
+        echo(
+            "Dumping output of run_id=*{run_id}* "
+            "step=*{step}* task_id=*{task_id}*".format(
+                run_id=ds.run_id, step=ds.step_name, task_id=ds.task_id
+            ),
+            fg="magenta",
+        )
+
+        if file is None:
+            echo_always(
+                ds.format(**kwargs), highlight="green", highlight_bold=False, err=False
+            )
+        else:
+            output[ds.pathspec] = ds.to_dict(**kwargs)
+
+    if file is not None:
+        with open(file, "wb") as f:
+            pickle.dump(output, f, protocol=pickle.HIGHEST_PROTOCOL)
+        echo("Artifacts written to *%s*" % file)
diff --git a/metaflow/cli_components/init_cmd.py b/metaflow/cli_components/init_cmd.py
new file mode 100644
index 00000000000..92e18ee9e57
--- /dev/null
+++ b/metaflow/cli_components/init_cmd.py
@@ -0,0 +1,52 @@
+from metaflow._vendor import click
+
+from .. import parameters
+from ..runtime import NativeRuntime
+
+
+@parameters.add_custom_parameters(deploy_mode=False)
+@click.command(help="Internal command to initialize a run.", hidden=True)
+@click.option(
+    "--run-id",
+    default=None,
+    required=True,
+    help="ID for one execution of all steps in the flow.",
+)
+@click.option(
+    "--task-id", default=None, required=True, help="ID for this instance of the step."
+)
+@click.option(
+    "--tag",
+    "tags",
+    multiple=True,
+    default=None,
+    help="Tags for this instance of the step.",
+)
+@click.pass_obj
+def init(obj, run_id=None, task_id=None, tags=None, **kwargs):
+    # init is a separate command instead of an option in 'step'
+    # since we need to capture user-specified parameters with
+    # @add_custom_parameters. Adding custom parameters to 'step'
+    # is not desirable due to the possibility of name clashes between
+    # user-specified parameters and our internal options. Note that
+    # user-specified parameters are often defined as environment
+    # variables.
+
+    obj.metadata.add_sticky_tags(tags=tags)
+
+    runtime = NativeRuntime(
+        obj.flow,
+        obj.graph,
+        obj.flow_datastore,
+        obj.metadata,
+        obj.environment,
+        obj.package,
+        obj.logger,
+        obj.entrypoint,
+        obj.event_logger,
+        obj.monitor,
+        run_id=run_id,
+        skip_decorator_hooks=True,
+    )
+    obj.flow._set_constants(obj.graph, kwargs, obj.config_options)
+    runtime.persist_constants(task_id=task_id)
diff --git a/metaflow/cli_components/run_cmds.py b/metaflow/cli_components/run_cmds.py
new file mode 100644
index 00000000000..d44f97c52ac
--- /dev/null
+++ b/metaflow/cli_components/run_cmds.py
@@ -0,0 +1,388 @@
+import json
+
+from functools import wraps
+
+from metaflow._vendor import click
+
+from .. import decorators, namespace, parameters, tracing
+from ..exception import CommandException
+from ..graph import FlowGraph
+from ..metaflow_current import current
+from ..metaflow_config import DEFAULT_DECOSPECS, FEAT_ALWAYS_UPLOAD_CODE_PACKAGE
+from ..package import MetaflowPackage
+from ..runtime import NativeRuntime
+from ..system import _system_logger
+
+from ..tagging_util import validate_tags
+from ..util import get_latest_run_id, write_latest_run_id
+
+
+def before_run(obj, tags, decospecs):
+    validate_tags(tags)
+
+    # There's a --with option both at the top-level and for the run
+    # subcommand. Why?
+    #
+    # "run --with shoes" looks so much better than "--with shoes run".
+    # This is a very common use case of --with.
+    #
+    # A downside is that we need to have the following decorators handling
+    # in two places in this module and make sure _init_step_decorators
+    # doesn't get called twice.
+
+    # We want the order to be the following:
+    # - run level decospecs
+    # - top level decospecs
+    # - environment decospecs
+    all_decospecs = (
+        list(decospecs or [])
+        + obj.tl_decospecs
+        + list(obj.environment.decospecs() or [])
+    )
+    if all_decospecs:
+        # These decospecs are the ones from run/resume PLUS the ones from the
+        # environment (for example the @conda)
+        decorators._attach_decorators(obj.flow, all_decospecs)
+        decorators._init(obj.flow)
+        # Regenerate graph if we attached more decorators
+        obj.flow.__class__._init_graph()
+        obj.graph = obj.flow._graph
+
+    obj.check(obj.graph, obj.flow, obj.environment, pylint=obj.pylint)
+    # obj.environment.init_environment(obj.logger)
+
+    decorators._init_step_decorators(
+        obj.flow, obj.graph, obj.environment, obj.flow_datastore, obj.logger
+    )
+    # Re-read graph since it may have been modified by mutators
+    obj.graph = obj.flow._graph
+
+    obj.metadata.add_sticky_tags(tags=tags)
+
+    # Package working directory only once per run.
+    # We explicitly avoid doing this in `start` since it is invoked for every
+    # step in the run.
+    obj.package = MetaflowPackage(
+        obj.flow,
+        obj.environment,
+        obj.echo,
+        suffixes=obj.package_suffixes,
+        flow_datastore=obj.flow_datastore if FEAT_ALWAYS_UPLOAD_CODE_PACKAGE else None,
+    )
+
+
+def write_file(file_path, content):
+    if file_path is not None:
+        with open(file_path, "w", encoding="utf-8") as f:
+            f.write(str(content))
+
+
+def config_callback(ctx, param, value):
+    # Callback to:
+    #  - read  the Click auto_envvar variable from both the
+    #    environment AND the configuration
+    #  - merge that value with the value passed in the command line (value)
+    #  - return the value as a tuple
+    # Note that this function gets called even if there is no option passed on the
+    # command line.
+    # NOTE: Assumes that ctx.auto_envvar_prefix is set to METAFLOW (same as in
+    # from_conf)
+
+    # Read decospecs options from the environment (METAFLOW_DEFAULT_DECOSPECS=...)
+    # and merge them with the one provided as --with.
+    splits = DEFAULT_DECOSPECS.split()
+    return tuple(list(value) + splits)
+
+
+def common_run_options(func):
+    @click.option(
+        "--tag",
+        "tags",
+        multiple=True,
+        default=None,
+        help="Annotate this run with the given tag. You can specify "
+        "this option multiple times to attach multiple tags in "
+        "the run.",
+    )
+    @click.option(
+        "--max-workers",
+        default=16,
+        show_default=True,
+        help="Maximum number of parallel processes.",
+    )
+    @click.option(
+        "--max-num-splits",
+        default=100,
+        show_default=True,
+        help="Maximum number of splits allowed in a foreach. This "
+        "is a safety check preventing bugs from triggering "
+        "thousands of steps inadvertently.",
+    )
+    @click.option(
+        "--max-log-size",
+        default=10,
+        show_default=True,
+        help="Maximum size of stdout and stderr captured in "
+        "megabytes. If a step outputs more than this to "
+        "stdout/stderr, its output will be truncated.",
+    )
+    @click.option(
+        "--with",
+        "decospecs",
+        multiple=True,
+        help="Add a decorator to all steps. You can specify this "
+        "option multiple times to attach multiple decorators "
+        "in steps.",
+        callback=config_callback,
+    )
+    @click.option(
+        "--run-id-file",
+        default=None,
+        show_default=True,
+        type=str,
+        help="Write the ID of this run to the file specified.",
+    )
+    @click.option(
+        "--runner-attribute-file",
+        default=None,
+        show_default=True,
+        type=str,
+        help="Write the metadata and pathspec of this run to the file specified. Used internally for Metaflow's Runner API.",
+    )
+    @wraps(func)
+    def wrapper(*args, **kwargs):
+        return func(*args, **kwargs)
+
+    return wrapper
+
+
+@click.option(
+    "--origin-run-id",
+    default=None,
+    help="ID of the run that should be resumed. By default, the "
+    "last run executed locally.",
+)
+@click.option(
+    "--run-id",
+    default=None,
+    help="Run ID for the new run. By default, a new run-id will be generated",
+    hidden=True,
+)
+@click.option(
+    "--clone-only/--no-clone-only",
+    default=False,
+    show_default=True,
+    help="Only clone tasks without continuing execution",
+    hidden=True,
+)
+@click.option(
+    "--reentrant/--no-reentrant",
+    default=False,
+    show_default=True,
+    hidden=True,
+    help="If specified, allows this call to be called in parallel",
+)
+@click.option(
+    "--resume-identifier",
+    default=None,
+    show_default=True,
+    hidden=True,
+    help="If specified, it identifies the task that started this resume call. It is in the form of {step_name}-{task_id}",
+)
+@click.argument("step-to-rerun", required=False)
+@click.command(help="Resume execution of a previous run of this flow.")
+@tracing.cli("cli/resume")
+@common_run_options
+@click.pass_obj
+def resume(
+    obj,
+    tags=None,
+    step_to_rerun=None,
+    origin_run_id=None,
+    run_id=None,
+    clone_only=False,
+    reentrant=False,
+    max_workers=None,
+    max_num_splits=None,
+    max_log_size=None,
+    decospecs=None,
+    run_id_file=None,
+    resume_identifier=None,
+    runner_attribute_file=None,
+):
+    before_run(obj, tags, decospecs)
+
+    if origin_run_id is None:
+        origin_run_id = get_latest_run_id(obj.echo, obj.flow.name)
+        if origin_run_id is None:
+            raise CommandException(
+                "A previous run id was not found. Specify --origin-run-id."
+            )
+
+    if step_to_rerun is None:
+        steps_to_rerun = set()
+    else:
+        # validate step name
+        if step_to_rerun not in obj.graph.nodes:
+            raise CommandException(
+                "invalid step name {0} specified, must be step present in "
+                "current form of execution graph. Valid step names include: {1}".format(
+                    step_to_rerun, ",".join(list(obj.graph.nodes.keys()))
+                )
+            )
+        steps_to_rerun = {step_to_rerun}
+
+    if run_id:
+        # Run-ids that are provided by the metadata service are always integers.
+        # External providers or run-ids (like external schedulers) always need to
+        # be non-integers to avoid any clashes. This condition ensures this.
+        try:
+            int(run_id)
+        except:
+            pass
+        else:
+            raise CommandException("run-id %s cannot be an integer" % run_id)
+
+    runtime = NativeRuntime(
+        obj.flow,
+        obj.graph,
+        obj.flow_datastore,
+        obj.metadata,
+        obj.environment,
+        obj.package,
+        obj.logger,
+        obj.entrypoint,
+        obj.event_logger,
+        obj.monitor,
+        run_id=run_id,
+        clone_run_id=origin_run_id,
+        clone_only=clone_only,
+        reentrant=reentrant,
+        steps_to_rerun=steps_to_rerun,
+        max_workers=max_workers,
+        max_num_splits=max_num_splits,
+        max_log_size=max_log_size * 1024 * 1024,
+        resume_identifier=resume_identifier,
+    )
+    write_file(run_id_file, runtime.run_id)
+    runtime.print_workflow_info()
+
+    runtime.persist_constants()
+
+    if runner_attribute_file:
+        with open(runner_attribute_file, "w", encoding="utf-8") as f:
+            json.dump(
+                {
+                    "run_id": runtime.run_id,
+                    "flow_name": obj.flow.name,
+                    "metadata": obj.metadata.metadata_str(),
+                },
+                f,
+            )
+
+    # We may skip clone-only resume if this is not a resume leader,
+    # and clone is already complete.
+    if runtime.should_skip_clone_only_execution():
+        return
+
+    current._update_env(
+        {
+            "run_id": runtime.run_id,
+        }
+    )
+    _system_logger.log_event(
+        level="info",
+        module="metaflow.resume",
+        name="start",
+        payload={
+            "msg": "Resuming run",
+        },
+    )
+
+    with runtime.run_heartbeat():
+        if clone_only:
+            runtime.clone_original_run()
+        else:
+            runtime.clone_original_run(generate_task_obj=True, verbose=False)
+            runtime.execute()
+
+
+@parameters.add_custom_parameters(deploy_mode=True)
+@click.command(help="Run the workflow locally.")
+@tracing.cli("cli/run")
+@common_run_options
+@click.option(
+    "--namespace",
+    "user_namespace",
+    default=None,
+    help="Change namespace from the default (your username) to "
+    "the specified tag. Note that this option does not alter "
+    "tags assigned to the objects produced by this run, just "
+    "what existing objects are visible in the client API. You "
+    "can enable the global namespace with an empty string."
+    "--namespace=",
+)
+@click.pass_obj
+def run(
+    obj,
+    tags=None,
+    max_workers=None,
+    max_num_splits=None,
+    max_log_size=None,
+    decospecs=None,
+    run_id_file=None,
+    runner_attribute_file=None,
+    user_namespace=None,
+    **kwargs
+):
+    if user_namespace is not None:
+        namespace(user_namespace or None)
+    before_run(obj, tags, decospecs)
+
+    runtime = NativeRuntime(
+        obj.flow,
+        obj.graph,
+        obj.flow_datastore,
+        obj.metadata,
+        obj.environment,
+        obj.package,
+        obj.logger,
+        obj.entrypoint,
+        obj.event_logger,
+        obj.monitor,
+        max_workers=max_workers,
+        max_num_splits=max_num_splits,
+        max_log_size=max_log_size * 1024 * 1024,
+    )
+    write_latest_run_id(obj, runtime.run_id)
+    write_file(run_id_file, runtime.run_id)
+
+    obj.flow._set_constants(obj.graph, kwargs, obj.config_options)
+    current._update_env(
+        {
+            "run_id": runtime.run_id,
+        }
+    )
+    _system_logger.log_event(
+        level="info",
+        module="metaflow.run",
+        name="start",
+        payload={
+            "msg": "Starting run",
+        },
+    )
+
+    runtime.print_workflow_info()
+    runtime.persist_constants()
+    if runner_attribute_file:
+        with open(runner_attribute_file, "w", encoding="utf-8") as f:
+            json.dump(
+                {
+                    "run_id": runtime.run_id,
+                    "flow_name": obj.flow.name,
+                    "metadata": obj.metadata.metadata_str(),
+                },
+                f,
+            )
+    with runtime.run_heartbeat():
+        runtime.execute()
diff --git a/metaflow/cli_components/step_cmd.py b/metaflow/cli_components/step_cmd.py
new file mode 100644
index 00000000000..f4bef099e42
--- /dev/null
+++ b/metaflow/cli_components/step_cmd.py
@@ -0,0 +1,178 @@
+from metaflow._vendor import click
+
+from .. import decorators, namespace
+from ..cli import echo_always, echo_dev_null
+from ..cli_args import cli_args
+from ..exception import CommandException
+from ..task import MetaflowTask
+from ..unbounded_foreach import UBF_CONTROL, UBF_TASK
+from ..util import decompress_list
+import metaflow.tracing as tracing
+
+
+@click.command(help="Internal command to execute a single task.", hidden=True)
+@tracing.cli("cli/step")
+@click.argument("step-name")
+@click.option(
+    "--run-id",
+    default=None,
+    required=True,
+    help="ID for one execution of all steps in the flow.",
+)
+@click.option(
+    "--task-id",
+    default=None,
+    required=True,
+    show_default=True,
+    help="ID for this instance of the step.",
+)
+@click.option(
+    "--input-paths",
+    help="A comma-separated list of pathspecs specifying inputs for this step.",
+)
+@click.option(
+    "--input-paths-filename",
+    type=click.Path(exists=True, readable=True, dir_okay=False, resolve_path=True),
+    help="A filename containing the argument typically passed to `input-paths`",
+    hidden=True,
+)
+@click.option(
+    "--split-index",
+    type=int,
+    default=None,
+    show_default=True,
+    help="Index of this foreach split.",
+)
+@click.option(
+    "--tag",
+    "opt_tag",
+    multiple=True,
+    default=None,
+    help="Annotate this run with the given tag. You can specify "
+    "this option multiple times to attach multiple tags in "
+    "the task.",
+)
+@click.option(
+    "--namespace",
+    "opt_namespace",
+    default=None,
+    help="Change namespace from the default (your username) to the specified tag.",
+)
+@click.option(
+    "--retry-count",
+    default=0,
+    help="How many times we have attempted to run this task.",
+)
+@click.option(
+    "--max-user-code-retries",
+    default=0,
+    help="How many times we should attempt running the user code.",
+)
+@click.option(
+    "--clone-only",
+    default=None,
+    help="Pathspec of the origin task for this task to clone. Do "
+    "not execute anything.",
+)
+@click.option(
+    "--clone-run-id",
+    default=None,
+    help="Run id of the origin flow, if this task is part of a flow being resumed.",
+)
+@click.option(
+    "--ubf-context",
+    default="none",
+    type=click.Choice(["none", UBF_CONTROL, UBF_TASK]),
+    help="Provides additional context if this task is of type unbounded foreach.",
+)
+@click.option(
+    "--num-parallel",
+    default=0,
+    type=int,
+    help="Number of parallel instances of a step. Ignored in local mode (see parallel decorator code).",
+)
+@click.pass_context
+def step(
+    ctx,
+    step_name,
+    opt_tag=None,
+    run_id=None,
+    task_id=None,
+    input_paths=None,
+    input_paths_filename=None,
+    split_index=None,
+    opt_namespace=None,
+    retry_count=None,
+    max_user_code_retries=None,
+    clone_only=None,
+    clone_run_id=None,
+    ubf_context="none",
+    num_parallel=None,
+):
+
+    if ctx.obj.is_quiet:
+        echo = echo_dev_null
+    else:
+        echo = echo_always
+
+    if ubf_context == "none":
+        ubf_context = None
+    if opt_namespace is not None:
+        namespace(opt_namespace or None)
+
+    func = None
+    try:
+        func = getattr(ctx.obj.flow, step_name)
+    except:
+        raise CommandException("Step *%s* doesn't exist." % step_name)
+    if not func.is_step:
+        raise CommandException("Function *%s* is not a step." % step_name)
+    echo("Executing a step, *%s*" % step_name, fg="magenta", bold=False)
+
+    step_kwargs = ctx.params
+    # Remove argument `step_name` from `step_kwargs`.
+    step_kwargs.pop("step_name", None)
+    # Remove `opt_*` prefix from (some) option keys.
+    step_kwargs = dict(
+        [(k[4:], v) if k.startswith("opt_") else (k, v) for k, v in step_kwargs.items()]
+    )
+    cli_args._set_step_kwargs(step_kwargs)
+
+    ctx.obj.metadata.add_sticky_tags(tags=opt_tag)
+    if not input_paths and input_paths_filename:
+        with open(input_paths_filename, mode="r", encoding="utf-8") as f:
+            input_paths = f.read().strip(" \n\"'")
+
+    paths = decompress_list(input_paths) if input_paths else []
+
+    task = MetaflowTask(
+        ctx.obj.flow,
+        ctx.obj.flow_datastore,
+        ctx.obj.metadata,
+        ctx.obj.environment,
+        ctx.obj.echo,
+        ctx.obj.event_logger,
+        ctx.obj.monitor,
+        ubf_context,
+    )
+    if clone_only:
+        task.clone_only(
+            step_name,
+            run_id,
+            task_id,
+            clone_only,
+            retry_count,
+        )
+    else:
+        task.run_step(
+            step_name,
+            run_id,
+            task_id,
+            clone_run_id,
+            paths,
+            split_index,
+            retry_count,
+            max_user_code_retries,
+        )
+
+    echo("Success", fg="green", bold=True, indent=True)
diff --git a/metaflow/cli_components/utils.py b/metaflow/cli_components/utils.py
new file mode 100644
index 00000000000..88c0684c1f4
--- /dev/null
+++ b/metaflow/cli_components/utils.py
@@ -0,0 +1,140 @@
+import importlib
+from metaflow._vendor import click
+from metaflow.extension_support.plugins import get_plugin
+
+
+class LazyPluginCommandCollection(click.CommandCollection):
+    # lazy_source should only point to things that are resolved as CLI plugins.
+    def __init__(self, *args, lazy_sources=None, **kwargs):
+        super().__init__(*args, **kwargs)
+        # lazy_sources is a list of strings in the form
+        # "{plugin_name}" -> "{module-name}.{command-object-name}"
+        self.lazy_sources = lazy_sources or {}
+        self._lazy_loaded = {}
+
+    def invoke(self, ctx):
+        # NOTE: This is copied from MultiCommand.invoke. The change is that we
+        # behave like chain in the sense that we evaluate the subcommand *after*
+        # invoking the base command but we don't chain the commands like self.chain
+        # would otherwise indicate.
+        # The goal of this is to make sure that the first command is properly executed
+        # *first* prior to loading the other subcommands. It's more a lazy_subcommand_load
+        # than a chain.
+        # Look for CHANGE HERE in this code to see where the changes are made.
+        # If click is updated, this may also need to be updated. This version is for
+        # click 7.1.2.
+        def _process_result(value):
+            if self.result_callback is not None:
+                value = ctx.invoke(self.result_callback, value, **ctx.params)
+            return value
+
+        if not ctx.protected_args:
+            # If we are invoked without command the chain flag controls
+            # how this happens.  If we are not in chain mode, the return
+            # value here is the return value of the command.
+            # If however we are in chain mode, the return value is the
+            # return value of the result processor invoked with an empty
+            # list (which means that no subcommand actually was executed).
+            if self.invoke_without_command:
+                # CHANGE HERE: We behave like self.chain = False here
+
+                # if not self.chain:
+                return click.Command.invoke(self, ctx)
+                # with ctx:
+                #    click.Command.invoke(self, ctx)
+                #    return _process_result([])
+
+            ctx.fail("Missing command.")
+
+        # Fetch args back out
+        args = ctx.protected_args + ctx.args
+        ctx.args = []
+        ctx.protected_args = []
+        # CHANGE HERE: Add saved_args so we have access to it in the command to be
+        # able to infer what we are calling next
+        ctx.saved_args = args
+
+        # If we're not in chain mode, we only allow the invocation of a
+        # single command but we also inform the current context about the
+        # name of the command to invoke.
+        # CHANGE HERE: We change this block to do the invoke *before* the resolve_command
+        # Make sure the context is entered so we do not clean up
+        # resources until the result processor has worked.
+        with ctx:
+            ctx.invoked_subcommand = "*" if args else None
+            click.Command.invoke(self, ctx)
+            cmd_name, cmd, args = self.resolve_command(ctx, args)
+            sub_ctx = cmd.make_context(cmd_name, args, parent=ctx)
+            with sub_ctx:
+                return _process_result(sub_ctx.command.invoke(sub_ctx))
+
+        # CHANGE HERE: Removed all the part of chain mode.
+
+    def list_commands(self, ctx):
+        base = super().list_commands(ctx)
+        for source_name, source in self.lazy_sources.items():
+            subgroup = self._lazy_load(source_name, source)
+            base.extend(subgroup.list_commands(ctx))
+        return base
+
+    def get_command(self, ctx, cmd_name):
+        base_cmd = super().get_command(ctx, cmd_name)
+        if base_cmd is not None:
+            return base_cmd
+        for source_name, source in self.lazy_sources.items():
+            subgroup = self._lazy_load(source_name, source)
+            cmd = subgroup.get_command(ctx, cmd_name)
+            if cmd is not None:
+                return cmd
+        return None
+
+    def _lazy_load(self, source_name, source_path):
+        if source_name in self._lazy_loaded:
+            return self._lazy_loaded[source_name]
+        cmd_object = get_plugin("cli", source_path, source_name)
+        if not isinstance(cmd_object, click.Group):
+            raise ValueError(
+                f"Lazy loading of {source_name} failed by returning "
+                "a non-group object"
+            )
+        self._lazy_loaded[source_name] = cmd_object
+        return cmd_object
+
+
+class LazyGroup(click.Group):
+    def __init__(self, *args, lazy_subcommands=None, **kwargs):
+        super().__init__(*args, **kwargs)
+        # lazy_subcommands is a list of strings in the form
+        # "{command} -> "{module-name}.{command-object-name}"
+        self.lazy_subcommands = lazy_subcommands or {}
+        self._lazy_loaded = {}
+
+    def list_commands(self, ctx):
+        base = super().list_commands(ctx)
+        lazy = sorted(self.lazy_subcommands.keys())
+        return base + lazy
+
+    def get_command(self, ctx, cmd_name):
+        if cmd_name in self.lazy_subcommands:
+            return self._lazy_load(cmd_name)
+        return super().get_command(ctx, cmd_name)
+
+    def _lazy_load(self, cmd_name):
+        if cmd_name in self._lazy_loaded:
+            return self._lazy_loaded[cmd_name]
+
+        import_path = self.lazy_subcommands[cmd_name]
+        modname, cmd = import_path.rsplit(".", 1)
+        # do the import
+        mod = importlib.import_module(modname)
+        # get the Command object from that module
+        cmd_object = getattr(mod, cmd)
+        # check the result to make debugging easier. note that wrapped BaseCommand
+        # can be functions
+        if not isinstance(cmd_object, click.BaseCommand):
+            raise ValueError(
+                f"Lazy loading of {import_path} failed by returning "
+                f"a non-command object {type(cmd_object)}"
+            )
+        self._lazy_loaded[cmd_name] = cmd_object
+        return cmd_object
diff --git a/metaflow/client/core.py b/metaflow/client/core.py
index 3900b60a7f1..7c77467256c 100644
--- a/metaflow/client/core.py
+++ b/metaflow/client/core.py
@@ -5,11 +5,23 @@
 import tarfile
 from collections import namedtuple
 from datetime import datetime
+from tempfile import TemporaryDirectory
 from io import BytesIO
 from itertools import chain
-from typing import Any, Dict, FrozenSet, Iterable, List, Optional, Tuple
+from typing import (
+    Any,
+    Dict,
+    FrozenSet,
+    Iterable,
+    Iterator,
+    List,
+    NamedTuple,
+    Optional,
+    TYPE_CHECKING,
+    Tuple,
+)
 
-from metaflow.current import current
+from metaflow.metaflow_current import current
 from metaflow.events import Trigger
 from metaflow.exception import (
     MetaflowInternalError,
@@ -20,13 +32,17 @@
 from metaflow.includefile import IncludedFile
 from metaflow.metaflow_config import DEFAULT_METADATA, MAX_ATTEMPTS
 from metaflow.metaflow_environment import MetaflowEnvironment
+from metaflow.package import MetaflowPackage
+from metaflow.packaging_sys import ContentType
 from metaflow.plugins import ENVIRONMENTS, METADATA_PROVIDERS
 from metaflow.unbounded_foreach import CONTROL_TASK_TAG
 from metaflow.util import cached_property, is_stringish, resolve_identity, to_unicode
 
-from .. import INFO_FILE
 from .filecache import FileCache
 
+if TYPE_CHECKING:
+    from metaflow.metadata_provider import MetadataProvider
+
 try:
     # python2
     import cPickle as pickle
@@ -72,28 +88,16 @@ def metadata(ms: str) -> str:
         get_metadata()).
     """
     global current_metadata
-    infos = ms.split("@", 1)
-    types = [m.TYPE for m in METADATA_PROVIDERS]
-    if infos[0] in types:
-        current_metadata = [m for m in METADATA_PROVIDERS if m.TYPE == infos[0]][0]
-        if len(infos) > 1:
-            current_metadata.INFO = infos[1]
-    else:
-        # Deduce from ms; if starts with http, use service or else use local
-        if ms.startswith("http"):
-            metadata_type = "service"
-        else:
-            metadata_type = "local"
-        res = [m for m in METADATA_PROVIDERS if m.TYPE == metadata_type]
-        if not res:
-            print(
-                "Cannot find a '%s' metadata provider -- "
-                "try specifying one explicitly using @",
-                metadata_type,
-            )
-            return get_metadata()
-        current_metadata = res[0]
-        current_metadata.INFO = ms
+    provider, info = _metadata(ms)
+    if provider is None:
+        print(
+            "Cannot find a metadata provider -- "
+            "try specifying one explicitly using @",
+        )
+        return get_metadata()
+    current_metadata = provider
+    if info:
+        current_metadata.INFO = info
     return get_metadata()
 
 
@@ -117,7 +121,7 @@ def get_metadata() -> str:
     """
     if current_metadata is False:
         default_metadata()
-    return "%s@%s" % (current_metadata.TYPE, current_metadata.INFO)
+    return current_metadata.metadata_str()
 
 
 def default_metadata() -> str:
@@ -141,7 +145,7 @@ def default_metadata() -> str:
     if default:
         current_metadata = default[0]
     else:
-        from metaflow.plugins.metadata import LocalMetadataProvider
+        from metaflow.plugins.metadata_providers import LocalMetadataProvider
 
         current_metadata = LocalMetadataProvider
     return get_metadata()
@@ -203,6 +207,9 @@ def default_namespace() -> str:
     return get_namespace()
 
 
+MetaflowArtifacts = NamedTuple
+
+
 class MetaflowObject(object):
     """
     Base class for all Metaflow objects.
@@ -255,12 +262,26 @@ def __init__(
         _object: Optional["MetaflowObject"] = None,
         _parent: Optional["MetaflowObject"] = None,
         _namespace_check: bool = True,
+        _metaflow: Optional["Metaflow"] = None,
+        _current_namespace: Optional[str] = None,
+        _current_metadata: Optional[str] = None,
     ):
-        self._metaflow = Metaflow()
+        # the default namespace is activated lazily at the first
+        # get_namespace(). The other option of activating
+        # the namespace at the import time is problematic, since there
+        # may be other modules that alter environment variables etc.
+        # which may affect the namespace setting.
+        self._metaflow = Metaflow(_current_metadata) or _metaflow
         self._parent = _parent
         self._path_components = None
         self._attempt = attempt
+        self._current_namespace = _current_namespace or get_namespace()
         self._namespace_check = _namespace_check
+        # If the current namespace is False, we disable checking for namespace for this
+        # and all children objects. Not setting namespace_check to False has the consequence
+        # of preventing access to children objects after the namespace changes
+        if self._current_namespace is None:
+            self._namespace_check = False
 
         if self._attempt is not None:
             if self._NAME not in ["task", "artifact"]:
@@ -326,8 +347,8 @@ def __init__(
         self._user_tags = frozenset(self._object.get("tags") or [])
         self._system_tags = frozenset(self._object.get("system_tags") or [])
 
-        if self._namespace_check and not self.is_in_namespace():
-            raise MetaflowNamespaceMismatch(current_namespace)
+        if self._namespace_check and not self._is_in_namespace(self._current_namespace):
+            raise MetaflowNamespaceMismatch(self._current_namespace)
 
     def _get_object(self, *path_components):
         result = self._metaflow.metadata.get_object(
@@ -337,30 +358,30 @@ def _get_object(self, *path_components):
             raise MetaflowNotFound("%s does not exist" % self)
         return result
 
-    def __iter__(self) -> Iterable["MetaflowObject"]:
+    def __iter__(self) -> Iterator["MetaflowObject"]:
         """
         Iterate over all child objects of this object if any.
 
-        Note that only children present in the current namespace are returned iff
-        _namespace_check is set.
+        Note that only children present in the current namespace are returned if and
+        only if _namespace_check is set.
 
-        Returns
-        -------
-        Iterable[MetaflowObject]
-            Iterator over all children
+        Yields
+        ------
+        MetaflowObject
+            Children of this object
         """
         query_filter = {}
 
         # skip namespace filtering if _namespace_check is unset.
-        if self._namespace_check and current_namespace:
-            query_filter = {"any_tags": current_namespace}
+        if self._namespace_check and self._current_namespace:
+            query_filter = {"any_tags": self._current_namespace}
 
         unfiltered_children = self._metaflow.metadata.get_object(
             self._NAME,
             _CLASSES[self._CHILD_CLASS]._NAME,
             query_filter,
             self._attempt,
-            *self.path_components
+            *self.path_components,
         )
         unfiltered_children = unfiltered_children if unfiltered_children else []
         children = filter(
@@ -370,7 +391,11 @@ def __iter__(self) -> Iterable["MetaflowObject"]:
                     attempt=self._attempt,
                     _object=obj,
                     _parent=self,
-                    _namespace_check=False,
+                    _metaflow=self._metaflow,
+                    _namespace_check=self._namespace_check,
+                    _current_namespace=(
+                        self._current_namespace if self._namespace_check else None
+                    ),
                 )
                 for obj in unfiltered_children
             ),
@@ -409,6 +434,23 @@ def is_in_namespace(self) -> bool:
 
         If the current namespace is None, this will always return True.
 
+        Returns
+        -------
+        bool
+            Whether or not the object is in the current namespace
+        """
+        return self._is_in_namespace(current_namespace)
+
+    def _is_in_namespace(self, ns: str) -> bool:
+        """
+        Returns whether this object is in namespace passed in.
+
+        If the current namespace is None, this will always return True.
+
+        Parameters
+        ----------
+        ns : str
+            Namespace to check if the object is in.
         Returns
         -------
         bool
@@ -417,7 +459,7 @@ def is_in_namespace(self) -> bool:
         if self._NAME == "flow":
             return any(True for _ in self)
         else:
-            return current_namespace is None or current_namespace in self._tags
+            return ns is None or ns in self._tags
 
     def __str__(self):
         if self._attempt is not None:
@@ -465,7 +507,11 @@ def __getitem__(self, id: str) -> "MetaflowObject":
                 attempt=self._attempt,
                 _object=obj,
                 _parent=self,
+                _metaflow=self._metaflow,
                 _namespace_check=self._namespace_check,
+                _current_namespace=(
+                    self._current_namespace if self._namespace_check else None
+                ),
             )
         else:
             raise KeyError(id)
@@ -496,7 +542,38 @@ def _unpickle_284(self, data):
             pathspec=pathspec, attempt=attempt, _namespace_check=namespace_check
         )
 
-    _UNPICKLE_FUNC = {"2.8.4": _unpickle_284}
+    def _unpickle_2124(self, data):
+        if len(data) != 4:
+            raise MetaflowInternalError(
+                "Unexpected size of array: {}".format(len(data))
+            )
+        pathspec, attempt, ns, namespace_check = data
+        self.__init__(
+            pathspec=pathspec,
+            attempt=attempt,
+            _namespace_check=namespace_check,
+            _current_namespace=ns,
+        )
+
+    def _unpickle_21227(self, data):
+        if len(data) != 5:
+            raise MetaflowInternalError(
+                "Unexpected size of array: {}".format(len(data))
+            )
+        pathspec, attempt, md, ns, namespace_check = data
+        self.__init__(
+            pathspec=pathspec,
+            attempt=attempt,
+            _namespace_check=namespace_check,
+            _current_metadata=md,
+            _current_namespace=ns,
+        )
+
+    _UNPICKLE_FUNC = {
+        "2.8.4": _unpickle_284,
+        "2.12.4": _unpickle_2124,
+        "2.12.27": _unpickle_21227,
+    }
 
     def __setstate__(self, state):
         """
@@ -516,10 +593,13 @@ def __setstate__(self, state):
             self._UNPICKLE_FUNC[version](self, state["data"])
         else:
             # For backward compatibility: handles pickled objects that were serialized without a __getstate__ override
+            # We set namespace_check to False if it doesn't exist so that the user can
+            # continue accessing this object once unpickled.
             self.__init__(
                 pathspec=state.get("_pathspec", None),
                 attempt=state.get("_attempt", None),
-                _namespace_check=state.get("_namespace_check", True),
+                _namespace_check=state.get("_namespace_check", False),
+                _current_namespace=None,
             )
 
     def __getstate__(self):
@@ -531,11 +611,17 @@ def __getstate__(self):
         from this object) are pickled (serialized) in a later version of Metaflow, it may not be possible
         to unpickle (deserialize) them in a previous version of Metaflow.
         """
+        # Note that we now record the namespace at the time of the object creation so
+        # we don't need to force namespace_check to be False and can properly continue
+        # checking for the namespace even after unpickling since we will know which
+        # namespace to check.
         return {
-            "version": "2.8.4",
+            "version": "2.12.27",
             "data": [
                 self.pathspec,
                 self._attempt,
+                self._metaflow.metadata.metadata_str(),
+                self._current_namespace,
                 self._namespace_check,
             ],
         }
@@ -606,7 +692,7 @@ def origin_pathspec(self) -> Optional[str]:
         origin_pathspec = None
         if self._NAME == "run":
             latest_step = next(self.steps())
-            if latest_step:
+            if latest_step and latest_step.task:
                 # If we had a step
                 task = latest_step.task
                 origin_run_id = [
@@ -697,50 +783,6 @@ def path_components(self) -> List[str]:
         return list(self._path_components)
 
 
-class MetaflowData(object):
-    """
-    Container of data artifacts produced by a `Task`. This object is
-    instantiated through `Task.data`.
-
-    `MetaflowData` allows results to be retrieved by their name
-    through a convenient dot notation:
-
-    ```python
-    Task(...).data.my_object
-    ```
-
-    You can also test the existence of an object
-
-    ```python
-    if 'my_object' in Task(...).data:
-        print('my_object found')
-    ```
-
-    Note that this container relies on the local cache to load all data
-    artifacts. If your `Task` contains a lot of data, a more efficient
-    approach is to load artifacts individually like so
-
-    ```
-    Task(...)['my_object'].data
-    ```
-    """
-
-    def __init__(self, artifacts: Iterable["DataArtifact"]):
-        self._artifacts = dict((art.id, art) for art in artifacts)
-
-    def __getattr__(self, name: str):
-        return self._artifacts[name].data
-
-    def __contains__(self, var):
-        return var in self._artifacts
-
-    def __str__(self):
-        return "" % ", ".join(self._artifacts)
-
-    def __repr__(self):
-        return str(self)
-
-
 class MetaflowCode(object):
     """
     Snapshot of the code used to execute this `Run`. Instantiate the object through
@@ -775,20 +817,26 @@ def __init__(self, flow_name: str, code_package: str):
         self._path = info["location"]
         self._ds_type = info["ds_type"]
         self._sha = info["sha"]
+        self._code_metadata = info.get(
+            "metadata",
+            '{"version": 0, "archive_format": "tgz", "mfcontent_version": 0}',
+        )
+
+        self._backend = MetaflowPackage.get_backend(self._code_metadata)
 
         if filecache is None:
             filecache = FileCache()
         _, blobdata = filecache.get_data(
             self._ds_type, self._flow_name, self._path, self._sha
         )
-        code_obj = BytesIO(blobdata)
-        self._tar = tarfile.open(fileobj=code_obj, mode="r:gz")
-        # The JSON module in Python3 deals with Unicode. Tar gives bytes.
-        info_str = (
-            self._tar.extractfile(os.path.basename(INFO_FILE)).read().decode("utf-8")
-        )
-        self._info = json.loads(info_str)
-        self._flowspec = self._tar.extractfile(self._info["script"]).read()
+        self._code_obj = BytesIO(blobdata)
+        self._info = MetaflowPackage.cls_get_info(self._code_metadata, self._code_obj)
+        if self._info:
+            self._flowspec = MetaflowPackage.cls_get_content(
+                self._code_metadata, self._code_obj, self._info["script"]
+            )
+        else:
+            raise MetaflowInternalError("Code package metadata is invalid.")
 
     @property
     def path(self) -> str:
@@ -836,7 +884,59 @@ def tarball(self) -> tarfile.TarFile:
         TarFile
             TarFile for everything in this code package
         """
-        return self._tar
+        if self._backend.type == "tgz":
+            return self._backend.cls_open(self._code_obj)
+        raise RuntimeError("Archive is not a tarball")
+
+    def extract(self) -> TemporaryDirectory:
+        """
+        Extracts the code package to a temporary directory.
+
+        This creates a temporary directory containing all user code
+        files from the code package. The temporary directory is
+        automatically deleted when the returned TemporaryDirectory
+        object is garbage collected or when its cleanup() is called.
+
+        To preserve the contents to a permanent location, use
+        os.replace() which performs a zero-copy move on the same
+        filesystem:
+
+        ```python
+        with task.code.extract() as tmp_dir:
+            # Move contents to permanent location
+            for item in os.listdir(tmp_dir):
+                src = os.path.join(tmp_dir, item)
+                dst = os.path.join('/path/to/permanent/dir', item)
+                os.makedirs(os.path.dirname(dst), exist_ok=True)
+                os.replace(src, dst)  # Atomic move operation
+        ```
+        Returns
+        -------
+        TemporaryDirectory
+            A temporary directory containing the extracted code files.
+            The directory and its contents are automatically deleted when
+            this object is garbage collected.
+        """
+        tmp = TemporaryDirectory()
+        MetaflowPackage.cls_extract_into(
+            self._code_metadata, self._code_obj, tmp.name, ContentType.USER_CONTENT
+        )
+        return tmp
+
+    @property
+    def script_name(self) -> str:
+        """
+        Returns the filename of the Python script containing the FlowSpec.
+
+        This is the main Python file that was used to execute the flow. For example,
+        if your flow is defined in 'myflow.py', this property will return 'myflow.py'.
+
+        Returns
+        -------
+        str
+            Name of the Python file containing the FlowSpec
+        """
+        return self._info["script"]
 
     def __str__(self):
         return "" % self._info["script"]
@@ -975,6 +1075,52 @@ def __setstate__(self, state):
         super(DataArtifact, self).__setstate__(state)
 
 
+class MetaflowData(object):
+    """
+    Container of data artifacts produced by a `Task`. This object is
+    instantiated through `Task.data`.
+
+    `MetaflowData` allows results to be retrieved by their name
+    through a convenient dot notation:
+
+    ```python
+    Task(...).data.my_object
+    ```
+
+    You can also test the existence of an object
+
+    ```python
+    if 'my_object' in Task(...).data:
+        print('my_object found')
+    ```
+
+    Note that this container relies on the local cache to load all data
+    artifacts. If your `Task` contains a lot of data, a more efficient
+    approach is to load artifacts individually like so
+
+    ```
+    Task(...)['my_object'].data
+    ```
+    """
+
+    def __init__(self, artifacts: Iterable[DataArtifact]):
+        self._artifacts = dict((art.id, art) for art in artifacts)
+
+    def __getattr__(self, name: str):
+        if name not in self._artifacts:
+            raise AttributeError(name)
+        return self._artifacts[name].data
+
+    def __contains__(self, var):
+        return var in self._artifacts
+
+    def __str__(self):
+        return "" % ", ".join(self._artifacts)
+
+    def __repr__(self):
+        return str(self)
+
+
 class Task(MetaflowObject):
     """
     A `Task` represents an execution of a `Step`.
@@ -1037,6 +1183,143 @@ def _iter_filter(self, x):
         # exclude private data artifacts
         return x.id[0] != "_"
 
+    def _iter_matching_tasks(self, steps, metadata_key, metadata_pattern):
+        """
+        Yield tasks from specified steps matching a foreach path pattern.
+
+        Parameters
+        ----------
+        steps : List[str]
+            List of step names to search for tasks
+        pattern : str
+            Regex pattern to match foreach-indices metadata
+
+        Returns
+        -------
+        Iterator[Task]
+            Tasks matching the foreach path pattern
+        """
+        flow_id, run_id, _, _ = self.path_components
+
+        for step in steps:
+            task_pathspecs = self._metaflow.metadata.filter_tasks_by_metadata(
+                flow_id, run_id, step.id, metadata_key, metadata_pattern
+            )
+            for task_pathspec in task_pathspecs:
+                yield Task(pathspec=task_pathspec, _namespace_check=False)
+
+    @property
+    def parent_tasks(self) -> Iterator["Task"]:
+        """
+        Yields all parent tasks of the current task if one exists.
+
+        Yields
+        ------
+        Task
+            Parent task of the current task
+
+        """
+        flow_id, run_id, _, _ = self.path_components
+
+        steps = list(self.parent.parent_steps)
+        if not steps:
+            return []
+
+        current_path = self.metadata_dict.get("foreach-execution-path", "")
+
+        if len(steps) > 1:
+            # Static join - use exact path matching
+            pattern = current_path or ".*"
+            yield from self._iter_matching_tasks(
+                steps, "foreach-execution-path", pattern
+            )
+            return
+
+        # Handle single step case
+        target_task = Step(
+            f"{flow_id}/{run_id}/{steps[0].id}", _namespace_check=False
+        ).task
+        target_path = target_task.metadata_dict.get("foreach-execution-path")
+
+        if not target_path or not current_path:
+            # (Current task, "A:10") and (Parent task, "")
+            # Pattern: ".*"
+            pattern = ".*"
+        else:
+            current_depth = len(current_path.split(","))
+            target_depth = len(target_path.split(","))
+
+            if current_depth < target_depth:
+                # Foreach join
+                # (Current task, "A:10,B:13") and (Parent task, "A:10,B:13,C:21")
+                # Pattern: "A:10,B:13,.*"
+                pattern = f"{current_path},.*"
+            else:
+                # Foreach split or linear step
+                # Option 1:
+                # (Current task, "A:10,B:13,C:21") and (Parent task, "A:10,B:13")
+                # Option 2:
+                # (Current task, "A:10,B:13") and (Parent task, "A:10,B:13")
+                # Pattern: "A:10,B:13"
+                pattern = ",".join(current_path.split(",")[:target_depth])
+
+        yield from self._iter_matching_tasks(steps, "foreach-execution-path", pattern)
+
+    @property
+    def child_tasks(self) -> Iterator["Task"]:
+        """
+        Yield all child tasks of the current task if one exists.
+
+        Yields
+        ------
+        Task
+            Child task of the current task
+        """
+        flow_id, run_id, _, _ = self.path_components
+        steps = list(self.parent.child_steps)
+        if not steps:
+            return []
+
+        current_path = self.metadata_dict.get("foreach-execution-path", "")
+
+        if len(steps) > 1:
+            # Static split - use exact path matching
+            pattern = current_path or ".*"
+            yield from self._iter_matching_tasks(
+                steps, "foreach-execution-path", pattern
+            )
+            return
+
+        # Handle single step case
+        target_task = Step(
+            f"{flow_id}/{run_id}/{steps[0].id}", _namespace_check=False
+        ).task
+        target_path = target_task.metadata_dict.get("foreach-execution-path")
+
+        if not target_path or not current_path:
+            # (Current task, "A:10") and (Child task, "")
+            # Pattern: ".*"
+            pattern = ".*"
+        else:
+            current_depth = len(current_path.split(","))
+            target_depth = len(target_path.split(","))
+
+            if current_depth < target_depth:
+                # Foreach split
+                # (Current task, "A:10,B:13") and (Child task, "A:10,B:13,C:21")
+                # Pattern: "A:10,B:13,.*"
+                pattern = f"{current_path},.*"
+            else:
+                # Foreach join or linear step
+                # Option 1:
+                # (Current task, "A:10,B:13,C:21") and (Child task, "A:10,B:13")
+                # Option 2:
+                # (Current task, "A:10,B:13") and (Child task, "A:10,B:13")
+                # Pattern: "A:10,B:13"
+                pattern = ",".join(current_path.split(",")[:target_depth])
+
+        yield from self._iter_matching_tasks(steps, "foreach-execution-path", pattern)
+
     @property
     def metadata(self) -> List[Metadata]:
         """
@@ -1166,7 +1449,7 @@ def data(self) -> MetaflowData:
         return MetaflowData(self)
 
     @property
-    def artifacts(self) -> "MetaflowArtifacts":
+    def artifacts(self) -> MetaflowArtifacts:
         """
         Returns a container of DataArtifacts produced by this task.
 
@@ -1432,8 +1715,11 @@ def _get_logsize(self, stream):
             return self._log_size(stream, meta_dict)
 
     def loglines(
-        self, stream: str, as_unicode: bool = True, meta_dict: Dict[str, Any] = None
-    ) -> Iterable[Tuple[datetime, str]]:
+        self,
+        stream: str,
+        as_unicode: bool = True,
+        meta_dict: Optional[Dict[str, Any]] = None,
+    ) -> Iterator[Tuple[datetime, str]]:
         """
         Return an iterator over (utc_timestamp, logline) tuples.
 
@@ -1445,10 +1731,10 @@ def loglines(
             If as_unicode=False, each logline is returned as a byte object. Otherwise,
             it is returned as a (unicode) string.
 
-        Returns
-        -------
-        Iterable[(datetime, str)]
-            Iterator over timestamp, logline pairs.
+        Yields
+        ------
+        Tuple[datetime, str]
+            Tuple of timestamp, logline pairs.
         """
         from metaflow.mflog.mflog import merge_logs
 
@@ -1519,6 +1805,39 @@ def _log_size(self, stream, meta_dict):
             ds_type, ds_root, stream, attempt, *self.path_components
         )
 
+    def __iter__(self) -> Iterator[DataArtifact]:
+        """
+        Iterate over all children DataArtifact of this Task
+
+        Yields
+        ------
+        DataArtifact
+            A DataArtifact in this Step
+        """
+        for d in super(Task, self).__iter__():
+            yield d
+
+    def __getitem__(self, name: str) -> DataArtifact:
+        """
+        Returns the DataArtifact object with the artifact name 'name'
+
+        Parameters
+        ----------
+        name : str
+            Data artifact name
+
+        Returns
+        -------
+        DataArtifact
+            DataArtifact for this artifact name in this task
+
+        Raises
+        ------
+        KeyError
+            If the name does not identify a valid DataArtifact object
+        """
+        return super(Task, self).__getitem__(name)
+
     def __getstate__(self):
         return super(Task, self).__getstate__()
 
@@ -1579,10 +1898,10 @@ def tasks(self, *tags: str) -> Iterable[Task]:
         tags : str
             No op (legacy functionality)
 
-        Returns
-        -------
-        Iterable[Task]
-            Iterator over all `Task` objects in this step.
+        Yields
+        ------
+        Task
+            `Task` objects in this step.
         """
         return self._filtered_children(*tags)
 
@@ -1601,7 +1920,7 @@ def control_task(self) -> Optional[Task]:
         """
         return next(self.control_tasks(), None)
 
-    def control_tasks(self, *tags: str) -> Iterable[Task]:
+    def control_tasks(self, *tags: str) -> Iterator[Task]:
         """
         [Unpublished API - use with caution!]
 
@@ -1613,10 +1932,11 @@ def control_tasks(self, *tags: str) -> Iterable[Task]:
         ----------
         tags : str
             Tags to match
-        Returns
-        -------
-        Iterable[Task]
-            Iterator over Control Task objects in this step
+
+        Yields
+        ------
+        Task
+            Control Task objects for this step
         """
         children = super(Step, self).__iter__()
         for child in children:
@@ -1637,11 +1957,39 @@ def control_tasks(self, *tags: str) -> Iterable[Task]:
                     ):
                         yield child
 
-    def __iter__(self):
-        children = super(Step, self).__iter__()
-        for t in children:
+    def __iter__(self) -> Iterator[Task]:
+        """
+        Iterate over all children Task of this Step
+
+        Yields
+        ------
+        Task
+            A Task in this Step
+        """
+        for t in super(Step, self).__iter__():
             yield t
 
+    def __getitem__(self, task_id: str) -> Task:
+        """
+        Returns the Task object with the task ID 'task_id'
+
+        Parameters
+        ----------
+        task_id : str
+            Task ID
+
+        Returns
+        -------
+        Task
+            Task for this task ID in this Step
+
+        Raises
+        ------
+        KeyError
+            If the task_id does not identify a valid Task object
+        """
+        return super(Step, self).__getitem__(task_id)
+
     def __getstate__(self):
         return super(Step, self).__getstate__()
 
@@ -1686,6 +2034,41 @@ def environment_info(self) -> Optional[Dict[str, Any]]:
         for t in self:
             return t.environment_info
 
+    @property
+    def parent_steps(self) -> Iterator["Step"]:
+        """
+        Yields parent steps for the current step.
+
+        Yields
+        ------
+        Step
+            Parent step
+        """
+        graph_info = self.task["_graph_info"].data
+
+        if self.id != "start":
+            flow, run, _ = self.path_components
+            for node_name, attributes in graph_info["steps"].items():
+                if self.id in attributes["next"]:
+                    yield Step(f"{flow}/{run}/{node_name}", _namespace_check=False)
+
+    @property
+    def child_steps(self) -> Iterator["Step"]:
+        """
+        Yields child steps for the current step.
+
+        Yields
+        ------
+        Step
+            Child step
+        """
+        graph_info = self.task["_graph_info"].data
+
+        if self.id != "end":
+            flow, run, _ = self.path_components
+            for next_step in graph_info["steps"][self.id]["next"]:
+                yield Step(f"{flow}/{run}/{next_step}", _namespace_check=False)
+
 
 class Run(MetaflowObject):
     """
@@ -1703,6 +2086,8 @@ class Run(MetaflowObject):
         Time this run finished.
     code : MetaflowCode
         Code package for this run (if present). See `MetaflowCode`.
+    trigger : MetaflowTrigger
+        Information about event(s) that triggered this run (if present). See `MetaflowTrigger`.
     end_task : Task
         `Task` for the end step (if it is present already).
     """
@@ -1715,7 +2100,7 @@ def _iter_filter(self, x):
         # exclude _parameters step
         return x.id[0] != "_"
 
-    def steps(self, *tags: str) -> Iterable[Step]:
+    def steps(self, *tags: str) -> Iterator[Step]:
         """
         [Legacy function - do not use]
 
@@ -1730,10 +2115,10 @@ def steps(self, *tags: str) -> Iterable[Step]:
         tags : str
             No op (legacy functionality)
 
-        Returns
-        -------
-        Iterable[Step]
-            Iterator over `Step` objects in this run.
+        Yields
+        ------
+        Step
+            `Step` objects in this run.
         """
         return self._filtered_children(*tags)
 
@@ -1755,9 +2140,10 @@ def code(self) -> Optional[MetaflowCode]:
         # TODO: A more optimized way of figuring out if a run has remote steps (and thus a codepackage) available.
         # This might require changes to the metadata-service as well.
         for step in self:
-            code = step.task.code
-            if code:
-                return code
+            if step.task:
+                code = step.task.code
+                if code:
+                    return code
 
     @property
     def data(self) -> Optional[MetaflowData]:
@@ -1968,6 +2354,39 @@ def replace_tags(self, tags_to_remove: Iterable[str], tags_to_add: Iterable[str]
         self._user_tags = frozenset(final_user_tags)
         self._tags = frozenset([*self._user_tags, *self._system_tags])
 
+    def __iter__(self) -> Iterator[Step]:
+        """
+        Iterate over all children Step of this Run
+
+        Yields
+        ------
+        Step
+            A Step in this Run
+        """
+        for s in super(Run, self).__iter__():
+            yield s
+
+    def __getitem__(self, name: str) -> Step:
+        """
+        Returns the Step object with the step name 'name'
+
+        Parameters
+        ----------
+        name : str
+            Step name
+
+        Returns
+        -------
+        Step
+            Step for this step name in this Run
+
+        Raises
+        ------
+        KeyError
+            If the name does not identify a valid Step object
+        """
+        return super(Run, self).__getitem__(name)
+
     def __getstate__(self):
         return super(Run, self).__getstate__()
 
@@ -1986,7 +2405,7 @@ def trigger(self) -> Optional[Trigger]:
         Trigger, optional
             Container of triggering events
         """
-        if "start" in self:
+        if "start" in self and self["start"].task:
             meta = self["start"].task.metadata_dict.get("execution-triggers")
             if meta:
                 return Trigger(json.loads(meta))
@@ -2043,7 +2462,7 @@ def latest_successful_run(self) -> Optional[Run]:
             if run.successful:
                 return run
 
-    def runs(self, *tags: str) -> Iterable[Run]:
+    def runs(self, *tags: str) -> Iterator[Run]:
         """
         Returns an iterator over all `Run`s of this flow.
 
@@ -2056,12 +2475,48 @@ def runs(self, *tags: str) -> Iterable[Run]:
         tags : str
             Tags to match.
 
+        Yields
+        ------
+        Run
+            `Run` objects in this flow.
+        """
+        return self._filtered_children(*tags)
+
+    def __iter__(self) -> Iterator[Task]:
+        """
+        Iterate over all children Run of this Flow.
+
+        Note that only runs in the current namespace are returned unless
+        _namespace_check is False
+
+        Yields
+        ------
+        Run
+            A Run in this Flow
+        """
+        for r in super(Flow, self).__iter__():
+            yield r
+
+    def __getitem__(self, run_id: str) -> Run:
+        """
+        Returns the Run object with the run ID 'run_id'
+
+        Parameters
+        ----------
+        run_id : str
+            Run OD
+
         Returns
         -------
-        Iterable[Run]
-            Iterator over `Run` objects in this flow.
+        Run
+            Run for this run ID in this Flow
+
+        Raises
+        ------
+        KeyError
+            If the run_id does not identify a valid Run object
         """
-        return self._filtered_children(*tags)
+        return super(Flow, self).__getitem__(run_id)
 
     def __getstate__(self):
         return super(Flow, self).__getstate__()
@@ -2085,17 +2540,16 @@ class Metaflow(object):
         if it has at least one run in the namespace.
     """
 
-    def __init__(self):
-        # the default namespace is activated lazily at the first object
-        # invocation or get_namespace(). The other option of activating
-        # the namespace at the import time is problematic, since there
-        # may be other modules that alter environment variables etc.
-        # which may affect the namescape setting.
-        if current_namespace is False:
-            default_namespace()
-        if current_metadata is False:
-            default_metadata()
-        self.metadata = current_metadata
+    def __init__(self, _current_metadata: Optional[str] = None):
+        if _current_metadata:
+            provider, info = _metadata(_current_metadata)
+            self.metadata = provider
+            if info:
+                self.metadata.INFO = info
+        else:
+            if current_metadata is False:
+                default_metadata()
+            self.metadata = current_metadata
 
     @property
     def flows(self) -> List[Flow]:
@@ -2112,12 +2566,12 @@ def flows(self) -> List[Flow]:
         """
         return list(self)
 
-    def __iter__(self):
+    def __iter__(self) -> Iterator[Flow]:
         """
         Iterator over all flows present.
 
-        Only flows present in the set namespace are returned. A flow is present in a namespace if
-        it has at least one run that is in the namespace.
+        Only flows present in the set namespace are returned. A flow is present in a
+        namespace if it has at least one run that is in the namespace.
 
         Yields
         -------
@@ -2132,15 +2586,15 @@ def __iter__(self):
         all_flows = all_flows if all_flows else []
         for flow in all_flows:
             try:
-                v = Flow(_object=flow)
+                v = Flow(_object=flow, _metaflow=self)
                 yield v
             except MetaflowNamespaceMismatch:
                 continue
 
-    def __str__(self):
+    def __str__(self) -> str:
         return "Metaflow()"
 
-    def __getitem__(self, id: str) -> Flow:
+    def __getitem__(self, name: str) -> Flow:
         """
         Returns a specific flow by name.
 
@@ -2148,15 +2602,34 @@ def __getitem__(self, id: str) -> Flow:
 
         Parameters
         ----------
-        id : string
+        name : str
             Name of the Flow
 
         Returns
         -------
         Flow
-            Flow with the given ID.
+            Flow with the given name.
         """
-        return Flow(id)
+        return Flow(name, _metaflow=self)
+
+
+def _metadata(ms: str) -> Tuple[Optional["MetadataProvider"], Optional[str]]:
+    infos = ms.split("@", 1)
+    types = [m.TYPE for m in METADATA_PROVIDERS]
+    if infos[0] in types:
+        provider = [m for m in METADATA_PROVIDERS if m.TYPE == infos[0]][0]
+        if len(infos) > 1:
+            return provider, infos[1]
+        return provider, None
+    # Deduce from ms; if starts with http, use service or else use local
+    if ms.startswith("http"):
+        metadata_type = "service"
+    else:
+        metadata_type = "local"
+    res = [m for m in METADATA_PROVIDERS if m.TYPE == metadata_type]
+    if not res:
+        return None, None
+    return res[0], ms
 
 
 _CLASSES["flow"] = Flow
diff --git a/metaflow/client/filecache.py b/metaflow/client/filecache.py
index 8c7d945d588..83a38811eff 100644
--- a/metaflow/client/filecache.py
+++ b/metaflow/client/filecache.py
@@ -6,6 +6,8 @@
 from tempfile import NamedTemporaryFile
 from hashlib import sha1
 
+from urllib.parse import urlparse
+
 from metaflow.datastore import FlowDataStore
 from metaflow.datastore.content_addressed_store import BlobCache
 from metaflow.exception import MetaflowException
@@ -83,7 +85,6 @@ def get_logs_stream(
     def get_log_legacy(
         self, ds_type, location, logtype, attempt, flow_name, run_id, step_name, task_id
     ):
-
         ds_cls = self._get_datastore_storage_impl(ds_type)
         ds_root = ds_cls.path_join(*ds_cls.path_split(location)[:-5])
         cache_id = self._flow_ds_id(ds_type, ds_root, flow_name)
@@ -311,12 +312,24 @@ def _index_objects(self):
 
     @staticmethod
     def _flow_ds_id(ds_type, ds_root, flow_name):
-        return ".".join([ds_type, ds_root, flow_name])
+        p = urlparse(ds_root)
+        sanitized_root = (p.netloc + p.path).replace("/", "_")
+        return ".".join([ds_type, sanitized_root, flow_name])
 
     @staticmethod
     def _task_ds_id(ds_type, ds_root, flow_name, run_id, step_name, task_id, attempt):
+        p = urlparse(ds_root)
+        sanitized_root = (p.netloc + p.path).replace("/", "_")
         return ".".join(
-            [ds_type, ds_root, flow_name, run_id, step_name, task_id, str(attempt)]
+            [
+                ds_type,
+                sanitized_root,
+                flow_name,
+                run_id,
+                step_name,
+                task_id,
+                str(attempt),
+            ]
         )
 
     def _garbage_collect(self):
diff --git a/metaflow/clone_util.py b/metaflow/clone_util.py
new file mode 100644
index 00000000000..5b092474333
--- /dev/null
+++ b/metaflow/clone_util.py
@@ -0,0 +1,77 @@
+import time
+from .metadata_provider import MetaDatum
+
+
+def clone_task_helper(
+    flow_name,
+    clone_run_id,
+    run_id,
+    step_name,
+    clone_task_id,
+    task_id,
+    flow_datastore,
+    metadata_service,
+    origin_ds_set=None,
+    attempt_id=0,
+):
+    # 1. initialize output datastore
+    output = flow_datastore.get_task_datastore(
+        run_id, step_name, task_id, attempt=attempt_id, mode="w"
+    )
+    output.init_task()
+
+    origin_run_id, origin_step_name, origin_task_id = (
+        clone_run_id,
+        step_name,
+        clone_task_id,
+    )
+    # 2. initialize origin datastore
+    origin = None
+    if origin_ds_set:
+        origin = origin_ds_set.get_with_pathspec(
+            "{}/{}/{}".format(origin_run_id, origin_step_name, origin_task_id)
+        )
+    else:
+        origin = flow_datastore.get_task_datastore(
+            origin_run_id, origin_step_name, origin_task_id
+        )
+    metadata_tags = ["attempt_id:{0}".format(attempt_id)]
+    output.clone(origin)
+    _ = metadata_service.register_task_id(
+        run_id,
+        step_name,
+        task_id,
+        attempt_id,
+    )
+    metadata_service.register_metadata(
+        run_id,
+        step_name,
+        task_id,
+        [
+            MetaDatum(
+                field="origin-task-id",
+                value=str(origin_task_id),
+                type="origin-task-id",
+                tags=metadata_tags,
+            ),
+            MetaDatum(
+                field="origin-run-id",
+                value=str(origin_run_id),
+                type="origin-run-id",
+                tags=metadata_tags,
+            ),
+            MetaDatum(
+                field="attempt",
+                value=str(attempt_id),
+                type="attempt",
+                tags=metadata_tags,
+            ),
+            MetaDatum(
+                field="attempt_ok",
+                value="True",  # During clone, the task is always considered successful.
+                type="internal_attempt_status",
+                tags=metadata_tags,
+            ),
+        ],
+    )
+    output.done()
diff --git a/metaflow/cmd/code/__init__.py b/metaflow/cmd/code/__init__.py
new file mode 100644
index 00000000000..709b78cd1d7
--- /dev/null
+++ b/metaflow/cmd/code/__init__.py
@@ -0,0 +1,230 @@
+import os
+import shutil
+import sys
+from subprocess import PIPE, CompletedProcess, run
+from tempfile import TemporaryDirectory
+from typing import Any, Callable, List, Mapping, Optional, cast
+
+from metaflow import Run
+from metaflow._vendor import click
+from metaflow.cli import echo_always
+
+
+@click.group()
+def cli():
+    pass
+
+
+@cli.group(help="Access, compare, and manage code associated with Metaflow runs.")
+def code():
+    pass
+
+
+def echo(line: str) -> None:
+    echo_always(line, err=True, fg="magenta")
+
+
+def extract_code_package(runspec: str) -> TemporaryDirectory:
+    try:
+        mf_run = Run(runspec, _namespace_check=False)
+        echo(f"βœ…  Run *{runspec}* found, downloading code..")
+    except Exception as e:
+        echo(f"❌  Run **{runspec}** not found")
+        raise e
+
+    if mf_run.code is None:
+        echo(
+            f"❌  Run **{runspec}** doesn't have a code package. Maybe it's a local run?"
+        )
+        raise RuntimeError("no code package found")
+
+    return mf_run.code.extract()
+
+
+def perform_diff(
+    source_dir: str,
+    target_dir: Optional[str] = None,
+    output: bool = False,
+    **kwargs: Mapping[str, Any],
+) -> Optional[List[str]]:
+    if target_dir is None:
+        target_dir = os.getcwd()
+
+    diffs = []
+    for dirpath, dirnames, filenames in os.walk(source_dir, followlinks=True):
+        for fname in filenames:
+            # NOTE: the paths below need to be set up carefully
+            # for the `patch` command to work. Better not to touch
+            # the directories below. If you must, test that patches
+            # work after your changes.
+            #
+            # target_file is the git repo in the current working directory
+            rel = os.path.relpath(dirpath, source_dir)
+            target_file = os.path.join(rel, fname)
+            # source_file is the run file loaded in a tmp directory
+            source_file = os.path.join(dirpath, fname)
+
+            if sys.stdout.isatty() and not output:
+                color = ["--color"]
+            else:
+                color = ["--no-color"]
+
+            if os.path.exists(os.path.join(target_dir, target_file)):
+                cmd = (
+                    ["git", "diff", "--no-index", "--exit-code"]
+                    + color
+                    + [
+                        target_file,
+                        source_file,
+                    ]
+                )
+                result: CompletedProcess = run(
+                    cmd, text=True, stdout=PIPE, cwd=target_dir
+                )
+                if result.returncode == 0:
+                    if not output:
+                        echo(f"βœ…  {target_file} is identical, skipping")
+                    continue
+
+                if output:
+                    diffs.append(result.stdout)
+                else:
+                    run(["less", "-R"], input=result.stdout, text=True)
+            else:
+                if not output:
+                    echo(f"❗  {target_file} not in the target directory, skipping")
+    return diffs if output else None
+
+
+def run_op(
+    runspec: str, op: Callable[..., Optional[List[str]]], **op_args: Mapping[str, Any]
+) -> Optional[List[str]]:
+    tmp = None
+    try:
+        tmp = extract_code_package(runspec)
+        return op(tmp.name, **op_args)
+    finally:
+        if tmp and os.path.exists(tmp.name):
+            shutil.rmtree(tmp.name)
+
+
+def run_op_diff_runs(
+    source_run_pathspec: str, target_run_pathspec: str, **op_args: Mapping[str, Any]
+) -> Optional[List[str]]:
+    source_tmp = None
+    target_tmp = None
+    try:
+        source_tmp = extract_code_package(source_run_pathspec)
+        target_tmp = extract_code_package(target_run_pathspec)
+        return perform_diff(source_tmp.name, target_tmp.name, **op_args)
+    finally:
+        for d in [source_tmp, target_tmp]:
+            if d and os.path.exists(d.name):
+                shutil.rmtree(d.name)
+
+
+def op_diff(tmpdir: str, **kwargs: Mapping[str, Any]) -> Optional[List[str]]:
+    kwargs_dict = dict(kwargs)
+    target_dir = cast(Optional[str], kwargs_dict.pop("target_dir", None))
+    output: bool = bool(kwargs_dict.pop("output", False))
+    op_args: Mapping[str, Any] = {**kwargs_dict}
+    return perform_diff(tmpdir, target_dir=target_dir, output=output, **op_args)
+
+
+def op_pull(tmpdir: str, dst: str, **op_args: Mapping[str, Any]) -> None:
+    if os.path.exists(dst):
+        echo(f"❌  Directory *{dst}* already exists")
+    else:
+        shutil.move(tmpdir, dst)
+        echo(f"Code downloaded to *{dst}*")
+
+
+def op_patch(tmpdir: str, dst: str, **kwargs: Mapping[str, Any]) -> None:
+    diffs = perform_diff(tmpdir, output=True) or []
+    with open(dst, "w", encoding="utf-8") as f:
+        for out in diffs:
+            out = out.replace(tmpdir, "/.")
+            out = out.replace("+++ b/./", "+++ b/")
+            out = out.replace("--- b/./", "--- b/")
+            out = out.replace("--- a/./", "--- a/")
+            out = out.replace("+++ a/./", "+++ a/")
+            f.write(out)
+    echo(f"Patch saved in *{dst}*")
+    path = run(
+        ["git", "rev-parse", "--show-prefix"], text=True, stdout=PIPE
+    ).stdout.strip()
+    if path:
+        diropt = f" --directory={path.rstrip('/')}"
+    else:
+        diropt = ""
+    echo("Apply the patch by running:")
+    echo_always(
+        f"git apply --verbose{diropt} {dst}", highlight=True, bold=True, err=True
+    )
+
+
+@code.command()
+@click.argument("run_pathspec")
+def diff(run_pathspec: str, **kwargs: Mapping[str, Any]) -> None:
+    """
+    Do a 'git diff' of the current directory and a Metaflow run.
+    """
+    _ = run_op(run_pathspec, op_diff, **kwargs)
+
+
+@code.command()
+@click.argument("source_run_pathspec")
+@click.argument("target_run_pathspec")
+def diff_runs(
+    source_run_pathspec: str, target_run_pathspec: str, **kwargs: Mapping[str, Any]
+) -> None:
+    """
+    Do a 'git diff' between two Metaflow runs.
+    """
+    _ = run_op_diff_runs(source_run_pathspec, target_run_pathspec, **kwargs)
+
+
+@code.command()
+@click.argument("run_pathspec")
+@click.option(
+    "--dir", help="Destination directory (default: {run_pathspec}_code)", default=None
+)
+def pull(
+    run_pathspec: str, dir: Optional[str] = None, **kwargs: Mapping[str, Any]
+) -> None:
+    """
+    Pull the code of a Metaflow run.
+    """
+    if dir is None:
+        dir = run_pathspec.lower().replace("/", "_") + "_code"
+    op_args: Mapping[str, Any] = {**kwargs, "dst": dir}
+    run_op(run_pathspec, op_pull, **op_args)
+
+
+@code.command()
+@click.argument("run_pathspec")
+@click.option(
+    "--file_path",
+    help="Patch file name. If not provided, defaults to a sanitized version of RUN_PATHSPEC "
+    "with slashes replaced by underscores, plus '.patch'.",
+    show_default=False,
+)
+@click.option(
+    "--overwrite", is_flag=True, help="Overwrite the patch file if it exists."
+)
+def patch(
+    run_pathspec: str,
+    file_path: Optional[str] = None,
+    overwrite: bool = False,
+    **kwargs: Mapping[str, Any],
+) -> None:
+    """
+    Create a patch by comparing current dir with a Metaflow run.
+    """
+    if file_path is None:
+        file_path = run_pathspec.lower().replace("/", "_") + ".patch"
+    if os.path.exists(file_path) and not overwrite:
+        echo(f"File *{file_path}* already exists. To overwrite, specify --overwrite.")
+        return
+    op_args: Mapping[str, Any] = {**kwargs, "dst": file_path}
+    run_op(run_pathspec, op_patch, **op_args)
diff --git a/metaflow/cmd/configure_cmd.py b/metaflow/cmd/configure_cmd.py
index 281259e9c3b..d4ef1a5a541 100644
--- a/metaflow/cmd/configure_cmd.py
+++ b/metaflow/cmd/configure_cmd.py
@@ -18,6 +18,7 @@
 METAFLOW_CONFIGURATION_DIR = expanduser(
     os.environ.get("METAFLOW_HOME", "~/.metaflowconfig")
 )
+METAFLOW_PROFILE = os.environ.get("METAFLOW_PROFILE", "")
 
 
 @click.group()
@@ -91,7 +92,9 @@ def persist_env(env_dict, profile):
 
 
 @configure.command(help="Reset configuration to disable cloud access.")
-@click.option("--profile", "-p", default="", help="Optional named profile.")
+@click.option(
+    "--profile", "-p", default=METAFLOW_PROFILE, help="Optional named profile."
+)
 def reset(profile):
     check_for_missing_profile(profile)
     path = get_config_path(profile)
@@ -108,7 +111,9 @@ def reset(profile):
 
 
 @configure.command(help="Show existing configuration.")
-@click.option("--profile", "-p", default="", help="Optional named profile.")
+@click.option(
+    "--profile", "-p", default=METAFLOW_PROFILE, help="Optional named profile."
+)
 def show(profile):
     check_for_missing_profile(profile)
     path = get_config_path(profile)
@@ -129,7 +134,7 @@ def show(profile):
 @click.option(
     "--profile",
     "-p",
-    default="",
+    default=METAFLOW_PROFILE,
     help="Optional named profile whose configuration must be " "exported.",
 )
 @click.argument("output_filename", type=click.Path(resolve_path=True))
@@ -162,7 +167,7 @@ def export(profile, output_filename):
 @click.option(
     "--profile",
     "-p",
-    default="",
+    default=METAFLOW_PROFILE,
     help="Optional named profile to which the configuration must be " "imported into.",
 )
 @click.argument("input_filename", type=click.Path(exists=True, resolve_path=True))
@@ -577,6 +582,63 @@ def check_kubernetes_config(ctx):
         )
 
 
+def configure_argo_events(existing_env):
+    env = {}
+
+    # Argo events service account
+    env["METAFLOW_ARGO_EVENTS_SERVICE_ACCOUNT"] = click.prompt(
+        cyan("[METAFLOW_ARGO_EVENTS_SERVICE_ACCOUNT]")
+        + " Service Account for Argo Events. ",
+        default=existing_env.get("METAFLOW_ARGO_EVENTS_SERVICE_ACCOUNT", ""),
+        show_default=True,
+    )
+
+    # Argo events event bus
+    env["METAFLOW_ARGO_EVENTS_EVENT_BUS"] = click.prompt(
+        cyan("[METAFLOW_ARGO_EVENTS_EVENT_BUS]")
+        + yellow(" (optional)")
+        + " Event Bus for Argo Events.",
+        default=existing_env.get("METAFLOW_ARGO_EVENTS_EVENT_BUS", "default"),
+        show_default=True,
+    )
+
+    # Argo events event source
+    env["METAFLOW_ARGO_EVENTS_EVENT_SOURCE"] = click.prompt(
+        cyan("[METAFLOW_ARGO_EVENTS_EVENT_SOURCE]") + " Event Source for Argo Events.",
+        default=existing_env.get("METAFLOW_ARGO_EVENTS_EVENT_SOURCE", ""),
+        show_default=True,
+    )
+
+    # Argo events event name
+    env["METAFLOW_ARGO_EVENTS_EVENT"] = click.prompt(
+        cyan("[METAFLOW_ARGO_EVENTS_EVENT]") + " Event name for Argo Events.",
+        default=existing_env.get("METAFLOW_ARGO_EVENTS_EVENT", ""),
+        show_default=True,
+    )
+
+    # Argo events webhook url
+    env["METAFLOW_ARGO_EVENTS_WEBHOOK_URL"] = click.prompt(
+        cyan("[METAFLOW_ARGO_EVENTS_WEBHOOK_URL]")
+        + " Publicly accessible URL for Argo Events Webhook.",
+        default=existing_env.get("METAFLOW_ARGO_EVENTS_WEBHOOK_URL", ""),
+        show_default=True,
+    )
+    # Set internal URL for Argo events webhook
+    env["METAFLOW_ARGO_EVENTS_INTERNAL_WEBHOOK_URL"] = click.prompt(
+        cyan("[METAFLOW_ARGO_EVENTS_INTERNAL_WEBHOOK_URL]")
+        + yellow(" (optional)")
+        + " URL for Argo Events Webhook "
+        + "(Accessible only within a Kubernetes cluster).",
+        default=existing_env.get(
+            "METAFLOW_ARGO_EVENTS_INTERNAL_WEBHOOK_URL",
+            env["METAFLOW_ARGO_EVENTS_WEBHOOK_URL"],
+        ),
+        show_default=True,
+    )
+
+    return env
+
+
 def configure_kubernetes(existing_env):
     empty_profile = False
     if not existing_env:
@@ -723,7 +785,6 @@ def verify_gcp_credentials(ctx):
 )
 @click.pass_context
 def azure(ctx, profile):
-
     # Greet the user!
     echo(
         "Welcome to Metaflow! Follow the prompts to configure your installation.\n",
@@ -765,7 +826,6 @@ def azure(ctx, profile):
 )
 @click.pass_context
 def gcp(ctx, profile):
-
     # Greet the user!
     echo(
         "Welcome to Metaflow! Follow the prompts to configure your installation.\n",
@@ -807,7 +867,6 @@ def gcp(ctx, profile):
 )
 @click.pass_context
 def aws(ctx, profile):
-
     # Greet the user!
     echo(
         "Welcome to Metaflow! Follow the prompts to configure your " "installation.\n",
@@ -855,7 +914,6 @@ def aws(ctx, profile):
 )
 @click.pass_context
 def kubernetes(ctx, profile):
-
     check_kubernetes_client(ctx)
 
     # Greet the user!
@@ -903,4 +961,8 @@ def kubernetes(ctx, profile):
     # Configure Kubernetes for compute.
     env.update(configure_kubernetes(existing_env))
 
+    # Configure Argo Workflows Events
+    if click.confirm("\nConfigure support for Argo Workflow Events?"):
+        env.update(configure_argo_events(existing_env))
+
     persist_env({k: v for k, v in env.items() if v}, profile)
diff --git a/metaflow/cmd/develop/__init__.py b/metaflow/cmd/develop/__init__.py
new file mode 100644
index 00000000000..25fad818c98
--- /dev/null
+++ b/metaflow/cmd/develop/__init__.py
@@ -0,0 +1,42 @@
+from typing import Any
+
+from metaflow.cli import echo_dev_null, echo_always
+from metaflow._vendor import click
+
+
+class CommandObj:
+    def __init__(self):
+        pass
+
+
+@click.group()
+@click.pass_context
+def cli(ctx):
+    pass
+
+
+@cli.group(help="Metaflow develop commands")
+@click.option(
+    "--quiet/--no-quiet",
+    show_default=True,
+    default=False,
+    help="Suppress unnecessary messages",
+)
+@click.pass_context
+def develop(
+    ctx: Any,
+    quiet: bool,
+):
+    if quiet:
+        echo = echo_dev_null
+    else:
+        echo = echo_always
+
+    obj = CommandObj()
+    obj.quiet = quiet
+    obj.echo = echo
+    obj.echo_always = echo_always
+    ctx.obj = obj
+
+
+from . import stubs
diff --git a/metaflow/cmd/develop/stub_generator.py b/metaflow/cmd/develop/stub_generator.py
new file mode 100644
index 00000000000..50d9ca29fc3
--- /dev/null
+++ b/metaflow/cmd/develop/stub_generator.py
@@ -0,0 +1,1568 @@
+import functools
+import importlib
+import inspect
+import math
+import os
+import pathlib
+import re
+import time
+import typing
+from datetime import datetime
+from io import StringIO
+from types import ModuleType
+from typing import (
+    Any,
+    Callable,
+    Dict,
+    ForwardRef,
+    Iterable,
+    List,
+    NewType,
+    Optional,
+    Set,
+    Tuple,
+    TypeVar,
+    Union,
+    cast,
+)
+
+from metaflow import FlowSpec, step
+from metaflow.debug import debug
+from metaflow.decorators import Decorator, FlowDecorator
+from metaflow.extension_support import get_aliased_modules
+from metaflow.metaflow_current import Current
+from metaflow.metaflow_version import get_version
+from metaflow.runner.deployer import DeployedFlow, Deployer, TriggeredRun
+from metaflow.runner.deployer_impl import DeployerImpl
+
+TAB = "    "
+METAFLOW_CURRENT_MODULE_NAME = "metaflow.metaflow_current"
+METAFLOW_DEPLOYER_MODULE_NAME = "metaflow.runner.deployer"
+
+param_section_header = re.compile(r"Parameters\s*\n----------\s*\n", flags=re.M)
+return_section_header = re.compile(r"Returns\s*\n-------\s*\n", flags=re.M)
+add_to_current_header = re.compile(
+    r"MF Add To Current\s*\n-----------------\s*\n", flags=re.M
+)
+non_indented_line = re.compile(r"^\S+.*$")
+param_name_type = re.compile(r"^(?P\S+)(?:\s*:\s*(?P.*))?$")
+type_annotations = re.compile(
+    r"(?P.*?)(?P, optional|\(optional\))?(?:, [Dd]efault(?: is | = |: |s to |)\s*(?P.*))?$"
+)
+
+FlowSpecDerived = TypeVar("FlowSpecDerived", bound=FlowSpec)
+
+StepFlag = NewType("StepFlag", bool)
+
+MetaflowStepFunction = Union[
+    Callable[[FlowSpecDerived, StepFlag], None],
+    Callable[[FlowSpecDerived, Any, StepFlag], None],
+]
+
+
+# Object that has start() and end() like a Match object to make the code simpler when
+# we are parsing different sections of doc
+class StartEnd:
+    def __init__(self, start: int, end: int):
+        self._start = start
+        self._end = end
+
+    def start(self):
+        return self._start
+
+    def end(self):
+        return self._end
+
+
+def type_var_to_str(t: TypeVar) -> str:
+    bound_name = None
+    if t.__bound__ is not None:
+        if isinstance(t.__bound__, typing.ForwardRef):
+            bound_name = t.__bound__.__forward_arg__
+        else:
+            bound_name = t.__bound__.__name__
+    return 'typing.TypeVar("%s", %scontravariant=%s, covariant=%s%s)' % (
+        t.__name__,
+        'bound="%s", ' % bound_name if t.__bound__ else "",
+        t.__contravariant__,
+        t.__covariant__,
+        ", ".join([""] + [c.__name__ for c in t.__constraints__]),
+    )
+
+
+def new_type_to_str(t: typing.NewType) -> str:
+    return 'typing.NewType("%s", %s)' % (t.__name__, t.__supertype__.__name__)
+
+
+def descend_object(object: str, options: Iterable[str]):
+    # Returns true if:
+    #  - options contains a prefix of object
+    #  - the component after the prefix does not start with _
+    for opt in options:
+        new_object = object.removeprefix(opt)
+        if len(new_object) == len(object):
+            # There was no prefix, so we continue
+            continue
+        # Using [1] to skip the inevitable "."
+        if len(new_object) == 0 or new_object[1] != "_":
+            return True
+    return False
+
+
+def parse_params_from_doc(doc: str) -> Tuple[List[inspect.Parameter], bool]:
+    parameters = []
+    no_arg_version = True
+    for line in doc.splitlines():
+        if non_indented_line.match(line):
+            match = param_name_type.match(line)
+            arg_name = type_name = is_optional = default = None
+            default_set = False
+            if match is not None:
+                arg_name = match.group("name")
+                type_name = match.group("type")
+                if type_name is not None:
+                    type_detail = type_annotations.match(type_name)
+                    if type_detail is not None:
+                        type_name = type_detail.group("type")
+                        is_optional = type_detail.group("optional") is not None
+                        default = type_detail.group("default")
+                        if default:
+                            default_set = True
+                        try:
+                            default = eval(default)
+                        except:
+                            pass
+                        try:
+                            type_name = eval(type_name)
+                        except:
+                            pass
+                parameters.append(
+                    inspect.Parameter(
+                        name=arg_name,
+                        kind=inspect.Parameter.KEYWORD_ONLY,
+                        default=(
+                            default
+                            if default_set
+                            else None if is_optional else inspect.Parameter.empty
+                        ),
+                        annotation=(Optional[type_name] if is_optional else type_name),
+                    )
+                )
+                if not default_set:
+                    # If we don't have a default set for any parameter, we can't
+                    # have a no-arg version since the function would be incomplete
+                    no_arg_version = False
+    return parameters, no_arg_version
+
+
+def split_docs(
+    raw_doc: str, boundaries: List[Tuple[str, Union[StartEnd, re.Match]]]
+) -> Dict[str, str]:
+    docs = dict()
+    boundaries.sort(key=lambda x: x[1].start())
+
+    section_start = 0
+    for idx in range(1, len(boundaries)):
+        docs[boundaries[idx - 1][0]] = raw_doc[
+            section_start : boundaries[idx][1].start()
+        ]
+        section_start = boundaries[idx][1].end()
+    docs[boundaries[-1][0]] = raw_doc[section_start:]
+    return docs
+
+
+def parse_add_to_docs(
+    raw_doc: str,
+) -> Dict[str, Union[Tuple[inspect.Signature, str], str]]:
+    prop = None
+    return_type = None
+    property_indent = None
+    doc = []
+    add_to_docs = dict()  # type: Dict[str, Union[str, Tuple[inspect.Signature, str]]]
+
+    def _add():
+        if prop:
+            add_to_docs[prop] = (
+                inspect.Signature(
+                    [
+                        inspect.Parameter(
+                            "self", inspect.Parameter.POSITIONAL_OR_KEYWORD
+                        )
+                    ],
+                    return_annotation=return_type,
+                ),
+                "\n".join(doc),
+            )
+
+    for line in raw_doc.splitlines():
+        # Parse stanzas that look like the following:
+        #  -> type
+        # indented doc string
+        if property_indent is not None and (
+            line.startswith(property_indent + " ") or line.strip() == ""
+        ):
+            offset = len(property_indent)
+            if line.lstrip().startswith("@@ "):
+                line = line.replace("@@ ", "")
+            doc.append(line[offset:].rstrip())
+        else:
+            if line.strip() == 0:
+                continue
+            if prop:
+                # Ends a property stanza
+                _add()
+            # Now start a new one
+            line = line.rstrip()
+            property_indent = line[: len(line) - len(line.lstrip())]
+            # Either this has a -> to denote a property or it is a pure name
+            # to denote a reference to a function (starting with #)
+            line = line.lstrip()
+            if line.startswith("#"):
+                # The name of the function is the last part like metaflow.deployer.run
+                add_to_docs[line.split(".")[-1]] = line[1:]
+                continue
+            # This is a line so we split it using "->"
+            prop, return_type = line.split("->")
+            prop = prop.strip()
+            return_type = return_type.strip()
+            doc = []
+    _add()
+    return add_to_docs
+
+
+def add_indent(indentation: str, text: str) -> str:
+    return "\n".join([indentation + line for line in text.splitlines()])
+
+
+class StubGenerator:
+    """
+    This class takes the name of a library as input and a directory as output.
+
+    It will then generate the corresponding stub files for each defined type
+    (generic variables, functions, classes, etc.) at run time.
+    This means that the code for the library is not statically parsed, but it is
+    executed and then the types are dynamically created and analyzed to produce the stub
+    files.
+
+    The only items analyzes are those that belong to the library (ie: anything in
+    the library or below it but not any external imports)
+    """
+
+    def __init__(self, output_dir: str, include_generated_for: bool = True):
+        """
+        Initializes the StubGenerator.
+        :param file_path: the file path
+        :type file_path: str
+        :param members_from_other_modules: the names of the members defined in other module to be analyzed
+        :type members_from_other_modules: List[str]
+        """
+
+        # Let metaflow know we are in stubgen mode. This is sometimes useful to skip
+        # some processing like loading libraries, etc. It is used in Metaflow extensions
+        # so do not remove even if you do not see a use for it directly in the code.
+        os.environ["METAFLOW_STUBGEN"] = "1"
+
+        self._write_generated_for = include_generated_for
+        # First element is the name it should be installed in (alias) and second is the
+        # actual module name
+        self._pending_modules = [
+            ("metaflow", "metaflow")
+        ]  # type: List[Tuple[str, str]]
+        self._root_module = "metaflow."
+        self._safe_modules = ["metaflow.", "metaflow_extensions."]
+
+        self._pending_modules.extend(
+            (self._get_module_name_alias(x), x) for x in get_aliased_modules()
+        )
+
+        # We exclude some modules to not create a bunch of random non-user facing
+        # .pyi files.
+        self._exclude_modules = set(
+            [
+                "metaflow.cli_args",
+                "metaflow.cmd",
+                "metaflow.cmd_with_io",
+                "metaflow.datastore",
+                "metaflow.debug",
+                "metaflow.decorators",
+                "metaflow.event_logger",
+                "metaflow.extension_support",
+                "metaflow.graph",
+                "metaflow.integrations",
+                "metaflow.lint",
+                "metaflow.metaflow_metadata",
+                "metaflow.metaflow_config_funcs",
+                "metaflow.metaflow_environment",
+                "metaflow.metaflow_profile",
+                "metaflow.metaflow_version",
+                "metaflow.mflog",
+                "metaflow.monitor",
+                "metaflow.package",
+                "metaflow.plugins.datastores",
+                "metaflow.plugins.env_escape",
+                "metaflow.plugins.metadata_providers",
+                "metaflow.procpoll.py",
+                "metaflow.R",
+                "metaflow.runtime",
+                "metaflow.sidecar",
+                "metaflow.task",
+                "metaflow.tracing",
+                "metaflow.unbounded_foreach",
+                "metaflow.util",
+                "metaflow._vendor",
+            ]
+        )
+
+        self._done_modules = set()  # type: Set[str]
+        self._output_dir = output_dir
+        self._mf_version = get_version()
+
+        # Contains the names of the methods that are injected in Deployer
+        self._deployer_injected_methods = (
+            {}
+        )  # type: Dict[str, Dict[str, Union[Tuple[str, str], str]]]
+        # Contains information to add to the Current object (injected by decorators)
+        self._addl_current = (
+            dict()
+        )  # type: Dict[str, Dict[str, Tuple[inspect.Signature, str]]]
+
+        self._reset()
+
+    def _reset(self):
+        # "Globals" that are used throughout processing. This is not the cleanest
+        # but simplifies code quite a bit.
+
+        # Imports that are needed at the top of the file
+        self._imports = set()  # type: Set[str]
+
+        self._sub_module_imports = set()  # type: Set[Tuple[str, str]]``
+        # Typing imports (behind if TYPE_CHECKING) that are needed at the top of the file
+        self._typing_imports = set()  # type: Set[str]
+        # Typevars that are defined
+        self._typevars = dict()  # type: Dict[str, Union[TypeVar, type]]
+        # Current objects in the file being processed
+        self._current_objects = {}  # type: Dict[str, Any]
+        self._current_references = []  # type: List[str]
+        # Current stubs in the file being processed
+        self._stubs = []  # type: List[str]
+
+        # These have a shorter "scope"
+        # Current parent module of the object being processed -- used to determine
+        # the "globals()"
+        self._current_parent_module = None  # type: Optional[ModuleType]
+
+    def _get_module_name_alias(self, module_name):
+        if any(
+            module_name.startswith(x) for x in self._safe_modules
+        ) and not module_name.startswith(self._root_module):
+            return self._root_module + ".".join(
+                ["mf_extensions", *module_name.split(".")[1:]]
+            )
+        return module_name
+
+    def _get_relative_import(
+        self, new_module_name, cur_module_name, is_init_module=False
+    ):
+        new_components = new_module_name.split(".")
+        cur_components = cur_module_name.split(".")
+        init_module_count = 1 if is_init_module else 0
+        common_idx = 0
+        max_idx = min(len(new_components), len(cur_components))
+        while (
+            common_idx < max_idx
+            and new_components[common_idx] == cur_components[common_idx]
+        ):
+            common_idx += 1
+        # current: a.b and parent: a.b.e.d -> from .e.d import 
+        # current: a.b.c.d and parent: a.b.e.f -> from ...e.f import 
+        return "." * (len(cur_components) - common_idx + init_module_count) + ".".join(
+            new_components[common_idx:]
+        )
+
+    def _get_module(self, alias, name):
+        debug.stubgen_exec("Analyzing module %s (aliased at %s)..." % (name, alias))
+        self._current_module = importlib.import_module(name)
+        self._current_module_name = alias
+        for objname, obj in self._current_module.__dict__.items():
+            if objname == "_addl_stubgen_modules":
+                debug.stubgen_exec(
+                    "Adding modules %s from _addl_stubgen_modules" % str(obj)
+                )
+                self._pending_modules.extend(
+                    (self._get_module_name_alias(m), m) for m in obj
+                )
+                continue
+            if objname.startswith("_"):
+                debug.stubgen_exec(
+                    "Skipping object because it starts with _ %s" % objname
+                )
+                continue
+            if inspect.ismodule(obj):
+                # Only consider modules that are safe modules
+                if (
+                    any(obj.__name__.startswith(m) for m in self._safe_modules)
+                    and not obj.__name__ in self._exclude_modules
+                ):
+                    debug.stubgen_exec(
+                        "Adding child module %s to process" % obj.__name__
+                    )
+
+                    new_module_alias = self._get_module_name_alias(obj.__name__)
+                    self._pending_modules.append((new_module_alias, obj.__name__))
+
+                    new_parent, new_name = new_module_alias.rsplit(".", 1)
+                    self._current_references.append(
+                        "from %s import %s as %s"
+                        % (
+                            self._get_relative_import(
+                                new_parent,
+                                alias,
+                                hasattr(self._current_module, "__path__"),
+                            ),
+                            new_name,
+                            objname,
+                        )
+                    )
+                else:
+                    debug.stubgen_exec("Skipping child module %s" % obj.__name__)
+            else:
+                parent_module = inspect.getmodule(obj)
+                # For objects we include:
+                #  - stuff that is a functools.partial (these are all the decorators;
+                #    we could be more specific but good enough for now) for root module.
+                #    We also include the step decorator (it's from metaflow.decorators
+                #    which is typically excluded)
+                #  - Stuff that is defined in this module itself
+                #  - a reference to anything in the modules we will process later
+                #    (so we don't duplicate a ton of times)
+
+                if (
+                    parent_module is None
+                    or (
+                        name + "." == self._root_module
+                        and (
+                            (parent_module.__name__.startswith("functools"))
+                            or obj == step
+                        )
+                    )
+                    or parent_module.__name__ == name
+                ):
+                    debug.stubgen_exec("Adding object %s to process" % objname)
+                    self._current_objects[objname] = obj
+
+                elif not any(
+                    [
+                        parent_module.__name__.startswith(p)
+                        for p in self._exclude_modules
+                    ]
+                ) and any(
+                    [parent_module.__name__.startswith(p) for p in self._safe_modules]
+                ):
+                    parent_alias = self._get_module_name_alias(parent_module.__name__)
+
+                    relative_import = self._get_relative_import(
+                        parent_alias, alias, hasattr(self._current_module, "__path__")
+                    )
+
+                    debug.stubgen_exec(
+                        "Adding reference %s and adding module %s as %s"
+                        % (objname, parent_module.__name__, parent_alias)
+                    )
+                    obj_import_name = getattr(obj, "__name__", objname)
+                    if obj_import_name == "":
+                        # We have one case of this
+                        obj_import_name = objname
+                    self._current_references.append(
+                        "from %s import %s as %s"
+                        % (relative_import, obj_import_name, objname)
+                    )
+                    self._pending_modules.append((parent_alias, parent_module.__name__))
+                else:
+                    debug.stubgen_exec("Skipping object %s" % objname)
+
+    def _get_element_name_with_module(
+        self, element: Union[TypeVar, type, Any], force_import=False
+    ) -> str:
+        # The element can be a string, for example "def f() -> 'SameClass':..."
+        def _add_to_import(name):
+            if name != self._current_module_name:
+                self._imports.add(name)
+
+        def _add_to_typing_check(name, is_module=False):
+            if name == "None":
+                return
+            if is_module:
+                self._typing_imports.add(name)
+            else:
+                splits = name.rsplit(".", 1)
+                if len(splits) > 1 and not (
+                    len(splits) == 2 and splits[0] == self._current_module_name
+                ):
+                    # We don't add things that are just one name -- probably things within
+                    # the current file
+                    self._typing_imports.add(splits[0])
+
+        def _format_qualified_class_name(cls: type) -> str:
+            """Helper to format a class with its qualified module name"""
+            # Special case for NoneType - return None
+            if cls.__name__ == "NoneType":
+                return "None"
+
+            module = inspect.getmodule(cls)
+            if (
+                module
+                and module.__name__ != "builtins"
+                and module.__name__ != "__main__"
+            ):
+                module_name = self._get_module_name_alias(module.__name__)
+                _add_to_typing_check(module_name, is_module=True)
+                return f"{module_name}.{cls.__name__}"
+            else:
+                return cls.__name__
+
+        if isinstance(element, str):
+            # Special case for self referential things (particularly in a class)
+            if element == self._current_name:
+                return '"%s"' % element
+            # We first try to eval the annotation because with the annotations future
+            # it is always a string
+            try:
+                potential_element = eval(
+                    element,
+                    (
+                        self._current_parent_module.__dict__
+                        if self._current_parent_module
+                        else None
+                    ),
+                )
+                if potential_element:
+                    element = potential_element
+            except:
+                pass
+
+        if isinstance(element, str):
+            # If we are in our "safe" modules, make sure we alias properly
+            if any(element.startswith(x) for x in self._safe_modules):
+                element = self._get_module_name_alias(element)
+            _add_to_typing_check(element)
+            return '"%s"' % element
+        # 3.10+ has NewType as a class but not before so hack around to check for NewType
+        elif isinstance(element, TypeVar) or hasattr(element, "__supertype__"):
+            if not element.__name__ in self._typevars:
+                self._typevars[element.__name__] = element
+            return element.__name__
+        elif isinstance(element, type):
+            module = inspect.getmodule(element)
+            if (
+                module is None
+                or module.__name__ == "builtins"
+                or module.__name__ == "__main__"
+            ):
+                # Special case for "NoneType" -- return None as NoneType is only 3.10+
+                if element.__name__ == "NoneType":
+                    return "None"
+                return element.__name__
+
+            module_name = self._get_module_name_alias(module.__name__)
+            if force_import:
+                _add_to_import(module_name.split(".")[0])
+            _add_to_typing_check(module_name, is_module=True)
+            if module_name != self._current_module_name:
+                return "{0}.{1}".format(module_name, element.__name__)
+            else:
+                return element.__name__
+        elif isinstance(element, type(Ellipsis)):
+            return "..."
+        elif isinstance(element, typing._GenericAlias):
+            # We need to check things recursively in __args__ if it exists
+            args_str = []
+            for arg in getattr(element, "__args__", []):
+                # Special handling for class objects in type arguments
+                if isinstance(arg, type):
+                    args_str.append(_format_qualified_class_name(arg))
+                else:
+                    args_str.append(self._get_element_name_with_module(arg))
+
+            _add_to_import("typing")
+            if element._name:
+                if element._name == "Optional":
+                    # We don't want to include NoneType in the string -- it breaks things
+                    args_str = args_str[:1]
+                elif element._name == "Callable":
+                    # We need to make this a list of everything except the end one
+                    # except if it is an ellipsis
+                    if args_str[0] != "...":
+                        call_args = "[" + ", ".join(args_str[:-1]) + "]"
+                        args_str = [call_args, args_str[-1]]
+                return "typing.%s[%s]" % (element._name, ", ".join(args_str))
+            else:
+                # Handle the case where we have a generic type without a _name
+                origin = element.__origin__
+                if isinstance(origin, type):
+                    origin_str = _format_qualified_class_name(origin)
+                else:
+                    origin_str = str(origin)
+                return "%s[%s]" % (origin_str, ", ".join(args_str))
+        elif isinstance(element, ForwardRef):
+            f_arg = self._get_module_name_alias(element.__forward_arg__)
+            _add_to_typing_check(f_arg)
+            return '"%s"' % f_arg
+        elif inspect.getmodule(element) == inspect.getmodule(typing):
+            _add_to_import("typing")
+            # Special handling for NamedTuple which is a function
+            if hasattr(element, "__name__") and element.__name__ == "NamedTuple":
+                return "typing.NamedTuple"
+            return str(element)
+        else:
+            if hasattr(element, "__module__"):
+                elem_module = self._get_module_name_alias(element.__module__)
+                if elem_module == "builtins":
+                    return getattr(element, "__name__", str(element))
+                _add_to_typing_check(elem_module, is_module=True)
+                return "{0}.{1}".format(
+                    elem_module, getattr(element, "__name__", element)
+                )
+            else:
+                # A constant
+                return str(element)
+
+    def _exploit_annotation(self, annotation: Any, starting: str = ": ") -> str:
+        annotation_string = ""
+        if annotation and annotation != inspect.Parameter.empty:
+            annotation_string += starting + self._get_element_name_with_module(
+                annotation
+            )
+        return annotation_string
+
+    def _generate_class_stub(self, name: str, clazz: type) -> str:
+        debug.stubgen_exec("Generating class stub for %s" % name)
+        skip_init = issubclass(clazz, (TriggeredRun, DeployedFlow))
+        if issubclass(clazz, DeployerImpl):
+            if clazz.TYPE is not None:
+                clazz_type = clazz.TYPE.replace("-", "_")
+                self._deployer_injected_methods.setdefault(clazz_type, {})[
+                    "deployer"
+                ] = (self._current_module_name + "." + name)
+
+        # Handle TypedDict gracefully for Python 3.7 compatibility
+        # _TypedDictMeta is not available in Python 3.7
+        typed_dict_meta = getattr(typing, "_TypedDictMeta", None)
+        if typed_dict_meta is not None and isinstance(clazz, typed_dict_meta):
+            self._sub_module_imports.add(("typing", "TypedDict"))
+            total_flag = getattr(clazz, "__total__", False)
+            buff = StringIO()
+            # Emit the TypedDict base and total flag
+            buff.write(f"class {name}(TypedDict, total={total_flag}):\n")
+            # Write out each field from __annotations__
+            for field_name, field_type in clazz.__annotations__.items():
+                ann = self._get_element_name_with_module(field_type)
+                buff.write(f"{TAB}{field_name}: {ann}\n")
+            return buff.getvalue()
+
+        buff = StringIO()
+        # Class prototype
+        buff.write("class " + name.split(".")[-1] + "(")
+
+        # Add super classes
+        for c in clazz.__bases__:
+            name_with_module = self._get_element_name_with_module(c, force_import=True)
+            buff.write(name_with_module + ", ")
+
+        # Add metaclass
+        name_with_module = self._get_element_name_with_module(
+            clazz.__class__, force_import=True
+        )
+        buff.write("metaclass=" + name_with_module + "):\n")
+
+        # Add class docstring
+        if clazz.__doc__:
+            buff.write('%s"""\n' % TAB)
+            my_doc = inspect.cleandoc(clazz.__doc__)
+            init_blank = True
+            for line in my_doc.split("\n"):
+                if init_blank and len(line.strip()) == 0:
+                    continue
+                init_blank = False
+                buff.write("%s%s\n" % (TAB, line.rstrip()))
+            buff.write('%s"""\n' % TAB)
+
+        # For NamedTuple, we have __annotations__ but no __init__. In that case,
+        # we are going to "create" a __init__ function with the annotations
+        # to show what the class takes.
+        annotation_dict = None
+        init_func = None
+        for key, element in clazz.__dict__.items():
+            func_deco = None
+            if isinstance(element, staticmethod):
+                func_deco = "@staticmethod"
+                element = element.__func__
+            elif isinstance(element, classmethod):
+                func_deco = "@classmethod"
+                element = element.__func__
+            if key == "__init__":
+                if skip_init:
+                    continue
+                init_func = element
+            elif key == "__annotations__":
+                annotation_dict = element
+            if inspect.isfunction(element):
+                if not element.__name__.startswith("_") or element.__name__.startswith(
+                    "__"
+                ):
+                    if (
+                        clazz == Deployer
+                        and element.__name__ in self._deployer_injected_methods
+                    ):
+                        # This is a method that was injected. It has docs but we need
+                        # to parse it to generate the proper signature
+                        func_doc = inspect.cleandoc(element.__doc__)
+                        docs = split_docs(
+                            func_doc,
+                            [
+                                ("func_doc", StartEnd(0, 0)),
+                                (
+                                    "param_doc",
+                                    param_section_header.search(func_doc)
+                                    or StartEnd(len(func_doc), len(func_doc)),
+                                ),
+                                (
+                                    "return_doc",
+                                    return_section_header.search(func_doc)
+                                    or StartEnd(len(func_doc), len(func_doc)),
+                                ),
+                            ],
+                        )
+
+                        parameters, _ = parse_params_from_doc(docs["param_doc"])
+                        return_type = self._deployer_injected_methods[element.__name__][
+                            "deployer"
+                        ]
+
+                        buff.write(
+                            self._generate_function_stub(
+                                key,
+                                element,
+                                sign=[
+                                    inspect.Signature(
+                                        parameters=[
+                                            inspect.Parameter(
+                                                "self",
+                                                inspect.Parameter.POSITIONAL_OR_KEYWORD,
+                                            )
+                                        ]
+                                        + parameters,
+                                        return_annotation=return_type,
+                                    )
+                                ],
+                                indentation=TAB,
+                                deco=func_deco,
+                            )
+                        )
+                    elif (
+                        clazz == DeployedFlow and element.__name__ == "from_deployment"
+                    ):
+                        # We simply update the signature to list the return
+                        # type as a union of all possible deployers
+                        func_doc = inspect.cleandoc(element.__doc__)
+                        docs = split_docs(
+                            func_doc,
+                            [
+                                ("func_doc", StartEnd(0, 0)),
+                                (
+                                    "param_doc",
+                                    param_section_header.search(func_doc)
+                                    or StartEnd(len(func_doc), len(func_doc)),
+                                ),
+                                (
+                                    "return_doc",
+                                    return_section_header.search(func_doc)
+                                    or StartEnd(len(func_doc), len(func_doc)),
+                                ),
+                            ],
+                        )
+
+                        parameters, _ = parse_params_from_doc(docs["param_doc"])
+
+                        def _create_multi_type(*l):
+                            return typing.Union[l]
+
+                        all_types = [
+                            v["from_deployment"][0]
+                            for v in self._deployer_injected_methods.values()
+                        ]
+
+                        if len(all_types) > 1:
+                            return_type = _create_multi_type(*all_types)
+                        else:
+                            return_type = all_types[0] if len(all_types) else None
+
+                        buff.write(
+                            self._generate_function_stub(
+                                key,
+                                element,
+                                sign=[
+                                    inspect.Signature(
+                                        parameters=[
+                                            inspect.Parameter(
+                                                "cls",
+                                                inspect.Parameter.POSITIONAL_OR_KEYWORD,
+                                            )
+                                        ]
+                                        + parameters,
+                                        return_annotation=return_type,
+                                    )
+                                ],
+                                indentation=TAB,
+                                doc=docs["func_doc"]
+                                + "\n\nParameters\n----------\n"
+                                + docs["param_doc"]
+                                + "\n\nReturns\n-------\n"
+                                + "%s\nA `DeployedFlow` object" % str(return_type),
+                                deco=func_deco,
+                            )
+                        )
+                    elif (
+                        clazz == DeployedFlow
+                        and element.__name__.startswith("from_")
+                        and element.__name__[5:] in self._deployer_injected_methods
+                    ):
+                        # Get the doc from the from_deployment method stored in
+                        # self._deployer_injected_methods
+                        func_doc = inspect.cleandoc(
+                            self._deployer_injected_methods[element.__name__[5:]][
+                                "from_deployment"
+                            ][1]
+                            or ""
+                        )
+                        docs = split_docs(
+                            func_doc,
+                            [
+                                ("func_doc", StartEnd(0, 0)),
+                                (
+                                    "param_doc",
+                                    param_section_header.search(func_doc)
+                                    or StartEnd(len(func_doc), len(func_doc)),
+                                ),
+                                (
+                                    "return_doc",
+                                    return_section_header.search(func_doc)
+                                    or StartEnd(len(func_doc), len(func_doc)),
+                                ),
+                            ],
+                        )
+
+                        parameters, _ = parse_params_from_doc(docs["param_doc"])
+                        return_type = self._deployer_injected_methods[
+                            element.__name__[5:]
+                        ]["from_deployment"][0]
+
+                        buff.write(
+                            self._generate_function_stub(
+                                key,
+                                element,
+                                sign=[
+                                    inspect.Signature(
+                                        parameters=[
+                                            inspect.Parameter(
+                                                "cls",
+                                                inspect.Parameter.POSITIONAL_OR_KEYWORD,
+                                            )
+                                        ]
+                                        + parameters,
+                                        return_annotation=return_type,
+                                    )
+                                ],
+                                indentation=TAB,
+                                doc=docs["func_doc"]
+                                + "\n\nParameters\n----------\n"
+                                + docs["param_doc"]
+                                + "\n\nReturns\n-------\n"
+                                + docs["return_doc"],
+                                deco=func_deco,
+                            )
+                        )
+                    else:
+                        if (
+                            issubclass(clazz, DeployedFlow)
+                            and clazz.TYPE is not None
+                            and key == "from_deployment"
+                        ):
+                            clazz_type = clazz.TYPE.replace("-", "_")
+                            # Record docstring for this function
+                            self._deployer_injected_methods.setdefault(clazz_type, {})[
+                                "from_deployment"
+                            ] = (
+                                self._current_module_name + "." + name,
+                                element.__doc__,
+                            )
+                        buff.write(
+                            self._generate_function_stub(
+                                key,
+                                element,
+                                indentation=TAB,
+                                deco=func_deco,
+                            )
+                        )
+
+            elif isinstance(element, property):
+                if element.fget:
+                    buff.write(
+                        self._generate_function_stub(
+                            key, element.fget, indentation=TAB, deco="@property"
+                        )
+                    )
+                if element.fset:
+                    buff.write(
+                        self._generate_function_stub(
+                            key, element.fset, indentation=TAB, deco="@%s.setter" % key
+                        )
+                    )
+
+        # Special handling of classes that have injected methods
+        if clazz == Current:
+            # Multiple decorators can add the same object (trigger and trigger_on_finish)
+            # as examples so we sort it out.
+            resulting_dict = (
+                dict()
+            )  # type Dict[str, List[inspect.Signature, str, List[str]]]
+            for deco_name, addl_current in self._addl_current.items():
+                for name, (sign, doc) in addl_current.items():
+                    r = resulting_dict.setdefault(name, [sign, doc, []])
+                    r[2].append("@%s" % deco_name)
+            for name, (sign, doc, decos) in resulting_dict.items():
+                buff.write(
+                    self._generate_function_stub(
+                        name,
+                        sign=[sign],
+                        indentation=TAB,
+                        doc="(only in the presence of the %s decorator%s)\n\n"
+                        % (", or ".join(decos), "" if len(decos) == 1 else "s")
+                        + doc,
+                        deco="@property",
+                    )
+                )
+
+        if not skip_init and init_func is None and annotation_dict:
+            buff.write(
+                self._generate_function_stub(
+                    "__init__",
+                    func=None,
+                    sign=[
+                        inspect.Signature(
+                            parameters=[
+                                inspect.Parameter(
+                                    name="self",
+                                    kind=inspect.Parameter.POSITIONAL_OR_KEYWORD,
+                                )
+                            ]
+                            + [
+                                inspect.Parameter(
+                                    name=name,
+                                    kind=inspect.Parameter.POSITIONAL_OR_KEYWORD,
+                                    annotation=annotation,
+                                )
+                                for name, annotation in annotation_dict.items()
+                            ]
+                        )
+                    ],
+                    indentation=TAB,
+                )
+            )
+        buff.write("%s...\n" % TAB)
+
+        return buff.getvalue()
+
+    def _extract_signature_from_decorator(
+        self, name: str, raw_doc: Optional[str], is_flow_decorator: bool = False
+    ) -> Optional[List[Tuple[inspect.Signature, str]]]:
+        # TODO: This only handles the `Parameters` section for now; we are
+        # using it only to parse the documentation for step/flow decorators so
+        # this is enough for now but it could be extended more.
+        # Inspired from:
+        # https://github.com/rr-/docstring_parser/blob/master/docstring_parser/numpydoc.py
+        if raw_doc is None:
+            return None
+
+        if not "FlowSpecDerived" in self._typevars:
+            self._typevars["FlowSpecDerived"] = FlowSpecDerived
+            self._typevars["StepFlag"] = StepFlag
+
+        raw_doc = inspect.cleandoc(raw_doc)
+        section_boundaries = [
+            ("func_doc", StartEnd(0, 0)),
+            (
+                "param_doc",
+                param_section_header.search(raw_doc)
+                or StartEnd(len(raw_doc), len(raw_doc)),
+            ),
+            (
+                "add_to_current_doc",
+                add_to_current_header.search(raw_doc)
+                or StartEnd(len(raw_doc), len(raw_doc)),
+            ),
+        ]
+
+        docs = split_docs(raw_doc, section_boundaries)
+        parameters, no_arg_version = parse_params_from_doc(docs["param_doc"])
+
+        if docs["add_to_current_doc"]:
+            self._addl_current[name] = parse_add_to_docs(docs["add_to_current_doc"])
+
+        result = []
+        if no_arg_version:
+            if is_flow_decorator:
+                if docs["param_doc"]:
+                    result.append(
+                        (
+                            inspect.Signature(
+                                parameters=parameters,
+                                return_annotation=Callable[
+                                    [typing.Type[FlowSpecDerived]],
+                                    typing.Type[FlowSpecDerived],
+                                ],
+                            ),
+                            "",
+                        )
+                    )
+                result.append(
+                    (
+                        inspect.Signature(
+                            parameters=[
+                                inspect.Parameter(
+                                    name="f",
+                                    kind=inspect.Parameter.POSITIONAL_OR_KEYWORD,
+                                    annotation=typing.Type[FlowSpecDerived],
+                                )
+                            ],
+                            return_annotation=typing.Type[FlowSpecDerived],
+                        ),
+                        "",
+                    ),
+                )
+            else:
+                if docs["param_doc"]:
+                    result.append(
+                        (
+                            inspect.Signature(
+                                parameters=parameters,
+                                return_annotation=typing.Callable[
+                                    [MetaflowStepFunction], MetaflowStepFunction
+                                ],
+                            ),
+                            "",
+                        )
+                    )
+                result.extend(
+                    [
+                        (
+                            inspect.Signature(
+                                parameters=[
+                                    inspect.Parameter(
+                                        name="f",
+                                        kind=inspect.Parameter.POSITIONAL_OR_KEYWORD,
+                                        annotation=Callable[
+                                            [FlowSpecDerived, StepFlag], None
+                                        ],
+                                    )
+                                ],
+                                return_annotation=Callable[
+                                    [FlowSpecDerived, StepFlag], None
+                                ],
+                            ),
+                            "",
+                        ),
+                        (
+                            inspect.Signature(
+                                parameters=[
+                                    inspect.Parameter(
+                                        name="f",
+                                        kind=inspect.Parameter.POSITIONAL_OR_KEYWORD,
+                                        annotation=Callable[
+                                            [FlowSpecDerived, Any, StepFlag], None
+                                        ],
+                                    )
+                                ],
+                                return_annotation=Callable[
+                                    [FlowSpecDerived, Any, StepFlag], None
+                                ],
+                            ),
+                            "",
+                        ),
+                    ]
+                )
+
+        if is_flow_decorator:
+            result = result + [
+                (
+                    inspect.Signature(
+                        parameters=(
+                            [
+                                inspect.Parameter(
+                                    name="f",
+                                    kind=inspect.Parameter.POSITIONAL_OR_KEYWORD,
+                                    annotation=Optional[typing.Type[FlowSpecDerived]],
+                                    default=(
+                                        None
+                                        if no_arg_version
+                                        else inspect.Parameter.empty
+                                    ),
+                                )
+                            ]
+                            + parameters
+                            if no_arg_version
+                            else [] + parameters
+                        ),
+                        return_annotation=(
+                            inspect.Signature.empty
+                            if no_arg_version
+                            else Callable[
+                                [typing.Type[FlowSpecDerived]],
+                                typing.Type[FlowSpecDerived],
+                            ]
+                        ),
+                    ),
+                    "",
+                ),
+            ]
+        else:
+            result = result + [
+                (
+                    inspect.Signature(
+                        parameters=(
+                            [
+                                inspect.Parameter(
+                                    name="f",
+                                    kind=inspect.Parameter.POSITIONAL_OR_KEYWORD,
+                                    annotation=Optional[MetaflowStepFunction],
+                                    default=(
+                                        None
+                                        if no_arg_version
+                                        else inspect.Parameter.empty
+                                    ),
+                                )
+                            ]
+                            + parameters
+                            if no_arg_version
+                            else [] + parameters
+                        ),
+                        return_annotation=(
+                            inspect.Signature.empty
+                            if no_arg_version
+                            else typing.Callable[
+                                [MetaflowStepFunction], MetaflowStepFunction
+                            ]
+                        ),
+                    ),
+                    "",
+                ),
+            ]
+        if len(result) == 2:
+            # If we only have one overload -- we don't need it at all. Happens for
+            # flow-level decorators that don't take any arguments
+            result = result[1:]
+        # Add doc to first and last overloads. Jedi uses the last one and pycharm
+        # the first one. Go figure.
+        result_docstring = docs["func_doc"]
+        if docs["param_doc"]:
+            result_docstring += "\nParameters\n----------\n" + docs["param_doc"]
+        result[0] = (
+            result[0][0],
+            result_docstring,
+        )
+        result[-1] = (
+            result[-1][0],
+            result_docstring,
+        )
+        return result
+
+    def _generate_function_stub(
+        self,
+        name: str,
+        func: Optional[Union[Callable, classmethod]] = None,
+        sign: Optional[List[inspect.Signature]] = None,
+        indentation: Optional[str] = None,
+        doc: Optional[str] = None,
+        deco: Optional[str] = None,
+    ) -> str:
+        debug.stubgen_exec("Generating function stub for %s" % name)
+
+        def exploit_default(default_value: Any) -> Optional[str]:
+            if default_value == inspect.Parameter.empty:
+                return None
+            if type(default_value).__module__ == "builtins":
+                if isinstance(default_value, list):
+                    return (
+                        "["
+                        + ", ".join(
+                            [cast(str, exploit_default(v)) for v in default_value]
+                        )
+                        + "]"
+                    )
+                elif isinstance(default_value, tuple):
+                    return (
+                        "("
+                        + ", ".join(
+                            [cast(str, exploit_default(v)) for v in default_value]
+                        )
+                        + ")"
+                    )
+                elif isinstance(default_value, dict):
+                    return (
+                        "{"
+                        + ", ".join(
+                            [
+                                cast(str, exploit_default(k))
+                                + ": "
+                                + cast(str, exploit_default(v))
+                                for k, v in default_value.items()
+                            ]
+                        )
+                        + "}"
+                    )
+                elif isinstance(default_value, str):
+                    return repr(default_value)  # Use repr() for proper escaping
+                elif isinstance(default_value, (int, float, bool)):
+                    return str(default_value)
+                elif default_value is None:
+                    return "None"
+                else:
+                    return "..."  # For other built-in types not explicitly handled
+            elif inspect.isclass(default_value) or inspect.isfunction(default_value):
+                if default_value.__module__ == "builtins":
+                    return default_value.__name__
+                else:
+                    self._typing_imports.add(default_value.__module__)
+                    return ".".join([default_value.__module__, default_value.__name__])
+            else:
+                return "..."  # For complex objects like class instances
+
+        buff = StringIO()
+        if sign is None and func is None:
+            raise RuntimeError(
+                "Cannot generate stub for function %s with either a function or signature"
+                % name
+            )
+        try:
+            sign = sign or [inspect.signature(cast(Callable, func))]
+        except ValueError:
+            # In 3.7, NamedTuples have properties that then give an operator.itemgetter
+            # which doesn't have a signature. We ignore for now. It doesn't have much
+            # value
+            return ""
+        doc = doc or func.__doc__
+        if doc == "STUBGEN_IGNORE":
+            # Ignore methods that have STUBGEN_IGNORE. Used to ignore certain
+            # methods for the Deployer
+            return ""
+        indentation = indentation or ""
+
+        # Deal with overload annotations -- the last one will be non overloaded and
+        # will be the one that shows up as the type hint (for Jedi and PyCharm which
+        # don't handle overloads as well)
+        do_overload = False
+        if sign and len(sign) > 1:
+            do_overload = True
+        for count, my_sign in enumerate(sign):
+            if count > 0:
+                buff.write("\n")
+
+            if do_overload and count < len(sign) - 1:
+                # According to mypy, we should have this on all variants but
+                # some IDEs seem to prefer if there is one non-overloaded
+                # This also changes our checks so if changing, modify tests
+                buff.write(indentation + "@typing.overload\n")
+            if deco:
+                buff.write(indentation + deco + "\n")
+            buff.write(indentation + "def " + name + "(")
+            kw_only_param = False
+            has_var_args = False
+            for i, (par_name, parameter) in enumerate(my_sign.parameters.items()):
+                annotation = self._exploit_annotation(parameter.annotation)
+                default = exploit_default(parameter.default)
+
+                if (
+                    kw_only_param
+                    and not has_var_args
+                    and parameter.kind != inspect.Parameter.KEYWORD_ONLY
+                ):
+                    raise RuntimeError(
+                        "In function '%s': cannot have a positional parameter after a "
+                        "keyword only parameter" % name
+                    )
+
+                if (
+                    parameter.kind == inspect.Parameter.KEYWORD_ONLY
+                    and not kw_only_param
+                    and not has_var_args
+                ):
+                    kw_only_param = True
+                    buff.write("*, ")
+                if parameter.kind == inspect.Parameter.VAR_KEYWORD:
+                    par_name = "**%s" % par_name
+                elif parameter.kind == inspect.Parameter.VAR_POSITIONAL:
+                    has_var_args = True
+                    par_name = "*%s" % par_name
+
+                if default:
+                    buff.write(par_name + annotation + " = " + default)
+                else:
+                    buff.write(par_name + annotation)
+
+                if i < len(my_sign.parameters) - 1:
+                    buff.write(", ")
+            ret_annotation = self._exploit_annotation(
+                my_sign.return_annotation, starting=" -> "
+            )
+            buff.write(")" + ret_annotation + ":\n")
+
+            if (count == 0 or count == len(sign) - 1) and doc is not None:
+                buff.write('%s%s"""\n' % (indentation, TAB))
+                my_doc = inspect.cleandoc(doc)
+                init_blank = True
+                for line in my_doc.split("\n"):
+                    if init_blank and len(line.strip()) == 0:
+                        continue
+                    init_blank = False
+                    buff.write("%s%s%s\n" % (indentation, TAB, line.rstrip()))
+                buff.write('%s%s"""\n' % (indentation, TAB))
+            buff.write("%s%s...\n" % (indentation, TAB))
+        return buff.getvalue()
+
+    def _generate_generic_stub(self, element_name: str, element: Any) -> str:
+        return "{0}: {1}\n".format(
+            element_name, self._get_element_name_with_module(type(element))
+        )
+
+    def _generate_stubs(self):
+        for name, attr in self._current_objects.items():
+            self._current_parent_module = inspect.getmodule(attr)
+            self._current_name = name
+            if inspect.isclass(attr):
+                self._stubs.append(self._generate_class_stub(name, attr))
+            elif inspect.isfunction(attr):
+                # Special handling of the `step` function where we want to add an
+                # overload. This is just a single case so we don't make it general.
+                # Unfortunately, when iterating, it doesn't see the @overload
+                if (
+                    name == "step"
+                    and self._current_module_name == self._root_module[:-1]
+                ):
+                    self._stubs.append(
+                        self._generate_function_stub(
+                            name,
+                            func=attr,
+                            sign=[
+                                inspect.Signature(
+                                    parameters=[
+                                        inspect.Parameter(
+                                            name="f",
+                                            kind=inspect.Parameter.POSITIONAL_OR_KEYWORD,
+                                            annotation=Callable[
+                                                [FlowSpecDerived], None
+                                            ],
+                                        )
+                                    ],
+                                    return_annotation=Callable[
+                                        [FlowSpecDerived, StepFlag], None
+                                    ],
+                                ),
+                                inspect.Signature(
+                                    parameters=[
+                                        inspect.Parameter(
+                                            name="f",
+                                            kind=inspect.Parameter.POSITIONAL_OR_KEYWORD,
+                                            annotation=Callable[
+                                                [FlowSpecDerived, Any], None
+                                            ],
+                                        )
+                                    ],
+                                    return_annotation=Callable[
+                                        [FlowSpecDerived, Any, StepFlag], None
+                                    ],
+                                ),
+                                inspect.signature(attr),
+                            ],
+                        )
+                    )
+                else:
+                    self._stubs.append(self._generate_function_stub(name, attr))
+            elif isinstance(attr, functools.partial):
+                if issubclass(attr.args[0], Decorator):
+                    # Special case where we are going to extract the parameters from
+                    # the docstring to make the decorator look nicer
+                    res = self._extract_signature_from_decorator(
+                        name,
+                        attr.args[0].__doc__,
+                        is_flow_decorator=issubclass(attr.args[0], FlowDecorator),
+                    )
+                    if res:
+                        self._stubs.append(
+                            self._generate_function_stub(
+                                name,
+                                func=attr.func,
+                                sign=[r[0] for r in res],
+                                doc=res[-1][1],
+                            )
+                        )
+                    else:
+                        # print(
+                        #    "WARNING: Could not extract decorator signature for %s"
+                        #    % name
+                        # )
+                        pass
+                else:
+                    self._stubs.append(
+                        self._generate_function_stub(
+                            name, attr.func, doc=attr.args[0].__doc__
+                        )
+                    )
+            elif not inspect.ismodule(attr):
+                self._stubs.append(self._generate_generic_stub(name, attr))
+
+    def _write_header(self, f, width):
+        title_line = "Auto-generated Metaflow stub file"
+        title_white_space = (width - len(title_line)) / 2
+        title_line = "#%s%s%s#\n" % (
+            " " * math.floor(title_white_space),
+            title_line,
+            " " * math.ceil(title_white_space),
+        )
+        f.write(
+            "#" * (width + 2)
+            + "\n"
+            + title_line
+            + "# MF version: %s%s#\n"
+            % (self._mf_version, " " * (width - 13 - len(self._mf_version)))
+            + "# Generated on %s%s#\n"
+            % (
+                datetime.fromtimestamp(time.time()).isoformat(),
+                " " * (width - 14 - 26),
+            )
+            + "#" * (width + 2)
+            + "\n\n"
+        )
+
+    def write_out(self):
+        out_dir = self._output_dir
+        os.makedirs(out_dir, exist_ok=True)
+        # Write out py.typed (pylance seems to require it even though it is not
+        # required in PEP 561) as well as a file we will use to check the "version"
+        # of the stubs -- this helps to inform the user if the stubs were generated
+        # for another version of Metaflow.
+        pathlib.Path(os.path.join(out_dir, "py.typed")).touch()
+        if self._write_generated_for:
+            pathlib.Path(os.path.join(out_dir, "generated_for.txt")).write_text(
+                "%s %s"
+                % (self._mf_version, datetime.fromtimestamp(time.time()).isoformat())
+            )
+        post_process_modules = []
+        is_post_processing = False
+        while len(self._pending_modules) != 0 or len(post_process_modules) != 0:
+            if is_post_processing or len(self._pending_modules) == 0:
+                is_post_processing = True
+                module_alias, module_name = post_process_modules.pop(0)
+            else:
+                module_alias, module_name = self._pending_modules.pop(0)
+            # Skip vendored stuff
+            if module_alias.startswith("metaflow._vendor") or module_name.startswith(
+                "metaflow._vendor"
+            ):
+                continue
+            # We delay current module and deployer module to the end since they
+            # depend on info we gather elsewhere
+            if (
+                module_alias
+                in (
+                    METAFLOW_CURRENT_MODULE_NAME,
+                    METAFLOW_DEPLOYER_MODULE_NAME,
+                )
+                and len(self._pending_modules) != 0
+            ):
+                post_process_modules.append((module_alias, module_name))
+                continue
+            if module_alias in self._done_modules:
+                continue
+            self._done_modules.add(module_alias)
+            # If not, we process the module
+            self._reset()
+            self._get_module(module_alias, module_name)
+            if module_name == "metaflow" and not is_post_processing:
+                # We will want to regenerate this at the end to take into account
+                # any changes to the Deployer
+                post_process_modules.append((module_name, module_name))
+                self._done_modules.remove(module_name)
+                continue
+            self._generate_stubs()
+
+            if hasattr(self._current_module, "__path__"):
+                # This is a package (so a directory) and we are dealing with
+                # a __init__.pyi type of case
+                dir_path = os.path.join(self._output_dir, *module_alias.split(".")[1:])
+            else:
+                # This is NOT a package so the original source file is not a __init__.py
+                dir_path = os.path.join(
+                    self._output_dir, *module_alias.split(".")[1:-1]
+                )
+            out_file = os.path.join(
+                dir_path, os.path.basename(self._current_module.__file__) + "i"
+            )
+
+            width = 100
+
+            os.makedirs(os.path.dirname(out_file), exist_ok=True)
+            # We want to make sure we always have a __init__.pyi in the directories
+            # we are creating
+            parts = dir_path.split(os.sep)[len(self._output_dir.split(os.sep)) :]
+            for i in range(1, len(parts) + 1):
+                init_file_path = os.path.join(
+                    self._output_dir, *parts[:i], "__init__.pyi"
+                )
+                if not os.path.exists(init_file_path):
+                    with open(init_file_path, mode="w", encoding="utf-8") as f:
+                        self._write_header(f, width)
+
+            with open(out_file, mode="w", encoding="utf-8") as f:
+                self._write_header(f, width)
+
+                f.write("from __future__ import annotations\n\n")
+                imported_typing = False
+                for module in self._imports:
+                    f.write("import " + module + "\n")
+                    if module == "typing":
+                        imported_typing = True
+                for module, sub_module in self._sub_module_imports:
+                    f.write(f"from {module} import {sub_module}\n")
+                if self._typing_imports:
+                    if not imported_typing:
+                        f.write("import typing\n")
+                        imported_typing = True
+                    f.write("if typing.TYPE_CHECKING:\n")
+                    for module in self._typing_imports:
+                        f.write(TAB + "import " + module + "\n")
+                if self._typevars:
+                    if not imported_typing:
+                        f.write("import typing\n")
+                        imported_typing = True
+                    for type_name, type_var in self._typevars.items():
+                        if isinstance(type_var, TypeVar):
+                            f.write(
+                                "%s = %s\n" % (type_name, type_var_to_str(type_var))
+                            )
+                        else:
+                            f.write(
+                                "%s = %s\n" % (type_name, new_type_to_str(type_var))
+                            )
+                f.write("\n")
+                for import_line in self._current_references:
+                    f.write(import_line + "\n")
+                f.write("\n")
+                for stub in self._stubs:
+                    f.write(stub + "\n")
+            if is_post_processing:
+                # Don't consider any pending modules if we are post processing
+                self._pending_modules.clear()
+
+
+if __name__ == "__main__":
+    gen = StubGenerator("./stubs")
+    gen.write_out()
diff --git a/metaflow/cmd/develop/stubs.py b/metaflow/cmd/develop/stubs.py
new file mode 100644
index 00000000000..dcabae27f27
--- /dev/null
+++ b/metaflow/cmd/develop/stubs.py
@@ -0,0 +1,343 @@
+import importlib
+import os
+import subprocess
+import sys
+import tempfile
+
+from typing import Any, List, Optional, Tuple
+
+from metaflow._vendor import click
+
+from . import develop
+from .stub_generator import StubGenerator
+
+_py_ver = sys.version_info[:2]
+
+if _py_ver >= (3, 8):
+    from importlib import metadata
+elif _py_ver >= (3, 7):
+    from metaflow._vendor.v3_7 import importlib_metadata as metadata
+else:
+    from metaflow._vendor.v3_6 import importlib_metadata as metadata
+
+
+@develop.group(short_help="Stubs management")
+@click.pass_context
+def stubs(ctx: Any):
+    """
+    Stubs provide type hints and documentation hints to IDEs and are typically provided
+    inline with the code where a static analyzer can pick them up. In Metaflow's case,
+    however, proper stubs rely on dynamic behavior (ie: the decorators are
+    generated at runtime). This makes it necessary to have separate stub files.
+
+    This CLI provides utilities to check and generate stubs for your current Metaflow
+    installation.
+    """
+
+
+@stubs.command(short_help="Check validity of stubs")
+@click.pass_context
+def check(ctx: Any):
+    """
+    Checks the currently installed stubs (if they exist) and validates that they
+    match the currently installed version of Metaflow.
+    """
+
+    dist_packages, paths = get_packages_for_stubs()
+
+    if len(dist_packages) + len(paths) == 0:
+        return print_status(ctx, "no package provides `metaflow-stubs`", False)
+    if len(dist_packages) + len(paths) == 1:
+        if dist_packages:
+            return print_status(
+                ctx, *internal_check(dist_packages[0][1], dist_packages[0][0])
+            )
+        return print_status(ctx, *internal_check(paths[0]))
+
+    pkg_names = None
+    pkg_paths = None
+    if dist_packages:
+        pkg_names = " packages " + ", ".join([p[0] for p in dist_packages])
+    if paths:
+        pkg_paths = "directories at " + ", ".join(paths)
+    return print_status(
+        ctx,
+        "metaflow-stubs is provided multiple times by%s %s%s"
+        % (
+            pkg_names if pkg_names else "",
+            "and " if pkg_names and pkg_paths else "",
+            pkg_paths if pkg_paths else "",
+        ),
+        False,
+    )
+
+
+@stubs.command(short_help="Remove all packages providing metaflow stubs")
+@click.pass_context
+def remove(ctx: Any):
+    """
+    Removes all packages that provide metaflow-stubs from the current Python environment.
+    """
+    dist_packages, paths = get_packages_for_stubs()
+    if len(dist_packages) + len(paths) == 0:
+        if ctx.obj.quiet:
+            ctx.obj.echo_always("not_installed")
+        else:
+            ctx.obj.echo("No packages provide `metaflow-stubs")
+
+    if paths:
+        raise RuntimeError(
+            "Cannot remove stubs when metaflow-stubs is already provided by a directory. "
+            "Please remove the following and try again: %s" % ", ".join(paths)
+        )
+
+    pkgs_to_remove = [p[0] for p in dist_packages]
+    ctx.obj.echo(
+        "Uninstalling existing packages providing metaflow-stubs: %s"
+        % ", ".join(pkgs_to_remove)
+    )
+
+    subprocess.check_call(
+        [
+            sys.executable,
+            "-m",
+            "pip",
+            "uninstall",
+            "-y",
+            *pkgs_to_remove,
+        ],
+        stderr=subprocess.DEVNULL if ctx.obj.quiet else None,
+        stdout=subprocess.DEVNULL if ctx.obj.quiet else None,
+    )
+    if ctx.obj.quiet:
+        ctx.obj.echo_always("ok")
+    else:
+        ctx.obj.echo("All packages providing metaflow-stubs have been removed.")
+
+
+@stubs.command(short_help="Generate Python stubs")
+@click.pass_context
+@click.option(
+    "--force/--no-force",
+    default=False,
+    show_default=True,
+    help="Force installation of stubs even if they exist and are valid",
+)
+def install(ctx: Any, force: bool):
+    """
+    Generates the Python stubs for Metaflow considering the installed version of
+    Metaflow. The stubs will be generated if they do not exist or do not match the
+    current version of Metaflow and installed in the Python environment.
+    """
+    try:
+        import build
+    except ImportError:
+        raise RuntimeError(
+            "Installing stubs requires 'build' -- please install it and try again"
+        )
+
+    dist_packages, paths = get_packages_for_stubs()
+    if paths:
+        raise RuntimeError(
+            "Cannot install stubs when metaflow-stubs is already provided by a directory. "
+            "Please remove the following and try again: %s" % ", ".join(paths)
+        )
+
+    if len(dist_packages) == 1:
+        if internal_check(dist_packages[0][1])[1] == True and not force:
+            if ctx.obj.quiet:
+                ctx.obj.echo_always("already_installed")
+            else:
+                ctx.obj.echo(
+                    "Metaflow stubs are already installed and valid -- use --force to reinstall"
+                )
+            return
+    mf_version, _ = get_mf_version(True)
+    with tempfile.TemporaryDirectory() as tmp_dir:
+        with open(os.path.join(tmp_dir, "setup.py"), "w") as f:
+            f.write(
+                f"""
+from setuptools import setup, find_namespace_packages
+setup(
+    include_package_data=True,
+    name="metaflow-stubs",
+    version="{mf_version}",
+    description="Metaflow: More Data Science, Less Engineering",
+    author="Metaflow Developers",
+    author_email="help@metaflow.org",
+    license="Apache Software License",
+    packages=find_namespace_packages(),
+    package_data={{"metaflow-stubs": ["generated_for.txt", "py.typed", "**/*.pyi"]}},
+    install_requires=["metaflow=={mf_version}"],
+    python_requires=">=3.6.1",
+)
+                """
+            )
+        with open(os.path.join(tmp_dir, "MANIFEST.in"), "w") as f:
+            f.write(
+                """
+include metaflow-stubs/generated_for.txt
+include metaflow-stubs/py.typed
+global-include *.pyi
+                """
+            )
+
+        StubGenerator(os.path.join(tmp_dir, "metaflow-stubs")).write_out()
+
+        subprocess.check_call(
+            [sys.executable, "-m", "build", "--wheel"],
+            cwd=tmp_dir,
+            stderr=subprocess.DEVNULL if ctx.obj.quiet else None,
+            stdout=subprocess.DEVNULL if ctx.obj.quiet else None,
+        )
+
+        if dist_packages:
+            # We need to uninstall all the other packages first
+            pkgs_to_remove = [p[0] for p in dist_packages]
+            ctx.obj.echo(
+                "Uninstalling existing packages providing metaflow-stubs: %s"
+                % ", ".join(pkgs_to_remove)
+            )
+
+            subprocess.check_call(
+                [
+                    sys.executable,
+                    "-m",
+                    "pip",
+                    "uninstall",
+                    "-y",
+                    *pkgs_to_remove,
+                ],
+                cwd=tmp_dir,
+                stderr=subprocess.DEVNULL if ctx.obj.quiet else None,
+                stdout=subprocess.DEVNULL if ctx.obj.quiet else None,
+            )
+
+        subprocess.check_call(
+            [
+                sys.executable,
+                "-m",
+                "pip",
+                "install",
+                "--force-reinstall",
+                "--no-deps",
+                "--no-index",
+                "--find-links",
+                os.path.join(tmp_dir, "dist"),
+                "metaflow-stubs",
+            ],
+            cwd=tmp_dir,
+            stderr=subprocess.DEVNULL if ctx.obj.quiet else None,
+            stdout=subprocess.DEVNULL if ctx.obj.quiet else None,
+        )
+    if ctx.obj.quiet:
+        ctx.obj.echo_always("installed")
+    else:
+        ctx.obj.echo("Metaflow stubs successfully installed")
+
+
+def split_version(vers: str) -> Tuple[str, Optional[str]]:
+    vers_split = vers.split("+", 1)
+    if len(vers_split) == 1:
+        return vers_split[0], None
+    return vers_split[0], vers_split[1]
+
+
+def get_mf_version(public: bool = False) -> Tuple[str, Optional[str]]:
+    from metaflow.metaflow_version import get_version
+
+    return split_version(get_version(public))
+
+
+def get_stubs_version(stubs_root_path: Optional[str]) -> Tuple[str, Optional[str]]:
+    if stubs_root_path is None:
+        # The stubs are NOT an integrated part of metaflow
+        return None, None
+    if not os.path.isfile(os.path.join(stubs_root_path, "generated_for.txt")):
+        return None, None
+
+    with open(
+        os.path.join(stubs_root_path, "generated_for.txt"), "r", encoding="utf-8"
+    ) as f:
+        return split_version(f.read().strip().split(" ", 1)[0])
+
+
+def internal_check(stubs_path: str, pkg_name: Optional[str] = None) -> Tuple[str, bool]:
+    mf_version = get_mf_version()
+    stub_version = get_stubs_version(stubs_path)
+
+    if stub_version == (None, None):
+        return "the installed stubs package does not seem valid", False
+    elif stub_version != mf_version:
+        return (
+            "the stubs package was generated for Metaflow version %s%s "
+            "but you have Metaflow version %s%s installed."
+            % (
+                stub_version[0],
+                " and extensions %s" % stub_version[1] if stub_version[1] else "",
+                mf_version[0],
+                " and extensions %s" % mf_version[1] if mf_version[1] else "",
+            ),
+            False,
+        )
+    return (
+        "the stubs package %s matches your current Metaflow version"
+        % (pkg_name if pkg_name else "installed at '%s'" % stubs_path),
+        True,
+    )
+
+
+def get_packages_for_stubs() -> Tuple[List[Tuple[str, str]], List[str]]:
+    """
+    Gets the packages that provide metaflow-stubs.
+
+    This returns two lists:
+      - the first list contains tuples of package names and root path for the package
+      - the second list contains all non package names (ie: things in path for example)
+
+    Returns
+    -------
+    Tuple[List[Tuple[str, str]], Optional[List[Tuple[str, str]]]]
+        Packages or paths providing metaflow-stubs
+    """
+    try:
+        m = importlib.import_module("metaflow-stubs")
+        all_paths = set(m.__path__)
+    except:
+        return [], []
+
+    dist_list = []
+
+    # We check the type because if the user has multiple importlib metadata, for
+    # some reason it shows up multiple times.
+    interesting_dists = [
+        d
+        for d in metadata.distributions()
+        if any(
+            [
+                p == "metaflow-stubs"
+                for p in (d.read_text("top_level.txt") or "").split()
+            ]
+        )
+        and isinstance(d, metadata.PathDistribution)
+    ]
+
+    for dist in interesting_dists:
+        # This is a package we care about
+        root_path = dist.locate_file("metaflow-stubs").as_posix()
+        dist_list.append((dist.metadata["Name"], root_path))
+        all_paths.discard(root_path)
+    return dist_list, list(all_paths)
+
+
+def print_status(ctx: click.Context, msg: str, valid: bool):
+    if ctx.obj.quiet:
+        ctx.obj.echo_always("valid" if valid else "invalid")
+    else:
+        ctx.obj.echo("Metaflow stubs are ", nl=False)
+        if valid:
+            ctx.obj.echo("valid", fg="green", nl=False)
+        else:
+            ctx.obj.echo("invalid", fg="red", nl=False)
+        ctx.obj.echo(": " + msg)
+    return
diff --git a/metaflow/cmd/main_cli.py b/metaflow/cmd/main_cli.py
index 0ba635086f0..c552b32a24b 100644
--- a/metaflow/cmd/main_cli.py
+++ b/metaflow/cmd/main_cli.py
@@ -8,9 +8,11 @@
 from metaflow.metaflow_version import get_version
 
 from .util import echo_always
+import metaflow.tracing as tracing
 
 
 @click.group()
+@tracing.cli("cli/main")
 def main():
     pass
 
@@ -61,7 +63,12 @@ def status():
         echo("* %s" % flow, fg="cyan")
 
 
-CMDS_DESC = [("configure", ".configure_cmd.cli"), ("tutorials", ".tutorials_cmd.cli")]
+CMDS_DESC = [
+    ("configure", ".configure_cmd.cli"),
+    ("tutorials", ".tutorials_cmd.cli"),
+    ("develop", ".develop.cli"),
+    ("code", ".code.cli"),
+]
 
 process_cmds(globals())
 
@@ -78,15 +85,16 @@ def start(ctx):
 
     import metaflow
 
+    version = get_version()
     echo("Metaflow ", fg="magenta", bold=True, nl=False)
 
     if ctx.invoked_subcommand is None:
-        echo("(%s): " % get_version(), fg="magenta", bold=False, nl=False)
+        echo("(%s): " % version, fg="magenta", bold=False, nl=False)
     else:
-        echo("(%s)\n" % get_version(), fg="magenta", bold=False)
+        echo("(%s)\n" % version, fg="magenta", bold=False)
 
     if ctx.invoked_subcommand is None:
-        echo("More data science, less engineering\n", fg="magenta")
+        echo("More AI, less engineering\n", fg="magenta")
 
         lnk_sz = max(len(lnk) for lnk in CONTACT_INFO.values()) + 1
         for what, lnk in CONTACT_INFO.items():
diff --git a/metaflow/cmd/make_wrapper.py b/metaflow/cmd/make_wrapper.py
new file mode 100644
index 00000000000..7bded1a2161
--- /dev/null
+++ b/metaflow/cmd/make_wrapper.py
@@ -0,0 +1,48 @@
+import sys
+import subprocess
+from pathlib import Path
+import sysconfig
+import site
+
+
+def find_makefile():
+    possible_dirs = []
+
+    # 1) The standard sysconfig-based location
+    data_dir = sysconfig.get_paths()["data"]
+    possible_dirs.append(Path(data_dir) / "share" / "metaflow" / "devtools")
+
+    # 2) The user base (e.g. ~/.local on many systems)
+    user_base = site.getuserbase()  # e.g. /home/runner/.local
+    possible_dirs.append(Path(user_base) / "share" / "metaflow" / "devtools")
+
+    # 3) site-packages can vary, we can guess share/.. near each site-packages
+    # (Works if pip actually placed devtools near site-packages.)
+    for p in site.getsitepackages():
+        possible_dirs.append(Path(p).parent / "share" / "metaflow" / "devtools")
+    user_site = site.getusersitepackages()
+    possible_dirs.append(Path(user_site).parent / "share" / "metaflow" / "devtools")
+
+    for candidate_dir in possible_dirs:
+        makefile_candidate = candidate_dir / "Makefile"
+        if makefile_candidate.is_file():
+            return makefile_candidate
+
+    return None
+
+
+def main():
+    makefile_path = find_makefile()
+    if not makefile_path:
+        print("ERROR: Could not find executable in any known location.")
+        sys.exit(1)
+    cmd = ["make", "-f", str(makefile_path)] + sys.argv[1:]
+
+    try:
+        completed = subprocess.run(cmd, check=True)
+        sys.exit(completed.returncode)
+    except subprocess.CalledProcessError as ex:
+        sys.exit(ex.returncode)
+    except KeyboardInterrupt:
+        print("Process interrupted by user. Exiting cleanly.")
+        sys.exit(1)
diff --git a/metaflow/datastore/content_addressed_store.py b/metaflow/datastore/content_addressed_store.py
index e88ca1275de..e0533565ffa 100644
--- a/metaflow/datastore/content_addressed_store.py
+++ b/metaflow/datastore/content_addressed_store.py
@@ -133,7 +133,7 @@ def load_blobs(self, keys, force_raw=False):
                 load_paths.append((key, path))
 
         with self._storage_impl.load_bytes([p for _, p in load_paths]) as loaded:
-            for (path_key, file_path, meta) in loaded:
+            for path_key, file_path, meta in loaded:
                 key = self._storage_impl.path_split(path_key)[-1]
                 # At this point, we either return the object as is (if raw) or
                 # decode it according to the encoding version
diff --git a/metaflow/datastore/datastore_set.py b/metaflow/datastore/datastore_set.py
index e8561f8d134..f60642de73f 100644
--- a/metaflow/datastore/datastore_set.py
+++ b/metaflow/datastore/datastore_set.py
@@ -22,7 +22,7 @@ def __init__(
         prefetch_data_artifacts=None,
         allow_not_done=False,
     ):
-        self.task_datastores = flow_datastore.get_latest_task_datastores(
+        self.task_datastores = flow_datastore.get_task_datastores(
             run_id, steps=steps, pathspecs=pathspecs, allow_not_done=allow_not_done
         )
 
diff --git a/metaflow/datastore/flow_datastore.py b/metaflow/datastore/flow_datastore.py
index 6bddfce9e86..16318ed7693 100644
--- a/metaflow/datastore/flow_datastore.py
+++ b/metaflow/datastore/flow_datastore.py
@@ -13,7 +13,7 @@ class FlowDataStore(object):
     def __init__(
         self,
         flow_name,
-        environment,
+        environment=None,
         metadata=None,
         event_logger=None,
         monitor=None,
@@ -31,7 +31,7 @@ def __init__(
         ----------
         flow_name : str
             The name of the flow
-        environment : MetaflowEnvironment
+        environment : MetaflowEnvironment, optional
             Environment this datastore is operating in
         metadata : MetadataProvider, optional
             The metadata provider to use and update if needed, by default None
@@ -67,8 +67,15 @@ def __init__(
     def datastore_root(self):
         return self._storage_impl.datastore_root
 
-    def get_latest_task_datastores(
-        self, run_id=None, steps=None, pathspecs=None, allow_not_done=False
+    def get_task_datastores(
+        self,
+        run_id=None,
+        steps=None,
+        pathspecs=None,
+        allow_not_done=False,
+        attempt=None,
+        include_prior=False,
+        mode="r",
     ):
         """
         Return a list of TaskDataStore for a subset of the tasks.
@@ -93,6 +100,12 @@ def get_latest_task_datastores(
         allow_not_done : bool, optional
             If True, returns the latest attempt of a task even if that attempt
             wasn't marked as done, by default False
+        attempt : int, optional
+            Attempt number of the tasks to return.  If not provided, returns latest attempt.
+        include_prior : boolean, default False
+            If True, returns all attempts up to and including attempt.
+        mode : str, default "r"
+            Mode to initialize the returned TaskDataStores in.
 
         Returns
         -------
@@ -126,8 +139,13 @@ def get_latest_task_datastores(
                 if task.is_file is False
             ]
         urls = []
+        # parse content urls for specific attempt only, or for all attempts in max range
+        attempt_range = range(metaflow_config.MAX_ATTEMPTS)
+        # we have no reason to check for attempts greater than MAX_ATTEMPTS, as they do not exist.
+        if attempt is not None and attempt <= metaflow_config.MAX_ATTEMPTS - 1:
+            attempt_range = range(attempt + 1) if include_prior else [attempt]
         for task_url in task_urls:
-            for attempt in range(metaflow_config.MAX_ATTEMPTS):
+            for attempt in attempt_range:
                 for suffix in [
                     TaskDataStore.METADATA_DATA_SUFFIX,
                     TaskDataStore.METADATA_ATTEMPT_SUFFIX,
@@ -168,11 +186,19 @@ def get_latest_task_datastores(
             for (run, step, task), attempt in latest_started_attempts.items()
         )
         if allow_not_done:
-            latest_to_fetch = latest_started_attempts
+            latest_to_fetch = (
+                done_attempts.union(latest_started_attempts)
+                if include_prior
+                else latest_started_attempts
+            )
         else:
-            latest_to_fetch = latest_started_attempts & done_attempts
+            latest_to_fetch = (
+                done_attempts
+                if include_prior
+                else (latest_started_attempts & done_attempts)
+            )
         latest_to_fetch = [
-            (v[0], v[1], v[2], v[3], data_objs.get(v), "r", allow_not_done)
+            (v[0], v[1], v[2], v[3], data_objs.get(v), mode, allow_not_done)
             for v in latest_to_fetch
         ]
         return list(itertools.starmap(self.get_task_datastore, latest_to_fetch))
diff --git a/metaflow/datastore/task_datastore.py b/metaflow/datastore/task_datastore.py
index 3f003173a0f..8d5c39c0dde 100644
--- a/metaflow/datastore/task_datastore.py
+++ b/metaflow/datastore/task_datastore.py
@@ -10,7 +10,7 @@
 
 from .. import metaflow_config
 from ..exception import MetaflowInternalError
-from ..metadata import DataArtifact, MetaDatum
+from ..metadata_provider import DataArtifact, MetaDatum
 from ..parameters import Parameter
 from ..util import Path, is_stringish, to_fileobj
 
@@ -99,7 +99,6 @@ def __init__(
         mode="r",
         allow_not_done=False,
     ):
-
         self._storage_impl = flow_datastore._storage_impl
         self.TYPE = self._storage_impl.TYPE
         self._ca_store = flow_datastore.ca_store
@@ -118,7 +117,7 @@ def __init__(
         # The GZIP encodings are for backward compatibility
         self._encodings = {"pickle-v2", "gzip+pickle-v2"}
         ver = sys.version_info[0] * 10 + sys.version_info[1]
-        if ver >= 34:
+        if ver >= 36:
             self._encodings.add("pickle-v4")
             self._encodings.add("gzip+pickle-v4")
 
@@ -173,6 +172,26 @@ def __init__(
                 if data_obj is not None:
                     self._objects = data_obj.get("objects", {})
                     self._info = data_obj.get("info", {})
+        elif self._mode == "d":
+            self._objects = {}
+            self._info = {}
+
+            if self._attempt is None:
+                for i in range(metaflow_config.MAX_ATTEMPTS):
+                    check_meta = self._metadata_name_for_attempt(
+                        self.METADATA_ATTEMPT_SUFFIX, i
+                    )
+                    if self.has_metadata(check_meta, add_attempt=False):
+                        self._attempt = i
+
+            # Do not allow destructive operations on the datastore if attempt is still in flight
+            # and we explicitly did not allow operating on running tasks.
+            if not allow_not_done and not self.has_metadata(self.METADATA_DONE_SUFFIX):
+                raise DataException(
+                    "No completed attempts of the task was found for task '%s'"
+                    % self._path
+                )
+
         else:
             raise DataException("Unknown datastore mode: '%s'" % self._mode)
 
@@ -233,7 +252,7 @@ def init_task(self):
 
     @only_if_not_done
     @require_mode("w")
-    def save_artifacts(self, artifacts_iter, force_v4=False, len_hint=0):
+    def save_artifacts(self, artifacts_iter, len_hint=0):
         """
         Saves Metaflow Artifacts (Python objects) to the datastore and stores
         any relevant metadata needed to retrieve them.
@@ -249,11 +268,6 @@ def save_artifacts(self, artifacts_iter, force_v4=False, len_hint=0):
         artifacts : Iterator[(string, object)]
             Iterator over the human-readable name of the object to save
             and the object itself
-        force_v4 : boolean or Dict[string -> boolean]
-            Indicates whether the artifact should be pickled using the v4
-            version of pickle. If a single boolean, applies to all artifacts.
-            If a dictionary, applies to the object named only. Defaults to False
-            if not present or not specified
         len_hint: integer
             Estimated number of items in artifacts_iter
         """
@@ -261,40 +275,24 @@ def save_artifacts(self, artifacts_iter, force_v4=False, len_hint=0):
 
         def pickle_iter():
             for name, obj in artifacts_iter:
-                do_v4 = (
-                    force_v4 and force_v4
-                    if isinstance(force_v4, bool)
-                    else force_v4.get(name, False)
-                )
-                if do_v4:
-                    encode_type = "gzip+pickle-v4"
-                    if encode_type not in self._encodings:
-                        raise DataException(
-                            "Artifact *%s* requires a serialization encoding that "
-                            "requires Python 3.4 or newer." % name
-                        )
+                encode_type = "gzip+pickle-v4"
+                if encode_type in self._encodings:
                     try:
                         blob = pickle.dumps(obj, protocol=4)
                     except TypeError as e:
-                        raise UnpicklableArtifactException(name)
+                        raise UnpicklableArtifactException(name) from e
                 else:
                     try:
                         blob = pickle.dumps(obj, protocol=2)
                         encode_type = "gzip+pickle-v2"
-                    except (SystemError, OverflowError):
-                        encode_type = "gzip+pickle-v4"
-                        if encode_type not in self._encodings:
-                            raise DataException(
-                                "Artifact *%s* is very large (over 2GB). "
-                                "You need to use Python 3.4 or newer if you want to "
-                                "serialize large objects." % name
-                            )
-                        try:
-                            blob = pickle.dumps(obj, protocol=4)
-                        except TypeError as e:
-                            raise UnpicklableArtifactException(name)
+                    except (SystemError, OverflowError) as e:
+                        raise DataException(
+                            "Artifact *%s* is very large (over 2GB). "
+                            "You need to use Python 3.6 or newer if you want to "
+                            "serialize large objects." % name
+                        ) from e
                     except TypeError as e:
-                        raise UnpicklableArtifactException(name)
+                        raise UnpicklableArtifactException(name) from e
 
                 self._info[name] = {
                     "size": len(blob),
@@ -353,7 +351,7 @@ def load_artifacts(self, names):
                 encode_type = "gzip+pickle-v2"
             if encode_type not in self._encodings:
                 raise DataException(
-                    "Python 3.4 or later is required to load artifact '%s'" % name
+                    "Python 3.6 or later is required to load artifact '%s'" % name
                 )
             else:
                 to_load[self._objects[name]].append(name)
@@ -361,7 +359,7 @@ def load_artifacts(self, names):
         # We assume that if we have one "old" style artifact, all of them are
         # like that which is an easy assumption to make since artifacts are all
         # stored by the same implementation of the datastore for a given task.
-        for (key, blob) in self._ca_store.load_blobs(to_load.keys()):
+        for key, blob in self._ca_store.load_blobs(to_load.keys()):
             names = to_load[key]
             for name in names:
                 # We unpickle everytime to have fully distinct objects (the user
@@ -750,6 +748,36 @@ def save_logs(self, logsource, stream_data):
                 to_store_dict[n] = data
         self._save_file(to_store_dict)
 
+    @require_mode("d")
+    def scrub_logs(self, logsources, stream, attempt_override=None):
+        path_logsources = {
+            self._metadata_name_for_attempt(
+                self._get_log_location(s, stream),
+                attempt_override=attempt_override,
+            ): s
+            for s in logsources
+        }
+
+        # Legacy log paths
+        legacy_log = self._metadata_name_for_attempt(
+            "%s.log" % stream, attempt_override
+        )
+        path_logsources[legacy_log] = stream
+
+        existing_paths = [
+            path
+            for path in path_logsources.keys()
+            if self.has_metadata(path, add_attempt=False)
+        ]
+
+        # Replace log contents with [REDACTED source stream]
+        to_store_dict = {
+            path: bytes("[REDACTED %s %s]" % (path_logsources[path], stream), "utf-8")
+            for path in existing_paths
+        }
+
+        self._save_file(to_store_dict, add_attempt=False, allow_overwrite=True)
+
     @require_mode("r")
     def load_log_legacy(self, stream, attempt_override=None):
         """
diff --git a/metaflow/debug.py b/metaflow/debug.py
index fbfaca95dc8..1462df2bb7a 100644
--- a/metaflow/debug.py
+++ b/metaflow/debug.py
@@ -42,6 +42,11 @@ def log(self, typ, args):
         filename = inspect.stack()[1][1]
         print("debug[%s %s:%s]: %s" % (typ, filename, lineno, s), file=sys.stderr)
 
+    def __getattr__(self, name):
+        # Small piece of code to get pyright and other linters to recognize that there
+        # are dynamic attributes.
+        return getattr(self, name)
+
     def noop(self, args):
         pass
 
diff --git a/metaflow/decorators.py b/metaflow/decorators.py
index ebdd77ebd7e..583f8a4515e 100644
--- a/metaflow/decorators.py
+++ b/metaflow/decorators.py
@@ -1,24 +1,34 @@
-from functools import partial
+import importlib
 import json
 import re
-import os
-import sys
 
-from .flowspec import FlowSpec
+from functools import partial
+from typing import Any, Callable, Dict, List, NewType, Tuple, TypeVar, Union, overload
+
+from .flowspec import FlowSpec, _FlowState
 from .exception import (
     MetaflowInternalError,
     MetaflowException,
     InvalidDecoratorAttribute,
 )
 
-from metaflow._vendor import click
-
+from .debug import debug
+from .parameters import current_flow
+from .user_configs.config_parameters import (
+    UNPACK_KEY,
+    resolve_delayed_evaluator,
+    unpack_delayed_evaluator,
+)
+from .user_decorators.mutable_flow import MutableFlow
+from .user_decorators.mutable_step import MutableStep
+from .user_decorators.user_flow_decorator import FlowMutator, FlowMutatorMeta
+from .user_decorators.user_step_decorator import (
+    StepMutator,
+    UserStepDecoratorBase,
+    UserStepDecoratorMeta,
+)
 
-try:
-    unicode
-except NameError:
-    unicode = str
-    basestring = str
+from metaflow._vendor import click
 
 
 class BadStepDecoratorException(MetaflowException):
@@ -51,11 +61,14 @@ class UnknownStepDecoratorException(MetaflowException):
     headline = "Unknown step decorator"
 
     def __init__(self, deconame):
-        from .plugins import STEP_DECORATORS
-
         decos = ", ".join(
-            t.name for t in STEP_DECORATORS if not t.name.endswith("_internal")
+            [
+                x
+                for x in UserStepDecoratorMeta.all_decorators().keys()
+                if not x.endswith("_internal")
+            ]
         )
+
         msg = (
             "Unknown step decorator *{deconame}*. The following decorators are "
             "supported: *{decos}*".format(deconame=deconame, decos=decos)
@@ -80,9 +93,7 @@ class UnknownFlowDecoratorException(MetaflowException):
     headline = "Unknown flow decorator"
 
     def __init__(self, deconame):
-        from .plugins import FLOW_DECORATORS
-
-        decos = ", ".join(t.name for t in FLOW_DECORATORS)
+        decos = ", ".join(FlowMutatorMeta.all_decorators().keys())
         msg = (
             "Unknown flow decorator *{deconame}*. The following decorators are "
             "supported: *{decos}*".format(deconame=deconame, decos=decos)
@@ -111,25 +122,52 @@ class Decorator(object):
     # `allow_multiple` allows setting many decorators of the same type to a step/flow.
     allow_multiple = False
 
-    def __init__(self, attributes=None, statically_defined=False):
+    def __init__(self, attributes=None, statically_defined=False, inserted_by=None):
         self.attributes = self.defaults.copy()
         self.statically_defined = statically_defined
+        self.inserted_by = inserted_by
+        self._user_defined_attributes = set()
+        self._ran_init = False
 
         if attributes:
             for k, v in attributes.items():
-                if k in self.defaults:
+                if k in self.defaults or k.startswith(UNPACK_KEY):
                     self.attributes[k] = v
+                    if not k.startswith(UNPACK_KEY):
+                        self._user_defined_attributes.add(k)
                 else:
                     raise InvalidDecoratorAttribute(self.name, k, self.defaults)
 
+    def init(self):
+        """
+        Initializes the decorator. In general, any operation you would do in __init__
+        should be done here.
+        """
+        pass
+
+    def external_init(self):
+        # In some cases (specifically when using remove_decorator), we may need to call
+        # init multiple times. Short-circuit re-evaluating.
+        if self._ran_init:
+            return
+
+        # Note that by design, later values override previous ones.
+        self.attributes, new_user_attributes = unpack_delayed_evaluator(self.attributes)
+        self._user_defined_attributes.update(new_user_attributes)
+        self.attributes = resolve_delayed_evaluator(self.attributes, to_dict=True)
+
+        if "init" in self.__class__.__dict__:
+            self.init()
+        self._ran_init = True
+
     @classmethod
-    def _parse_decorator_spec(cls, deco_spec):
+    def extract_args_kwargs_from_decorator_spec(cls, deco_spec):
         if len(deco_spec) == 0:
-            return cls()
+            return [], {}
 
         attrs = {}
         # TODO: Do we really want to allow spaces in the names of attributes?!?
-        for a in re.split(""",(?=[\s\w]+=)""", deco_spec):
+        for a in re.split(r""",(?=[\s\w]+=)""", deco_spec):
             name, val = a.split("=", 1)
             try:
                 val_parsed = json.loads(val.strip().replace('\\"', '"'))
@@ -145,9 +183,20 @@ def _parse_decorator_spec(cls, deco_spec):
                         val_parsed = val.strip()
 
             attrs[name.strip()] = val_parsed
-        return cls(attributes=attrs)
+
+        return [], attrs
+
+    @classmethod
+    def parse_decorator_spec(cls, deco_spec):
+        if len(deco_spec) == 0:
+            return cls()
+
+        _, kwargs = cls.extract_args_kwargs_from_decorator_spec(deco_spec)
+        return cls(attributes=kwargs)
 
     def make_decorator_spec(self):
+        # Make sure all attributes are evaluated
+        self.external_init()
         attrs = {k: v for k, v in self.attributes.items() if v is not None}
         if attrs:
             attr_list = []
@@ -155,17 +204,31 @@ def make_decorator_spec(self):
             # escaping but for more complex types (typically dictionaries or lists),
             # we dump using JSON.
             for k, v in attrs.items():
-                if isinstance(v, (int, float, unicode, basestring)):
+                if isinstance(v, (int, float, str)):
                     attr_list.append("%s=%s" % (k, str(v)))
                 else:
                     attr_list.append("%s=%s" % (k, json.dumps(v).replace('"', '\\"')))
+
             attrstr = ",".join(attr_list)
             return "%s:%s" % (self.name, attrstr)
         else:
             return self.name
 
+    def get_args_kwargs(self) -> Tuple[List[Any], Dict[str, Any]]:
+        """
+        Get the arguments and keyword arguments of the decorator.
+
+        Returns
+        -------
+        Tuple[List[Any], Dict[str, Any]]
+            A tuple containing a list of arguments and a dictionary of keyword arguments.
+        """
+        return [], dict(self.attributes)
+
     def __str__(self):
-        mode = "decorated" if self.statically_defined else "cli"
+        mode = "static" if self.statically_defined else "dynamic"
+        if self.inserted_by:
+            mode += " (inserted by %s)" % " from ".join(self.inserted_by)
         attrs = " ".join("%s=%s" % x for x in self.attributes.items())
         if attrs:
             attrs = " " + attrs
@@ -174,14 +237,9 @@ def __str__(self):
 
 
 class FlowDecorator(Decorator):
-
-    _flow_decorators = []
     options = {}
 
     def __init__(self, *args, **kwargs):
-        # Note that this assumes we are executing one flow per process, so we have a global list of
-        # _flow_decorators. A similar setup is used in parameters.
-        self._flow_decorators.append(self)
         super(FlowDecorator, self).__init__(*args, **kwargs)
 
     def flow_init(
@@ -206,8 +264,14 @@ def get_top_level_options(self):
 
 # compare this to parameters.add_custom_parameters
 def add_decorator_options(cmd):
+    flow_cls = getattr(current_flow, "flow_cls", None)
+    if flow_cls is None:
+        return cmd
+
     seen = {}
-    for deco in flow_decorators():
+    existing_params = set(p.name.lower() for p in cmd.params)
+    # Add decorator options
+    for deco in flow_decorators(flow_cls):
         for option, kwargs in deco.options.items():
             if option in seen:
                 msg = (
@@ -217,14 +281,20 @@ def add_decorator_options(cmd):
                     % (deco.name, option, seen[option])
                 )
                 raise MetaflowInternalError(msg)
+            elif deco.name.lower() in existing_params:
+                raise MetaflowInternalError(
+                    "Flow decorator '%s' uses an option '%s' which is a reserved "
+                    "keyword. Please use a different option name." % (deco.name, option)
+                )
             else:
+                kwargs["envvar"] = "METAFLOW_FLOW_%s" % option.upper()
                 seen[option] = deco.name
                 cmd.params.insert(0, click.Option(("--" + option,), **kwargs))
     return cmd
 
 
-def flow_decorators():
-    return FlowDecorator._flow_decorators
+def flow_decorators(flow_cls):
+    return [d for deco_list in flow_cls._flow_decorators.values() for d in deco_list]
 
 
 class StepDecorator(Decorator):
@@ -273,15 +343,36 @@ def package_init(self, flow, step_name, environment):
 
     def add_to_package(self):
         """
-        Called to add custom packages needed for a decorator. This hook will be
+        Called to add custom files needed for this environment. This hook will be
         called in the `MetaflowPackage` class where metaflow compiles the code package
-        tarball. This hook is invoked in the `MetaflowPackage`'s `path_tuples`
-        function. The `path_tuples` function is a generator that yields a tuple of
-        `(file_path, arcname)`.`file_path` is the path of the file in the local file system;
-        the `arcname` is the path of the file in the constructed tarball or the path of the file
-        after decompressing the tarball.
-
-        Returns a list of tuples where each tuple represents (file_path, arcname)
+        tarball. This hook can return one of two things (the first is for backwards
+        compatibility -- move to the second):
+          - a generator yielding a tuple of `(file_path, arcname)` to add files to
+            the code package. `file_path` is the path to the file on the local filesystem
+            and `arcname` is the path relative to the packaged code.
+          - a generator yielding a tuple of `(content, arcname, type)` where:
+            - type is one of
+            ContentType.{USER_CONTENT, CODE_CONTENT, MODULE_CONTENT, OTHER_CONTENT}
+            - for USER_CONTENT:
+              - the file will be included relative to the directory containing the
+                user's flow file.
+              - content: path to the file to include
+              - arcname: path relative to the directory containing the user's flow file
+            - for CODE_CONTENT:
+              - the file will be included relative to the code directory in the package.
+                This will be the directory containing `metaflow`.
+              - content: path to the file to include
+              - arcname: path relative to the code directory in the package
+            - for MODULE_CONTENT:
+              - the module will be added to the code package as a python module. It will
+                be accessible as usual (import )
+              - content: name of the module
+              - arcname: None (ignored)
+            - for OTHER_CONTENT:
+              - the file will be included relative to any other configuration/metadata
+                files for the flow
+              - content: path to the file to include
+              - arcname: path relative to the config directory in the package
         """
         return []
 
@@ -425,10 +516,13 @@ def _base_step_decorator(decotype, *args, **kwargs):
     Decorator prototype for all step decorators. This function gets specialized
     and imported for all decorators types by _import_plugin_decorators().
     """
+
     if args:
         # No keyword arguments specified for the decorator, e.g. @foobar.
         # The first argument is the function to be decorated.
         func = args[0]
+        if isinstance(func, StepMutator):
+            func = func._my_step
         if not hasattr(func, "is_step"):
             raise BadStepDecoratorException(decotype.name, func)
 
@@ -452,6 +546,80 @@ def wrap(f):
         return wrap
 
 
+_all_step_decos = None
+_all_flow_decos = None
+
+
+def get_all_step_decos():
+    global _all_step_decos
+    if _all_step_decos is None:
+        from .plugins import STEP_DECORATORS
+
+        _all_step_decos = {decotype.name: decotype for decotype in STEP_DECORATORS}
+    return _all_step_decos
+
+
+def get_all_flow_decos():
+    global _all_flow_decos
+    if _all_flow_decos is None:
+        from .plugins import FLOW_DECORATORS
+
+        _all_flow_decos = {decotype.name: decotype for decotype in FLOW_DECORATORS}
+    return _all_flow_decos
+
+
+def extract_step_decorator_from_decospec(decospec: str):
+    splits = decospec.split(":", 1)
+    deconame = splits[0]
+
+    # Check if it is a user-defined decorator or metaflow decorator
+    deco_cls = UserStepDecoratorMeta.get_decorator_by_name(deconame)
+    if deco_cls is not None:
+        return (
+            deco_cls.parse_decorator_spec(splits[1] if len(splits) > 1 else ""),
+            len(splits) > 1,
+        )
+
+    # Check if this is a decorator we can import
+    if "." in deconame:
+        # We consider this to be a import path to a user decorator so
+        # something like "my_package.my_decorator"
+        module_name, class_name = deconame.rsplit(".", 1)
+        try:
+            module = importlib.import_module(module_name)
+        except ImportError as e:
+            raise MetaflowException(
+                "Could not import user decorator %s" % deconame
+            ) from e
+        deco_cls = getattr(module, class_name, None)
+        if (
+            deco_cls is None
+            or not isinstance(deco_cls, type)
+            or not issubclass(deco_cls, UserStepDecoratorBase)
+        ):
+            raise UnknownStepDecoratorException(deconame)
+        return (
+            deco_cls.parse_decorator_spec(splits[1] if len(splits) > 1 else ""),
+            len(splits) > 1,
+        )
+
+    raise UnknownStepDecoratorException(deconame)
+
+
+def extract_flow_decorator_from_decospec(decospec: str):
+    splits = decospec.split(":", 1)
+    deconame = splits[0]
+    # Check if it is a user-defined decorator or metaflow decorator
+    deco_cls = FlowMutatorMeta.get_decorator_by_name(deconame)
+    if deco_cls is not None:
+        return (
+            deco_cls.parse_decorator_spec(splits[1] if len(splits) > 1 else ""),
+            len(splits) > 1,
+        )
+    else:
+        raise UnknownFlowDecoratorException(deconame)
+
+
 def _attach_decorators(flow, decospecs):
     """
     Attach decorators to all steps during runtime. This has the same
@@ -464,6 +632,7 @@ def _attach_decorators(flow, decospecs):
     #
     # Note that each step gets its own instance of the decorator class,
     # so decorator can maintain step-specific state.
+
     for step in flow:
         _attach_decorators_to_step(step, decospecs)
 
@@ -474,28 +643,33 @@ def _attach_decorators_to_step(step, decospecs):
     effect as if you defined the decorators statically in the source for
     the step.
     """
-    from .plugins import STEP_DECORATORS
+    for decospec in decospecs:
+        step_deco, _ = extract_step_decorator_from_decospec(decospec)
+        if isinstance(step_deco, StepDecorator):
+            # Check multiple
+            if (
+                step_deco.name not in [deco.name for deco in step.decorators]
+                or step_deco.allow_multiple
+            ):
+                step.decorators.append(step_deco)
+            # Else it is ignored -- this is a non-static decorator
 
-    decos = {decotype.name: decotype for decotype in STEP_DECORATORS}
+        else:
+            step_deco.add_or_raise(step, False, 1, None)
 
-    for decospec in decospecs:
-        splits = decospec.split(":", 1)
-        deconame = splits[0]
-        if deconame not in decos:
-            raise UnknownStepDecoratorException(deconame)
-        # Attach the decorator to step if it doesn't have the decorator
-        # already. This means that statically defined decorators are always
-        # preferred over runtime decorators.
-        if (
-            deconame not in [deco.name for deco in step.decorators]
-            or decos[deconame].allow_multiple
-        ):
-            # if the decorator is present in a step and is of type allow_multiple
-            # then add the decorator to the step
-            deco = decos[deconame]._parse_decorator_spec(
-                splits[1] if len(splits) > 1 else ""
-            )
-            step.decorators.append(deco)
+
+def _init(flow, only_non_static=False):
+    for decorators in flow._flow_decorators.values():
+        for deco in decorators:
+            deco.external_init()
+
+    for flowstep in flow:
+        for deco in flowstep.decorators:
+            deco.external_init()
+        for deco in flowstep.config_decorators or []:
+            deco.external_init()
+        for deco in flowstep.wrappers or []:
+            deco.external_init()
 
 
 def _init_flow_decorators(
@@ -519,8 +693,13 @@ def _init_flow_decorators(
                 )
         else:
             # Each "non-multiple" flow decorator is only allowed to have one set of options
+            # Note that there may be no deco_options if a MutableFlow config injected
+            # the decorator.
             deco_flow_init_options = {
-                option: deco_options[option] for option in deco.options
+                option: deco_options.get(
+                    option.replace("-", "_"), option_info["default"]
+                )
+                for option, option_info in deco.options.items()
             }
         for deco in decorators:
             deco.flow_init(
@@ -536,6 +715,74 @@ def _init_flow_decorators(
 
 
 def _init_step_decorators(flow, graph, environment, flow_datastore, logger):
+    # NOTE: We don't need graph but keeping it for backwards compatibility with
+    # extensions that use it directly. We will remove it at some point.
+
+    # We call the mutate method for both the flow and step mutators.
+    cls = flow.__class__
+    # Run all the decorators. We first run the flow-level decorators
+    # and then the step level ones to maintain a consistent order with how
+    # other decorators are run.
+
+    for deco in cls._flow_state.get(_FlowState.FLOW_MUTATORS, []):
+        if isinstance(deco, FlowMutator):
+            inserted_by_value = [deco.decorator_name] + (deco.inserted_by or [])
+            mutable_flow = MutableFlow(
+                cls,
+                pre_mutate=False,
+                statically_defined=deco.statically_defined,
+                inserted_by=inserted_by_value,
+            )
+            # Sanity check to make sure we are applying the decorator to the right
+            # class
+            if not deco._flow_cls == cls and not issubclass(cls, deco._flow_cls):
+                raise MetaflowInternalError(
+                    "FlowMutator registered on the wrong flow -- "
+                    "expected %s but got %s" % (deco._flow_cls.__name__, cls.__name__)
+                )
+            debug.userconf_exec(
+                "Evaluating flow level decorator %s (post)" % deco.__class__.__name__
+            )
+            deco.mutate(mutable_flow)
+            # We reset cached_parameters on the very off chance that the user added
+            # more configurations based on the configuration
+            if _FlowState.CACHED_PARAMETERS in cls._flow_state:
+                del cls._flow_state[_FlowState.CACHED_PARAMETERS]
+        else:
+            raise MetaflowInternalError(
+                "A non FlowMutator found in flow custom decorators"
+            )
+
+    for step in cls._steps:
+        for deco in step.config_decorators:
+            inserted_by_value = [deco.decorator_name] + (deco.inserted_by or [])
+
+            if isinstance(deco, StepMutator):
+                debug.userconf_exec(
+                    "Evaluating step level decorator %s (post) for %s"
+                    % (deco.__class__.__name__, step.name)
+                )
+                deco.mutate(
+                    MutableStep(
+                        cls,
+                        step,
+                        pre_mutate=False,
+                        statically_defined=deco.statically_defined,
+                        inserted_by=inserted_by_value,
+                    )
+                )
+            else:
+                raise MetaflowInternalError(
+                    "A non StepMutator found in step custom decorators"
+                )
+
+        if step.config_decorators:
+            # We remove all mention of the custom step decorator
+            setattr(cls, step.name, step)
+
+    cls._init_graph()
+    graph = flow._graph
+
     for step in flow:
         for deco in step.decorators:
             deco.step_init(
@@ -549,18 +796,67 @@ def _init_step_decorators(flow, graph, environment, flow_datastore, logger):
             )
 
 
-def step(f):
+FlowSpecDerived = TypeVar("FlowSpecDerived", bound=FlowSpec)
+
+# The StepFlag is a "fake" input item to be able to distinguish
+# callables and those that have had a `@step` decorator on them. This enables us
+# to check the ordering of decorators (ie: put @step first) with the type
+# system. There should be a better way to do this with a more flexible type
+# system but this is what works for now with the Python type system
+StepFlag = NewType("StepFlag", bool)
+
+
+@overload
+def step(
+    f: Callable[[FlowSpecDerived], None],
+) -> Callable[[FlowSpecDerived, StepFlag], None]: ...
+
+
+@overload
+def step(
+    f: Callable[[FlowSpecDerived, Any], None],
+) -> Callable[[FlowSpecDerived, Any, StepFlag], None]: ...
+
+
+def step(
+    f: Union[Callable[[FlowSpecDerived], None], Callable[[FlowSpecDerived, Any], None]],
+):
     """
-    The step decorator. Makes a method a step in the workflow.
+    Marks a method in a FlowSpec as a Metaflow Step. Note that this
+    decorator needs to be placed as close to the method as possible (ie:
+    before other decorators).
+
+    In other words, this is valid:
+    ```
+    @batch
+    @step
+    def foo(self):
+        pass
+    ```
+
+    whereas this is not:
+    ```
+    @step
+    @batch
+    def foo(self):
+        pass
+    ```
+
+    Parameters
+    ----------
+    f : Union[Callable[[FlowSpecDerived], None], Callable[[FlowSpecDerived, Any], None]]
+        Function to make into a Metaflow Step
+
+    Returns
+    -------
+    Union[Callable[[FlowSpecDerived, StepFlag], None], Callable[[FlowSpecDerived, Any, StepFlag], None]]
+        Function that is a Metaflow Step
     """
     f.is_step = True
     f.decorators = []
-    try:
-        # python 3
-        f.name = f.__name__
-    except:
-        # python 2
-        f.name = f.__func__.func_name
+    f.config_decorators = []
+    f.wrappers = []
+    f.name = f.__name__
     return f
 
 
diff --git a/metaflow/events.py b/metaflow/events.py
index ef6d216bdac..54c52ecd7d3 100644
--- a/metaflow/events.py
+++ b/metaflow/events.py
@@ -1,6 +1,11 @@
 from collections import OrderedDict, namedtuple
 from datetime import datetime
 
+from typing import List, Optional, TYPE_CHECKING, Union
+
+if TYPE_CHECKING:
+    import metaflow
+
 MetaflowEvent = namedtuple("MetaflowEvent", ["name", "id", "timestamp", "type"])
 MetaflowEvent.__doc__ = """
     Container of metadata that identifies the event that triggered
@@ -50,7 +55,7 @@ def __init__(self, _meta=None):
         ]
 
     @classmethod
-    def from_runs(cls, run_objs):
+    def from_runs(cls, run_objs: List["metaflow.Run"]):
         run_objs.sort(key=lambda x: x.finished_at, reverse=True)
         trigger = Trigger(
             [
@@ -67,7 +72,7 @@ def from_runs(cls, run_objs):
         return trigger
 
     @property
-    def event(self):
+    def event(self) -> Optional[MetaflowEvent]:
         """
         The `MetaflowEvent` object corresponding to the triggering event.
 
@@ -81,7 +86,7 @@ def event(self):
         return next(iter(self._events), None)
 
     @property
-    def events(self):
+    def events(self) -> Optional[List[MetaflowEvent]]:
         """
         The list of `MetaflowEvent` objects correspondings to all the triggering events.
 
@@ -93,7 +98,7 @@ def events(self):
         return list(self._events) or None
 
     @property
-    def run(self):
+    def run(self) -> Optional["metaflow.Run"]:
         """
         The corresponding `Run` object if the triggering event is a Metaflow run.
 
@@ -110,7 +115,7 @@ def run(self):
         return next(iter(self._runs), None)
 
     @property
-    def runs(self):
+    def runs(self) -> Optional[List["metaflow.Run"]]:
         """
         The list of `Run` objects in the triggering events.
         Returns `None` if none of the triggering events are `Run` objects.
@@ -136,9 +141,15 @@ def runs(self):
 
         return list(self._runs) or None
 
-    def __getitem__(self, key):
+    def __getitem__(self, key: str) -> Union["metaflow.Run", MetaflowEvent]:
         """
-        If triggering events are runs, `key` corresponds to the flow name of the triggering run. Returns a triggering `Run` object corresponding to the key. If triggering events are not runs, `key` corresponds to the event name and a `MetaflowEvent` object is returned.
+        If triggering events are runs, `key` corresponds to the flow name of the triggering run.
+        Otherwise, `key` corresponds to the event name and a `MetaflowEvent` object is returned.
+
+        Returns
+        -------
+        Union[Run, MetaflowEvent]
+            `Run` object if triggered by a run. Otherwise returns a `MetaflowEvent`.
         """
         if self.runs:
             for run in self.runs:
@@ -155,8 +166,8 @@ def __iter__(self):
             return iter(self.events)
         return iter([])
 
-    def __contains__(self, id):
+    def __contains__(self, ident: str) -> bool:
         try:
-            return bool(self.__getitem__(id))
+            return bool(self.__getitem__(ident))
         except KeyError:
             return False
diff --git a/metaflow/exception.py b/metaflow/exception.py
index 4b85a2d1b39..f0f021a2a05 100644
--- a/metaflow/exception.py
+++ b/metaflow/exception.py
@@ -43,13 +43,19 @@ def __str__(self):
 class MetaflowException(Exception):
     headline = "Flow failed"
 
-    def __init__(self, msg="", lineno=None):
+    def __init__(self, msg="", lineno=None, source_file=None):
         self.message = msg
         self.line_no = lineno
+        self.source_file = source_file
         super(MetaflowException, self).__init__()
 
     def __str__(self):
-        prefix = "line %d: " % self.line_no if self.line_no else ""
+        prefix = ""
+        if self.source_file:
+            prefix = "%s:" % self.source_file
+        if self.line_no:
+            prefix = "line %d:" % self.line_no
+        prefix = "%s: " % prefix if prefix else ""
         return "%s%s" % (prefix, self.message)
 
 
diff --git a/metaflow/extension_support/__init__.py b/metaflow/extension_support/__init__.py
index ff55652a217..81f5f5be2b3 100644
--- a/metaflow/extension_support/__init__.py
+++ b/metaflow/extension_support/__init__.py
@@ -1,7 +1,6 @@
 from __future__ import print_function
 
 import importlib
-import json
 import os
 import re
 import sys
@@ -11,6 +10,10 @@
 
 from importlib.abc import MetaPathFinder, Loader
 from itertools import chain
+from pathlib import Path
+
+from metaflow.meta_files import read_info_file
+
 
 #
 # This file provides the support for Metaflow's extension mechanism which allows
@@ -59,6 +62,9 @@
     "load_module",
     "get_modules",
     "dump_module_info",
+    "get_extensions_in_dir",
+    "extension_info",
+    "update_package_info",
     "get_aliased_modules",
     "package_mfext_package",
     "package_mfext_all",
@@ -80,9 +86,14 @@
 # To get verbose messages, set METAFLOW_DEBUG_EXT to 1
 DEBUG_EXT = os.environ.get("METAFLOW_DEBUG_EXT", False)
 
+# This is extracted only from environment variable and here separately from
+# metaflow_config to prevent nasty circular dependencies
+EXTENSIONS_SEARCH_DIRS = os.environ.get("METAFLOW_EXTENSIONS_SEARCH_DIRS", "").split(
+    os.pathsep
+)
 
 MFExtPackage = namedtuple("MFExtPackage", "package_name tl_package config_module")
-MFExtModule = namedtuple("MFExtModule", "tl_package module")
+MFExtModule = namedtuple("MFExtModule", "package_name tl_package module")
 
 
 def load_module(module_name):
@@ -92,9 +103,6 @@ def load_module(module_name):
 
 def get_modules(extension_point):
     modules_to_load = []
-    if not _mfext_supported:
-        _ext_debug("Not supported for your Python version -- 3.4+ is needed")
-        return []
     if extension_point not in _extension_points:
         raise RuntimeError(
             "Metaflow extension point '%s' not supported" % extension_point
@@ -113,17 +121,61 @@ def get_modules(extension_point):
     return modules_to_load
 
 
-def dump_module_info():
-    _filter_files_all()
+def dump_module_info(all_packages=None, pkgs_per_extension_point=None):
+    if all_packages is None:
+        all_packages = _all_packages
+    if pkgs_per_extension_point is None:
+        pkgs_per_extension_point = _pkgs_per_extension_point
+
+    _filter_files_all(all_packages)
     sanitized_all_packages = dict()
     # Strip out root_paths (we don't need it and no need to expose user's dir structure)
-    for k, v in _all_packages.items():
+    for k, v in all_packages.items():
         sanitized_all_packages[k] = {
             "root_paths": None,
             "meta_module": v["meta_module"],
             "files": v["files"],
+            "version": v["version"],
+            "package_version": v.get("package_version", ""),
+            "extension_name": v.get("extension_name", ""),
         }
-    return "ext_info", [sanitized_all_packages, _pkgs_per_extension_point]
+    return "ext_info", [sanitized_all_packages, pkgs_per_extension_point]
+
+
+def get_extensions_in_dir(d):
+    return _get_extension_packages(ignore_info_file=True, restrict_to_directories=[d])
+
+
+def extension_info(packages=None):
+    if packages is None:
+        packages = _all_packages
+    # Returns information about installed extensions so it it can be stored in
+    # _graph_info.
+    return {
+        "installed": {
+            k: {
+                "dist_version": v["version"],
+                "package_version": v.get("package_version", ""),
+                "extension_name": v.get("extension_name", ""),
+            }
+            for k, v in packages.items()
+        },
+    }
+
+
+def update_package_info(pkg_to_update=None, package_name=None, **kwargs):
+    pkg = None
+    if pkg_to_update:
+        pkg = pkg_to_update
+    elif package_name:
+        pkg = _all_packages.get(package_name)
+    for k, v in kwargs.items():
+        if k in pkg:
+            raise ValueError(
+                "Trying to overwrite existing key '%s' for package %s" % (k, str(pkg))
+            )
+        pkg[k] = v
+    return pkg
 
 
 def get_aliased_modules():
@@ -134,8 +186,8 @@ def package_mfext_package(package_name):
     from metaflow.util import to_unicode
 
     _ext_debug("Packaging '%s'" % package_name)
-    _filter_files_package(package_name)
     pkg_info = _all_packages.get(package_name, None)
+    _filter_files_package(pkg_info)
     if pkg_info and pkg_info.get("root_paths", None):
         single_path = len(pkg_info["root_paths"]) == 1
         for p in pkg_info["root_paths"]:
@@ -162,6 +214,10 @@ def package_mfext_all():
             yield path_tuple
 
 
+def package_mfext_all_descriptions():
+    return _all_packages
+
+
 def load_globals(module, dst_globals, extra_indent=False):
     if extra_indent:
         extra_indent = "    "
@@ -254,19 +310,16 @@ def multiload_all(modules, extension_point, dst_globals):
 
 
 _py_ver = sys.version_info[:2]
-_mfext_supported = False
 _aliased_modules = []
 
-if _py_ver >= (3, 4):
-    import importlib.util
+import importlib.util
 
-    if _py_ver >= (3, 8):
-        from importlib import metadata
-    elif _py_ver >= (3, 6):
-        from metaflow._vendor.v3_6 import importlib_metadata as metadata
-    else:
-        from metaflow._vendor.v3_5 import importlib_metadata as metadata
-    _mfext_supported = True
+if _py_ver >= (3, 8):
+    from importlib import metadata
+elif _py_ver >= (3, 7):
+    from metaflow._vendor.v3_7 import importlib_metadata as metadata
+else:
+    from metaflow._vendor.v3_6 import importlib_metadata as metadata
 
 # Extension points are the directories that can be present in a EXT_PKG to
 # contribute to that extension point. For example, if you have
@@ -294,41 +347,41 @@ def _ext_debug(*args, **kwargs):
         print(init_str, *args, **kwargs)
 
 
-def _get_extension_packages():
-    if not _mfext_supported:
-        _ext_debug("Not supported for your Python version -- 3.4+ is needed")
-        return {}, {}
-
+def _get_extension_packages(ignore_info_file=False, restrict_to_directories=None):
     # If we have an INFO file with the appropriate information (if running from a saved
     # code package for example), we use that directly
     # Pre-compute on _extension_points
-    from metaflow import INFO_FILE
-
-    try:
-        with open(INFO_FILE, encoding="utf-8") as contents:
-            all_pkg, ext_to_pkg = json.load(contents).get("ext_info", (None, None))
-            if all_pkg is not None and ext_to_pkg is not None:
-                _ext_debug("Loading pre-computed information from INFO file")
-                # We need to properly convert stuff in ext_to_pkg
-                for k, v in ext_to_pkg.items():
-                    v = [MFExtPackage(*d) for d in v]
-                    ext_to_pkg[k] = v
-                return all_pkg, ext_to_pkg
-    except IOError:
-        pass
+    info_content = read_info_file()
+    if not ignore_info_file and info_content:
+        all_pkg, ext_to_pkg = info_content.get("ext_info", (None, None))
+        if all_pkg is not None and ext_to_pkg is not None:
+            _ext_debug("Loading pre-computed information from INFO file")
+            # We need to properly convert stuff in ext_to_pkg
+            for k, v in ext_to_pkg.items():
+                v = [MFExtPackage(*d) for d in v]
+                ext_to_pkg[k] = v
+            return all_pkg, ext_to_pkg
+
+    # Late import to prevent some circular nastiness
+    if restrict_to_directories is None and EXTENSIONS_SEARCH_DIRS != [""]:
+        restrict_to_directories = EXTENSIONS_SEARCH_DIRS
 
     # Check if we even have extensions
     try:
         extensions_module = importlib.import_module(EXT_PKG)
     except ImportError as e:
-        if _py_ver >= (3, 6):
-            # e.name is set to the name of the package that fails to load
-            # so don't error ONLY IF the error is importing this module (but do
-            # error if there is a transitive import error)
-            if not (isinstance(e, ModuleNotFoundError) and e.name == EXT_PKG):
-                raise
+        # e.name is set to the name of the package that fails to load
+        # so don't error ONLY IF the error is importing this module (but do
+        # error if there is a transitive import error)
+        if not (isinstance(e, ModuleNotFoundError) and e.name == EXT_PKG):
+            raise
         return {}, {}
 
+    if restrict_to_directories:
+        restrict_to_directories = [
+            Path(p).resolve().as_posix() for p in restrict_to_directories
+        ]
+
     # There are two "types" of packages:
     #   - those installed on the system (distributions)
     #   - those present in the PYTHONPATH
@@ -341,8 +394,12 @@ def _get_extension_packages():
     # At this point, we look at all the paths and create a set. As we find distributions
     # that match it, we will remove from the set and then will be left with any
     # PYTHONPATH "packages"
-    all_paths = set(extensions_module.__path__)
+    all_paths = set(Path(p).resolve().as_posix() for p in extensions_module.__path__)
     _ext_debug("Found packages present at %s" % str(all_paths))
+    if restrict_to_directories:
+        _ext_debug(
+            "Processed packages will be restricted to %s" % str(restrict_to_directories)
+        )
 
     list_ext_points = [x.split(".") for x in _extension_points]
     init_ext_points = [x[0] for x in list_ext_points]
@@ -384,19 +441,34 @@ def _get_extension_packages():
         if any(
             [pkg == EXT_PKG for pkg in (dist.read_text("top_level.txt") or "").split()]
         ):
-            if dist.metadata["Name"] in mf_ext_packages:
+            # In all cases (whether duplicate package or not), we remove the package
+            # from the list of locations to look in.
+            # This is not 100% accurate because it is possible that at the same
+            # location there is a package and a non-package, but this is extremely
+            # unlikely so we are going to ignore this case.
+            dist_root = dist.locate_file(EXT_PKG).resolve().as_posix()
+            all_paths.discard(dist_root)
+            dist_name = dist.metadata["Name"]
+            dist_version = dist.metadata["Version"]
+            if restrict_to_directories:
+                parent_dirs = list(
+                    p.as_posix() for p in Path(dist_root).resolve().parents
+                )
+                if all(p not in parent_dirs for p in restrict_to_directories):
+                    _ext_debug(
+                        "Ignoring package at %s as it is not in the considered directories"
+                        % dist_root
+                    )
+                    continue
+            if dist_name in mf_ext_packages:
                 _ext_debug(
                     "Ignoring duplicate package '%s' (duplicate paths in sys.path? (%s))"
-                    % (dist.metadata["Name"], str(sys.path))
+                    % (dist_name, str(sys.path))
                 )
                 continue
-            _ext_debug("Found extension package '%s'..." % dist.metadata["Name"])
-
-            # Remove the path from the paths to search. This is not 100% accurate because
-            # it is possible that at that same location there is a package and a non-package,
-            # but it is exceedingly unlikely, so we are going to ignore this.
-            dist_root = dist.locate_file(EXT_PKG).as_posix()
-            all_paths.discard(dist_root)
+            _ext_debug(
+                "Found extension package '%s' at '%s'..." % (dist_name, dist_root)
+            )
 
             files_to_include = []
             meta_module = None
@@ -414,7 +486,7 @@ def _get_extension_packages():
                     if parts[1] == "__init__.py":
                         raise RuntimeError(
                             "Package '%s' providing '%s' is not an implicit namespace "
-                            "package as required" % (dist.metadata["Name"], EXT_PKG)
+                            "package as required" % (dist_name, EXT_PKG)
                         )
 
                     # Record the file as a candidate for inclusion when packaging if
@@ -431,7 +503,7 @@ def _get_extension_packages():
                             "Package '%s' should conform to '%s.X.%s' and not '%s.%s' where "
                             "X is your organization's name for example"
                             % (
-                                dist.metadata["Name"],
+                                dist_name,
                                 EXT_PKG,
                                 parts[1],
                                 EXT_PKG,
@@ -448,7 +520,7 @@ def _get_extension_packages():
                                 "Package '%s' defines more than one meta configuration: "
                                 "'%s' and '%s' (at least)"
                                 % (
-                                    dist.metadata["Name"],
+                                    dist_name,
                                     meta_module,
                                     potential_meta_module,
                                 )
@@ -465,7 +537,7 @@ def _get_extension_packages():
 
                     # To give useful errors in case multiple top-level packages in
                     # one package
-                    dist_full_name = "%s[%s]" % (dist.metadata["Name"], parts[1])
+                    dist_full_name = "%s[%s]" % (dist_name, parts[1])
                     for idx, ext_list in enumerate(list_ext_points):
                         if (
                             len(parts) > len(ext_list) + 2
@@ -484,7 +556,7 @@ def _get_extension_packages():
                                 config_to_pkg[config_module].append(dist_full_name)
                             cur_pkg = (
                                 extension_points_to_pkg[_extension_points[idx]]
-                                .setdefault(dist.metadata["Name"], {})
+                                .setdefault(dist_name, {})
                                 .get(parts[1])
                             )
                             if cur_pkg is not None:
@@ -508,9 +580,9 @@ def _get_extension_packages():
                                         % (parts[1], config_module)
                                     )
                                     extension_points_to_pkg[_extension_points[idx]][
-                                        dist.metadata["Name"]
+                                        dist_name
                                     ][parts[1]] = MFExtPackage(
-                                        package_name=dist.metadata["Name"],
+                                        package_name=dist_name,
                                         tl_package=parts[1],
                                         config_module=config_module,
                                     )
@@ -520,17 +592,18 @@ def _get_extension_packages():
                                     % (parts[1], _extension_points[idx], config_module)
                                 )
                                 extension_points_to_pkg[_extension_points[idx]][
-                                    dist.metadata["Name"]
+                                    dist_name
                                 ][parts[1]] = MFExtPackage(
-                                    package_name=dist.metadata["Name"],
+                                    package_name=dist_name,
                                     tl_package=parts[1],
                                     config_module=config_module,
                                 )
                             break
-            mf_ext_packages[dist.metadata["Name"]] = {
+            mf_ext_packages[dist_name] = {
                 "root_paths": [dist_root],
                 "meta_module": meta_module,
                 "files": files_to_include,
+                "version": dist_version,
             }
     # At this point, we have all the packages that contribute to EXT_PKG,
     # we now check to see if there is an order to respect based on dependencies. We will
@@ -599,6 +672,16 @@ def _get_extension_packages():
     if len(all_paths_list) > 0:
         _ext_debug("Non installed packages present at %s" % str(all_paths))
         for package_count, package_path in enumerate(all_paths_list):
+            if restrict_to_directories:
+                parent_dirs = list(
+                    p.as_posix() for p in Path(package_path).resolve().parents
+                )
+                if all(p not in parent_dirs for p in restrict_to_directories):
+                    _ext_debug(
+                        "Ignoring non-installed package at %s as it is not in "
+                        "the considered directories" % package_path
+                    )
+                    continue
             # We give an alternate name for the visible package name. It is
             # not exposed to the end user but used to refer to the package, and it
             # doesn't provide much additional information to have the full path
@@ -729,12 +812,16 @@ def _get_extension_packages():
                                 "    Extends '%s' with config '%s'"
                                 % (_extension_points[idx], config_module)
                             )
-            mf_pkg_list.append(package_name)
-            mf_ext_packages[package_name] = {
-                "root_paths": [package_path],
-                "meta_module": meta_module,
-                "files": files_to_include,
-            }
+            if files_to_include:
+                mf_pkg_list.append(package_name)
+                mf_ext_packages[package_name] = {
+                    "root_paths": [package_path],
+                    "meta_module": meta_module,
+                    "files": files_to_include,
+                    "version": "_local_",
+                }
+            else:
+                _ext_debug("Skipping package as no files found (empty dir?)")
 
     # Sanity check that we only have one package per configuration file.
     # This prevents multiple packages from providing the same named configuration
@@ -798,20 +885,19 @@ def _attempt_load_module(module_name):
     try:
         extension_module = importlib.import_module(module_name)
     except ImportError as e:
-        if _py_ver >= (3, 6):
-            # e.name is set to the name of the package that fails to load
-            # so don't error ONLY IF the error is importing this module (but do
-            # error if there is a transitive import error)
-            errored_names = [EXT_PKG]
-            parts = module_name.split(".")
-            for p in parts[1:]:
-                errored_names.append("%s.%s" % (errored_names[-1], p))
-            if not (isinstance(e, ModuleNotFoundError) and e.name in errored_names):
-                print(
-                    "The following exception occurred while trying to load '%s' ('%s')"
-                    % (EXT_PKG, module_name)
-                )
-                raise
+        # e.name is set to the name of the package that fails to load
+        # so don't error ONLY IF the error is importing this module (but do
+        # error if there is a transitive import error)
+        errored_names = [EXT_PKG]
+        parts = module_name.split(".")
+        for p in parts[1:]:
+            errored_names.append("%s.%s" % (errored_names[-1], p))
+        if not (isinstance(e, ModuleNotFoundError) and e.name in errored_names):
+            print(
+                "The following exception occurred while trying to load '%s' ('%s')"
+                % (EXT_PKG, module_name)
+            )
+            raise
         _ext_debug("        Unknown error when loading '%s': %s" % (module_name, e))
         return None
     else:
@@ -862,12 +948,13 @@ def _get_extension_config(distribution_name, tl_pkg, extension_point, config_mod
             _ext_debug("Package '%s' is rooted at %s" % (distribution_name, root_paths))
             _all_packages[distribution_name]["root_paths"] = root_paths
 
-        return MFExtModule(tl_package=tl_pkg, module=extension_module)
+        return MFExtModule(
+            package_name=distribution_name, tl_package=tl_pkg, module=extension_module
+        )
     return None
 
 
-def _filter_files_package(package_name):
-    pkg = _all_packages.get(package_name)
+def _filter_files_package(pkg):
     if pkg and pkg["root_paths"] and pkg["meta_module"]:
         meta_module = _attempt_load_module(pkg["meta_module"])
         if meta_module:
@@ -896,8 +983,8 @@ def _filter_files_package(package_name):
             pkg["files"] = new_files
 
 
-def _filter_files_all():
-    for p in _all_packages:
+def _filter_files_all(all_packages):
+    for p in all_packages.values():
         _filter_files_package(p)
 
 
@@ -974,9 +1061,9 @@ def exec_module(self, module):
             if self._previously_loaded_module:
                 sys.modules[self._orig_name] = self._previously_loaded_module
             if self._previously_loaded_parent_module:
-                sys.modules[
-                    ".".join(self._orig_name.split(".")[:-1])
-                ] = self._previously_loaded_parent_module
+                sys.modules[".".join(self._orig_name.split(".")[:-1])] = (
+                    self._previously_loaded_parent_module
+                )
 
 
 class _LazyFinder(MetaPathFinder):
diff --git a/metaflow/extension_support/_empty_file.py b/metaflow/extension_support/_empty_file.py
index d59e1556ddb..dbdcba34c17 100644
--- a/metaflow/extension_support/_empty_file.py
+++ b/metaflow/extension_support/_empty_file.py
@@ -1,2 +1,2 @@
-# This file serves as a __init__.py for metaflow_extensions when it is packaged
-# and needs to remain empty.
+# This file serves as a __init__.py for metaflow_extensions or metaflow
+# packages when they are packaged and needs to remain empty.
diff --git a/metaflow/extension_support/plugins.py b/metaflow/extension_support/plugins.py
index 15d4c90bdcd..4cad3a9762d 100644
--- a/metaflow/extension_support/plugins.py
+++ b/metaflow/extension_support/plugins.py
@@ -93,7 +93,32 @@ def merge_lists(base, overrides, attr):
     base[:] = l[:]
 
 
-def resolve_plugins(category):
+def get_plugin(category, class_path, name):
+    path, cls_name = class_path.rsplit(".", 1)
+    try:
+        plugin_module = importlib.import_module(path)
+    except ImportError as e:
+        raise ValueError(
+            "Cannot locate %s plugin '%s' at '%s'" % (category, name, path)
+        ) from e
+    cls = getattr(plugin_module, cls_name, None)
+    if cls is None:
+        raise ValueError(
+            "Cannot locate '%s' class for %s plugin at '%s'"
+            % (cls_name, category, path)
+        )
+    extracted_name = get_plugin_name(category, cls)
+    if extracted_name and extracted_name != name:
+        raise ValueError(
+            "Class '%s' at '%s' for %s plugin expected to be named '%s' but got '%s'"
+            % (cls_name, path, category, name, extracted_name)
+        )
+    globals()[cls_name] = cls
+    _ext_debug("        Added %s plugin '%s' from '%s'" % (category, name, class_path))
+    return cls
+
+
+def resolve_plugins(category, path_only=False):
     # Called to return a list of classes that are the available plugins for 'category'
 
     # The ENABLED_ variable is set in process_plugins
@@ -114,7 +139,7 @@ def resolve_plugins(category):
 
     available_plugins = globals()[_dict_for_category(category)]
     name_extractor = _plugin_categories[category]
-    if not name_extractor:
+    if path_only or not name_extractor:
         # If we have no name function, it means we just use the name in the dictionary
         # and we return a dictionary. This is for sidecars mostly as they do not have
         # a field that indicates their name
@@ -132,32 +157,14 @@ def resolve_plugins(category):
                 "Configuration requested %s plugin '%s' but no such plugin is available"
                 % (category, name)
             )
-        path, cls_name = class_path.rsplit(".", 1)
-        try:
-            plugin_module = importlib.import_module(path)
-        except ImportError:
-            raise ValueError(
-                "Cannot locate %s plugin '%s' at '%s'" % (category, name, path)
-            )
-        cls = getattr(plugin_module, cls_name, None)
-        if cls is None:
-            raise ValueError(
-                "Cannot locate '%s' class for %s plugin at '%s'"
-                % (cls_name, category, path)
-            )
-        if name_extractor and name_extractor(cls) != name:
-            raise ValueError(
-                "Class '%s' at '%s' for %s plugin expected to be named '%s' but got '%s'"
-                % (cls_name, path, category, name, name_extractor(cls))
-            )
-        globals()[cls_name] = cls
-        if name_extractor is not None:
-            to_return.append(cls)
+        if path_only:
+            to_return[name] = class_path
         else:
-            to_return[name] = cls
-        _ext_debug(
-            "        Added %s plugin '%s' from '%s'" % (category, name, class_path)
-        )
+            if name_extractor is not None:
+                to_return.append(get_plugin(category, class_path, name))
+            else:
+                to_return[name] = get_plugin(category, class_path, name)
+
     return to_return
 
 
@@ -178,17 +185,30 @@ def resolve_plugins(category):
     "environment": lambda x: x.TYPE,
     "metadata_provider": lambda x: x.TYPE,
     "datastore": lambda x: x.TYPE,
+    "dataclient": lambda x: x.TYPE,
     "secrets_provider": lambda x: x.TYPE,
+    "gcp_client_provider": lambda x: x.name,
+    "deployer_impl_provider": lambda x: x.TYPE,
+    "azure_client_provider": lambda x: x.name,
     "sidecar": None,
     "logging_sidecar": None,
     "monitor_sidecar": None,
     "aws_client_provider": lambda x: x.name,
-    "cli": lambda x: list(x.commands)[0]
-    if len(x.commands) == 1
-    else "too many commands",
+    "cli": lambda x: (
+        list(x.commands)[0] if len(x.commands) == 1 else "too many commands"
+    ),
+    "runner_cli": lambda x: x.name,
+    "tl_plugin": None,
 }
 
 
+def get_plugin_name(category, plugin):
+    extractor = _plugin_categories[category]
+    if extractor:
+        return extractor(plugin)
+    return None
+
+
 def _list_for_category(category):
     # Convenience function to name the variable containing List[Tuple[str, str]] where
     # each tuple contains:
diff --git a/metaflow/flowspec.py b/metaflow/flowspec.py
index 827d7a34c15..d9410dd1ac6 100644
--- a/metaflow/flowspec.py
+++ b/metaflow/flowspec.py
@@ -2,20 +2,37 @@
 import os
 import sys
 import traceback
+import reprlib
 
+from enum import Enum
 from itertools import islice
 from types import FunctionType, MethodType
-from typing import Any, Callable, List, Optional, Tuple
+from typing import TYPE_CHECKING, Any, Callable, List, Optional, Tuple
 
-from . import cmd_with_io
+from . import cmd_with_io, parameters
+from .debug import debug
 from .parameters import DelayedEvaluationParameter, Parameter
 from .exception import (
     MetaflowException,
     MissingInMergeArtifactsException,
+    MetaflowInternalError,
     UnhandledInMergeArtifactsException,
 )
+
+from .extension_support import extension_info
+
 from .graph import FlowGraph
 from .unbounded_foreach import UnboundedForeachInput
+from .user_configs.config_parameters import ConfigValue
+
+from .user_decorators.mutable_flow import MutableFlow
+from .user_decorators.mutable_step import MutableStep
+from .user_decorators.user_flow_decorator import FlowMutator
+from .user_decorators.user_step_decorator import StepMutator
+
+
+from .util import to_pod
+from .metaflow_config import INCLUDE_FOREACH_STACK, MAXIMUM_FOREACH_VALUE_CHARS
 
 # For Python 3 compatibility
 try:
@@ -26,6 +43,16 @@
 
 from .datastore.inputs import Inputs
 
+INTERNAL_ARTIFACTS_SET = set(
+    [
+        "_foreach_values",
+        "_unbounded_foreach",
+        "_control_mapper_tasks",
+        "_control_task_is_mapper_zero",
+        "_parallel_ubf_iter",
+    ]
+)
+
 
 class InvalidNextException(MetaflowException):
     headline = "Invalid self.next() transition detected"
@@ -49,7 +76,87 @@ def __getitem__(self, item):
         return item or 0  # item is None for the control task, but it is also split 0
 
 
-class FlowSpec(object):
+class _FlowState(Enum):
+    CONFIGS = 1
+    FLOW_MUTATORS = 2
+    CACHED_PARAMETERS = 3
+    SET_CONFIG_PARAMETERS = (
+        4  # These are Parameters that now have a ConfigValue (converted)
+    )
+    # but we need to remember them.
+
+
+class FlowSpecMeta(type):
+    def __init__(cls, name, bases, attrs):
+        super().__init__(name, bases, attrs)
+        if name == "FlowSpec":
+            return
+
+        cls._init_attrs()
+
+    def _init_attrs(cls):
+        from .decorators import (
+            DuplicateFlowDecoratorException,
+        )  # Prevent circular import
+
+        # We store some state in the flow class itself. This is primarily used to
+        # attach global state to a flow. It is *not* an actual global because of
+        # Runner/NBRunner. This is also created here in the meta class to avoid it being
+        # shared between different children classes.
+
+        # We should move _flow_decorators into this structure as well but keeping it
+        # out to limit the changes for now.
+        cls._flow_decorators = {}
+
+        # Keys are _FlowState enum values
+        cls._flow_state = {}
+
+        # Keep track if configs have been processed -- this is particularly applicable
+        # for the Runner/Deployer where calling multiple APIs on the same flow could
+        # cause the configs to be processed multiple times. For a given flow, once
+        # the configs have been processed, we do not process them again.
+        cls._configs_processed = False
+
+        # We inherit stuff from our parent classes as well -- we need to be careful
+        # in terms of the order; we will follow the MRO with the following rules:
+        #  - decorators (cls._flow_decorators) will cause an error if they do not
+        #    support multiple and we see multiple instances of the same
+        #  - config decorators will be joined
+        #  - configs will be added later directly by the class; base class configs will
+        #    be taken into account as they would be inherited.
+
+        # We only need to do this for the base classes since the current class will
+        # get updated as decorators are parsed.
+        for base in cls.__mro__:
+            if base != cls and base != FlowSpec and issubclass(base, FlowSpec):
+                # Take care of decorators
+                for deco_name, deco in base._flow_decorators.items():
+                    if deco_name in cls._flow_decorators and not deco.allow_multiple:
+                        raise DuplicateFlowDecoratorException(deco_name)
+                    cls._flow_decorators.setdefault(deco_name, []).extend(deco)
+
+                # Take care of configs and flow mutators
+                base_configs = base._flow_state.get(_FlowState.CONFIGS)
+                if base_configs:
+                    cls._flow_state.setdefault(_FlowState.CONFIGS, {}).update(
+                        base_configs
+                    )
+                base_mutators = base._flow_state.get(_FlowState.FLOW_MUTATORS)
+                if base_mutators:
+                    cls._flow_state.setdefault(_FlowState.FLOW_MUTATORS, []).extend(
+                        base_mutators
+                    )
+
+        cls._init_graph()
+
+    def _init_graph(cls):
+        # Graph and steps are specific to the class -- store here so we can access
+        # in class method _process_config_decorators
+        cls._graph = FlowGraph(cls)
+        cls._steps = [getattr(cls, node.name) for node in cls._graph]
+
+
+class FlowSpec(metaclass=FlowSpecMeta):
     """
     Main class from which all Flows should inherit.
 
@@ -69,6 +176,7 @@ class FlowSpec(object):
         "_cached_input",
         "_graph",
         "_flow_decorators",
+        "_flow_state",
         "_steps",
         "index",
         "input",
@@ -79,15 +187,13 @@ class FlowSpec(object):
     # names starting with `_` as those are already excluded from `_get_parameters`.
     _NON_PARAMETERS = {"cmd", "foreach_stack", "index", "input", "script_name", "name"}
 
-    _flow_decorators = {}
-
     def __init__(self, use_cli=True):
         """
         Construct a FlowSpec
 
         Parameters
         ----------
-        use_cli : bool, default: True
+        use_cli : bool, default True
             Set to True if the flow is invoked from __main__ or the command line
         """
 
@@ -97,15 +203,11 @@ def __init__(self, use_cli=True):
         self._transition = None
         self._cached_input = {}
 
-        self._graph = FlowGraph(self.__class__)
-        self._steps = [getattr(self, node.name) for node in self._graph]
-
         if use_cli:
-            # we import cli here to make sure custom parameters in
-            # args.py get fully evaluated before cli.py is imported.
-            from . import cli
+            with parameters.flow_context(self.__class__) as _:
+                from . import cli
 
-            cli.main(self)
+                cli.main(self)
 
     @property
     def script_name(self) -> str:
@@ -124,16 +226,12 @@ def script_name(self) -> str:
             fname = fname[:-1]
         return os.path.basename(fname)
 
-    def _set_constants(self, graph, kwargs):
-        from metaflow.decorators import (
-            flow_decorators,
-        )  # To prevent circular dependency
-
-        # Persist values for parameters and other constants (class level variables)
-        # only once. This method is called before persist_constants is called to
-        # persist all values set using setattr
+    @classmethod
+    def _check_parameters(cls, config_parameters=False):
         seen = set()
-        for var, param in self._get_parameters():
+        for _, param in cls._get_parameters():
+            if param.IS_CONFIG_PARAMETER != config_parameters:
+                continue
             norm = param.name.lower()
             if norm in seen:
                 raise MetaflowException(
@@ -142,17 +240,151 @@ def _set_constants(self, graph, kwargs):
                     "case-insensitive." % param.name
                 )
             seen.add(norm)
-        seen.clear()
+
+    @classmethod
+    def _process_config_decorators(cls, config_options, process_configs=True):
+        if cls._configs_processed:
+            debug.userconf_exec("Mutating step/flow decorators already processed")
+            return None
+        cls._configs_processed = True
+
+        # Fast path for no user configurations
+        if not process_configs or (
+            not cls._flow_state.get(_FlowState.FLOW_MUTATORS)
+            and all(len(step.config_decorators) == 0 for step in cls._steps)
+        ):
+            # Process parameters to allow them to also use config values easily
+            for var, param in cls._get_parameters():
+                if isinstance(param, ConfigValue) or param.IS_CONFIG_PARAMETER:
+                    continue
+                param.init(not process_configs)
+            return None
+
+        debug.userconf_exec("Processing mutating step/flow decorators")
+        # We need to convert all the user configurations from DelayedEvaluationParameters
+        # to actual values so they can be used as is in the mutators.
+
+        # We, however, need to make sure _get_parameters still works properly so
+        # we store what was a config and has been set to a specific value.
+        # This is safe to do for now because all other uses of _get_parameters typically
+        # do not rely on the variable itself but just the parameter.
+        to_save_configs = []
+        cls._check_parameters(config_parameters=True)
+        for var, param in cls._get_parameters():
+            if not param.IS_CONFIG_PARAMETER:
+                continue
+            # Note that a config with no default and not required will be None
+            val = config_options.get(param.name.replace("-", "_").lower())
+            if isinstance(val, DelayedEvaluationParameter):
+                val = val()
+            # We store the value as well so that in _set_constants, we don't try
+            # to recompute (no guarantee that it is stable)
+            param._store_value(val)
+            to_save_configs.append((var, param))
+            debug.userconf_exec("Setting config %s to %s" % (var, str(val)))
+            setattr(cls, var, val)
+
+        cls._flow_state[_FlowState.SET_CONFIG_PARAMETERS] = to_save_configs
+        # Run all the decorators. We first run the flow-level decorators
+        # and then the step level ones to maintain a consistent order with how
+        # other decorators are run.
+
+        for deco in cls._flow_state.get(_FlowState.FLOW_MUTATORS, []):
+            if isinstance(deco, FlowMutator):
+                inserted_by_value = [deco.decorator_name] + (deco.inserted_by or [])
+                mutable_flow = MutableFlow(
+                    cls,
+                    pre_mutate=True,
+                    statically_defined=deco.statically_defined,
+                    inserted_by=inserted_by_value,
+                )
+                # Sanity check to make sure we are applying the decorator to the right
+                # class
+                if not deco._flow_cls == cls and not issubclass(cls, deco._flow_cls):
+                    raise MetaflowInternalError(
+                        "FlowMutator registered on the wrong flow -- "
+                        "expected %s but got %s"
+                        % (deco._flow_cls.__name__, cls.__name__)
+                    )
+                debug.userconf_exec(
+                    "Evaluating flow level decorator %s" % deco.__class__.__name__
+                )
+                deco.pre_mutate(mutable_flow)
+                # We reset cached_parameters on the very off chance that the user added
+                # more configurations based on the configuration
+                if _FlowState.CACHED_PARAMETERS in cls._flow_state:
+                    del cls._flow_state[_FlowState.CACHED_PARAMETERS]
+            else:
+                raise MetaflowInternalError(
+                    "A non FlowMutator found in flow custom decorators"
+                )
+
+        for step in cls._steps:
+            for deco in step.config_decorators:
+                if isinstance(deco, StepMutator):
+                    inserted_by_value = [deco.decorator_name] + (deco.inserted_by or [])
+                    debug.userconf_exec(
+                        "Evaluating step level decorator %s for %s"
+                        % (deco.__class__.__name__, step.name)
+                    )
+                    deco.pre_mutate(
+                        MutableStep(
+                            cls,
+                            step,
+                            pre_mutate=True,
+                            statically_defined=deco.statically_defined,
+                            inserted_by=inserted_by_value,
+                        )
+                    )
+                else:
+                    raise MetaflowInternalError(
+                        "A non StepMutator found in step custom decorators"
+                    )
+
+        # Process parameters to allow them to also use config values easily
+        for var, param in cls._get_parameters():
+            if param.IS_CONFIG_PARAMETER:
+                continue
+            param.init()
+
+        # Set the current flow class we are in (the one we just created)
+        parameters.replace_flow_context(cls)
+
+        # Re-calculate class level attributes after modifying the class
+        cls._init_graph()
+        return cls
+
+    def _set_constants(self, graph, kwargs, config_options):
+        from metaflow.decorators import (
+            flow_decorators,
+        )  # To prevent circular dependency
+
+        # Persist values for parameters and other constants (class level variables)
+        # only once. This method is called before persist_constants is called to
+        # persist all values set using setattr
+        self._check_parameters(config_parameters=False)
+
+        seen = set()
         self._success = True
 
         parameters_info = []
         for var, param in self._get_parameters():
             seen.add(var)
-            val = kwargs[param.name.replace("-", "_").lower()]
+            if param.IS_CONFIG_PARAMETER:
+                # Use computed value if already evaluated, else get from config_options
+                val = param._computed_value or config_options.get(
+                    param.name.replace("-", "_").lower()
+                )
+            else:
+                val = kwargs[param.name.replace("-", "_").lower()]
             # Support for delayed evaluation of parameters.
             if isinstance(val, DelayedEvaluationParameter):
                 val = val()
             val = val.split(param.separator) if val and param.separator else val
+            if isinstance(val, ConfigValue):
+                # We store config values as dict so they are accessible with older
+                # metaflow clients. It also makes it easier to access.
+                val = val.to_dict()
             setattr(self, var, val)
             parameters_info.append({"name": var, "type": param.__class__.__name__})
 
@@ -182,25 +414,53 @@ def _set_constants(self, graph, kwargs):
             "decorators": [
                 {
                     "name": deco.name,
-                    "attributes": deco.attributes,
+                    "attributes": to_pod(deco.attributes),
                     "statically_defined": deco.statically_defined,
+                    "inserted_by": deco.inserted_by,
                 }
-                for deco in flow_decorators()
+                for deco in flow_decorators(self)
                 if not deco.name.startswith("_")
+            ]
+            + [
+                {
+                    "name": deco.__class__.__name__,
+                    "attributes": {},
+                    "statically_defined": deco.statically_defined,
+                    "inserted_by": deco.inserted_by,
+                }
+                for deco in self._flow_state.get(_FlowState.FLOW_MUTATORS, [])
             ],
+            "extensions": extension_info(),
         }
         self._graph_info = graph_info
 
-    def _get_parameters(self):
-        for var in dir(self):
-            if var[0] == "_" or var in self._NON_PARAMETERS:
+    @classmethod
+    def _get_parameters(cls):
+        cached = cls._flow_state.get(_FlowState.CACHED_PARAMETERS)
+        returned = set()
+        if cached is not None:
+            for set_config in cls._flow_state.get(_FlowState.SET_CONFIG_PARAMETERS, []):
+                returned.add(set_config[0])
+                yield set_config[0], set_config[1]
+            for var in cached:
+                if var not in returned:
+                    yield var, getattr(cls, var)
+            return
+        build_list = []
+        for set_config in cls._flow_state.get(_FlowState.SET_CONFIG_PARAMETERS, []):
+            returned.add(set_config[0])
+            yield set_config[0], set_config[1]
+        for var in dir(cls):
+            if var[0] == "_" or var in cls._NON_PARAMETERS:
                 continue
             try:
-                val = getattr(self, var)
+                val = getattr(cls, var)
             except:
                 continue
-            if isinstance(val, Parameter):
+            if isinstance(val, Parameter) and var not in returned:
+                build_list.append(var)
                 yield var, val
+        cls._flow_state[_FlowState.CACHED_PARAMETERS] = build_list
 
     def _set_datastore(self, datastore):
         self._datastore = datastore
@@ -317,7 +577,7 @@ def nest_2(self):
 
         Returns
         -------
-        List[Tuple[int, int, object]]
+        List[Tuple[int, int, Any]]
             An array describing the current stack of foreach steps.
         """
         return [
@@ -397,10 +657,10 @@ def merge_artifacts(
         ----------
         inputs : Inputs
             Incoming steps to the join point.
-        exclude : List[str], optional
+        exclude : List[str], optional, default None
             If specified, do not consider merging artifacts with a name in `exclude`.
             Cannot specify if `include` is also specified.
-        include : List[str], optional
+        include : List[str], optional, default None
             If specified, only merge artifacts specified. Cannot specify if `exclude` is
             also specified.
 
@@ -441,7 +701,9 @@ def merge_artifacts(
                 available_vars = (
                     (var, sha)
                     for var, sha in inp._datastore.items()
-                    if (var not in exclude) and (not hasattr(self, var))
+                    if (var not in exclude)
+                    and (not hasattr(self, var))
+                    and (var not in INTERNAL_ARTIFACTS_SET)
                 )
             for var, sha in available_vars:
                 _, previous_sha = to_merge.setdefault(var, (inp, sha))
@@ -499,6 +761,33 @@ def _validate_ubf_step(self, step_name):
             )
             raise InvalidNextException(msg)
 
+    def _get_foreach_item_value(self, item: Any):
+        """
+        Get the unique value for the item in the foreach iterator.  If no suitable value
+        is found, return the value formatted by reprlib, which is at most 30 characters long.
+
+        Parameters
+        ----------
+        item : Any
+            The item to get the value from.
+
+        Returns
+        -------
+        str
+            The value to use for the item.
+        """
+
+        def _is_primitive_type(item):
+            return (
+                isinstance(item, basestring)
+                or isinstance(item, int)
+                or isinstance(item, float)
+                or isinstance(item, bool)
+            )
+
+        value = item if _is_primitive_type(item) else reprlib.Repr().repr(item)
+        return basestring(value)[:MAXIMUM_FOREACH_VALUE_CHARS]
+
     def next(self, *dsts: Callable[..., None], **kwargs) -> None:
         """
         Indicates the next step to execute after this step has completed.
@@ -525,7 +814,7 @@ def next(self, *dsts: Callable[..., None], **kwargs) -> None:
 
         Parameters
         ----------
-        dsts : Method
+        dsts : Callable[..., None]
             One or more methods annotated with `@step`.
 
         Raises
@@ -608,19 +897,26 @@ def next(self, *dsts: Callable[..., None], **kwargs) -> None:
                     )
                 )
                 raise InvalidNextException(msg)
-
+            self._foreach_values = None
             if issubclass(type(foreach_iter), UnboundedForeachInput):
                 self._unbounded_foreach = True
                 self._foreach_num_splits = None
                 self._validate_ubf_step(funcs[0])
             else:
                 try:
-                    self._foreach_num_splits = sum(1 for _ in foreach_iter)
-                except TypeError:
+                    if INCLUDE_FOREACH_STACK:
+                        self._foreach_values = []
+                        for item in foreach_iter:
+                            value = self._get_foreach_item_value(item)
+                            self._foreach_values.append(value)
+                        self._foreach_num_splits = len(self._foreach_values)
+                    else:
+                        self._foreach_num_splits = sum(1 for _ in foreach_iter)
+                except Exception as e:
                     msg = (
                         "Foreach variable *self.{var}* in step *{step}* "
-                        "is not iterable. Check your variable.".format(
-                            step=step, var=foreach
+                        "is not iterable. Please check details: {err}".format(
+                            step=step, var=foreach, err=str(e)
                         )
                     )
                     raise InvalidNextException(msg)
diff --git a/metaflow/graph.py b/metaflow/graph.py
index 4ea41e0114f..025b4a36505 100644
--- a/metaflow/graph.py
+++ b/metaflow/graph.py
@@ -2,6 +2,11 @@
 import ast
 import re
 
+from itertools import chain
+
+
+from .util import to_pod
+
 
 def deindent_docstring(doc):
     if doc:
@@ -42,10 +47,17 @@ def deindent_docstring(doc):
 
 
 class DAGNode(object):
-    def __init__(self, func_ast, decos, doc):
+    def __init__(
+        self, func_ast, decos, wrappers, config_decorators, doc, source_file, lineno
+    ):
         self.name = func_ast.name
-        self.func_lineno = func_ast.lineno
+        self.source_file = source_file
+        # lineno is the start line of decorators in source_file
+        # func_ast.lineno is lines from decorators start to def of function
+        self.func_lineno = lineno + func_ast.lineno - 1
         self.decorators = decos
+        self.wrappers = wrappers
+        self.config_decorators = config_decorators
         self.doc = deindent_docstring(doc)
         self.parallel_step = any(getattr(deco, "IS_PARALLEL", False) for deco in decos)
 
@@ -59,7 +71,7 @@ def __init__(self, func_ast, decos, doc):
         self.foreach_param = None
         self.num_parallel = 0
         self.parallel_foreach = False
-        self._parse(func_ast)
+        self._parse(func_ast, lineno)
 
         # these attributes are populated by _traverse_graph
         self.in_funcs = set()
@@ -71,8 +83,7 @@ def __init__(self, func_ast, decos, doc):
     def _expr_str(self, expr):
         return "%s.%s" % (expr.value.id, expr.attr)
 
-    def _parse(self, func_ast):
-
+    def _parse(self, func_ast, lineno):
         self.num_args = len(func_ast.args.args)
         tail = func_ast.body[-1]
 
@@ -92,7 +103,7 @@ def _parse(self, func_ast):
 
             self.has_tail_next = True
             self.invalid_tail_next = True
-            self.tail_next_lineno = tail.lineno
+            self.tail_next_lineno = lineno + tail.lineno - 1
             self.out_funcs = [e.attr for e in tail.value.args]
 
             keywords = dict(
@@ -129,7 +140,7 @@ def _parse(self, func_ast):
             return
 
     def __str__(self):
-        return """*[{0.name} {0.type} (line {0.func_lineno})]*
+        return """*[{0.name} {0.type} ({0.source_file} line {0.func_lineno})]*
     in_funcs={in_funcs}
     out_funcs={out_funcs}
     split_parents={parents}
@@ -154,34 +165,37 @@ def __str__(self):
         )
 
 
-class StepVisitor(ast.NodeVisitor):
-    def __init__(self, nodes, flow):
-        self.nodes = nodes
-        self.flow = flow
-        super(StepVisitor, self).__init__()
-
-    def visit_FunctionDef(self, node):
-        func = getattr(self.flow, node.name)
-        if hasattr(func, "is_step"):
-            self.nodes[node.name] = DAGNode(node, func.decorators, func.__doc__)
-
-
 class FlowGraph(object):
     def __init__(self, flow):
         self.name = flow.__name__
         self.nodes = self._create_nodes(flow)
         self.doc = deindent_docstring(flow.__doc__)
+        # nodes sorted in topological order.
+        self.sorted_nodes = []
         self._traverse_graph()
         self._postprocess()
 
     def _create_nodes(self, flow):
-        module = __import__(flow.__module__)
-        tree = ast.parse(inspect.getsource(module)).body
-        root = [n for n in tree if isinstance(n, ast.ClassDef) and n.name == self.name][
-            0
-        ]
         nodes = {}
-        StepVisitor(nodes, flow).visit(root)
+        for element in dir(flow):
+            func = getattr(flow, element)
+            if callable(func) and hasattr(func, "is_step"):
+                source_file = inspect.getsourcefile(func)
+                source_lines, lineno = inspect.getsourcelines(func)
+                # This also works for code (strips out leading whitspace based on
+                # first line)
+                source_code = deindent_docstring("".join(source_lines))
+                function_ast = ast.parse(source_code).body[0]
+                node = DAGNode(
+                    function_ast,
+                    func.decorators,
+                    func.wrappers,
+                    func.config_decorators,
+                    func.__doc__,
+                    source_file,
+                    lineno,
+                )
+                nodes[element] = node
         return nodes
 
     def _postprocess(self):
@@ -197,6 +211,11 @@ def _postprocess(self):
 
     def _traverse_graph(self):
         def traverse(node, seen, split_parents):
+            try:
+                self.sorted_nodes.remove(node.name)
+            except ValueError:
+                pass
+            self.sorted_nodes.append(node.name)
             if node.type in ("split", "foreach"):
                 node.split_parents = split_parents
                 split_parents = split_parents + [node.name]
@@ -235,9 +254,7 @@ def __iter__(self):
         return iter(self.nodes.values())
 
     def __str__(self):
-        return "\n".join(
-            str(n) for _, n in sorted((n.func_lineno, n) for n in self.nodes.values())
-        )
+        return "\n".join(str(self[n]) for n in self.sorted_nodes)
 
     def output_dot(self):
         def edge_specs():
@@ -262,7 +279,6 @@ def node_specs():
         )
 
     def output_steps(self):
-
         steps_info = {}
         graph_structure = []
 
@@ -282,15 +298,26 @@ def node_to_dict(name, node):
                 "name": name,
                 "type": node_to_type(node),
                 "line": node.func_lineno,
+                "source_file": node.source_file,
                 "doc": node.doc,
                 "decorators": [
                     {
                         "name": deco.name,
-                        "attributes": deco.attributes,
+                        "attributes": to_pod(deco.attributes),
                         "statically_defined": deco.statically_defined,
+                        "inserted_by": deco.inserted_by,
                     }
                     for deco in node.decorators
                     if not deco.name.startswith("_")
+                ]
+                + [
+                    {
+                        "name": deco.decorator_name,
+                        "attributes": {"_args": deco._args, **deco._kwargs},
+                        "statically_defined": deco.statically_defined,
+                        "inserted_by": deco.inserted_by,
+                    }
+                    for deco in chain(node.wrappers, node.config_decorators)
                 ],
                 "next": node.out_funcs,
             }
diff --git a/metaflow/includefile.py b/metaflow/includefile.py
index 66396c88c8c..c968b70c715 100644
--- a/metaflow/includefile.py
+++ b/metaflow/includefile.py
@@ -1,12 +1,13 @@
 from collections import namedtuple
 import gzip
 
+import importlib
 import io
 import json
 import os
 
 from hashlib import sha1
-from typing import Any, Dict, Optional
+from typing import Any, Callable, Dict, Optional
 
 from metaflow._vendor import click
 
@@ -17,6 +18,9 @@
     Parameter,
     ParameterContext,
 )
+
+from .plugins import DATACLIENTS
+from .user_configs.config_parameters import ConfigValue
 from .util import get_username
 
 import functools
@@ -47,16 +51,7 @@
 
 
 # From here on out, this is the IncludeFile implementation.
-from metaflow.plugins.datatools import Local, S3
-from metaflow.plugins.azure.includefile_support import Azure
-from metaflow.plugins.gcp.includefile_support import GS
-
-DATACLIENTS = {
-    "local": Local,
-    "s3": S3,
-    "azure": Azure,
-    "gs": GS,
-}
+_dict_dataclients = {d.TYPE: d for d in DATACLIENTS}
 
 
 class IncludedFile(object):
@@ -142,12 +137,16 @@ def convert(self, value, param, ctx):
                 parameter_name=param.name,
                 logger=ctx.obj.echo,
                 ds_type=ctx.obj.datastore_impl.TYPE,
+                configs=None,
             )
 
-        if len(value) > 0 and value[0] == "{":
+        if len(value) > 0 and (value.startswith("{") or value.startswith('"{')):
             # This is a blob; no URL starts with `{`. We are thus in scenario A
             try:
                 value = json.loads(value)
+                # to handle quoted json strings
+                if not isinstance(value, dict):
+                    value = json.loads(value)
             except json.JSONDecodeError as e:
                 raise MetaflowException(
                     "IncludeFile '%s' (value: %s) is malformed" % (param.name, value)
@@ -164,7 +163,7 @@ def convert(self, value, param, ctx):
                 "IncludeFile using a direct reference to a file in cloud storage is no "
                 "longer supported. Contact the Metaflow team if you need this supported"
             )
-            # if DATACLIENTS.get(path[:prefix_pos]) is None:
+            # if _dict_dataclients.get(path[:prefix_pos]) is None:
             #     self.fail(
             #         "IncludeFile: no handler for external file of type '%s' "
             #         "(given path is '%s')" % (path[:prefix_pos], path)
@@ -184,7 +183,7 @@ def convert(self, value, param, ctx):
                     pass
             except OSError:
                 self.fail("IncludeFile: could not open file '%s' for reading" % path)
-            handler = DATACLIENTS.get(ctx.ds_type)
+            handler = _dict_dataclients.get(ctx.ds_type)
             if handler is None:
                 self.fail(
                     "IncludeFile: no data-client for datastore of type '%s'"
@@ -210,7 +209,7 @@ def _delayed_eval_func(ctx=lambda_ctx, return_str=False):
                         ctx.path,
                         ctx.is_text,
                         ctx.encoding,
-                        DATACLIENTS[ctx.handler_type],
+                        _dict_dataclients[ctx.handler_type],
                         ctx.echo,
                     )
                 )
@@ -243,32 +242,66 @@ class IncludeFile(Parameter):
     ----------
     name : str
         User-visible parameter name.
-    default : str or a function
+    default : Union[str, Callable[ParameterContext, str]]
         Default path to a local file. A function
         implies that the parameter corresponds to a *deploy-time parameter*.
-    is_text : bool, default: True
+    is_text : bool, optional, default None
         Convert the file contents to a string using the provided `encoding`.
-        If False, the artifact is stored in `bytes`.
-    encoding : str, optional, default: 'utf-8'
-        Use this encoding to decode the file contexts if `is_text=True`.
-    required : bool, default: False
+        If False, the artifact is stored in `bytes`. A value of None is equivalent to
+        True.
+    encoding : str, optional, default None
+        Use this encoding to decode the file contexts if `is_text=True`. A value of None
+        is equivalent to "utf-8".
+    required : bool, optional, default None
         Require that the user specified a value for the parameter.
-        `required=True` implies that the `default` is not used.
+        `required=True` implies that the `default` is not used. A value of None is
+        equivalent to False
     help : str, optional
         Help text to show in `run --help`.
-    show_default : bool, default: True
-        If True, show the default value in the help text.
+    show_default : bool, default True
+        If True, show the default value in the help text. A value of None is equivalent
+        to True.
     """
 
     def __init__(
         self,
         name: str,
-        required: bool = False,
-        is_text: bool = True,
-        encoding: str = "utf-8",
+        required: Optional[bool] = None,
+        is_text: Optional[bool] = None,
+        encoding: Optional[str] = None,
         help: Optional[str] = None,
         **kwargs: Dict[str, str]
     ):
+        self._includefile_overrides = {}
+        if is_text is not None:
+            self._includefile_overrides["is_text"] = is_text
+        if encoding is not None:
+            self._includefile_overrides["encoding"] = encoding
+        # NOTA: Right now, there is an issue where these can't be overridden by config
+        # in all circumstances. Ignoring for now.
+        super(IncludeFile, self).__init__(
+            name,
+            required=required,
+            help=help,
+            type=FilePathClass(
+                self._includefile_overrides.get("is_text", True),
+                self._includefile_overrides.get("encoding", "utf-8"),
+            ),
+            **kwargs,
+        )
+
+    def init(self, ignore_errors=False):
+        super(IncludeFile, self).init(ignore_errors)
+
+        # This will use the values set explicitly in the args if present, else will
+        # use and remove from kwargs else will use True/utf-8
+        is_text = self._includefile_overrides.get(
+            "is_text", self.kwargs.pop("is_text", True)
+        )
+        encoding = self._includefile_overrides.get(
+            "encoding", self.kwargs.pop("encoding", "utf-8")
+        )
+
         # If a default is specified, it needs to be uploaded when the flow is deployed
         # (for example when doing a `step-functions create`) so we make the default
         # be a DeployTimeField. This means that it will be evaluated in two cases:
@@ -278,7 +311,7 @@ def __init__(
         # In the first case, we will need to fully upload the file whereas in the
         # second case, we can just return the string as the FilePath.convert method
         # will take care of evaluating things.
-        v = kwargs.get("default")
+        v = self.kwargs.get("default")
         if v is not None:
             # If the default is a callable, we have two DeployTimeField:
             #  - the callable nature of the default will require us to "call" the default
@@ -291,23 +324,15 @@ def __init__(
             # (call the default)
             if callable(v) and not isinstance(v, DeployTimeField):
                 # If default is a callable, make it a DeployTimeField (the inner one)
-                v = DeployTimeField(name, str, "default", v, return_str=True)
-            kwargs["default"] = DeployTimeField(
-                name,
+                v = DeployTimeField(self.name, str, "default", v, return_str=True)
+            self.kwargs["default"] = DeployTimeField(
+                self.name,
                 str,
                 "default",
                 IncludeFile._eval_default(is_text, encoding, v),
                 print_representation=v,
             )
 
-        super(IncludeFile, self).__init__(
-            name,
-            required=required,
-            help=help,
-            type=FilePathClass(is_text, encoding),
-            **kwargs,
-        )
-
     def load_parameter(self, v):
         if v is None:
             return v
@@ -422,20 +447,19 @@ def _get_handler(url):
         if prefix_pos < 0:
             raise MetaflowException("Malformed URL: '%s'" % url)
         prefix = url[:prefix_pos]
-        handler = DATACLIENTS.get(prefix)
+        handler = _dict_dataclients.get(prefix)
         if handler is None:
             raise MetaflowException("Could not find data client for '%s'" % prefix)
         return handler
 
 
 class UploaderV2:
-
     file_type = "uploader-v2"
 
     @classmethod
     def encode_url(cls, url_type, url, **kwargs):
         return_value = {
-            "note": "Internal representation of IncludeFile(%s)" % url,
+            "note": "Internal representation of IncludeFile",
             "type": cls.file_type,
             "sub-type": url_type,
             "url": url,
@@ -448,7 +472,7 @@ def store(cls, flow_name, path, is_text, encoding, handler, echo):
         r = UploaderV1.store(flow_name, path, is_text, encoding, handler, echo)
 
         # In V2, we store size for faster access
-        r["note"] = "Internal representation of IncludeFile(%s)" % path
+        r["note"] = "Internal representation of IncludeFile"
         r["type"] = cls.file_type
         r["sub-type"] = "uploaded"
         r["size"] = os.stat(path).st_size
diff --git a/metaflow/lint.py b/metaflow/lint.py
index ebcb8e98e24..ecca1605405 100644
--- a/metaflow/lint.py
+++ b/metaflow/lint.py
@@ -52,7 +52,7 @@ def check_reserved_words(graph):
     msg = "Step name *%s* is a reserved word. Choose another name for the " "step."
     for node in graph:
         if node.name in RESERVED:
-            raise LintWarn(msg % node.name)
+            raise LintWarn(msg % node.name, node.func_lineno, node.source_file)
 
 
 @linter.ensure_fundamentals
@@ -76,9 +76,9 @@ def check_that_end_is_end(graph):
     node = graph["end"]
 
     if node.has_tail_next or node.invalid_tail_next:
-        raise LintWarn(msg0, node.tail_next_lineno)
+        raise LintWarn(msg0, node.tail_next_lineno, node.source_file)
     if node.num_args > 1:
-        raise LintWarn(msg1, node.tail_next_lineno)
+        raise LintWarn(msg1, node.tail_next_lineno, node.source_file)
 
 
 @linter.ensure_fundamentals
@@ -90,7 +90,7 @@ def check_step_names(graph):
     )
     for node in graph:
         if re.search("[^a-z0-9_]", node.name) or node.name[0] == "_":
-            raise LintWarn(msg.format(node), node.func_lineno)
+            raise LintWarn(msg.format(node), node.func_lineno, node.source_file)
 
 
 @linter.ensure_fundamentals
@@ -108,11 +108,11 @@ def check_num_args(graph):
     msg2 = "Step *{0.name}* is missing the 'self' argument."
     for node in graph:
         if node.num_args > 2:
-            raise LintWarn(msg0.format(node), node.func_lineno)
+            raise LintWarn(msg0.format(node), node.func_lineno, node.source_file)
         elif node.num_args == 2 and node.type != "join":
-            raise LintWarn(msg1.format(node), node.func_lineno)
+            raise LintWarn(msg1.format(node), node.func_lineno, node.source_file)
         elif node.num_args == 0:
-            raise LintWarn(msg2.format(node), node.func_lineno)
+            raise LintWarn(msg2.format(node), node.func_lineno, node.source_file)
 
 
 @linter.ensure_static_graph
@@ -125,7 +125,7 @@ def check_static_transitions(graph):
     )
     for node in graph:
         if node.type != "end" and not node.has_tail_next:
-            raise LintWarn(msg.format(node), node.func_lineno)
+            raise LintWarn(msg.format(node), node.func_lineno, node.source_file)
 
 
 @linter.ensure_static_graph
@@ -138,7 +138,7 @@ def check_valid_transitions(graph):
     )
     for node in graph:
         if node.type != "end" and node.has_tail_next and node.invalid_tail_next:
-            raise LintWarn(msg.format(node), node.tail_next_lineno)
+            raise LintWarn(msg.format(node), node.tail_next_lineno, node.source_file)
 
 
 @linter.ensure_static_graph
@@ -151,7 +151,11 @@ def check_unknown_transitions(graph):
     for node in graph:
         unknown = [n for n in node.out_funcs if n not in graph]
         if unknown:
-            raise LintWarn(msg.format(node, step=unknown[0]), node.tail_next_lineno)
+            raise LintWarn(
+                msg.format(node, step=unknown[0]),
+                node.tail_next_lineno,
+                node.source_file,
+            )
 
 
 @linter.ensure_acyclicity
@@ -167,7 +171,9 @@ def check_path(node, seen):
         for n in node.out_funcs:
             if n in seen:
                 path = "->".join(seen + [n])
-                raise LintWarn(msg.format(path), node.tail_next_lineno)
+                raise LintWarn(
+                    msg.format(path), node.tail_next_lineno, node.source_file
+                )
             else:
                 check_path(graph[n], seen + [n])
 
@@ -195,7 +201,7 @@ def traverse(node):
     orphans = nodeset - seen
     if orphans:
         orphan = graph[list(orphans)[0]]
-        raise LintWarn(msg.format(orphan), orphan.func_lineno)
+        raise LintWarn(msg.format(orphan), orphan.func_lineno, orphan.source_file)
 
 
 @linter.ensure_static_graph
@@ -230,7 +236,9 @@ def traverse(node, split_stack):
             if split_stack:
                 _, split_roots = split_stack.pop()
                 roots = ", ".join(split_roots)
-                raise LintWarn(msg0.format(roots=roots))
+                raise LintWarn(
+                    msg0.format(roots=roots), node.func_lineno, node.source_file
+                )
         elif node.type == "join":
             if split_stack:
                 _, split_roots = split_stack[-1]
@@ -243,9 +251,10 @@ def traverse(node, split_stack):
                             node, paths=paths, num_roots=len(split_roots), roots=roots
                         ),
                         node.func_lineno,
+                        node.source_file,
                     )
             else:
-                raise LintWarn(msg2.format(node), node.func_lineno)
+                raise LintWarn(msg2.format(node), node.func_lineno, node.source_file)
 
             # check that incoming steps come from the same lineage
             # (no cross joins)
@@ -256,7 +265,7 @@ def parents(n):
                     return tuple(graph[n].split_parents)
 
             if not all_equal(map(parents, node.in_funcs)):
-                raise LintWarn(msg3.format(node), node.func_lineno)
+                raise LintWarn(msg3.format(node), node.func_lineno, node.source_file)
 
         for n in node.out_funcs:
             traverse(graph[n], new_stack)
@@ -276,7 +285,9 @@ def check_empty_foreaches(graph):
         if node.type == "foreach":
             joins = [n for n in node.out_funcs if graph[n].type == "join"]
             if joins:
-                raise LintWarn(msg.format(node, join=joins[0]))
+                raise LintWarn(
+                    msg.format(node, join=joins[0]), node.func_lineno, node.source_file
+                )
 
 
 @linter.ensure_static_graph
@@ -290,7 +301,22 @@ def check_parallel_step_after_next(graph):
         if node.parallel_foreach and not all(
             graph[out_node].parallel_step for out_node in node.out_funcs
         ):
-            raise LintWarn(msg.format(node))
+            raise LintWarn(msg.format(node), node.func_lineno, node.source_file)
+
+
+@linter.ensure_static_graph
+@linter.check
+def check_join_followed_by_parallel_step(graph):
+    msg = (
+        "An @parallel step should be followed by a join step. Step *{0}* is called "
+        "after an @parallel step but is not a join step. Please add an extra `inputs` "
+        "argument to the step."
+    )
+    for node in graph:
+        if node.parallel_step and not graph[node.out_funcs[0]].type == "join":
+            raise LintWarn(
+                msg.format(node.out_funcs[0]), node.func_lineno, node.source_file
+            )
 
 
 @linter.ensure_static_graph
@@ -305,7 +331,9 @@ def check_parallel_foreach_calls_parallel_step(graph):
             for node2 in graph:
                 if node2.out_funcs and node.name in node2.out_funcs:
                     if not node2.parallel_foreach:
-                        raise LintWarn(msg.format(node, node2))
+                        raise LintWarn(
+                            msg.format(node, node2), node.func_lineno, node.source_file
+                        )
 
 
 @linter.ensure_non_nested_foreach
@@ -318,4 +346,4 @@ def check_nested_foreach(graph):
     for node in graph:
         if node.type == "foreach":
             if any(graph[p].type == "foreach" for p in node.split_parents):
-                raise LintWarn(msg.format(node))
+                raise LintWarn(msg.format(node), node.func_lineno, node.source_file)
diff --git a/metaflow/meta_files.py b/metaflow/meta_files.py
new file mode 100644
index 00000000000..2c3d523fc24
--- /dev/null
+++ b/metaflow/meta_files.py
@@ -0,0 +1,13 @@
+_UNINITIALIZED = object()
+_info_file_content = _UNINITIALIZED
+
+
+def read_info_file():
+    # Prevent circular import
+    from .packaging_sys import MetaflowCodeContent
+
+    global _info_file_content
+
+    if id(_info_file_content) == id(_UNINITIALIZED):
+        _info_file_content = MetaflowCodeContent.get_info()
+    return _info_file_content
diff --git a/metaflow/metadata/__init__.py b/metaflow/metadata_provider/__init__.py
similarity index 100%
rename from metaflow/metadata/__init__.py
rename to metaflow/metadata_provider/__init__.py
diff --git a/metaflow/metadata/heartbeat.py b/metaflow/metadata_provider/heartbeat.py
similarity index 70%
rename from metaflow/metadata/heartbeat.py
rename to metaflow/metadata_provider/heartbeat.py
index 7a73c0cf90d..bbe76a0dde2 100644
--- a/metaflow/metadata/heartbeat.py
+++ b/metaflow/metadata_provider/heartbeat.py
@@ -1,11 +1,12 @@
+import json
 import time
+from threading import Thread
+
 import requests
-import json
 
-from threading import Thread
-from metaflow.sidecar import MessageTypes, Message
-from metaflow.metaflow_config import SERVICE_HEADERS
 from metaflow.exception import MetaflowException
+from metaflow.metaflow_config import SERVICE_HEADERS
+from metaflow.sidecar import Message, MessageTypes
 
 HB_URL_KEY = "hb_url"
 
@@ -51,12 +52,29 @@ def _ping(self):
                 time.sleep(frequency_secs)
                 retry_counter = 0
             except HeartBeatException as e:
+                print(e)
                 retry_counter = retry_counter + 1
-                time.sleep(4**retry_counter)
+                time.sleep(1.5**retry_counter)
 
     def _heartbeat(self):
         if self.hb_url is not None:
-            response = requests.post(url=self.hb_url, data="{}", headers=self.headers)
+            try:
+                response = requests.post(
+                    url=self.hb_url, data="{}", headers=self.headers.copy()
+                )
+            except requests.exceptions.ConnectionError as e:
+                raise HeartBeatException(
+                    "HeartBeat request (%s) failed" " (ConnectionError)" % (self.hb_url)
+                )
+            except requests.exceptions.Timeout as e:
+                raise HeartBeatException(
+                    "HeartBeat request (%s) failed" " (Timeout)" % (self.hb_url)
+                )
+            except requests.exceptions.RequestException as e:
+                raise HeartBeatException(
+                    "HeartBeat request (%s) failed"
+                    " (RequestException) %s" % (self.hb_url, str(e))
+                )
             # Unfortunately, response.json() returns a string that we need
             # to cast to json; however when the request encounters an error
             # the return type is a json blob :/
diff --git a/metaflow/metadata/metadata.py b/metaflow/metadata_provider/metadata.py
similarity index 91%
rename from metaflow/metadata/metadata.py
rename to metaflow/metadata_provider/metadata.py
index 70427c3589a..0f88be8c964 100644
--- a/metaflow/metadata/metadata.py
+++ b/metaflow/metadata_provider/metadata.py
@@ -5,6 +5,7 @@
 from collections import namedtuple
 from itertools import chain
 
+from typing import List
 from metaflow.exception import MetaflowInternalError, MetaflowTaggingError
 from metaflow.tagging_util import validate_tag
 from metaflow.util import get_username, resolve_identity_as_tuple, is_stringish
@@ -76,6 +77,12 @@ def type_to_order(obj_type):
 
 @with_metaclass(MetadataProviderMeta)
 class MetadataProvider(object):
+    TYPE = None
+
+    @classmethod
+    def metadata_str(cls):
+        return "%s@%s" % (cls.TYPE, cls.INFO)
+
     @classmethod
     def compute_info(cls, val):
         """
@@ -623,6 +630,20 @@ def _get_system_info_as_dict(self):
             sys_info["r_version"] = env["r_version_code"]
         return sys_info
 
+    def _get_git_info_as_dict(self):
+        git_info = {}
+        env = self._environment.get_environment_info()
+        for key in [
+            "repo_url",
+            "branch_name",
+            "commit_sha",
+            "has_uncommitted_changes",
+        ]:
+            if key in env and env[key]:
+                git_info[key] = env[key]
+
+        return git_info
+
     def _get_system_tags(self):
         """Convert system info dictionary into a list of system tags"""
         return [
@@ -653,19 +674,78 @@ def _register_system_metadata(self, run_id, step_name, task_id, attempt):
         if code_sha:
             code_url = os.environ.get("METAFLOW_CODE_URL")
             code_ds = os.environ.get("METAFLOW_CODE_DS")
+            code_metadata = os.environ.get("METAFLOW_CODE_METADATA")
             metadata.append(
                 MetaDatum(
                     field="code-package",
                     value=json.dumps(
-                        {"ds_type": code_ds, "sha": code_sha, "location": code_url}
+                        {
+                            "ds_type": code_ds,
+                            "sha": code_sha,
+                            "location": code_url,
+                            "metadata": code_metadata,
+                        }
                     ),
                     type="code-package",
                     tags=["attempt_id:{0}".format(attempt)],
                 )
             )
+        # Add script name as metadata
+        script_name = self._environment.get_environment_info()["script"]
+        metadata.append(
+            MetaDatum(
+                field="script-name",
+                value=script_name,
+                type="script-name",
+                tags=["attempt_id:{0}".format(attempt)],
+            )
+        )
+        # And add git metadata
+        git_info = self._get_git_info_as_dict()
+        if git_info:
+            metadata.append(
+                MetaDatum(
+                    field="git-info",
+                    value=json.dumps(git_info),
+                    type="git-info",
+                    tags=["attempt_id:{0}".format(attempt)],
+                )
+            )
         if metadata:
             self.register_metadata(run_id, step_name, task_id, metadata)
 
+    @classmethod
+    def filter_tasks_by_metadata(
+        cls,
+        flow_name: str,
+        run_id: str,
+        step_name: str,
+        field_name: str,
+        pattern: str,
+    ) -> List[str]:
+        """
+        Filter tasks by metadata field and pattern, returning task pathspecs that match criteria.
+
+        Parameters
+        ----------
+        flow_name : str
+            Flow name, that the run belongs to.
+        run_id: str
+            Run id, together with flow_id, that identifies the specific Run whose tasks to query
+        step_name: str
+            Step name to query tasks from
+        field_name: str
+            Metadata field name to query
+        pattern: str
+            Pattern to match in metadata field value
+
+        Returns
+        -------
+        List[str]
+            List of task pathspecs that satisfy the query
+        """
+        raise NotImplementedError()
+
     @staticmethod
     def _apply_filter(elts, filters):
         if filters is None:
diff --git a/metaflow/metadata/util.py b/metaflow/metadata_provider/util.py
similarity index 70%
rename from metaflow/metadata/util.py
rename to metaflow/metadata_provider/util.py
index 705cfb50f52..80edf6b08a6 100644
--- a/metaflow/metadata/util.py
+++ b/metaflow/metadata_provider/util.py
@@ -1,13 +1,30 @@
 from io import BytesIO
 import os
+import shutil
 import tarfile
 
-from distutils.dir_util import copy_tree
-
 from metaflow import util
 from metaflow.plugins.datastores.local_storage import LocalStorage
 
 
+def copy_tree(src, dst, update=False):
+    if not os.path.exists(dst):
+        os.makedirs(dst)
+    for item in os.listdir(src):
+        s = os.path.join(src, item)
+        d = os.path.join(dst, item)
+        if os.path.isdir(s):
+            copy_tree(s, d, update)
+        else:
+            if (
+                update
+                and os.path.exists(d)
+                and os.path.getmtime(s) <= os.path.getmtime(d)
+            ):
+                continue
+            shutil.copy2(s, d)
+
+
 def sync_local_metadata_to_datastore(metadata_local_dir, task_ds):
     with util.TempDir() as td:
         tar_file_path = os.path.join(td, "metadata.tgz")
diff --git a/metaflow/metaflow_config.py b/metaflow/metaflow_config.py
index bb8c3cab62b..fd9e0e817d9 100644
--- a/metaflow/metaflow_config.py
+++ b/metaflow/metaflow_config.py
@@ -1,10 +1,7 @@
-import logging
 import os
 import sys
 import types
 
-import pkg_resources
-
 from metaflow.exception import MetaflowException
 from metaflow.metaflow_config_funcs import from_conf, get_validate_choice_fn
 
@@ -18,6 +15,16 @@
 ## value, either set `METAFLOW_DEFAULT_DATASTORE` in your configuration file or set
 ## an environment variable called `METAFLOW_DEFAULT_DATASTORE`
 
+##
+# Constants (NOTE: these need to live before any from_conf)
+##
+
+# Path to the local directory to store artifacts for 'local' datastore.
+DATASTORE_LOCAL_DIR = ".metaflow"
+
+# Local configuration file (in .metaflow) containing overrides per-project
+LOCAL_CONFIG_FILE = "config.json"
+
 ###
 # Default configuration
 ###
@@ -29,14 +36,26 @@
 DEFAULT_MONITOR = from_conf("DEFAULT_MONITOR", "nullSidecarMonitor")
 DEFAULT_PACKAGE_SUFFIXES = from_conf("DEFAULT_PACKAGE_SUFFIXES", ".py,.R,.RDS")
 DEFAULT_AWS_CLIENT_PROVIDER = from_conf("DEFAULT_AWS_CLIENT_PROVIDER", "boto3")
+DEFAULT_AZURE_CLIENT_PROVIDER = from_conf(
+    "DEFAULT_AZURE_CLIENT_PROVIDER", "azure-default"
+)
+DEFAULT_GCP_CLIENT_PROVIDER = from_conf("DEFAULT_GCP_CLIENT_PROVIDER", "gcp-default")
 DEFAULT_SECRETS_BACKEND_TYPE = from_conf("DEFAULT_SECRETS_BACKEND_TYPE")
+DEFAULT_SECRETS_ROLE = from_conf("DEFAULT_SECRETS_ROLE")
+
+DEFAULT_FROM_DEPLOYMENT_IMPL = from_conf(
+    "DEFAULT_FROM_DEPLOYMENT_IMPL", "argo-workflows"
+)
+
+###
+# User configuration
+###
+USER = from_conf("USER")
 
 
 ###
 # Datastore configuration
 ###
-# Path to the local directory to store artifacts for 'local' datastore.
-DATASTORE_LOCAL_DIR = ".metaflow"
 DATASTORE_SYSROOT_LOCAL = from_conf("DATASTORE_SYSROOT_LOCAL")
 # S3 bucket and prefix to store artifacts for 's3' datastore.
 DATASTORE_SYSROOT_S3 = from_conf("DATASTORE_SYSROOT_S3")
@@ -69,12 +88,18 @@
 S3_ENDPOINT_URL = from_conf("S3_ENDPOINT_URL")
 S3_VERIFY_CERTIFICATE = from_conf("S3_VERIFY_CERTIFICATE")
 
+# Set ServerSideEncryption for S3 uploads
+S3_SERVER_SIDE_ENCRYPTION = from_conf("S3_SERVER_SIDE_ENCRYPTION")
+
 # S3 retry configuration
 # This is useful if you want to "fail fast" on S3 operations; use with caution
 # though as this may increase failures. Note that this is the number of *retries*
 # so setting it to 0 means each operation will be tried once.
 S3_RETRY_COUNT = from_conf("S3_RETRY_COUNT", 7)
 
+# Number of concurrent S3 processes for parallel operations.
+S3_WORKER_COUNT = from_conf("S3_WORKER_COUNT", 64)
+
 # Number of retries on *transient* failures (such as SlowDown errors). Note
 # that if after S3_TRANSIENT_RETRY_COUNT times, all operations haven't been done,
 # it will try up to S3_RETRY_COUNT again so the total number of tries can be up to
@@ -84,6 +109,12 @@
 # top-level retries)
 S3_TRANSIENT_RETRY_COUNT = from_conf("S3_TRANSIENT_RETRY_COUNT", 20)
 
+# S3 retry configuration used in the aws client
+# Use the adaptive retry strategy by default
+S3_CLIENT_RETRY_CONFIG = from_conf(
+    "S3_CLIENT_RETRY_CONFIG", {"max_attempts": 10, "mode": "adaptive"}
+)
+
 # Threshold to start printing warnings for an AWS retry
 RETRY_WARNING_THRESHOLD = 3
 
@@ -91,9 +122,11 @@
 DATATOOLS_SUFFIX = from_conf("DATATOOLS_SUFFIX", "data")
 DATATOOLS_S3ROOT = from_conf(
     "DATATOOLS_S3ROOT",
-    os.path.join(DATASTORE_SYSROOT_S3, DATATOOLS_SUFFIX)
-    if DATASTORE_SYSROOT_S3
-    else None,
+    (
+        os.path.join(DATASTORE_SYSROOT_S3, DATATOOLS_SUFFIX)
+        if DATASTORE_SYSROOT_S3
+        else None
+    ),
 )
 
 TEMPDIR = from_conf("TEMPDIR", ".")
@@ -111,30 +144,52 @@
 # Similar to DATATOOLS_LOCALROOT, this is used ONLY by the IncludeFile's internal implementation.
 DATATOOLS_AZUREROOT = from_conf(
     "DATATOOLS_AZUREROOT",
-    os.path.join(DATASTORE_SYSROOT_AZURE, DATATOOLS_SUFFIX)
-    if DATASTORE_SYSROOT_AZURE
-    else None,
+    (
+        os.path.join(DATASTORE_SYSROOT_AZURE, DATATOOLS_SUFFIX)
+        if DATASTORE_SYSROOT_AZURE
+        else None
+    ),
 )
 # GS datatools root location
 # Note: we do not expose an actual datatools library for GS (like we do for S3)
 # Similar to DATATOOLS_LOCALROOT, this is used ONLY by the IncludeFile's internal implementation.
 DATATOOLS_GSROOT = from_conf(
     "DATATOOLS_GSROOT",
-    os.path.join(DATASTORE_SYSROOT_GS, DATATOOLS_SUFFIX)
-    if DATASTORE_SYSROOT_GS
-    else None,
+    (
+        os.path.join(DATASTORE_SYSROOT_GS, DATATOOLS_SUFFIX)
+        if DATASTORE_SYSROOT_GS
+        else None
+    ),
 )
 # Local datatools root location
 DATATOOLS_LOCALROOT = from_conf(
     "DATATOOLS_LOCALROOT",
-    os.path.join(DATASTORE_SYSROOT_LOCAL, DATATOOLS_SUFFIX)
-    if DATASTORE_SYSROOT_LOCAL
-    else None,
+    (
+        os.path.join(DATASTORE_SYSROOT_LOCAL, DATATOOLS_SUFFIX)
+        if DATASTORE_SYSROOT_LOCAL
+        else None
+    ),
 )
 
 # Secrets Backend - AWS Secrets Manager configuration
 AWS_SECRETS_MANAGER_DEFAULT_REGION = from_conf("AWS_SECRETS_MANAGER_DEFAULT_REGION")
 
+# Secrets Backend - GCP Secrets name prefix. With this, users don't have
+# to specify the full secret name in the @secret decorator.
+#
+# Note that it makes a difference whether the prefix ends with a slash or not
+# E.g. if secret name passed to @secret decorator is mysecret:
+# - "projects/1234567890/secrets/" -> "projects/1234567890/secrets/mysecret"
+# - "projects/1234567890/secrets/foo-" -> "projects/1234567890/secrets/foo-mysecret"
+GCP_SECRET_MANAGER_PREFIX = from_conf("GCP_SECRET_MANAGER_PREFIX")
+
+# Secrets Backend - Azure Key Vault prefix. With this, users don't have to
+# specify the full https:// vault url in the @secret decorator.
+#
+# It does not make a difference if the prefix ends in a / or not. We will handle either
+# case correctly.
+AZURE_KEY_VAULT_PREFIX = from_conf("AZURE_KEY_VAULT_PREFIX")
+
 # The root directory to save artifact pulls in, when using S3 or Azure
 ARTIFACT_LOCALROOT = from_conf("ARTIFACT_LOCALROOT", os.getcwd())
 
@@ -147,9 +202,11 @@
 )
 CARD_AZUREROOT = from_conf(
     "CARD_AZUREROOT",
-    os.path.join(DATASTORE_SYSROOT_AZURE, CARD_SUFFIX)
-    if DATASTORE_SYSROOT_AZURE
-    else None,
+    (
+        os.path.join(DATASTORE_SYSROOT_AZURE, CARD_SUFFIX)
+        if DATASTORE_SYSROOT_AZURE
+        else None
+    ),
 )
 CARD_GSROOT = from_conf(
     "CARD_GSROOT",
@@ -157,7 +214,7 @@
 )
 CARD_NO_WARNING = from_conf("CARD_NO_WARNING", False)
 
-SKIP_CARD_DUALWRITE = from_conf("SKIP_CARD_DUALWRITE", False)
+RUNTIME_CARD_RENDER_INTERVAL = from_conf("RUNTIME_CARD_RENDER_INTERVAL", 60)
 
 # Azure storage account URL
 AZURE_STORAGE_BLOB_SERVICE_ENDPOINT = from_conf("AZURE_STORAGE_BLOB_SERVICE_ENDPOINT")
@@ -194,12 +251,23 @@
 DEFAULT_CONTAINER_IMAGE = from_conf("DEFAULT_CONTAINER_IMAGE")
 # Default container registry
 DEFAULT_CONTAINER_REGISTRY = from_conf("DEFAULT_CONTAINER_REGISTRY")
+# Controls whether to include foreach stack information in metadata.
+INCLUDE_FOREACH_STACK = from_conf("INCLUDE_FOREACH_STACK", True)
+# Maximum length of the foreach value string to be stored in each ForeachFrame.
+MAXIMUM_FOREACH_VALUE_CHARS = from_conf("MAXIMUM_FOREACH_VALUE_CHARS", 30)
+# The default runtime limit (In seconds) of jobs launched by any compute provider. Default of 5 days.
+DEFAULT_RUNTIME_LIMIT = from_conf("DEFAULT_RUNTIME_LIMIT", 5 * 24 * 60 * 60)
 
 ###
 # Organization customizations
 ###
 UI_URL = from_conf("UI_URL")
 
+###
+# Capture error logs from argo
+###
+ARGO_WORKFLOWS_CAPTURE_ERROR_SCRIPT = from_conf("ARGO_WORKFLOWS_CAPTURE_ERROR_SCRIPT")
+
 # Contact information displayed when running the `metaflow` command.
 # Value should be a dictionary where:
 #  - key is a string describing contact method
@@ -214,6 +282,14 @@
     },
 )
 
+
+###
+# Decorators
+###
+# Format is a space separated string of decospecs (what is passed
+# using --with)
+DEFAULT_DECOSPECS = from_conf("DEFAULT_DECOSPECS", "")
+
 ###
 # AWS Batch configuration
 ###
@@ -257,7 +333,15 @@
 # machine execution logs. This needs to be available when using the
 # `step-functions create --log-execution-history` command.
 SFN_EXECUTION_LOG_GROUP_ARN = from_conf("SFN_EXECUTION_LOG_GROUP_ARN")
-
+# Amazon S3 path for storing the results of AWS Step Functions Distributed Map
+SFN_S3_DISTRIBUTED_MAP_OUTPUT_PATH = from_conf(
+    "SFN_S3_DISTRIBUTED_MAP_OUTPUT_PATH",
+    (
+        os.path.join(DATASTORE_SYSROOT_S3, "sfn_distributed_map_output")
+        if DATASTORE_SYSROOT_S3
+        else None
+    ),
+)
 ###
 # Kubernetes configuration
 ###
@@ -268,23 +352,49 @@
 # Default node selectors to use by K8S jobs created by Metaflow - foo=bar,baz=bab
 KUBERNETES_NODE_SELECTOR = from_conf("KUBERNETES_NODE_SELECTOR", "")
 KUBERNETES_TOLERATIONS = from_conf("KUBERNETES_TOLERATIONS", "")
+KUBERNETES_PERSISTENT_VOLUME_CLAIMS = from_conf(
+    "KUBERNETES_PERSISTENT_VOLUME_CLAIMS", ""
+)
 KUBERNETES_SECRETS = from_conf("KUBERNETES_SECRETS", "")
+# Default labels for kubernetes pods
+KUBERNETES_LABELS = from_conf("KUBERNETES_LABELS", "")
+# Default annotations for kubernetes pods
+KUBERNETES_ANNOTATIONS = from_conf("KUBERNETES_ANNOTATIONS", "")
 # Default GPU vendor to use by K8S jobs created by Metaflow (supports nvidia, amd)
 KUBERNETES_GPU_VENDOR = from_conf("KUBERNETES_GPU_VENDOR", "nvidia")
 # Default container image for K8S
 KUBERNETES_CONTAINER_IMAGE = from_conf(
     "KUBERNETES_CONTAINER_IMAGE", DEFAULT_CONTAINER_IMAGE
 )
+# Image pull policy for container images
+KUBERNETES_IMAGE_PULL_POLICY = from_conf("KUBERNETES_IMAGE_PULL_POLICY", None)
+# Image pull secrets for container images
+KUBERNETES_IMAGE_PULL_SECRETS = from_conf("KUBERNETES_IMAGE_PULL_SECRETS", "")
 # Default container registry for K8S
 KUBERNETES_CONTAINER_REGISTRY = from_conf(
     "KUBERNETES_CONTAINER_REGISTRY", DEFAULT_CONTAINER_REGISTRY
 )
 # Toggle for trying to fetch EC2 instance metadata
 KUBERNETES_FETCH_EC2_METADATA = from_conf("KUBERNETES_FETCH_EC2_METADATA", False)
-
+# Shared memory in MB to use for this step
+KUBERNETES_SHARED_MEMORY = from_conf("KUBERNETES_SHARED_MEMORY", None)
+# Default port number to open on the pods
+KUBERNETES_PORT = from_conf("KUBERNETES_PORT", None)
+# Default kubernetes resource requests for CPU, memory and disk
+KUBERNETES_CPU = from_conf("KUBERNETES_CPU", None)
+KUBERNETES_MEMORY = from_conf("KUBERNETES_MEMORY", None)
+KUBERNETES_DISK = from_conf("KUBERNETES_DISK", None)
+# Default kubernetes QoS class
+KUBERNETES_QOS = from_conf("KUBERNETES_QOS", "burstable")
+
+# Architecture of kubernetes nodes - used for @conda/@pypi in metaflow-dev
+KUBERNETES_CONDA_ARCH = from_conf("KUBERNETES_CONDA_ARCH")
 ARGO_WORKFLOWS_KUBERNETES_SECRETS = from_conf("ARGO_WORKFLOWS_KUBERNETES_SECRETS", "")
 ARGO_WORKFLOWS_ENV_VARS_TO_SKIP = from_conf("ARGO_WORKFLOWS_ENV_VARS_TO_SKIP", "")
 
+KUBERNETES_JOBSET_GROUP = from_conf("KUBERNETES_JOBSET_GROUP", "jobset.x-k8s.io")
+KUBERNETES_JOBSET_VERSION = from_conf("KUBERNETES_JOBSET_VERSION", "v1alpha2")
+
 ##
 # Argo Events Configuration
 ##
@@ -293,7 +403,12 @@
 ARGO_EVENTS_EVENT_SOURCE = from_conf("ARGO_EVENTS_EVENT_SOURCE")
 ARGO_EVENTS_EVENT = from_conf("ARGO_EVENTS_EVENT")
 ARGO_EVENTS_WEBHOOK_URL = from_conf("ARGO_EVENTS_WEBHOOK_URL")
+ARGO_EVENTS_INTERNAL_WEBHOOK_URL = from_conf(
+    "ARGO_EVENTS_INTERNAL_WEBHOOK_URL", ARGO_EVENTS_WEBHOOK_URL
+)
+ARGO_EVENTS_WEBHOOK_AUTH = from_conf("ARGO_EVENTS_WEBHOOK_AUTH", "none")
 
+ARGO_WORKFLOWS_UI_URL = from_conf("ARGO_WORKFLOWS_UI_URL")
 
 ##
 # Airflow Configuration
@@ -325,13 +440,35 @@
 # should result in an appreciable speedup in flow environment initialization.
 CONDA_DEPENDENCY_RESOLVER = from_conf("CONDA_DEPENDENCY_RESOLVER", "conda")
 
+# Default to not using fast init binary.
+CONDA_USE_FAST_INIT = from_conf("CONDA_USE_FAST_INIT", False)
+
+###
+# Escape hatch configuration
+###
+# Print out warning if escape hatch is not used for the target packages
+ESCAPE_HATCH_WARNING = from_conf("ESCAPE_HATCH_WARNING", True)
+
+###
+# Features
+###
+FEAT_ALWAYS_UPLOAD_CODE_PACKAGE = from_conf("FEAT_ALWAYS_UPLOAD_CODE_PACKAGE", False)
 ###
 # Debug configuration
 ###
-DEBUG_OPTIONS = ["subcommand", "sidecar", "s3client"]
+DEBUG_OPTIONS = [
+    "subcommand",
+    "sidecar",
+    "s3client",
+    "tracing",
+    "stubgen",
+    "userconf",
+    "conda",
+    "package",
+]
 
 for typ in DEBUG_OPTIONS:
-    vars()["DEBUG_%s" % typ.upper()] = from_conf("DEBUG_%s" % typ.upper())
+    vars()["DEBUG_%s" % typ.upper()] = from_conf("DEBUG_%s" % typ.upper(), False)
 
 ###
 # Plugin configuration
@@ -378,6 +515,12 @@
 
 KUBERNETES_SANDBOX_INIT_SCRIPT = from_conf("KUBERNETES_SANDBOX_INIT_SCRIPT")
 
+OTEL_ENDPOINT = from_conf("OTEL_ENDPOINT")
+ZIPKIN_ENDPOINT = from_conf("ZIPKIN_ENDPOINT")
+CONSOLE_TRACE_ENABLED = from_conf("CONSOLE_TRACE_ENABLED", False)
+# internal env used for preventing the tracing module from loading during Conda bootstrapping.
+DISABLE_TRACING = bool(os.environ.get("DISABLE_TRACING", False))
+
 # MAX_ATTEMPTS is the maximum number of attempts, including the first
 # task, retries, and the final fallback task and its retries.
 #
@@ -390,23 +533,10 @@
 # lexicographic ordering of attempts. This won't work if MAX_ATTEMPTS > 99.
 MAX_ATTEMPTS = 6
 
+# Feature flag (experimental features that are *explicitly* unsupported)
 
-# the naughty, naughty driver.py imported by lib2to3 produces
-# spam messages to the root logger. This is what is required
-# to silence it:
-class Filter(logging.Filter):
-    def filter(self, record):
-        if record.pathname.endswith("driver.py") and "grammar" in record.msg:
-            return False
-        return True
-
-
-logger = logging.getLogger()
-logger.addFilter(Filter())
-
-
-def get_version(pkg):
-    return pkg_resources.get_distribution(pkg).version
+# Process configs even when using the click_api for Runner/Deployer
+CLICK_API_PROCESS_CONFIG = from_conf("CLICK_API_PROCESS_CONFIG", False)
 
 
 # PINNED_CONDA_LIBS are the libraries that metaflow depends on for execution
@@ -420,9 +550,13 @@ def get_pinned_conda_libs(python_version, datastore_type):
     elif datastore_type == "azure":
         pins["azure-identity"] = ">=1.10.0"
         pins["azure-storage-blob"] = ">=12.12.0"
+        pins["azure-keyvault-secrets"] = ">=4.7.0"
+        pins["simple-azure-blob-downloader"] = ">=0.1.0"
     elif datastore_type == "gs":
         pins["google-cloud-storage"] = ">=2.5.0"
         pins["google-auth"] = ">=2.11.0"
+        pins["google-cloud-secret-manager"] = ">=2.10.0"
+        pins["simple-gcp-object-downloader"] = ">=0.1.0"
     elif datastore_type == "local":
         pass
     else:
@@ -436,6 +570,8 @@ def get_pinned_conda_libs(python_version, datastore_type):
 try:
     from metaflow.extension_support import get_modules
 
+    _TOGGLE_DECOSPECS = []
+
     ext_modules = get_modules("config")
     for m in ext_modules:
         # We load into globals whatever we have in extension_module
@@ -445,7 +581,7 @@ def get_pinned_conda_libs(python_version, datastore_type):
                 DEBUG_OPTIONS.extend(o)
                 for typ in o:
                     vars()["DEBUG_%s" % typ.upper()] = from_conf(
-                        "DEBUG_%s" % typ.upper()
+                        "DEBUG_%s" % typ.upper(), False
                     )
             elif n == "get_pinned_conda_libs":
 
@@ -459,8 +595,18 @@ def _new_get_pinned_conda_libs(
                     return d1
 
                 globals()[n] = _new_get_pinned_conda_libs
+            elif n == "TOGGLE_DECOSPECS":
+                if any([x.startswith("-") for x in o]):
+                    raise ValueError("Removing decospecs is not currently supported")
+                if any(" " in x for x in o):
+                    raise ValueError("Decospecs cannot contain spaces")
+                _TOGGLE_DECOSPECS.extend(o)
             elif not n.startswith("__") and not isinstance(o, types.ModuleType):
                 globals()[n] = o
+    # If DEFAULT_DECOSPECS is set, use that, else extrapolate from extensions
+    if not DEFAULT_DECOSPECS:
+        DEFAULT_DECOSPECS = " ".join(_TOGGLE_DECOSPECS)
+
 finally:
     # Erase all temporary names to avoid leaking things
     for _n in [
@@ -477,6 +623,7 @@ def _new_get_pinned_conda_libs(
         "v",
         "f1",
         "f2",
+        "_TOGGLE_DECOSPECS",
     ]:
         try:
             del globals()[_n]
diff --git a/metaflow/metaflow_config_funcs.py b/metaflow/metaflow_config_funcs.py
index 365a18d75eb..863d6f1bc6d 100644
--- a/metaflow/metaflow_config_funcs.py
+++ b/metaflow/metaflow_config_funcs.py
@@ -32,8 +32,40 @@ def init_config():
     return config
 
 
+def init_local_config():
+    # This function is heavily inspired from LocalStorage.get_datastore_root_from_config
+    # but simplifies certain things and also does not depend on DATASTORE_SYSROOT_LOCAL.
+    #
+    # In other words, since this config is meant to be local to a directory, it does not
+    # check in DATASTORE_SYSROOT_LOCAL but only up the current getcwd() path. This also
+    # prevents nasty circular dependencies :)
+
+    from metaflow.metaflow_config import DATASTORE_LOCAL_DIR, LOCAL_CONFIG_FILE
+
+    current_path = os.getcwd()
+    check_dir = os.path.join(current_path, DATASTORE_LOCAL_DIR)
+    check_dir = os.path.realpath(check_dir)
+    while not os.path.isdir(check_dir):
+        new_path = os.path.dirname(current_path)
+        if new_path == current_path:  # No longer making upward progress
+            return {}
+        current_path = new_path
+        check_dir = os.path.join(current_path, DATASTORE_LOCAL_DIR)
+    path_to_config = os.path.join(check_dir, LOCAL_CONFIG_FILE)
+    # We found a directory to look for the config file in
+    if os.path.exists(path_to_config):
+        with open(path_to_config, encoding="utf-8") as f:
+            return json.load(f)
+    return {}
+
+
 # Initialize defaults required to setup environment variables.
-METAFLOW_CONFIG = init_config()
+# (initialized lazily in from_conf since init_local_config requires
+# some configuration values
+
+METAFLOW_CONFIG = None
+
+METAFLOW_LOCAL_CONFIG = None
 
 _all_configs = {}
 
@@ -51,7 +83,13 @@ def config_values(include=0):
 
 def from_conf(name, default=None, validate_fn=None):
     """
-    First try to pull value from environment, then from metaflow config JSON
+    Pull value from the environment or configuration.
+    Order is:
+    1. Environment (use any environment variable explicitly set by user)
+    2. Local config (use any value set in the local config file -- so stuff in
+       .metaflow/project.json for example)
+    3. Global config (use any value set in the global config file)
+    4. Default
 
     Prior to a value being returned, we will validate using validate_fn (if provided).
     Only non-None values are validated.
@@ -59,9 +97,19 @@ def from_conf(name, default=None, validate_fn=None):
     validate_fn should accept (name, value).
     If the value validates, return None, else raise an MetaflowException.
     """
-    env_name = "METAFLOW_%s" % name
+    global METAFLOW_CONFIG, METAFLOW_LOCAL_CONFIG
+
+    if METAFLOW_CONFIG is None:
+        METAFLOW_CONFIG = init_config()
+    if METAFLOW_LOCAL_CONFIG is None:
+        METAFLOW_LOCAL_CONFIG = init_local_config()
+
     is_default = True
-    value = os.environ.get(env_name, METAFLOW_CONFIG.get(env_name, default))
+    env_name = "METAFLOW_%s" % name
+    value = os.environ.get(
+        env_name,
+        METAFLOW_LOCAL_CONFIG.get(env_name, METAFLOW_CONFIG.get(env_name, default)),
+    )
     if validate_fn and value is not None:
         validate_fn(env_name, value)
     if default is not None:
@@ -79,6 +127,12 @@ def from_conf(name, default=None, validate_fn=None):
                     raise ValueError(
                         "Expected a valid JSON for %s, got: %s" % (env_name, value)
                     )
+                if type(value) != type(default):
+                    raise ValueError(
+                        "Expected value of type '%s' for %s, got: %s"
+                        % (type(default), env_name, value)
+                    )
+                is_default = value == default
             _all_configs[env_name] = ConfigValue(
                 value=value,
                 serializer=json.dumps,
@@ -87,8 +141,12 @@ def from_conf(name, default=None, validate_fn=None):
             return value
         elif isinstance(default, (bool, int, float)) or is_stringish(default):
             try:
-                value = type(default)(value)
-                # Here we can compare values
+                if type(value) != type(default):
+                    if isinstance(default, bool):
+                        # Env vars are strings so try to evaluate logically
+                        value = value.lower() not in ("0", "false", "")
+                    else:
+                        value = type(default)(value)
                 is_default = value == default
             except ValueError:
                 raise ValueError(
@@ -98,6 +156,8 @@ def from_conf(name, default=None, validate_fn=None):
             raise RuntimeError(
                 "Default of type %s for %s is not supported" % (type(default), env_name)
             )
+    else:
+        is_default = value is None
     _all_configs[env_name] = ConfigValue(
         value=value,
         serializer=str,
diff --git a/metaflow/current.py b/metaflow/metaflow_current.py
similarity index 91%
rename from metaflow/current.py
rename to metaflow/metaflow_current.py
index a4b959bdee3..8443c1d75ab 100644
--- a/metaflow/current.py
+++ b/metaflow/metaflow_current.py
@@ -1,14 +1,15 @@
 from collections import namedtuple
 import os
-from typing import Any, Optional
+from typing import Any, Optional, TYPE_CHECKING
 
 from metaflow.metaflow_config import TEMPDIR
 
-Parallel = namedtuple("Parallel", ["main_ip", "num_nodes", "node_index"])
+Parallel = namedtuple(
+    "Parallel", ["main_ip", "num_nodes", "node_index", "control_task_id"]
+)
 
-# Can add this if we are ok with 3.5.2+
-# if typing.TYPE_CHECKING:
-#     from metaflow.client.core import Run, Task
+if TYPE_CHECKING:
+    import metaflow
 
 
 class Current(object):
@@ -29,7 +30,7 @@ def _raise(ex):
             raise ex
 
         self.__class__.graph = property(
-            fget=lambda _: _raise(RuntimeError("Graph is not available"))
+            fget=lambda self: _raise(RuntimeError("Graph is not available"))
         )
 
     def _set_env(
@@ -199,7 +200,7 @@ def pathspec(self) -> Optional[str]:
         return "/".join(pathspec_components)
 
     @property
-    def task(self) -> Optional["Task"]:
+    def task(self) -> Optional["metaflow.Task"]:
         """
         Task object of the current task.
 
@@ -221,7 +222,7 @@ def task(self) -> Optional["Task"]:
         return Task("/".join(pathspec_components), _namespace_check=False)
 
     @property
-    def run(self) -> Optional["Run"]:
+    def run(self) -> Optional["metaflow.Run"]:
         """
         Run object of the current run.
 
@@ -261,14 +262,6 @@ def username(self) -> Optional[str]:
         """
         return self._username
 
-    @property
-    def parallel(self):
-        return Parallel(
-            main_ip=os.environ.get("MF_PARALLEL_MAIN_IP", "127.0.0.1"),
-            num_nodes=int(os.environ.get("MF_PARALLEL_NUM_NODES", "1")),
-            node_index=int(os.environ.get("MF_PARALLEL_NODE_INDEX", "0")),
-        )
-
     @property
     def tags(self):
         """
diff --git a/metaflow/metaflow_environment.py b/metaflow/metaflow_environment.py
index 57fc6e094f0..d0f4dc7d0dc 100644
--- a/metaflow/metaflow_environment.py
+++ b/metaflow/metaflow_environment.py
@@ -1,15 +1,17 @@
+import json
 import os
 import platform
 import sys
 
 from .util import get_username
 from . import metaflow_version
+from . import metaflow_git
 from metaflow.exception import MetaflowException
 from metaflow.extension_support import dump_module_info
-from metaflow.mflog import BASH_MFLOG
-from . import R
+from metaflow.mflog import BASH_MFLOG, BASH_FLUSH_LOGS
+from metaflow.package import MetaflowPackage
 
-version_cache = None
+from . import R
 
 
 class InvalidEnvironmentException(MetaflowException):
@@ -50,8 +52,36 @@ def bootstrap_commands(self, step_name, datastore_type):
 
     def add_to_package(self):
         """
-        A list of tuples (file, arcname) to add to the job package.
-        `arcname` is an alternative name for the file in the job package.
+        Called to add custom files needed for this environment. This hook will be
+        called in the `MetaflowPackage` class where metaflow compiles the code package
+        tarball. This hook can return one of two things (the first is for backwards
+        compatibility -- move to the second):
+          - a generator yielding a tuple of `(file_path, arcname)` to add files to
+            the code package. `file_path` is the path to the file on the local filesystem
+            and `arcname` is the path relative to the packaged code.
+          - a generator yielding a tuple of `(content, arcname, type)` where:
+            - type is one of
+            ContentType.{USER_CONTENT, CODE_CONTENT, MODULE_CONTENT, OTHER_CONTENT}
+            - for USER_CONTENT:
+              - the file will be included relative to the directory containing the
+                user's flow file.
+              - content: path to the file to include
+              - arcname: path relative to the directory containing the user's flow file
+            - for CODE_CONTENT:
+              - the file will be included relative to the code directory in the package.
+                This will be the directory containing `metaflow`.
+              - content: path to the file to include
+              - arcname: path relative to the code directory in the package
+            - for MODULE_CONTENT:
+              - the module will be added to the code package as a python module. It will
+                be accessible as usual (import )
+              - content: name of the module
+              - arcname: None (ignored)
+            - for OTHER_CONTENT:
+              - the file will be included relative to any other configuration/metadata
+                files for the flow
+              - content: path to the file to include
+              - arcname: path relative to the config directory in the package
         """
         return []
 
@@ -89,10 +119,18 @@ def _get_download_code_package_cmd(self, code_package_url, datastore_type):
         It should work silently if everything goes well.
         """
         if datastore_type == "s3":
-            return (
-                '%s -m awscli ${METAFLOW_S3_ENDPOINT_URL:+--endpoint-url=\\"${METAFLOW_S3_ENDPOINT_URL}\\"} '
-                + "s3 cp %s job.tar >/dev/null"
-            ) % (self._python(), code_package_url)
+            from .plugins.aws.aws_utils import parse_s3_full_path
+
+            bucket, s3_object = parse_s3_full_path(code_package_url)
+            # NOTE: the script quoting is extremely sensitive due to the way shlex.split operates and this being inserted
+            # into a quoted command elsewhere.
+            # NOTE: Reason for the extra conditionals in the script are because
+            # Boto3 does not play well with passing None or an empty string to endpoint_url
+            return "{python} -c '{script}'".format(
+                python=self._python(),
+                script='import boto3, os; ep=os.getenv(\\"METAFLOW_S3_ENDPOINT_URL\\"); boto3.client(\\"s3\\", **({\\"endpoint_url\\":ep} if ep else {})).download_file(\\"%s\\", \\"%s\\", \\"job.tar\\")'
+                % (bucket, s3_object),
+            )
         elif datastore_type == "azure":
             from .plugins.azure.azure_utils import parse_azure_full_path
 
@@ -119,54 +157,89 @@ def _get_download_code_package_cmd(self, code_package_url, datastore_type):
             )
 
     def _get_install_dependencies_cmd(self, datastore_type):
-        cmds = ["%s -m pip install requests -qqq" % self._python()]
-        if datastore_type == "s3":
-            cmds.append("%s -m pip install awscli boto3 -qqq" % self._python())
-        elif datastore_type == "azure":
-            cmds.append(
-                "%s -m pip install azure-identity azure-storage-blob simple-azure-blob-downloader -qqq"
-                % self._python()
+        base_cmd = "{} -m pip install -qqq --no-compile --no-cache-dir --disable-pip-version-check".format(
+            self._python()
+        )
+
+        datastore_packages = {
+            "s3": ["boto3"],
+            "azure": [
+                "azure-identity",
+                "azure-storage-blob",
+                "azure-keyvault-secrets",
+                "simple-azure-blob-downloader",
+            ],
+            "gs": [
+                "google-cloud-storage",
+                "google-auth",
+                "simple-gcp-object-downloader",
+                "google-cloud-secret-manager",
+            ],
+        }
+
+        if datastore_type not in datastore_packages:
+            raise NotImplementedError(
+                "Unknown datastore type: {}".format(datastore_type)
             )
-        elif datastore_type == "gs":
-            cmds.append(
-                "%s -m pip install google-cloud-storage google-auth simple-gcp-object-downloader -qqq"
-                % self._python()
+
+        cmd = "{} {}".format(
+            base_cmd, " ".join(datastore_packages[datastore_type] + ["requests"])
+        )
+        # skip pip installs if we know that packages might already be available
+        return "if [ -z $METAFLOW_SKIP_INSTALL_DEPENDENCIES ]; then {}; fi".format(cmd)
+
+    def get_package_commands(
+        self, code_package_url, datastore_type, code_package_metadata=None
+    ):
+        # HACK: We want to keep forward compatibility with compute layers so that
+        # they can still call get_package_commands and NOT pass any metadata. If
+        # there is no additional information, we *assume* that it is the default
+        # used.
+        if code_package_metadata is None:
+            code_package_metadata = json.dumps(
+                {
+                    "version": 0,
+                    "archive_format": "tgz",
+                    "mfcontent_version": 1,
+                }
             )
-        else:
-            raise NotImplementedError(
-                "We don't know how to generate an install dependencies cmd for datastore %s"
-                % datastore_type
+        cmds = (
+            [
+                BASH_MFLOG,
+                BASH_FLUSH_LOGS,
+                "mflog 'Setting up task environment.'",
+                self._get_install_dependencies_cmd(datastore_type),
+                "mkdir metaflow",
+                "cd metaflow",
+                "mkdir .metaflow",  # mute local datastore creation log
+                "i=0; while [ $i -le 5 ]; do "
+                "mflog 'Downloading code package...'; "
+                + self._get_download_code_package_cmd(code_package_url, datastore_type)
+                + " && mflog 'Code package downloaded.' && break; "
+                "sleep 10; i=$((i+1)); "
+                "done",
+                "if [ $i -gt 5 ]; then "
+                "mflog 'Failed to download code package from %s "
+                "after 6 tries. Exiting...' && exit 1; "
+                "fi" % code_package_url,
+            ]
+            + MetaflowPackage.get_extract_commands(
+                code_package_metadata, "job.tar", dest_dir="."
             )
-        return " && ".join(cmds)
-
-    def get_package_commands(self, code_package_url, datastore_type):
-        cmds = [
-            BASH_MFLOG,
-            "mflog 'Setting up task environment.'",
-            self._get_install_dependencies_cmd(datastore_type),
-            "mkdir metaflow",
-            "cd metaflow",
-            "mkdir .metaflow",  # mute local datastore creation log
-            "i=0; while [ $i -le 5 ]; do "
-            "mflog 'Downloading code package...'; "
-            + self._get_download_code_package_cmd(code_package_url, datastore_type)
-            + " && mflog 'Code package downloaded.' && break; "
-            "sleep 10; i=$((i+1)); "
-            "done",
-            "if [ $i -gt 5 ]; then "
-            "mflog 'Failed to download code package from %s "
-            "after 6 tries. Exiting...' && exit 1; "
-            "fi" % code_package_url,
-            "TAR_OPTIONS='--warning=no-timestamp' tar xf job.tar",
-            "mflog 'Task is starting.'",
-        ]
+            + [
+                "export %s=%s:$(printenv %s)" % (k, v.replace('"', '\\"'), k)
+                for k, v in MetaflowPackage.get_post_extract_env_vars(
+                    code_package_metadata, dest_dir="."
+                ).items()
+            ]
+            + [
+                "mflog 'Task is starting.'",
+                "flush_mflogs",
+            ]
+        )
         return cmds
 
     def get_environment_info(self, include_ext_info=False):
-        global version_cache
-        if version_cache is None:
-            version_cache = metaflow_version.get_version()
-
         # note that this dict goes into the code package
         # so variables here should be relatively stable (no
         # timestamps) so the hash won't change all the time
@@ -180,8 +253,12 @@ def get_environment_info(self, include_ext_info=False):
             "use_r": R.use_r(),
             "python_version": sys.version,
             "python_version_code": "%d.%d.%d" % sys.version_info[:3],
-            "metaflow_version": version_cache,
+            "metaflow_version": metaflow_version.get_version(),
             "script": os.path.basename(os.path.abspath(sys.argv[0])),
+            # Add git info
+            **metaflow_git.get_repository_info(
+                path=os.path.dirname(os.path.abspath(sys.argv[0]))
+            ),
         }
         if R.use_r():
             env["metaflow_r_version"] = R.metaflow_r_version()
@@ -191,9 +268,11 @@ def get_environment_info(self, include_ext_info=False):
             # Information about extension modules (to load them in the proper order)
             ext_key, ext_val = dump_module_info()
             env[ext_key] = ext_val
-        return env
+        return {k: v for k, v in env.items() if v is not None and v != ""}
 
-    def executable(self, step_name):
+    def executable(self, step_name, default=None):
+        if default is not None:
+            return default
         return self._python()
 
     def _python(self):
diff --git a/metaflow/metaflow_git.py b/metaflow/metaflow_git.py
new file mode 100644
index 00000000000..b90a2edb9b6
--- /dev/null
+++ b/metaflow/metaflow_git.py
@@ -0,0 +1,115 @@
+#!/usr/bin/env python
+"""Get git repository information for the package
+
+Functions to retrieve git repository details like URL, branch name, 
+and commit SHA for Metaflow code provenance tracking.
+"""
+
+import os
+import subprocess
+from typing import Dict, List, Optional, Tuple, Union
+
+# Cache for git information to avoid repeated subprocess calls
+_git_info_cache = None
+
+__all__ = ("get_repository_info",)
+
+
+def _call_git(
+    args: List[str], path=Union[str, os.PathLike]
+) -> Tuple[Optional[str], Optional[int], bool]:
+    """
+    Call git with provided args.
+
+    Returns
+    -------
+        tuple : Tuple containing
+            (stdout, exitcode, failure) of the call
+    """
+    try:
+        result = subprocess.run(
+            ["git", *args],
+            cwd=path,
+            capture_output=True,
+            text=True,
+            check=False,
+        )
+        return result.stdout.strip(), result.returncode, False
+    except (OSError, subprocess.SubprocessError):
+        # Covers subprocess timeouts and other errors which would not lead to an exit code
+        return None, None, True
+
+
+def _get_repo_url(path: Union[str, os.PathLike]) -> Optional[str]:
+    """Get the repository URL from git config"""
+    stdout, returncode, _failed = _call_git(
+        ["config", "--get", "remote.origin.url"], path
+    )
+    if returncode == 0:
+        url = stdout
+        # Convert SSH URLs to HTTPS for clickable links
+        if url.startswith("git@"):
+            parts = url.split(":", 1)
+            if len(parts) == 2:
+                domain = parts[0].replace("git@", "")
+                repo_path = parts[1]
+                url = f"https://{domain}/{repo_path}"
+        return url
+    return None
+
+
+def _get_branch_name(path: Union[str, os.PathLike]) -> Optional[str]:
+    """Get the current git branch name"""
+    stdout, returncode, _failed = _call_git(["rev-parse", "--abbrev-ref", "HEAD"], path)
+    return stdout if returncode == 0 else None
+
+
+def _get_commit_sha(path: Union[str, os.PathLike]) -> Optional[str]:
+    """Get the current git commit SHA"""
+    stdout, returncode, _failed = _call_git(["rev-parse", "HEAD"], path)
+    return stdout if returncode == 0 else None
+
+
+def _is_in_git_repo(path: Union[str, os.PathLike]) -> bool:
+    """Check if we're currently in a git repository"""
+    stdout, returncode, _failed = _call_git(
+        ["rev-parse", "--is-inside-work-tree"], path
+    )
+    return returncode == 0 and stdout == "true"
+
+
+def _has_uncommitted_changes(path: Union[str, os.PathLike]) -> Optional[bool]:
+    """Check if the git repository has uncommitted changes"""
+    _stdout, returncode, failed = _call_git(
+        ["diff-index", "--quiet", "HEAD", "--"], path
+    )
+    if failed:
+        return None
+    return returncode != 0
+
+
+def get_repository_info(path: Union[str, os.PathLike]) -> Dict[str, Union[str, bool]]:
+    """Get git repository information for a path
+
+    Returns:
+        dict: Dictionary containing:
+            repo_url: Repository URL (converted to HTTPS if from SSH)
+            branch_name: Current branch name
+            commit_sha: Current commit SHA
+            has_uncommitted_changes: Boolean indicating if there are uncommitted changes
+    """
+    global _git_info_cache
+
+    if _git_info_cache is not None:
+        return _git_info_cache
+
+    _git_info_cache = {}
+    if _is_in_git_repo(path):
+        _git_info_cache = {
+            "repo_url": _get_repo_url(path),
+            "branch_name": _get_branch_name(path),
+            "commit_sha": _get_commit_sha(path),
+            "has_uncommitted_changes": _has_uncommitted_changes(path),
+        }
+
+    return _git_info_cache
diff --git a/metaflow/metaflow_version.py b/metaflow/metaflow_version.py
index 9a36dc79ae0..90badf33657 100644
--- a/metaflow/metaflow_version.py
+++ b/metaflow/metaflow_version.py
@@ -7,11 +7,15 @@
 
 # This file is adapted from https://github.com/aebrahim/python-git-version
 
-from subprocess import check_output, CalledProcessError
-from os import path, name, devnull, environ, listdir
-import json
+import subprocess
+from os import path, name, environ, listdir
 
-from metaflow import CURRENT_DIRECTORY, INFO_FILE
+from metaflow.extension_support import update_package_info
+from metaflow.meta_files import read_info_file
+
+
+# True/False correspond to the value `public`` in get_version
+_version_cache = {True: None, False: None}
 
 __all__ = ("get_version",)
 
@@ -23,11 +27,11 @@ def find_git_on_windows():
         """find the path to the git executable on Windows"""
         # first see if git is in the path
         try:
-            check_output(["where", "/Q", "git"])
+            subprocess.check_output(["where", "/Q", "git"])
             # if this command succeeded, git is in the path
             return "git"
         # catch the exception thrown if git was not found
-        except CalledProcessError:
+        except subprocess.CalledProcessError:
             pass
         # There are several locations where git.exe may be hiding
         possible_locations = []
@@ -57,87 +61,152 @@ def find_git_on_windows():
     GIT_COMMAND = find_git_on_windows()
 
 
-def call_git_describe(abbrev=7):
+def call_git_describe(file_to_check, abbrev=7):
     """return the string output of git describe"""
     try:
-
-        # first, make sure we are actually in a Metaflow repo,
-        # not some other repo
-        with open(devnull, "w") as fnull:
-            arguments = [GIT_COMMAND, "rev-parse", "--show-toplevel"]
-            reponame = (
-                check_output(arguments, cwd=CURRENT_DIRECTORY, stderr=fnull)
-                .decode("ascii")
-                .strip()
-            )
-            if path.basename(reponame) != "metaflow":
-                return None
-
-        with open(devnull, "w") as fnull:
-            arguments = [GIT_COMMAND, "describe", "--tags", "--abbrev=%d" % abbrev]
-            return (
-                check_output(arguments, cwd=CURRENT_DIRECTORY, stderr=fnull)
-                .decode("ascii")
-                .strip()
-            )
-
-    except (OSError, CalledProcessError):
+        wd = path.dirname(file_to_check)
+        filename = path.basename(file_to_check)
+
+        # First check if the file is tracked in the GIT repository we are in
+        # We do this because in some setups and for some bizarre reason, python files
+        # are installed directly into a git repository (I am looking at you brew). We
+        # don't want to consider this a GIT install in that case.
+        args = [GIT_COMMAND, "ls-files", "--error-unmatch", filename]
+        git_return_code = subprocess.run(
+            args,
+            cwd=wd,
+            stderr=subprocess.DEVNULL,
+            stdout=subprocess.DEVNULL,
+            check=False,
+        ).returncode
+
+        if git_return_code != 0:
+            return None
+
+        args = [
+            GIT_COMMAND,
+            "describe",
+            "--tags",
+            "--dirty",
+            "--long",
+            "--abbrev=%d" % abbrev,
+        ]
+        return (
+            subprocess.check_output(args, cwd=wd, stderr=subprocess.DEVNULL)
+            .decode("ascii")
+            .strip()
+        )
+
+    except (OSError, subprocess.CalledProcessError):
         return None
 
 
-def format_git_describe(git_str, pep440=False):
+def format_git_describe(git_str, public=False):
     """format the result of calling 'git describe' as a python version"""
     if git_str is None:
         return None
-    if "-" not in git_str:  # currently at a tag
-        return git_str
+    splits = git_str.split("-")
+    if len(splits) == 4:
+        # Formatted as ---dirty
+        tag, post, h = splits[:3]
+        dirty = "-" + splits[3]
     else:
-        # formatted as version-N-githash
-        # want to convert to version.postN-githash
-        git_str = git_str.replace("-", ".post", 1)
-        if pep440:  # does not allow git hash afterwards
-            return git_str.split("-")[0]
-        else:
-            return git_str.replace("-g", "+git")
+        # Formatted as --
+        tag, post, h = splits
+        dirty = ""
+    if post == "0":
+        if public:
+            return tag
+        return tag + dirty
+
+    if public:
+        return "%s.post%s" % (tag, post)
+
+    return "%s.post%s-git%s%s" % (tag, post, h[1:], dirty)
 
 
 def read_info_version():
     """Read version information from INFO file"""
-    try:
-        with open(INFO_FILE, "r") as contents:
-            return json.load(contents).get("metaflow_version")
-    except IOError:
-        return None
+    info_file = read_info_file()
+    if info_file:
+        return info_file.get("metaflow_version")
+    return None
 
 
-def get_version(pep440=False):
+def get_version(public=False):
     """Tracks the version number.
 
-    pep440: bool
-        When True, this function returns a version string suitable for
-        a release as defined by PEP 440. When False, the githash (if
-        available) will be appended to the version string.
+    public: bool
+        When True, this function returns a *public* version specification which
+        doesn't include any local information (dirtiness or hash). See
+        https://packaging.python.org/en/latest/specifications/version-specifiers/#version-scheme
 
-    If the script is located within an active git repository,
-    git-describe is used to get the version information.
+    We first check the INFO file to see if we recorded a version of Metaflow. If there
+    is none, we check if we are in a GIT repository and if so, form the version
+    from that.
 
-    Otherwise, the version logged by package installer is returned.
-
-    If even that information isn't available (likely when executing on a
-    remote cloud instance), the version information is returned from INFO file
-    in the current directory.
+    Otherwise, we return the version of Metaflow that was installed.
 
     """
 
-    version = format_git_describe(call_git_describe(), pep440=pep440)
-    version_addl = None
-    if version is None:  # not a git repository
-        import metaflow
-
+    global _version_cache
+
+    # To get the version we do the following:
+    #  - Check if we have a cached version. If so, return that
+    #  - Then check if we have an INFO file present. If so, use that as it is
+    #    the most reliable way to get the version. In particular, when running remotely,
+    #    metaflow is installed in a directory and if any extension is using distutils to
+    #    determine its version, this would return None and querying the version directly
+    #    from the extension would fail to produce the correct result
+    #  - Then if we are in the GIT repository and if so, use the git describe
+    #  - If we don't have an INFO file, we look at the version information that is
+    #    populated by metaflow and the extensions.
+
+    if _version_cache[public] is not None:
+        return _version_cache[public]
+
+    version = (
+        read_info_version()
+    )  # Version info is cached in INFO file; includes extension info
+
+    if version:
+        _version_cache[public] = version
+        return version
+
+    # Get the version for Metaflow, favor the GIT version
+    import metaflow
+
+    version = format_git_describe(
+        call_git_describe(file_to_check=metaflow.__file__), public=public
+    )
+    if version is None:
         version = metaflow.__version__
-        version_addl = metaflow.__version_addl__
-    if version is None:  # not a proper python package
-        return read_info_version()
-    if version_addl:
-        return "+".join([version, version_addl])
+
+    # Look for extensions and compute their versions. Properly formed extensions have
+    # a toplevel file which will contain a __mf_extensions__ value and a __version__
+    # value. We already saved the properly formed modules when loading metaflow in
+    # __ext_tl_modules__.
+    ext_versions = []
+    for pkg_name, extension_module in metaflow.__ext_tl_modules__:
+        ext_name = getattr(extension_module, "__mf_extensions__", "")
+        ext_version = format_git_describe(
+            call_git_describe(file_to_check=extension_module.__file__), public=public
+        )
+        if ext_version is None:
+            ext_version = getattr(extension_module, "__version__", "")
+        # Update the package information about reported version for the extension
+        # (only for the full info which is called at least once -- if we update more
+        # it will error out since we can only update_package_info once)
+        if not public:
+            update_package_info(
+                package_name=pkg_name,
+                extension_name=ext_name,
+                package_version=ext_version,
+            )
+        ext_versions.append("%s(%s)" % (ext_name, ext_version))
+
+    # We now have all the information about extensions so we can form the final string
+    if ext_versions:
+        version = version + "+" + ";".join(ext_versions)
+    _version_cache[public] = version
     return version
diff --git a/metaflow/mflog/__init__.py b/metaflow/mflog/__init__.py
index 400fcaf11ba..586268b6dd0 100644
--- a/metaflow/mflog/__init__.py
+++ b/metaflow/mflog/__init__.py
@@ -44,6 +44,8 @@
 BASH_SAVE_LOGS_ARGS = ["python", "-m", "metaflow.mflog.save_logs"]
 BASH_SAVE_LOGS = " ".join(BASH_SAVE_LOGS_ARGS)
 
+BASH_FLUSH_LOGS = "flush_mflogs(){ " f"{BASH_SAVE_LOGS}; " "}"
+
 
 # this function returns a bash expression that redirects stdout
 # and stderr of the given bash expression to mflog.tee
@@ -63,7 +65,7 @@ def bash_capture_logs(bash_expr, var_transform=None):
 # update_delay determines how often logs should be uploaded to S3
 # as a function of the task execution time
 
-MIN_UPDATE_DELAY = 1.0  # the most frequent update interval
+MIN_UPDATE_DELAY = 0.25  # the most frequent update interval
 MAX_UPDATE_DELAY = 30.0  # the least frequent update interval
 
 
@@ -110,7 +112,6 @@ def export_mflog_env_vars(
 
 def tail_logs(prefix, stdout_tail, stderr_tail, echo, has_log_updates):
     def _available_logs(tail, stream, echo, should_persist=False):
-        # print the latest batch of lines
         try:
             for line in tail:
                 if should_persist:
@@ -128,7 +129,7 @@ def _available_logs(tail, stream, echo, should_persist=False):
 
     start_time = time.time()
     next_log_update = start_time
-    log_update_delay = 1
+    log_update_delay = update_delay(0)
     while has_log_updates():
         if time.time() > next_log_update:
             _available_logs(stdout_tail, "stdout", echo)
diff --git a/metaflow/mflog/mflog.py b/metaflow/mflog/mflog.py
index fd04cb768ba..0330cd63134 100644
--- a/metaflow/mflog/mflog.py
+++ b/metaflow/mflog/mflog.py
@@ -135,7 +135,13 @@ def line_iter(logblob):
                 missing.append(line)
         for line in missing:
             res = MFLogline(
-                False, None, MISSING_TIMESTAMP_STR, None, None, line, MISSING_TIMESTAMP
+                False,
+                None,
+                MISSING_TIMESTAMP_STR.encode("utf-8"),
+                None,
+                None,
+                line,
+                MISSING_TIMESTAMP,
             )
             yield res.utc_tstamp_str, res
 
diff --git a/metaflow/mflog/save_logs.py b/metaflow/mflog/save_logs.py
index ea7ca673288..beedfcb2a11 100644
--- a/metaflow/mflog/save_logs.py
+++ b/metaflow/mflog/save_logs.py
@@ -8,9 +8,12 @@
 from metaflow.util import Path
 from . import TASK_LOG_SOURCE
 
+from metaflow.tracing import cli
+
 SMALL_FILE_LIMIT = 1024 * 1024
 
 
+@cli("save_logs")
 def save_logs():
     def _read_file(path):
         with open(path, "rb") as f:
diff --git a/metaflow/multicore_utils.py b/metaflow/multicore_utils.py
index 1ac81039c12..165f9837e94 100644
--- a/metaflow/multicore_utils.py
+++ b/metaflow/multicore_utils.py
@@ -2,8 +2,22 @@
 import os
 import traceback
 from itertools import islice
-from multiprocessing import cpu_count
 from tempfile import NamedTemporaryFile
+import time
+import metaflow.tracing as tracing
+
+from typing import (
+    Any,
+    Callable,
+    Iterable,
+    Iterator,
+    List,
+    Optional,
+    NoReturn,
+    Tuple,
+    TypeVar,
+    Union,
+)
 
 try:
     # Python 2
@@ -27,7 +41,13 @@ class MulticoreException(Exception):
     pass
 
 
-def _spawn(func, arg, dir):
+_A = TypeVar("_A")
+_R = TypeVar("_R")
+
+
+def _spawn(
+    func: Callable[[_A], _R], arg: _A, dir: Optional[str]
+) -> Union[Tuple[int, str], NoReturn]:
     with NamedTemporaryFile(prefix="parallel_map_", dir=dir, delete=False) as tmpfile:
         output_file = tmpfile.name
 
@@ -39,37 +59,73 @@ def _spawn(func, arg, dir):
     if pid:
         return pid, output_file
     else:
-        try:
-            exit_code = 1
-            ret = func(arg)
-            with open(output_file, "wb") as f:
-                pickle.dump(ret, f, protocol=pickle.HIGHEST_PROTOCOL)
-            exit_code = 0
-        except:
-            # we must not let any exceptions escape this function
-            # which might trigger unintended side effects
-            traceback.print_exc()
-        finally:
-            sys.stderr.flush()
-            sys.stdout.flush()
-            # we can't use sys.exit(0) here since it raises SystemExit
-            # that may have unintended side effects (e.g. triggering
-            # finally blocks).
-            os._exit(exit_code)
-
-
-def parallel_imap_unordered(func, iterable, max_parallel=None, dir=None):
-
+        with tracing.post_fork():
+            try:
+                exit_code = 1
+                ret = func(arg)
+                with open(output_file, "wb") as f:
+                    pickle.dump(ret, f, protocol=pickle.HIGHEST_PROTOCOL)
+                exit_code = 0
+            except:
+                # we must not let any exceptions escape this function
+                # which might trigger unintended side-effects
+                traceback.print_exc()
+            finally:
+                sys.stderr.flush()
+                sys.stdout.flush()
+                # we can't use sys.exit(0) here since it raises SystemExit
+                # that may have unintended side-effects (e.g. triggering
+                # finally blocks).
+                os._exit(exit_code)
+
+
+def parallel_imap_unordered(
+    func: Callable[[_A], _R],
+    iterable: Iterable[_A],
+    max_parallel: Optional[int] = None,
+    dir: Optional[str] = None,
+) -> Iterator[_R]:
+    """
+    Parallelizes execution of a function using multiprocessing. The result
+    order is not guaranteed.
+
+    Parameters
+    ----------
+    func : Callable[[Any], Any]
+        Function taking a single argument and returning a result
+    iterable : Iterable[Any]
+        Iterable over arguments to pass to fun
+    max_parallel int, optional, default None
+        Maximum parallelism. If not specified, it uses the number of CPUs
+    dir : str, optional, default None
+        If specified, it's the directory where temporary files are created
+
+    Yields
+    ------
+    Any
+        One result from calling func on one argument
+    """
     if max_parallel is None:
+        # Lazy import to save on startup time for metaflow as a whole
+        from multiprocessing import cpu_count
+
         max_parallel = cpu_count()
 
     args_iter = iter(iterable)
     pids = [_spawn(func, arg, dir) for arg in islice(args_iter, max_parallel)]
 
     while pids:
-
-        pid, output_file = pids.pop()
-        if os.waitpid(pid, 0)[1]:
+        for idx, pid_info in enumerate(pids):
+            pid, output_file = pid_info
+            pid, exit_code = os.waitpid(pid, os.WNOHANG)
+            if pid:
+                pids.pop(idx)
+                break
+        else:
+            time.sleep(0.1)  # Wait a bit before re-checking
+            continue
+
+        if exit_code:
             raise MulticoreException("Child failed")
 
         with open(output_file, "rb") as f:
@@ -81,10 +137,39 @@ def parallel_imap_unordered(func, iterable, max_parallel=None, dir=None):
             pids.insert(0, _spawn(func, arg[0], dir))
 
 
-def parallel_map(func, iterable, **kwargs):
+def parallel_map(
+    func: Callable[[_A], _R],
+    iterable: Iterable[_A],
+    max_parallel: Optional[int] = None,
+    dir: Optional[str] = None,
+) -> List[_R]:
+    """
+    Parallelizes execution of a function using multiprocessing. The result
+    order is that of the arguments in `iterable`.
+
+    Parameters
+    ----------
+    func : Callable[[Any], Any]
+        Function taking a single argument and returning a result
+    iterable : Iterable[Any]
+        Iterable over arguments to pass to fun
+    max_parallel int, optional, default None
+        Maximum parallelism. If not specified, it uses the number of CPUs
+    dir : str, optional, default None
+        If specified, it's the directory where temporary files are created
+
+    Returns
+    -------
+    List[Any]
+        Results. The items in the list are in the same order as the items
+        in `iterable`.
+    """
+
     def wrapper(arg_with_idx):
         idx, arg = arg_with_idx
         return idx, func(arg)
 
-    res = parallel_imap_unordered(wrapper, enumerate(iterable), **kwargs)
-    return [r for idx, r in sorted(res)]
+    res = parallel_imap_unordered(
+        wrapper, enumerate(iterable), max_parallel=max_parallel, dir=dir
+    )
+    return [r for _, r in sorted(res)]
diff --git a/metaflow/package.py b/metaflow/package.py
deleted file mode 100644
index fe1fd4f1edf..00000000000
--- a/metaflow/package.py
+++ /dev/null
@@ -1,184 +0,0 @@
-import importlib
-import os
-import sys
-import tarfile
-import time
-import json
-from io import BytesIO
-
-from .extension_support import EXT_PKG, package_mfext_all
-from .metaflow_config import DEFAULT_PACKAGE_SUFFIXES
-from .exception import MetaflowException
-from .util import to_unicode
-from . import R, INFO_FILE
-
-DEFAULT_SUFFIXES_LIST = DEFAULT_PACKAGE_SUFFIXES.split(",")
-METAFLOW_SUFFIXES_LIST = [".py", ".html", ".css", ".js"]
-
-
-class NonUniqueFileNameToFilePathMappingException(MetaflowException):
-    headline = "Non Unique file path for a file name included in code package"
-
-    def __init__(self, filename, file_paths, lineno=None):
-        msg = (
-            "Filename %s included in the code package includes multiple different paths for the same name : %s.\n"
-            "The `filename` in the `add_to_package` decorator hook requires a unique `file_path` to `file_name` mapping"
-            % (filename, ", ".join(file_paths))
-        )
-        super().__init__(msg=msg, lineno=lineno)
-
-
-# this is os.walk(follow_symlinks=True) with cycle detection
-def walk_without_cycles(top_root):
-    seen = set()
-
-    def _recurse(root):
-        for parent, dirs, files in os.walk(root):
-            for d in dirs:
-                path = os.path.join(parent, d)
-                if os.path.islink(path):
-                    # Breaking loops: never follow the same symlink twice
-                    #
-                    # NOTE: this also means that links to sibling links are
-                    # not followed. In this case:
-                    #
-                    #   x -> y
-                    #   y -> oo
-                    #   oo/real_file
-                    #
-                    # real_file is only included twice, not three times
-                    reallink = os.path.realpath(path)
-                    if reallink not in seen:
-                        seen.add(reallink)
-                        for x in _recurse(path):
-                            yield x
-            yield parent, files
-
-    for x in _recurse(top_root):
-        yield x
-
-
-class MetaflowPackage(object):
-    def __init__(self, flow, environment, echo, suffixes=DEFAULT_SUFFIXES_LIST):
-        self.suffixes = list(set().union(suffixes, DEFAULT_SUFFIXES_LIST))
-        self.environment = environment
-        self.metaflow_root = os.path.dirname(__file__)
-
-        self.flow_name = flow.name
-        self._flow = flow
-        self.create_time = time.time()
-        environment.init_environment(echo)
-        for step in flow:
-            for deco in step.decorators:
-                deco.package_init(flow, step.__name__, environment)
-        self.blob = self._make()
-
-    def _walk(self, root, exclude_hidden=True, suffixes=None):
-        if suffixes is None:
-            suffixes = []
-        root = to_unicode(root)  # handle files/folder with non ascii chars
-        prefixlen = len("%s/" % os.path.dirname(root))
-        for (
-            path,
-            files,
-        ) in walk_without_cycles(root):
-            if exclude_hidden and "/." in path:
-                continue
-            # path = path[2:] # strip the ./ prefix
-            # if path and (path[0] == '.' or './' in path):
-            #    continue
-            for fname in files:
-                if fname[0] == ".":
-                    continue
-                if any(fname.endswith(suffix) for suffix in suffixes):
-                    p = os.path.join(path, fname)
-                    yield p, p[prefixlen:]
-
-    def path_tuples(self):
-        """
-        Returns list of (path, arcname) to be added to the job package, where
-        `arcname` is the alternative name for the file in the package.
-        """
-        # We want the following contents in the tarball
-        # Metaflow package itself
-        for path_tuple in self._walk(
-            self.metaflow_root, exclude_hidden=False, suffixes=METAFLOW_SUFFIXES_LIST
-        ):
-            yield path_tuple
-
-        # Metaflow extensions; for now, we package *all* extensions but this may change
-        # at a later date; it is possible to call `package_mfext_package` instead of
-        # `package_mfext_all` but in that case, make sure to also add a
-        # metaflow_extensions/__init__.py file to properly "close" the metaflow_extensions
-        # package and prevent other extensions from being loaded that may be
-        # present in the rest of the system
-        for path_tuple in package_mfext_all():
-            yield path_tuple
-
-        # Any custom packages exposed via decorators
-        deco_module_paths = {}
-        for step in self._flow:
-            for deco in step.decorators:
-                for path_tuple in deco.add_to_package():
-                    file_path, file_name = path_tuple
-                    # Check if the path is not duplicated as
-                    # many steps can have the same packages being imported
-                    if file_name not in deco_module_paths:
-                        deco_module_paths[file_name] = file_path
-                        yield path_tuple
-                    elif deco_module_paths[file_name] != file_path:
-                        raise NonUniqueFileNameToFilePathMappingException(
-                            file_name, [deco_module_paths[file_name], file_path]
-                        )
-
-        # the package folders for environment
-        for path_tuple in self.environment.add_to_package():
-            yield path_tuple
-        if R.use_r():
-            # the R working directory
-            for path_tuple in self._walk(
-                "%s/" % R.working_dir(), suffixes=self.suffixes
-            ):
-                yield path_tuple
-            # the R package
-            for path_tuple in R.package_paths():
-                yield path_tuple
-        else:
-            # the user's working directory
-            flowdir = os.path.dirname(os.path.abspath(sys.argv[0])) + "/"
-            for path_tuple in self._walk(flowdir, suffixes=self.suffixes):
-                yield path_tuple
-
-    def _add_info(self, tar):
-        info = tarfile.TarInfo(os.path.basename(INFO_FILE))
-        env = self.environment.get_environment_info(include_ext_info=True)
-        buf = BytesIO()
-        buf.write(json.dumps(env).encode("utf-8"))
-        buf.seek(0)
-        info.size = len(buf.getvalue())
-        tar.addfile(info, buf)
-
-    def _make(self):
-        def no_mtime(tarinfo):
-            # a modification time change should not change the hash of
-            # the package. Only content modifications will.
-            tarinfo.mtime = 0
-            return tarinfo
-
-        buf = BytesIO()
-        with tarfile.open(
-            fileobj=buf, mode="w:gz", compresslevel=3, dereference=True
-        ) as tar:
-            self._add_info(tar)
-            for path, arcname in self.path_tuples():
-                tar.add(path, arcname=arcname, recursive=False, filter=no_mtime)
-
-        blob = bytearray(buf.getvalue())
-        blob[4:8] = [0] * 4  # Reset 4 bytes from offset 4 to account for ts
-        return blob
-
-    def __str__(self):
-        return "" % (
-            self.flow_name,
-            time.strftime("%a, %d %b %Y %H:%M:%S", self.create_time),
-        )
diff --git a/metaflow/package/__init__.py b/metaflow/package/__init__.py
new file mode 100644
index 00000000000..aac2c39983e
--- /dev/null
+++ b/metaflow/package/__init__.py
@@ -0,0 +1,664 @@
+import json
+import os
+import sys
+import threading
+import time
+
+from io import BytesIO
+from types import ModuleType
+from typing import Any, Callable, Dict, List, Optional, TYPE_CHECKING, Type, cast
+
+from ..debug import debug
+from ..packaging_sys import ContentType, MetaflowCodeContent
+from ..packaging_sys.backend import PackagingBackend
+from ..packaging_sys.tar_backend import TarPackagingBackend
+from ..packaging_sys.v1 import MetaflowCodeContentV1
+from ..packaging_sys.utils import suffix_filter, walk
+from ..metaflow_config import DEFAULT_PACKAGE_SUFFIXES
+from ..exception import MetaflowException
+from ..user_configs.config_parameters import dump_config_values
+from ..util import get_metaflow_root
+from .. import R
+
+DEFAULT_SUFFIXES_LIST = DEFAULT_PACKAGE_SUFFIXES.split(",")
+
+
+if TYPE_CHECKING:
+    import metaflow.datastore
+
+
+class NonUniqueFileNameToFilePathMappingException(MetaflowException):
+    headline = "Non-unique file path for a file name included in code package"
+
+    def __init__(self, filename, file_paths, lineno=None):
+        msg = (
+            "Filename %s included in the code package includes multiple different "
+            "paths for the same name : %s.\n"
+            "The `filename` in the `add_to_package` decorator hook requires a unique "
+            "`file_path` to `file_name` mapping" % (filename, ", ".join(file_paths))
+        )
+        super().__init__(msg=msg, lineno=lineno)
+
+
+class MetaflowPackage(object):
+    def __init__(
+        self,
+        flow,
+        environment,
+        echo,
+        suffixes: Optional[List[str]] = DEFAULT_SUFFIXES_LIST,
+        user_code_filter: Optional[Callable[[str], bool]] = None,
+        flow_datastore: Optional["metaflow.datastore.FlowDataStore"] = None,
+        mfcontent: Optional[MetaflowCodeContent] = None,
+        exclude_tl_dirs=None,
+        backend: Type[PackagingBackend] = TarPackagingBackend,
+    ):
+        self._environment = environment
+        self._environment.init_environment(echo)
+
+        self._echo = echo
+        self._flow = flow
+        self._flow_datastore = flow_datastore
+        self._backend = backend
+
+        # Info about the package
+        self._name = None
+        self._create_time = time.time()
+        self._user_flow_dir = None
+
+        # Content of the package (and settings on how to create it)
+        if suffixes is not None:
+            self._suffixes = list(set().union(suffixes, DEFAULT_SUFFIXES_LIST))
+        else:
+            self._suffixes = None
+
+        def _module_selector(m) -> bool:
+            from ..user_decorators.user_flow_decorator import FlowMutatorMeta
+            from ..user_decorators.user_step_decorator import UserStepDecoratorMeta
+
+            if (
+                m.__name__ in FlowMutatorMeta._import_modules
+                or m.__name__ in UserStepDecoratorMeta._import_modules
+                or hasattr(m, "METAFLOW_PACKAGE")
+            ):
+                return True
+
+        if mfcontent is None:
+            self._mfcontent = MetaflowCodeContentV1(criteria=_module_selector)
+
+        else:
+            self._mfcontent = mfcontent
+        # We exclude the environment when packaging as this will be packaged separately.
+        # This comes into play primarily if packaging from a node already running packaged
+        # code.
+        # These directories are only excluded at the top-level (ie: not further down
+        # in sub-directories)
+        # "_escape_trampolines" is a special directory where trampoline escape hatch
+        # files are stored (used by Netflix Extension's Conda implementation).
+        self._exclude_tl_dirs = (
+            self._mfcontent.get_excluded_tl_entries()
+            + ["_escape_trampolines"]
+            + (exclude_tl_dirs or [])
+        )
+
+        if self._suffixes is not None and user_code_filter is not None:
+            self._user_code_filter = lambda x, f1=suffix_filter(
+                self._suffixes
+            ), f2=user_code_filter: f1(x) and f2(x)
+            self._filter_type = "suffixes and user filter"
+        elif self._suffixes is not None:
+            self._user_code_filter = suffix_filter(self._suffixes)
+            self._filter_type = "suffixes"
+        elif user_code_filter is not None:
+            self._user_code_filter = user_code_filter
+            self._filter_type = "user filter"
+        else:
+            self._user_code_filter = lambda x: True
+            self._filter_type = "no filter"
+
+        # Info about the package creation (it happens async)
+        self._is_package_available = None
+        self._blob_sha = None
+        self._blob_url = None
+        self._blob = None
+
+        # We launch a thread to create the package asynchronously and upload
+        # it opportunistically
+        self._create_thread = threading.Thread(
+            target=self._package_and_upload,
+            daemon=True,
+        )
+        self._create_thread.start()
+
+    # HORRIBLE HACK SO THAT CURRENT COMPUTE IMPLEMENTATIONS CAN STILL
+    # DO pkg.blob. Ideally, this goes away and blob_with_timeout becomes
+    # the main method (called blob).
+    @property
+    def blob(self) -> BytesIO:
+        return self.blob_with_timeout()
+
+    def blob_with_timeout(self, timeout: Optional[float] = None) -> BytesIO:
+        if self._blob is None:
+            self._create_thread.join(timeout)
+            if self._is_package_available is not None:
+                # We have our result now
+                if self._is_package_available:
+                    return self._blob
+                else:
+                    raise self._packaging_exception
+        return self._blob
+
+    def package_sha(self, timeout: Optional[float] = None) -> Optional[str]:
+        if self._blob_sha is None:
+            self._create_thread.join(timeout)
+            if self._is_package_available is not None:
+                # We have our result now
+                if self._is_package_available:
+                    return self._blob_sha
+                else:
+                    raise self._packaging_exception
+        return self._blob_sha
+
+    def package_url(self, timeout: Optional[float] = None) -> Optional[str]:
+        if self._blob_url is None:
+            self._create_thread.join(timeout)
+            if self._is_package_available is not None:
+                # We have our result now
+                if self._is_package_available:
+                    return self._blob_url
+                else:
+                    raise self._packaging_exception
+        return self._blob_url
+
+    @property
+    def package_metadata(self):
+        return json.dumps(
+            {
+                "version": 0,
+                "archive_format": self._backend.backend_type(),
+                "mfcontent_version": self._mfcontent.get_package_version(),
+            }
+        )
+
+    @classmethod
+    def get_backend(cls, pkg_metadata: str) -> PackagingBackend:
+        """
+        Method to get the backend type from the package metadata.
+
+        Parameters
+        ----------
+        pkg_metadata : str
+            The metadata of the package to extract.
+
+        Returns
+        -------
+        PackagingBackend
+            The backend type that can be used to extract the package.
+        """
+        backend_type = json.loads(pkg_metadata).get("archive_format", "tgz")
+        return PackagingBackend.get_backend(backend_type)
+
+    @classmethod
+    def get_extract_commands(
+        cls, pkg_metadata: str, archive_path: str, dest_dir: str = "."
+    ) -> List[str]:
+        """
+        Method to get the commands needed to extract the package into
+        the directory dest_dir. Note that this will return a list of commands
+        that can be passed to subprocess.run for example.
+
+        Parameters
+        ----------
+        pkg_metadata : str
+            The metadata of the package to extract.
+        archive_path : str
+            The path to the archive to extract.
+        dest_dir : str, default "."
+            The directory to extract the package into.
+
+        Returns
+        -------
+        List[str]
+            The commands needed to extract the package into the directory dest_dir.
+        """
+        backend_type = json.loads(pkg_metadata).get("archive_format", "tgz")
+        # We now ask the backend type how to extract itself
+        backend = PackagingBackend.get_backend(backend_type)
+        cmds = backend.get_extract_commands(archive_path, dest_dir)
+        debug.package_exec(f"Command to extract {archive_path} into {dest_dir}: {cmds}")
+        return cmds
+
+    @classmethod
+    def get_post_extract_env_vars(
+        cls, pkg_metadata: str, dest_dir: str = "."
+    ) -> Dict[str, str]:
+        """
+        Method to get the environment variables needed to access the content
+        that has been extracted into the directory dest_dir. This will
+        typically involve setting PYTHONPATH
+
+        Parameters
+        ----------
+        pkg_metadata : str
+            The metadata of the package to extract.
+        dest_dir : str, default "."
+            The directory where the content has been extracted to.
+
+        Returns
+        -------
+        Dict[str, str]
+            The post-extract environment variables that are needed to access the content
+            that has been extracted into dest_dir.
+        """
+        mfcontent_version = json.loads(pkg_metadata).get("mfcontent_version", 0)
+        env_vars = MetaflowCodeContent.get_post_extract_env_vars(
+            mfcontent_version, dest_dir
+        )
+        debug.package_exec(
+            f"Environment variables to access content extracted into {dest_dir}: {env_vars}"
+        )
+        return env_vars
+
+    @classmethod
+    def cls_get_content(
+        cls, pkg_metadata, archive: BytesIO, name: str
+    ) -> Optional[bytes]:
+        """
+        Method to get the content of a member in the package archive.
+
+        Parameters
+        ----------
+        pkg_metadata : str
+            The metadata of the package to extract.
+        archive : BytesIO
+            The archive to extract the member from.
+        name : str
+            The name of the member to extract.
+
+        Returns
+        -------
+        Optional[bytes]
+            The content of the member if it exists, None otherwise.
+        """
+        backend = cls.get_backend(pkg_metadata)
+        with backend.cls_open(archive) as opened_archive:
+            return backend.cls_get_member(opened_archive, name)
+
+    @classmethod
+    def cls_get_info(cls, pkg_metadata, archive: BytesIO) -> Optional[Dict[str, str]]:
+        """
+        Method to get the info of the package from the archive.
+        Parameters
+        ----------
+        pkg_metadata : str
+            The metadata of the package to extract.
+        archive : BytesIO
+            The archive to extract the info from.
+        Returns
+        -------
+        Optional[Dict[str, str]]
+            The info of the package if it exists, None otherwise.
+        """
+        backend = cls.get_backend(pkg_metadata)
+        with backend.cls_open(archive) as opened_archive:
+            return MetaflowCodeContent.get_archive_info(opened_archive, backend)
+
+    @classmethod
+    def cls_get_config(
+        cls, pkg_metadata: str, archive: BytesIO
+    ) -> Optional[Dict[str, str]]:
+        """
+        Method to get the config of the package from the archive.
+
+        Parameters
+        ----------
+        pkg_metadata : str
+            The metadata of the package to extract.
+        archive : BytesIO
+            The archive to extract the config from.
+
+        Returns
+        -------
+        Optional[Dict[str, str]]
+            The config of the package if it exists, None otherwise.
+        """
+        backend = cls.get_backend(pkg_metadata)
+        with backend.cls_open(archive) as opened_archive:
+            return MetaflowCodeContent.get_archive_config(opened_archive, backend)
+
+    @classmethod
+    def cls_extract_into(
+        cls,
+        pkg_metadata: str,
+        archive: BytesIO,
+        dest_dir: str = ".",
+        content_types: int = ContentType.ALL_CONTENT.value,
+    ):
+        """
+        Method to extract the package archive into a directory.
+
+        Parameters
+        ----------
+        pkg_metadata : str
+            The metadata of the package to extract.
+        archive : BytesIO
+            The archive to extract.
+        dest_dir : str, default "."
+            The directory to extract the package into.
+        content_types : int, default ALL_CONTENT
+            The types of content to extract. This is a bitmask of ContentType values.
+        """
+        backend = cls.get_backend(pkg_metadata)
+        with backend.cls_open(archive) as opened_archive:
+            include_names = MetaflowCodeContent.get_archive_content_names(
+                opened_archive, content_types, backend
+            )
+            backend.extract_members(include_names, dest_dir)
+
+    def user_tuples(self, timeout: Optional[float] = None):
+        # Wait for at least the blob to be formed
+        _ = self.blob_with_timeout(timeout=timeout)
+        for path, arcname in self._cached_user_members:
+            yield path, arcname
+
+    def path_tuples(self, timeout: Optional[float] = None):
+        # Wait for at least the blob to be formed
+        _ = self.blob_with_timeout(timeout=timeout)
+        # Files included in the environment
+        yield from self._mfcontent.content_names()
+
+        # Files included in the user code
+        yield from self.user_tuples()
+
+    def show(self, timeout: Optional[float] = None) -> str:
+        # Human-readable content of the package
+        blob = self.blob_with_timeout(timeout=timeout)  # Ensure the package is created
+        lines = [
+            f"Package size: {self._format_size(len(blob))}",
+            f"Number of files: {sum(1 for _ in self.path_tuples())}",
+            self._mfcontent.show(),
+        ]
+
+        if self._flow:
+            lines.append(f"\nUser code in flow {self._name}:")
+            lines.append(f"  - Packaged from directory {self._user_flow_dir}")
+            if self._filter_type != "no filter":
+                if self._suffixes:
+                    lines.append(
+                        f"  - Filtered by suffixes: {', '.join(self._suffixes)}"
+                    )
+                else:
+                    lines.append(f"  - Filtered by {self._filter_type}")
+            else:
+                lines.append("  - No user code filter applied")
+            if self._exclude_tl_dirs:
+                lines.append(
+                    f"  - Excluded directories: {', '.join(self._exclude_tl_dirs)}"
+                )
+        return "\n".join(lines)
+
+    def get_content(
+        self, name: str, content_type: ContentType, timeout: Optional[float] = None
+    ) -> Optional[bytes]:
+        """
+        Method to get the content of a file within the package. This method
+        should be used for one-off access to small-ish files. If more files are
+        needed, use extract_into to extract the package into a directory and
+        then access the files from there.
+
+        Parameters
+        ----------
+        name : str
+            The name of the file to get the content of. Note that this
+            is not necessarily the name in the archive but is the name
+            that was passed in when creating the archive (in the archive,
+            it may be prefixed by some directory structure).
+        content_type : ContentType
+            The type of file to get the content of.
+
+        Returns
+        -------
+        Optional[bytes]
+            The content of the file. If the file is not found, None is returned.
+        """
+        # Wait for at least the blob to be formed
+        _ = self.blob_with_timeout(timeout=timeout)
+        if content_type == ContentType.USER_CONTENT:
+            for path, arcname in self.user_tuples():
+                if name == arcname:
+                    return open(path, "rb").read()
+            return None
+        elif content_type in (
+            ContentType.CODE_CONTENT,
+            ContentType.MODULE_CONTENT,
+            ContentType.OTHER_CONTENT,
+        ):
+            mangled_name = self._mfcontent.get_archive_filename(name, content_type)
+            for path_or_bytes, arcname in self._mfcontent.contents(content_type):
+                if mangled_name == arcname:
+                    if isinstance(path_or_bytes, bytes):
+                        # In case this is generated content like an INFO file
+                        return path_or_bytes
+                    # Otherwise, it is a file path
+                    return open(path_or_bytes, "rb").read()
+            return None
+        raise ValueError(f"Unknown content type: {content_type}")
+
+    def extract_into(
+        self,
+        dest_dir: str = ".",
+        content_types: int = ContentType.ALL_CONTENT.value,
+        timeout: Optional[float] = None,
+    ):
+        """
+        Method to extract the package (or some of the files) into a directory.
+
+        Parameters
+        ----------
+        dest_dir : str, default "."
+            The directory to extract the package into.
+        content_types : int, default ALL_CONTENT
+            The types of content to extract.
+        """
+        _ = self.blob_with_timeout(timeout=timeout)  # Ensure the package is created
+        member_list = []
+        if content_types & ContentType.USER_CONTENT.value:
+            member_list.extend(
+                [(m[0], os.path.join(dest_dir, m[1])) for m in self.user_tuples()]
+            )
+        if content_types & (
+            ContentType.CODE_CONTENT.value | ContentType.MODULE_CONTENT.value
+        ):
+            # We need to get the name of the files in the content archive to extract
+            member_list.extend(
+                [
+                    (m[0], os.path.join(dest_dir, m[1]))
+                    for m in self._mfcontent.content_names(
+                        content_types & ~ContentType.OTHER_CONTENT.value
+                    )
+                ]
+            )
+        for orig_path, new_path in member_list:
+            os.makedirs(os.path.dirname(new_path), exist_ok=True)
+            # TODO: In case there are duplicate files -- that should not be the case
+            # but there is a bug currently with internal Netflix code.
+            if not os.path.exists(new_path):
+                os.symlink(orig_path, new_path)
+            # Could copy files as well if we want to split them out.
+            # shutil.copy(orig_path, new_path)
+        # OTHER_CONTENT requires special handling because sometimes the file isn't a file
+        # but generated content
+        member_list = []
+        if content_types & ContentType.OTHER_CONTENT.value:
+            member_list.extend(
+                [
+                    (m[0], os.path.join(dest_dir, m[1]))
+                    for m in self._mfcontent.contents(ContentType.OTHER_CONTENT)
+                ]
+            )
+        for path_or_content, new_path in member_list:
+            os.makedirs(os.path.dirname(new_path), exist_ok=True)
+            if not os.path.exists(new_path):
+                if isinstance(path_or_content, bytes):
+                    with open(new_path, "wb") as f:
+                        f.write(path_or_content)
+                else:
+                    os.symlink(path_or_content, new_path)
+
+    @staticmethod
+    def _format_size(size_in_bytes):
+        for unit in ["B", "KB", "MB", "GB", "TB"]:
+            if size_in_bytes < 1024.0:
+                return f"{size_in_bytes:.2f} {unit}"
+            size_in_bytes /= 1024.0
+        return f"{size_in_bytes:.2f} PB"
+
+    def _package_and_upload(self):
+        try:
+            # Can be called without a flow (Function)
+            if self._flow:
+                for step in self._flow:
+                    for deco in step.decorators:
+                        deco.package_init(self._flow, step.__name__, self._environment)
+                self._name = f"flow {self._flow.name}"
+            else:
+                self._name = ""
+
+            # Add metacontent
+            self._mfcontent.add_info(
+                self._environment.get_environment_info(include_ext_info=True)
+            )
+
+            self._mfcontent.add_config(dump_config_values(self._flow))
+
+            # Add user files (from decorators and environment)
+            if self._flow:
+                self._add_addl_files()
+                self._cached_user_members = list(self._user_code_tuples())
+                debug.package_exec(
+                    f"User files to package: {self._cached_user_members}"
+                )
+
+            self._blob = self._make()
+            if self._flow_datastore:
+                if len(self._blob) > 100 * 1024 * 1024:
+                    self._echo(
+                        f"Warning: The code package for {self._flow.name} is larger than "
+                        f"100MB (found it to be {self._format_size(len(self._blob))}) "
+                        "This may lead to slower upload times for remote runs and no "
+                        "uploads for local runs. Consider reducing the package size. "
+                        "Use ` package info` or ` package list` "
+                        "to get more information about what is included in the package."
+                    )
+                self._blob_url, self._blob_sha = self._flow_datastore.save_data(
+                    [self._blob], len_hint=1
+                )[0]
+            else:
+                self._blob_url = self._blob_sha = ""
+            self._is_package_available = True
+        except Exception as e:
+            self._packaging_exception = e
+            self._echo(f"Package creation/upload failed for {self._flow.name}: {e}")
+            self._is_package_available = False
+
+    def _add_addl_files(self):
+        # Look at all decorators that provide additional files
+        deco_module_paths = {}
+        addl_modules = set()
+
+        def _check_tuple(path_tuple):
+            if len(path_tuple) == 2:
+                path_tuple = (
+                    path_tuple[0],
+                    path_tuple[1],
+                    ContentType.CODE_CONTENT,
+                )
+            file_path, file_name, file_type = path_tuple
+            if file_type == ContentType.MODULE_CONTENT:
+                if file_path in addl_modules:
+                    return None  # Module was already added -- we don't add twice
+                addl_modules.add(file_path)
+            elif file_type in (
+                ContentType.OTHER_CONTENT,
+                ContentType.CODE_CONTENT,
+            ):
+                path_tuple = (os.path.realpath(path_tuple[0]), path_tuple[1], file_type)
+                # These are files
+                # Check if the path is not duplicated as
+                # many steps can have the same packages being imported
+                if file_name not in deco_module_paths:
+                    deco_module_paths[file_name] = file_path
+                elif deco_module_paths[file_name] != file_path:
+                    raise NonUniqueFileNameToFilePathMappingException(
+                        file_name, [deco_module_paths[file_name], file_path]
+                    )
+            else:
+                raise ValueError(f"Unknown file type: {file_type}")
+            return path_tuple
+
+        def _add_tuple(path_tuple):
+            file_path, file_name, file_type = path_tuple
+            if file_type == ContentType.MODULE_CONTENT:
+                # file_path is actually a module
+                self._mfcontent.add_module(cast(ModuleType, file_path))
+            elif file_type == ContentType.CODE_CONTENT:
+                self._mfcontent.add_code_file(file_path, file_name)
+            elif file_type == ContentType.OTHER_CONTENT:
+                self._mfcontent.add_other_file(file_path, file_name)
+
+        for step in self._flow:
+            for deco in step.decorators:
+                for path_tuple in deco.add_to_package():
+                    path_tuple = _check_tuple(path_tuple)
+                    if path_tuple is None:
+                        continue
+                    _add_tuple(path_tuple)
+
+        # the package folders for environment
+        for path_tuple in self._environment.add_to_package():
+            path_tuple = _check_tuple(path_tuple)
+            if path_tuple is None:
+                continue
+            _add_tuple(path_tuple)
+
+    def _user_code_tuples(self):
+        if R.use_r():
+            # the R working directory
+            self._user_flow_dir = R.working_dir()
+            for path_tuple in walk(
+                "%s/" % R.working_dir(), file_filter=self._user_code_filter
+            ):
+                yield path_tuple
+            # the R package
+            for path_tuple in R.package_paths():
+                yield path_tuple
+        else:
+            # the user's working directory
+            flowdir = os.path.dirname(os.path.abspath(sys.argv[0])) + "/"
+            self._user_flow_dir = flowdir
+            for path_tuple in walk(
+                flowdir,
+                file_filter=self._user_code_filter,
+                exclude_tl_dirs=self._exclude_tl_dirs,
+            ):
+                # TODO: This is where we will check if the file is already included
+                # in the mfcontent portion
+                yield path_tuple
+
+    def _make(self):
+        backend = self._backend()
+        with backend.create() as archive:
+            # Package the environment
+            for path_or_bytes, arcname in self._mfcontent.contents():
+                if isinstance(path_or_bytes, str):
+                    archive.add_file(path_or_bytes, arcname=arcname)
+                else:
+                    archive.add_data(BytesIO(path_or_bytes), arcname=arcname)
+
+            # Package the user code
+            for path, arcname in self._cached_user_members:
+                archive.add_file(path, arcname=arcname)
+        return backend.get_blob()
+
+    def __str__(self):
+        return f""
diff --git a/metaflow/packaging_sys/__init__.py b/metaflow/packaging_sys/__init__.py
new file mode 100644
index 00000000000..3cf6c74ca13
--- /dev/null
+++ b/metaflow/packaging_sys/__init__.py
@@ -0,0 +1,870 @@
+import json
+import os
+
+from enum import IntEnum
+from types import ModuleType
+from typing import (
+    Any,
+    Dict,
+    Generator,
+    List,
+    Optional,
+    TYPE_CHECKING,
+    Tuple,
+    Type,
+    Union,
+)
+
+from metaflow.packaging_sys.distribution_support import PackagedDistributionFinder
+
+
+from .backend import PackagingBackend
+from .tar_backend import TarPackagingBackend
+
+from ..util import get_metaflow_root
+
+MFCONTENT_MARKER = ".mf_install"
+
+if TYPE_CHECKING:
+    import metaflow.extension_support.metadata
+
+
+class ContentType(IntEnum):
+    USER_CONTENT = (
+        0x1  # File being added is user code (ie: the directory with the flow file)
+    )
+    CODE_CONTENT = (
+        0x2  # File being added is non-user code (libraries, metaflow itself, ...)
+    )
+    MODULE_CONTENT = 0x4  # File being added is a python module
+    OTHER_CONTENT = 0x8  # File being added is a non-python file
+
+    ALL_CONTENT = USER_CONTENT | CODE_CONTENT | MODULE_CONTENT | OTHER_CONTENT
+
+
+class MetaflowCodeContent:
+    """
+    Base class for all Metaflow code packages (non user code).
+
+    A Metaflow code package, at a minimum, contains:
+      - a special INFO file (containing a bunch of metadata about the Metaflow environment)
+      - a special CONFIG file (containing user configurations for the flow)
+
+    Declare all other MetaflowCodeContent subclasses (versions) here to handle just the functions
+    that are not implemented here. In a *separate* file, declare any other
+    function for that specific version.
+
+    NOTE: This file must remain as dependency-free as possible as it is loaded *very*
+    early on. This is why you must decleare a *separate* class implementing what you want
+    the Metaflow code package (non user) to do.
+    """
+
+    _cached_mfcontent_info = {}
+
+    _mappings = {}
+
+    @classmethod
+    def get_info(cls) -> Optional[Dict[str, Any]]:
+        """
+        Get the content of the special INFO file on the local filesystem after
+        the code package has been expanded.
+
+        Returns
+        -------
+        Optional[Dict[str, Any]]
+            The content of the INFO file -- None if there is no such file.
+        """
+        mfcontent_info = cls._extract_mfcontent_info()
+        handling_cls = cls._get_mfcontent_class(mfcontent_info)
+        return handling_cls.get_info_impl(mfcontent_info)
+
+    @classmethod
+    def get_config(cls) -> Optional[Dict[str, Any]]:
+        """
+        Get the content of the special CONFIG file on the local filesystem after
+        the code package has been expanded.
+
+        Returns
+        -------
+        Optional[Dict[str, Any]]
+            The content of the CONFIG file -- None if there is no such file.
+        """
+        mfcontent_info = cls._extract_mfcontent_info()
+        handling_cls = cls._get_mfcontent_class(mfcontent_info)
+        return handling_cls.get_config_impl(mfcontent_info)
+
+    @classmethod
+    def get_filename(cls, filename: str, content_type: ContentType) -> Optional[str]:
+        """
+        Get the path to a file extracted from the archive. The filename is the filename
+        passed in when creating the archive and content_type is the type of the content.
+
+        This function will return the local path where the file can be found after
+        the package has been extracted.
+
+        Parameters
+        ----------
+        filename: str
+            The name of the file on the filesystem.
+        content_type: ContentType
+
+        Returns
+        -------
+        str
+            The path to the file on the local filesystem or None if not found.
+        """
+        mfcontent_info = cls._extract_mfcontent_info()
+        handling_cls = cls._get_mfcontent_class(mfcontent_info)
+        return handling_cls.get_filename_impl(mfcontent_info, filename, content_type)
+
+    @classmethod
+    def get_env_vars_for_packaged_metaflow(
+        cls, dest_dir: str
+    ) -> Optional[Dict[str, str]]:
+        """
+        Get the environment variables that are needed to run Metaflow when it is
+        packaged. This is typically used to set the PYTHONPATH to include the
+        directory where the Metaflow code package has been extracted.
+
+        Returns
+        -------
+        Optional[Dict[str, str]]
+            The environment variables that are needed to run Metaflow when it is
+            packaged -- None if there are no such variables (not packaged for example)
+        """
+        mfcontent_info = cls._extract_mfcontent_info()
+        if mfcontent_info is None:
+            # No MFCONTENT_MARKER file found -- this is not a packaged Metaflow code
+            # package so no environment variables to set.
+            return None
+        handling_cls = cls._get_mfcontent_class(mfcontent_info)
+        return handling_cls.get_post_extract_env_vars_impl(dest_dir)
+
+    @classmethod
+    def get_archive_info(
+        cls,
+        archive: Any,
+        packaging_backend: Type[PackagingBackend] = TarPackagingBackend,
+    ) -> Optional[Dict[str, Any]]:
+        """
+        Get the content of the special INFO file in the archive.
+
+        Returns
+        -------
+        Optional[Dict[str, Any]]
+            The content of the INFO file -- None if there is no such file.
+        """
+        mfcontent_info = cls._extract_archive_mfcontent_info(archive, packaging_backend)
+        handling_cls = cls._get_mfcontent_class(mfcontent_info)
+        return handling_cls.get_archive_info_impl(
+            mfcontent_info, archive, packaging_backend
+        )
+
+    @classmethod
+    def get_archive_config(
+        cls,
+        archive: Any,
+        packaging_backend: Type[PackagingBackend] = TarPackagingBackend,
+    ) -> Optional[Dict[str, Any]]:
+        """
+        Get the content of the special CONFIG file in the archive.
+
+        Returns
+        -------
+        Optional[Dict[str, Any]]
+            The content of the CONFIG file -- None if there is no such file.
+        """
+        mfcontent_info = cls._extract_archive_mfcontent_info(archive, packaging_backend)
+        handling_cls = cls._get_mfcontent_class(mfcontent_info)
+        return handling_cls.get_archive_config_impl(
+            mfcontent_info, archive, packaging_backend
+        )
+
+    @classmethod
+    def get_archive_filename(
+        cls,
+        archive: Any,
+        filename: str,
+        content_type: ContentType,
+        packaging_backend: Type[PackagingBackend] = TarPackagingBackend,
+    ) -> Optional[str]:
+        """
+        Get the filename of the archive. This does not do any extraction but simply
+        returns where, in the archive, the file is located. This is the equivalent of
+        get_filename but for files not extracted yet.
+
+        Parameters
+        ----------
+        archive: Any
+            The archive to get the filename from.
+        filename: str
+            The name of the file in the archive.
+        content_type: ContentType
+            The type of the content (e.g., code, other, etc.).
+        packaging_backend: Type[PackagingBackend], default TarPackagingBackend
+            The packaging backend to use.
+
+        Returns
+        -------
+        str
+            The filename of the archive or None if not found.
+        """
+        mfcontent_info = cls._extract_archive_mfcontent_info(archive, packaging_backend)
+        handling_cls = cls._get_mfcontent_class(mfcontent_info)
+        return handling_cls.get_archive_filename_impl(
+            mfcontent_info, archive, filename, content_type, packaging_backend
+        )
+
+    @classmethod
+    def get_archive_content_names(
+        cls,
+        archive: Any,
+        content_types: Optional[int] = None,
+        packaging_backend: Type[PackagingBackend] = TarPackagingBackend,
+    ) -> List[str]:
+        mfcontent_info = cls._extract_archive_mfcontent_info(archive, packaging_backend)
+        handling_cls = cls._get_mfcontent_class(mfcontent_info)
+        return handling_cls.get_archive_content_names_impl(
+            mfcontent_info, archive, content_types, packaging_backend
+        )
+
+    @classmethod
+    def get_distribution_finder(
+        cls,
+    ) -> Optional["metaflow.extension_support.metadata.DistributionFinder"]:
+        """
+        Get the distribution finder for the Metaflow code package (if applicable).
+
+        Some packages will include distribution information to "pretend" that some packages
+        are actually distributions even if we just include them in the code package.
+
+        Returns
+        -------
+        Optional["metaflow.extension_support.metadata.DistributionFinder"]
+            The distribution finder for the Metaflow code package -- None if there is no
+            such finder.
+        """
+        mfcontent_info = cls._extract_mfcontent_info()
+        handling_cls = cls._get_mfcontent_class(mfcontent_info)
+        return handling_cls.get_distribution_finder_impl(mfcontent_info)
+
+    @classmethod
+    def get_post_extract_env_vars(
+        cls, version_id: int, dest_dir: str = "."
+    ) -> Dict[str, str]:
+        """
+        Get the post-extract environment variables that are needed to access the content
+        that has been extracted into dest_dir.
+
+        This will typically involve setting PYTHONPATH.
+
+        Parameters
+        ----------
+        version_id: int
+            The version of MetaflowCodeContent for this package.
+        dest_dir: str, default "."
+            The directory where the content has been extracted to.
+
+        Returns
+        -------
+        Dict[str, str]
+            The post-extract environment variables that are needed to access the content
+            that has been extracted into extracted_dir.
+        """
+        if version_id not in cls._mappings:
+            raise ValueError(
+                "Invalid package -- unknown version %s in info: %s"
+                % (version_id, cls._mappings)
+            )
+        return cls._mappings[version_id].get_post_extract_env_vars_impl(dest_dir)
+
+    # Implement the _impl methods in the base subclass (in this file). These need to
+    # happen with as few imports as possible to prevent circular dependencies.
+    @classmethod
+    def get_info_impl(
+        cls, mfcontent_info: Optional[Dict[str, Any]]
+    ) -> Optional[Dict[str, Any]]:
+        raise NotImplementedError("get_info_impl not implemented")
+
+    @classmethod
+    def get_config_impl(
+        cls, mfcontent_info: Optional[Dict[str, Any]]
+    ) -> Optional[Dict[str, Any]]:
+        raise NotImplementedError("get_config_impl not implemented")
+
+    @classmethod
+    def get_filename_impl(
+        cls,
+        mfcontent_info: Optional[Dict[str, Any]],
+        filename: str,
+        content_type: ContentType,
+    ) -> Optional[str]:
+        raise NotImplementedError("get_filename_impl not implemented")
+
+    @classmethod
+    def get_distribution_finder_impl(
+        cls, mfcontent_info: Optional[Dict[str, Any]]
+    ) -> Optional["metaflow.extension_support.metadata.DistributionFinder"]:
+        raise NotImplementedError("get_distribution_finder_impl not implemented")
+
+    @classmethod
+    def get_archive_info_impl(
+        cls,
+        mfcontent_info: Optional[Dict[str, Any]],
+        archive: Any,
+        packaging_backend: Type[PackagingBackend] = TarPackagingBackend,
+    ) -> Optional[Dict[str, Any]]:
+        raise NotImplementedError("get_archive_info_impl not implemented")
+
+    @classmethod
+    def get_archive_config_impl(
+        cls,
+        mfcontent_info: Optional[Dict[str, Any]],
+        archive: Any,
+        packaging_backend: Type[PackagingBackend] = TarPackagingBackend,
+    ) -> Optional[Dict[str, Any]]:
+        raise NotImplementedError("get_archive_config_impl not implemented")
+
+    @classmethod
+    def get_archive_filename_impl(
+        cls,
+        mfcontent_info: Optional[Dict[str, Any]],
+        archive: Any,
+        filename: str,
+        content_type: ContentType,
+        packaging_backend: Type[PackagingBackend] = TarPackagingBackend,
+    ) -> Optional[str]:
+        raise NotImplementedError("get_archive_filename_impl not implemented")
+
+    @classmethod
+    def get_archive_content_names_impl(
+        cls,
+        mfcontent_info: Optional[Dict[str, Any]],
+        archive: Any,
+        content_types: Optional[int] = None,
+        packaging_backend: Type[PackagingBackend] = TarPackagingBackend,
+    ) -> List[str]:
+        raise NotImplementedError("get_archive_content_names_impl not implemented")
+
+    @classmethod
+    def get_post_extract_env_vars_impl(cls, dest_dir: str) -> Dict[str, str]:
+        raise NotImplementedError("get_post_extract_env_vars_impl not implemented")
+
+    def __init_subclass__(cls, version_id, **kwargs) -> None:
+        super().__init_subclass__(**kwargs)
+        if version_id in MetaflowCodeContent._mappings:
+            raise ValueError(
+                "Version ID %s already exists in MetaflowCodeContent mappings "
+                "-- this is a bug in Metaflow." % str(version_id)
+            )
+        MetaflowCodeContent._mappings[version_id] = cls
+        cls._version_id = version_id
+
+    # Implement these methods in sub-classes of the base sub-classes. These methods
+    # are called later and can have more dependencies and so can live in other files.
+    def get_excluded_tl_entries(self) -> List[str]:
+        """
+        When packaging Metaflow from within an executing Metaflow flow, we need to
+        exclude the files that are inserted by this content from being packaged (possibly).
+
+        Use this function to return these files or top-level directories.
+
+        Returns
+        -------
+        List[str]
+            Files or directories to exclude
+        """
+        return []
+
+    def content_names(
+        self, content_types: Optional[int] = None
+    ) -> Generator[Tuple[str, str], None, None]:
+        """
+        Detailed list of the content of this MetaflowCodeContent. This will list all files
+        (or non files -- for the INFO or CONFIG data for example) present in the archive.
+
+        Parameters
+        ----------
+        content_types : Optional[int]
+            The type of content to get the names of. If None, all content is returned.
+
+        Yields
+        ------
+        Generator[Tuple[str, str], None, None]
+            Path on the filesystem and the name in the archive
+        """
+        raise NotImplementedError("content_names not implemented")
+
+    def contents(
+        self, content_types: Optional[int] = None
+    ) -> Generator[Tuple[Union[bytes, str], str], None, None]:
+        """
+        Very similar to content_names but returns the content of the non-files
+        as well as bytes. For files, identical output as content_names
+
+        Parameters
+        ----------
+        content_types : Optional[int]
+            The type of content to get the content of. If None, all content is returned.
+
+        Yields
+        ------
+        Generator[Tuple[Union[str, bytes], str], None, None]
+            Content of the MF content
+        """
+        raise NotImplementedError("content not implemented")
+
+    def show(self) -> str:
+        """
+        Returns a more human-readable string representation of the content of this
+        MetaflowCodeContent. This will not, for example, list all files but summarize what
+        is included at a more high level.
+
+        Returns
+        -------
+        str
+            A human-readable string representation of the content of this MetaflowCodeContent
+        """
+        raise NotImplementedError("show not implemented")
+
+    def add_info(self, info: Dict[str, Any]) -> None:
+        """
+        Add the content of the INFO file to the Metaflow content
+
+        Parameters
+        ----------
+        info: Dict[str, Any]
+            The content of the INFO file
+        """
+        raise NotImplementedError("add_info not implemented")
+
+    def add_config(self, config: Dict[str, Any]) -> None:
+        """
+        Add the content of the CONFIG file to the Metaflow content
+
+        Parameters
+        ----------
+        config: Dict[str, Any]
+            The content of the CONFIG file
+        """
+        raise NotImplementedError("add_config not implemented")
+
+    def add_module(self, module_path: ModuleType) -> None:
+        """
+        Add a python module to the Metaflow content
+
+        Parameters
+        ----------
+        module_path: ModuleType
+            The module to add
+        """
+        raise NotImplementedError("add_module not implemented")
+
+    def add_code_file(self, file_path: str, file_name: str) -> None:
+        """
+        Add a code file to the Metaflow content
+
+        Parameters
+        ----------
+        file_path: str
+            The path to the code file to add (on the filesystem)
+        file_name: str
+            The path in the archive to add the code file to
+        """
+        raise NotImplementedError("add_code_file not implemented")
+
+    def add_other_file(self, file_path: str, file_name: str) -> None:
+        """
+        Add a non-python file to the Metaflow content
+
+        Parameters
+        ----------
+        file_path: str
+            The path to the file to add (on the filesystem)
+        file_name: str
+            The path in the archive to add the file to
+        """
+        raise NotImplementedError("add_other_file not implemented")
+
+    @classmethod
+    def _get_mfcontent_class(
+        cls, info: Optional[Dict[str, Any]]
+    ) -> Type["MetaflowCodeContent"]:
+        if info is None:
+            return MetaflowCodeContentV0
+        if "version" not in info:
+            raise ValueError("Invalid package -- missing version in info: %s" % info)
+        version = info["version"]
+        if version not in cls._mappings:
+            raise ValueError(
+                "Invalid package -- unknown version %s in info: %s" % (version, info)
+            )
+
+        return cls._mappings[version]
+
+    @classmethod
+    def _extract_archive_mfcontent_info(
+        cls,
+        archive: Any,
+        packaging_backend: Type[PackagingBackend] = TarPackagingBackend,
+    ) -> Optional[Dict[str, Any]]:
+        if id(archive) in cls._cached_mfcontent_info:
+            return cls._cached_mfcontent_info[id(archive)]
+
+        mfcontent_info = None  # type: Optional[Dict[str, Any]]
+        # Here we need to extract the information from the archive
+        if packaging_backend.cls_has_member(archive, MFCONTENT_MARKER):
+            # The MFCONTENT_MARKER file is present in the archive
+            # We can extract the information from it
+            extracted_info = packaging_backend.cls_get_member(archive, MFCONTENT_MARKER)
+            if extracted_info:
+                mfcontent_info = json.loads(extracted_info)
+        cls._cached_mfcontent_info[id(archive)] = mfcontent_info
+        return mfcontent_info
+
+    @classmethod
+    def _extract_mfcontent_info(cls) -> Optional[Dict[str, Any]]:
+        if "_local" in cls._cached_mfcontent_info:
+            return cls._cached_mfcontent_info["_local"]
+
+        mfcontent_info = None  # type: Optional[Dict[str, Any]]
+        if os.path.exists(os.path.join(get_metaflow_root(), MFCONTENT_MARKER)):
+            with open(
+                os.path.join(get_metaflow_root(), MFCONTENT_MARKER),
+                "r",
+                encoding="utf-8",
+            ) as f:
+                mfcontent_info = json.load(f)
+        cls._cached_mfcontent_info["_local"] = mfcontent_info
+        return mfcontent_info
+
+    def get_package_version(self) -> int:
+        """
+        Get the version of MetaflowCodeContent for this package.
+        """
+        # _version_id is set in __init_subclass__ when the subclass is created
+        return self._version_id
+
+
+class MetaflowCodeContentV0(MetaflowCodeContent, version_id=0):
+    @classmethod
+    def get_info_impl(
+        cls, mfcontent_info: Optional[Dict[str, Any]]
+    ) -> Optional[Dict[str, Any]]:
+        path_to_file = os.path.join(get_metaflow_root(), "INFO")
+        if os.path.isfile(path_to_file):
+            with open(path_to_file, "r", encoding="utf-8") as f:
+                return json.load(f)
+        return None
+
+    @classmethod
+    def get_config_impl(
+        cls, mfcontent_info: Optional[Dict[str, Any]]
+    ) -> Optional[Dict[str, Any]]:
+        path_to_file = os.path.join(get_metaflow_root(), "CONFIG")
+        if os.path.isfile(path_to_file):
+            with open(path_to_file, "r", encoding="utf-8") as f:
+                return json.load(f)
+        return None
+
+    @classmethod
+    def get_filename_impl(
+        cls,
+        mfcontent_info: Optional[Dict[str, Any]],
+        filename: str,
+        content_type: ContentType,
+    ) -> Optional[str]:
+        """
+        For V0, the filename is simply the filename passed in.
+        """
+        path_to_file = os.path.join(get_metaflow_root(), filename)
+        if os.path.isfile(path_to_file):
+            return path_to_file
+        return None
+
+    @classmethod
+    def get_distribution_finder_impl(
+        cls, mfcontent_info: Optional[Dict[str, Any]]
+    ) -> Optional["metaflow.extension_support.metadata.DistributionFinder"]:
+        return None
+
+    @classmethod
+    def get_archive_info_impl(
+        cls,
+        mfcontent_info: Optional[Dict[str, Any]],
+        archive: Any,
+        packaging_backend: Type[PackagingBackend] = TarPackagingBackend,
+    ) -> Optional[Dict[str, Any]]:
+        info_content = packaging_backend.cls_get_member(archive, "INFO")
+        if info_content:
+            return json.loads(info_content)
+        return None
+
+    @classmethod
+    def get_archive_config_impl(
+        cls,
+        mfcontent_info: Optional[Dict[str, Any]],
+        archive: Any,
+        packaging_backend: Type[PackagingBackend] = TarPackagingBackend,
+    ) -> Optional[Dict[str, Any]]:
+        info_content = packaging_backend.cls_get_member(archive, "CONFIG")
+        if info_content:
+            return json.loads(info_content)
+        return None
+
+    @classmethod
+    def get_archive_filename_impl(
+        cls,
+        mfcontent_info: Optional[Dict[str, Any]],
+        archive: Any,
+        filename: str,
+        content_type: ContentType,
+        packaging_backend: Type[PackagingBackend] = TarPackagingBackend,
+    ) -> str:
+        if packaging_backend.cls_has_member(archive, filename):
+            # The file is present in the archive
+            return filename
+        return None
+
+    @classmethod
+    def get_archive_content_names_impl(
+        cls,
+        mfcontent_info: Optional[Dict[str, Any]],
+        archive: Any,
+        content_types: Optional[int] = None,
+        packaging_backend: Type[PackagingBackend] = TarPackagingBackend,
+    ) -> List[str]:
+        """
+        For V0, we use a static list of known files to classify the content
+        """
+        known_prefixes = {
+            "metaflow/": ContentType.CODE_CONTENT.value,
+            "metaflow_extensions/": ContentType.CODE_CONTENT.value,
+            "INFO": ContentType.OTHER_CONTENT.value,
+            "CONFIG": ContentType.OTHER_CONTENT.value,
+            "conda.manifest": ContentType.OTHER_CONTENT.value,
+            "uv.lock": ContentType.OTHER_CONTENT.value,
+            "pyproject.toml": ContentType.OTHER_CONTENT.value,
+            # Used in nflx-metaflow-extensions
+            "condav2-1.cnd": ContentType.OTHER_CONTENT.value,
+        }
+        to_return = []
+        for filename in packaging_backend.cls_list_members(archive):
+            for prefix, classification in known_prefixes.items():
+                if (
+                    prefix[-1] == "/" and filename.startswith(prefix)
+                ) or prefix == filename:
+                    if content_types & classification:
+                        to_return.append(filename)
+                elif content_types & ContentType.USER_CONTENT.value:
+                    # Everything else is user content
+                    to_return.append(filename)
+        return to_return
+
+    @classmethod
+    def get_post_extract_env_vars_impl(cls, dest_dir: str) -> Dict[str, str]:
+        return {"PYTHONPATH": dest_dir}
+
+    def get_excluded_tl_entries(self) -> List[str]:
+        """
+        When packaging Metaflow from within an executing Metaflow flow, we need to
+        exclude the files that are inserted by this content from being packaged (possibly).
+
+        Use this function to return these files or top-level directories.
+
+        Returns
+        -------
+        List[str]
+            Files or directories to exclude
+        """
+        return ["CONFIG", "INFO"]
+
+    # Other non-implemented methods are OK not being implemented as they will never
+    # be called as they are only used when creating the package and we are starting
+    # with V1.
+
+
+class MetaflowCodeContentV1Base(MetaflowCodeContent, version_id=1):
+    _code_dir = ".mf_code"
+    _other_dir = ".mf_meta"
+    _info_file = "INFO"
+    _config_file = "CONFIG"
+    _dist_info_file = "DIST_INFO"
+
+    def __init_subclass__(cls, **kwargs) -> None:
+        # Important to add this here to prevent the subclass of MetaflowCodeContentV1Base from
+        # also calling __init_subclass__ in MetaflowCodeContent (which would create a problem)
+        return None
+
+    def __init__(self, code_dir: str, other_dir: str) -> None:
+        self._code_dir = code_dir
+        self._other_dir = other_dir
+
+    @classmethod
+    def _get_otherfile_path(
+        cls, mfcontent_info: Optional[Dict[str, Any]], filename: str, in_archive: bool
+    ) -> str:
+        if in_archive:
+            return filename
+        return os.path.join(get_metaflow_root(), "..", cls._other_dir, filename)
+
+    @classmethod
+    def _get_codefile_path(
+        cls, mfcontent_info: Optional[Dict[str, Any]], filename: str, in_archive: bool
+    ) -> str:
+        if in_archive:
+            return filename
+        return os.path.join(get_metaflow_root(), filename)
+
+    @classmethod
+    def get_info_impl(
+        cls, mfcontent_info: Optional[Dict[str, Any]]
+    ) -> Optional[Dict[str, Any]]:
+        path_to_file = cls._get_otherfile_path(
+            mfcontent_info, cls._info_file, in_archive=False
+        )
+        if os.path.isfile(path_to_file):
+            with open(path_to_file, "r", encoding="utf-8") as f:
+                return json.load(f)
+        return None
+
+    @classmethod
+    def get_config_impl(
+        cls, mfcontent_info: Optional[Dict[str, Any]]
+    ) -> Optional[Dict[str, Any]]:
+        path_to_file = cls._get_otherfile_path(
+            mfcontent_info, cls._config_file, in_archive=False
+        )
+        if os.path.isfile(path_to_file):
+            with open(path_to_file, "r", encoding="utf-8") as f:
+                return json.load(f)
+        return None
+
+    @classmethod
+    def get_filename_impl(
+        cls,
+        mfcontent_info: Optional[Dict[str, Any]],
+        filename: str,
+        content_type: ContentType,
+    ) -> Optional[str]:
+        if content_type == ContentType.CODE_CONTENT:
+            path_to_file = cls._get_codefile_path(
+                mfcontent_info, filename, in_archive=False
+            )
+        elif content_type in (ContentType.OTHER_CONTENT, ContentType.MODULE_CONTENT):
+            path_to_file = cls._get_otherfile_path(
+                mfcontent_info, filename, in_archive=False
+            )
+        else:
+            raise ValueError(
+                f"Invalid content type {content_type} for filename {filename}"
+            )
+        if os.path.isfile(path_to_file):
+            return path_to_file
+        return None
+
+    @classmethod
+    def get_distribution_finder_impl(
+        cls, mfcontent_info: Optional[Dict[str, Any]]
+    ) -> Optional["metaflow.extension_support.metadata.DistributionFinder"]:
+        path_to_file = cls._get_otherfile_path(
+            mfcontent_info, cls._dist_info_file, in_archive=False
+        )
+        if os.path.isfile(path_to_file):
+            with open(path_to_file, "r", encoding="utf-8") as f:
+                return PackagedDistributionFinder(json.load(f))
+        return None
+
+    @classmethod
+    def get_archive_info_impl(
+        cls,
+        mfcontent_info: Optional[Dict[str, Any]],
+        archive: Any,
+        packaging_backend: Type[PackagingBackend] = TarPackagingBackend,
+    ) -> Optional[Dict[str, Any]]:
+        info_file = packaging_backend.cls_get_member(
+            archive,
+            cls._get_otherfile_path(mfcontent_info, cls._info_file, in_archive=True),
+        )
+        if info_file:
+            return json.loads(info_file)
+        return None
+
+    @classmethod
+    def get_archive_config_impl(
+        cls,
+        mfcontent_info: Optional[Dict[str, Any]],
+        archive: Any,
+        packaging_backend: Type[PackagingBackend] = TarPackagingBackend,
+    ) -> Optional[Dict[str, Any]]:
+        config_file = packaging_backend.cls_get_member(
+            archive,
+            cls._get_otherfile_path(mfcontent_info, cls._config_file, in_archive=True),
+        )
+        if config_file:
+            return json.loads(config_file)
+        return None
+
+    @classmethod
+    def get_archive_filename_impl(
+        cls,
+        mfcontent_info: Optional[Dict[str, Any]],
+        archive: Any,
+        filename: str,
+        content_type: ContentType,
+        packaging_backend: Type[PackagingBackend] = TarPackagingBackend,
+    ) -> str:
+        if content_type == ContentType.CODE_CONTENT:
+            path_to_file = cls._get_codefile_path(
+                mfcontent_info, filename, in_archive=False
+            )
+        elif content_type in (ContentType.OTHER_CONTENT, ContentType.MODULE_CONTENT):
+            path_to_file = cls._get_otherfile_path(
+                mfcontent_info, filename, in_archive=False
+            )
+        else:
+            raise ValueError(
+                f"Invalid content type {content_type} for filename {filename}"
+            )
+        if packaging_backend.cls_has_member(archive, path_to_file):
+            # The file is present in the archive
+            return path_to_file
+        return None
+
+    @classmethod
+    def get_archive_content_names_impl(
+        cls,
+        mfcontent_info: Optional[Dict[str, Any]],
+        archive: Any,
+        content_types: Optional[int] = None,
+        packaging_backend: Type[PackagingBackend] = TarPackagingBackend,
+    ) -> List[str]:
+        to_return = []
+        module_content = set(mfcontent_info.get("module_files", []))
+        for filename in packaging_backend.cls_list_members(archive):
+            if filename.startswith(cls._other_dir) and (
+                content_types & ContentType.OTHER_CONTENT.value
+            ):
+                to_return.append(filename)
+            elif filename.startswith(cls._code_dir):
+                # Special case for marker which is a other content even if in code.
+                if filename == f"{cls._code_dir}/{MFCONTENT_MARKER}":
+                    if content_types & ContentType.OTHER_CONTENT.value:
+                        to_return.append(filename)
+                    else:
+                        continue
+                # Here it is either module or code
+                if os.path.join(cls._code_dir, filename) in module_content:
+                    if content_types & ContentType.MODULE_CONTENT.value:
+                        to_return.append(filename)
+                elif content_types & ContentType.CODE_CONTENT.value:
+                    to_return.append(filename)
+            else:
+                if content_types & ContentType.USER_CONTENT.value:
+                    # Everything else is user content
+                    to_return.append(filename)
+        return to_return
+
+    @classmethod
+    def get_post_extract_env_vars_impl(cls, dest_dir: str) -> Dict[str, str]:
+        return {"PYTHONPATH": f"{dest_dir}/{cls._code_dir}"}
diff --git a/metaflow/packaging_sys/backend.py b/metaflow/packaging_sys/backend.py
new file mode 100644
index 00000000000..b2974640d1d
--- /dev/null
+++ b/metaflow/packaging_sys/backend.py
@@ -0,0 +1,113 @@
+from abc import ABC, abstractmethod
+from io import BytesIO
+from typing import Any, IO, List, Optional, Union
+
+
+class PackagingBackend(ABC):
+    _mappings = {}
+    type = "none"
+
+    def __init_subclass__(cls, **kwargs):
+        super().__init_subclass__(**kwargs)
+        if cls.type in cls._mappings:
+            raise ValueError(f"PackagingBackend {cls.type} already exists")
+        cls._mappings[cls.type] = cls
+
+    @classmethod
+    def get_backend(cls, name: str) -> "PackagingBackend":
+        if name not in cls._mappings:
+            raise ValueError(f"PackagingBackend {name} not found")
+        return cls._mappings[name]
+
+    @classmethod
+    def backend_type(cls) -> str:
+        return cls.type
+
+    @classmethod
+    @abstractmethod
+    def get_extract_commands(cls, archive_name: str, dest_dir: str) -> List[str]:
+        pass
+
+    def __init__(self):
+        self._archive = None
+
+    @abstractmethod
+    def create(self) -> "PackagingBackend":
+        pass
+
+    @abstractmethod
+    def add_file(self, filename: str, arcname: Optional[str] = None):
+        pass
+
+    @abstractmethod
+    def add_data(self, data: BytesIO, arcname: str):
+        pass
+
+    @abstractmethod
+    def close(self):
+        pass
+
+    @abstractmethod
+    def get_blob(self) -> Optional[Union[bytes, bytearray]]:
+        pass
+
+    @classmethod
+    @abstractmethod
+    def cls_open(cls, content: IO[bytes]) -> Any:
+        """Open the archive from the given content."""
+        pass
+
+    @classmethod
+    @abstractmethod
+    def cls_has_member(cls, archive: Any, name: str) -> bool:
+        pass
+
+    @classmethod
+    @abstractmethod
+    def cls_get_member(cls, archive: Any, name: str) -> Optional[bytes]:
+        pass
+
+    @classmethod
+    @abstractmethod
+    def cls_extract_members(
+        cls,
+        archive: Any,
+        members: Optional[List[str]] = None,
+        dest_dir: str = ".",
+    ) -> None:
+        pass
+
+    @classmethod
+    @abstractmethod
+    def cls_list_members(cls, archive: Any) -> Optional[List[str]]:
+        pass
+
+    def has_member(self, name: str) -> bool:
+        if self._archive:
+            return self.cls_has_member(self._archive, name)
+        raise ValueError("Cannot check for member in an uncreated archive")
+
+    def get_member(self, name: str) -> Optional[bytes]:
+        if self._archive:
+            return self.cls_get_member(self._archive, name)
+        raise ValueError("Cannot get member from an uncreated archive")
+
+    def extract_members(
+        self, members: Optional[List[str]] = None, dest_dir: str = "."
+    ) -> None:
+        if self._archive:
+            self.cls_extract_members(self._archive, members, dest_dir)
+        else:
+            raise ValueError("Cannot extract from an uncreated archive")
+
+    def list_members(self) -> Optional[List[str]]:
+        if self._archive:
+            return self.cls_list_members(self._archive)
+        raise ValueError("Cannot list members from an uncreated archive")
+
+    def __enter__(self):
+        self.create()
+        return self
+
+    def __exit__(self, exc_type, exc_value, traceback):
+        self.close()
diff --git a/metaflow/packaging_sys/distribution_support.py b/metaflow/packaging_sys/distribution_support.py
new file mode 100644
index 00000000000..9934849d951
--- /dev/null
+++ b/metaflow/packaging_sys/distribution_support.py
@@ -0,0 +1,153 @@
+# Support saving of distribution information so we can give it back to users even
+# if we do not install those distributions. This is used to package distributions in
+# the MetaflowCodeContent package and provide an experience as if the packages were installed
+# system-wide.
+
+import os
+import re
+import sys
+from pathlib import Path
+from types import ModuleType
+from typing import (
+    Callable,
+    Dict,
+    List,
+    Mapping,
+    NamedTuple,
+    Optional,
+    Set,
+    TYPE_CHECKING,
+    Union,
+    cast,
+)
+
+import inspect
+from collections import defaultdict
+
+from ..extension_support import metadata
+from ..util import get_metaflow_root
+
+if TYPE_CHECKING:
+    import pathlib
+
+_cached_distributions = None
+
+packages_distributions = None  # type: Optional[Callable[[], Mapping[str, List[str]]]]
+name_normalizer = re.compile(r"[-_.]+")
+
+if sys.version_info[:2] >= (3, 10):
+    packages_distributions = metadata.packages_distributions
+else:
+    # This is the code present in 3.10+ -- we replicate here for other versions
+    def _packages_distributions() -> Mapping[str, List[str]]:
+        """
+        Return a mapping of top-level packages to their
+        distributions.
+        """
+        pkg_to_dist = defaultdict(list)
+        for dist in metadata.distributions():
+            for pkg in _top_level_declared(dist) or _top_level_inferred(dist):
+                pkg_to_dist[pkg].append(dist.metadata["Name"])
+        return dict(pkg_to_dist)
+
+    def _top_level_declared(dist: metadata.Distribution) -> List[str]:
+        return (dist.read_text("top_level.txt") or "").split()
+
+    def _topmost(name: "pathlib.PurePosixPath") -> Optional[str]:
+        """
+        Return the top-most parent as long as there is a parent.
+        """
+        top, *rest = name.parts
+        return top if rest else None
+
+    def _get_toplevel_name(name: "pathlib.PurePosixPath") -> str:
+        return _topmost(name) or (
+            # python/typeshed#10328
+            inspect.getmodulename(name)  # type: ignore
+            or str(name)
+        )
+
+    def _top_level_inferred(dist: "metadata.Distribution"):
+        opt_names = set(map(_get_toplevel_name, dist.files or []))
+
+        def importable_name(name):
+            return "." not in name
+
+        return filter(importable_name, opt_names)
+
+    packages_distributions = _packages_distributions
+
+
+def modules_to_distributions() -> Dict[str, List[metadata.Distribution]]:
+    """
+    Return a mapping of top-level modules to their distributions.
+
+    Returns
+    -------
+    Dict[str, List[metadata.Distribution]]
+        A mapping of top-level modules to their distributions.
+    """
+    global _cached_distributions
+    pd = cast(Callable[[], Mapping[str, List[str]]], packages_distributions)
+    if _cached_distributions is None:
+        _cached_distributions = {
+            k: [metadata.distribution(d) for d in v] for k, v in pd().items()
+        }
+    return _cached_distributions
+
+
+_ModuleInfo = NamedTuple(
+    "_ModuleInfo",
+    [
+        ("name", str),
+        ("root_paths", Set[str]),
+        ("module", ModuleType),
+        ("metaflow_module", bool),
+    ],
+)
+
+
+class PackagedDistribution(metadata.Distribution):
+    """
+    A Python Package packaged within a MetaflowCodeContent. This allows users to use use importlib
+    as they would regularly and the packaged Python Package would be considered as a
+    distribution even if it really isn't (since it is just included in the PythonPath).
+    """
+
+    def __init__(self, root: str, content: Dict[str, str]):
+        self._root = Path(root)
+        self._content = content
+
+    # Strongly inspired from PathDistribution in metadata.py
+    def read_text(self, filename: Union[str, os.PathLike]) -> Optional[str]:
+        if str(filename) in self._content:
+            return self._content[str(filename)]
+        return None
+
+    read_text.__doc__ = metadata.Distribution.read_text.__doc__
+
+    # Returns a metadata.SimplePath but not always present in importlib.metadata libs so
+    # skipping return type.
+    def locate_file(self, path: Union[str, os.PathLike]):
+        return self._root / path
+
+
+class PackagedDistributionFinder(metadata.DistributionFinder):
+    def __init__(self, dist_info: Dict[str, Dict[str, str]]):
+        self._dist_info = dist_info
+
+    def find_distributions(self, context=metadata.DistributionFinder.Context()):
+        if context.name is None:
+            # Yields all known distributions
+            for name, info in self._dist_info.items():
+                yield PackagedDistribution(
+                    os.path.join(get_metaflow_root(), name), info
+                )
+            return None
+        name = name_normalizer.sub("-", cast(str, context.name)).lower()
+        if name in self._dist_info:
+            yield PackagedDistribution(
+                os.path.join(get_metaflow_root(), cast(str, context.name)),
+                self._dist_info[name],
+            )
+        return None
diff --git a/metaflow/packaging_sys/tar_backend.py b/metaflow/packaging_sys/tar_backend.py
new file mode 100644
index 00000000000..a606b1c53d3
--- /dev/null
+++ b/metaflow/packaging_sys/tar_backend.py
@@ -0,0 +1,86 @@
+import tarfile
+
+from io import BytesIO
+from typing import IO, List, Optional, Union
+
+from .backend import PackagingBackend
+
+
+class TarPackagingBackend(PackagingBackend):
+    type = "tgz"
+
+    @classmethod
+    def get_extract_commands(cls, archive_name: str, dest_dir: str) -> List[str]:
+        return [
+            f"TAR_OPTIONS='--warning=no-timestamp' tar -xzf {archive_name} -C {dest_dir}"
+        ]
+
+    def __init__(self):
+        super().__init__()
+        self._buf = None
+
+    def create(self):
+        self._buf = BytesIO()
+        self._archive = tarfile.open(
+            fileobj=self._buf, mode="w:gz", compresslevel=3, dereference=True
+        )
+        return self
+
+    def add_file(self, filename: str, arcname: Optional[str] = None):
+        info = self._archive.gettarinfo(filename, arcname)
+        # Setting this default to Dec 3, 2019
+        info.mtime = 1575360000
+        with open(filename, mode="rb") as f:
+            self._archive.addfile(info, f)
+
+    def add_data(self, data: BytesIO, arcname: str):
+        info = tarfile.TarInfo(arcname)
+        data.seek(0)
+        info.size = len(data.getvalue())
+        # Setting this default to Dec 3, 2019
+        info.mtime = 1575360000
+        self._archive.addfile(info, data)
+
+    def close(self):
+        if self._archive:
+            self._archive.close()
+
+    def get_blob(self) -> Optional[Union[bytes, bytearray]]:
+        if self._buf:
+            blob = bytearray(self._buf.getvalue())
+            blob[4:8] = [0] * 4  # Reset 4 bytes from offset 4 to account for ts
+            return blob
+        return None
+
+    @classmethod
+    def cls_open(cls, content: IO[bytes]) -> tarfile.TarFile:
+        return tarfile.open(fileobj=content, mode="r:gz")
+
+    @classmethod
+    def cls_has_member(cls, archive: tarfile.TarFile, name: str) -> bool:
+        try:
+            archive.getmember(name)
+            return True
+        except KeyError:
+            return False
+
+    @classmethod
+    def cls_get_member(cls, archive: tarfile.TarFile, name: str) -> Optional[bytes]:
+        try:
+            member = archive.getmember(name)
+            return archive.extractfile(member).read()
+        except KeyError:
+            return None
+
+    @classmethod
+    def cls_extract_members(
+        cls,
+        archive: tarfile.TarFile,
+        members: Optional[List[str]] = None,
+        dest_dir: str = ".",
+    ) -> None:
+        archive.extractall(path=dest_dir, members=members)
+
+    @classmethod
+    def cls_list_members(cls, archive: tarfile.TarFile) -> Optional[List[str]]:
+        return archive.getnames() or None
diff --git a/metaflow/packaging_sys/utils.py b/metaflow/packaging_sys/utils.py
new file mode 100644
index 00000000000..9f1bae3b627
--- /dev/null
+++ b/metaflow/packaging_sys/utils.py
@@ -0,0 +1,91 @@
+import os
+from contextlib import contextmanager
+from typing import Callable, Generator, List, Optional, Tuple
+
+from ..util import to_unicode
+
+
+# this is os.walk(follow_symlinks=True) with cycle detection
+def walk_without_cycles(
+    top_root: str,
+    exclude_dirs: Optional[List[str]] = None,
+) -> Generator[Tuple[str, List[str]], None, None]:
+    seen = set()
+
+    default_skip_dirs = ["__pycache__"]
+
+    def _recurse(root, skip_dirs):
+        for parent, dirs, files in os.walk(root):
+            dirs[:] = [d for d in dirs if d not in skip_dirs]
+            for d in dirs:
+                path = os.path.join(parent, d)
+                if os.path.islink(path):
+                    # Breaking loops: never follow the same symlink twice
+                    #
+                    # NOTE: this also means that links to sibling links are
+                    # not followed. In this case:
+                    #
+                    #   x -> y
+                    #   y -> oo
+                    #   oo/real_file
+                    #
+                    # real_file is only included twice, not three times
+                    reallink = os.path.realpath(path)
+                    if reallink not in seen:
+                        seen.add(reallink)
+                        for x in _recurse(path, default_skip_dirs):
+                            yield x
+            yield parent, files
+
+    skip_dirs = set(default_skip_dirs + (exclude_dirs or []))
+    for x in _recurse(top_root, skip_dirs):
+        skip_dirs = default_skip_dirs
+        yield x
+
+
+def walk(
+    root: str,
+    exclude_hidden: bool = True,
+    file_filter: Optional[Callable[[str], bool]] = None,
+    exclude_tl_dirs: Optional[List[str]] = None,
+) -> Generator[Tuple[str, str], None, None]:
+    root = to_unicode(root)  # handle files/folder with non ascii chars
+    prefixlen = len("%s/" % os.path.dirname(root))
+    for (
+        path,
+        files,
+    ) in walk_without_cycles(root, exclude_tl_dirs):
+        if exclude_hidden and "/." in path:
+            continue
+        # path = path[2:] # strip the ./ prefix
+        # if path and (path[0] == '.' or './' in path):
+        #    continue
+        for fname in files:
+            if file_filter is None or file_filter(fname):
+                p = os.path.join(path, fname)
+                yield p, p[prefixlen:]
+
+
+def suffix_filter(suffixes: List[str]) -> Callable[[str], bool]:
+    """
+    Returns a filter function that checks if a file ends with any of the given suffixes.
+    """
+    suffixes = [s.lower() for s in suffixes]
+
+    def _filter(fname: str) -> bool:
+        fname = fname.lower()
+        return (
+            suffixes is None
+            or (fname[0] == "." and fname in suffixes)
+            or (fname[0] != "." and any(fname.endswith(suffix) for suffix in suffixes))
+        )
+
+    return _filter
+
+
+@contextmanager
+def with_dir(new_dir):
+    current_dir = os.getcwd()
+    os.chdir(new_dir)
+    yield new_dir
+    os.chdir(current_dir)
diff --git a/metaflow/packaging_sys/v1.py b/metaflow/packaging_sys/v1.py
new file mode 100644
index 00000000000..d0379450a56
--- /dev/null
+++ b/metaflow/packaging_sys/v1.py
@@ -0,0 +1,480 @@
+import json
+import os
+import sys
+from pathlib import Path
+from types import ModuleType
+from typing import Any, Callable, Dict, Generator, List, Optional, Set, Tuple, Union
+
+from ..debug import debug
+from ..extension_support import (
+    EXT_EXCLUDE_SUFFIXES,
+    extension_info,
+    package_mfext_all,
+    package_mfext_all_descriptions,
+)
+from ..exception import MetaflowException
+from ..metaflow_version import get_version
+from ..user_decorators.user_flow_decorator import FlowMutatorMeta
+from ..user_decorators.user_step_decorator import UserStepDecoratorMeta
+from ..util import get_metaflow_root
+from . import ContentType, MFCONTENT_MARKER, MetaflowCodeContentV1Base
+from .distribution_support import _ModuleInfo, modules_to_distributions
+from .utils import suffix_filter, walk
+
+
+class MetaflowCodeContentV1(MetaflowCodeContentV1Base):
+    METAFLOW_SUFFIXES_LIST = [".py", ".html", ".css", ".js"]
+
+    def __init__(
+        self,
+        code_dir: str = MetaflowCodeContentV1Base._code_dir,
+        other_dir: str = MetaflowCodeContentV1Base._other_dir,
+        criteria: Callable[[ModuleType], bool] = lambda x: True,
+    ):
+        super().__init__(code_dir, other_dir)
+
+        self._metaflow_root = get_metaflow_root()
+        self._metaflow_version = get_version()
+
+        self._criteria = criteria
+
+        # We try to find the modules we need to package. We will first look at all modules
+        # and apply the criteria to them. Then we will use the most parent module that
+        # fits the criteria as the module to package
+
+        # Make a copy since sys.modules could be modified while we load other
+        # modules. See https://github.com/Netflix/metaflow/issues/2489
+        all_modules = dict(sys.modules)
+        modules = filter(lambda x: criteria(x[1]), all_modules.items())
+        # Ensure that we see the parent modules first
+        modules = sorted(modules, key=lambda x: x[0])
+        if modules:
+            last_prefix = modules[0][0]
+            new_modules = [modules[0]]
+            for name, mod in modules[1:]:
+                if name.startswith(last_prefix + "."):
+                    # This is a submodule of the last module, we can skip it
+                    continue
+                # Otherwise, we have a new top-level module
+                last_prefix = name
+                new_modules.append((name, mod))
+        else:
+            new_modules = []
+
+        self._modules = {
+            name: _ModuleInfo(
+                name,
+                set(
+                    Path(p).resolve().as_posix()
+                    for p in getattr(mod, "__path__", [mod.__file__])
+                ),
+                mod,
+                True,  # This is a Metaflow module (see filter below)
+            )
+            for (name, mod) in new_modules
+        }
+
+        # Filter the modules
+        self._modules = {
+            name: info for name, info in self._modules.items() if criteria(info.module)
+        }
+
+        # Contain metadata information regarding the distributions packaged.
+        # This allows Metaflow to "fake" distribution information when packaged
+        self._distmetainfo = {}  # type: Dict[str, Dict[str, str]]
+
+        # Maps an absolute path on the filesystem to the path of the file in the
+        # archive.
+        self._files = {}  # type: Dict[str, str]
+        self._files_from_modules = {}  # type: Dict[str, str]
+
+        self._other_files = {}  # type: Dict[str, str]
+        self._other_content = {}  # type: Dict[str, bytes]
+
+        debug.package_exec(f"Used system modules found: {str(self._modules)}")
+
+        # Populate with files from the third party modules
+        for k, v in self._modules.items():
+            self._files_from_modules.update(self._module_files(k, v.root_paths))
+
+        # Figure out the files to package for Metaflow and extensions
+        self._cached_metaflow_files = list(self._metaflow_distribution_files())
+        self._cached_metaflow_files.extend(list(self._metaflow_extension_files()))
+
+    def create_mfcontent_info(self) -> Dict[str, Any]:
+        return {"version": 1, "module_files": list(self._files_from_modules.values())}
+
+    def get_excluded_tl_entries(self) -> List[str]:
+        """
+        When packaging Metaflow from within an executing Metaflow flow, we need to
+        exclude the files that are inserted by this content from being packaged (possibly).
+
+        Use this function to return these files or top-level directories.
+
+        Returns
+        -------
+        List[str]
+            Files or directories to exclude
+        """
+        return [self._code_dir, self._other_dir]
+
+    def content_names(
+        self, content_types: Optional[int] = None
+    ) -> Generator[Tuple[str, str], None, None]:
+        """
+        Detailed list of the content of this MetaflowCodeContent. This will list all files
+        (or non files -- for the INFO or CONFIG data for example) present in the archive.
+
+        Parameters
+        ----------
+        content_types : Optional[int]
+            The type of content to get the names of. If None, all content is returned.
+
+        Yields
+        ------
+        Generator[Tuple[str, str], None, None]
+            Path on the filesystem and the name in the archive
+        """
+        yield from self._content(content_types, generate_value=False)
+
+    def contents(
+        self, content_types: Optional[int] = None
+    ) -> Generator[Tuple[Union[bytes, str], str], None, None]:
+        """
+        Very similar to content_names but returns the content of the non-files
+        as well as bytes. For files, identical output as content_names
+
+        Parameters
+        ----------
+        content_types : Optional[int]
+            The type of content to get the content of. If None, all content is returned.
+
+        Yields
+        ------
+        Generator[Tuple[Union[str, bytes], str], None, None]
+            Content of the MF content
+        """
+        yield from self._content(content_types, generate_value=True)
+
+    def show(self) -> str:
+        """
+        Returns a more human-readable string representation of the content of this
+        MetaflowCodeContent. This will not, for example, list all files but summarize what
+        is included at a more high level.
+
+        Returns
+        -------
+        str
+            A human-readable string representation of the content of this MetaflowCodeContent
+        """
+        all_user_step_decorators = {}
+        for k, v in UserStepDecoratorMeta.all_decorators().items():
+            all_user_step_decorators.setdefault(
+                getattr(v, "_original_module", v.__module__), []
+            ).append(k)
+
+        all_user_flow_decorators = {}
+        for k, v in FlowMutatorMeta.all_decorators().items():
+            all_user_flow_decorators.setdefault(
+                getattr(v, "_original_module", v.__module__), []
+            ).append(k)
+
+        result = []
+        if self._metaflow_version:
+            result.append(f"\nMetaflow version: {self._metaflow_version}")
+        ext_info = extension_info()
+        if ext_info["installed"]:
+            result.append("\nMetaflow extensions packaged:")
+            for ext_name, ext_info in ext_info["installed"].items():
+                result.append(
+                    f"  - {ext_name} ({ext_info['extension_name']}) @ {ext_info['dist_version']}"
+                )
+
+        if self._modules:
+            mf_modules = []
+            other_modules = []
+            for name, info in self._modules.items():
+                if info.metaflow_module:
+                    mf_modules.append(f"  - {name} @ {', '.join(info.root_paths)}")
+                    module_user_step_decorators = [
+                        ", ".join(v)
+                        for k, v in all_user_step_decorators.items()
+                        if k == info.name or k.startswith(info.name + ".")
+                    ]
+                    module_user_flow_decorators = [
+                        ", ".join(v)
+                        for k, v in all_user_flow_decorators.items()
+                        if k == info.name or k.startswith(info.name + ".")
+                    ]
+                    if module_user_step_decorators:
+                        mf_modules.append(
+                            f"    - Provides step decorators: {', '.join(module_user_step_decorators)}"
+                        )
+                    if module_user_flow_decorators:
+                        mf_modules.append(
+                            f"    - Provides flow mutators: {', '.join(module_user_flow_decorators)}"
+                        )
+                else:
+                    other_modules.append(f"  - {name} @ {', '.join(info.root_paths)}")
+            if mf_modules:
+                result.append("\nMetaflow modules:")
+                result.extend(mf_modules)
+            if other_modules:
+                result.append("\nNon-Metaflow packaged modules:")
+                result.extend(other_modules)
+
+        return "\n".join(result)
+
+    def add_info(self, info: Dict[str, Any]) -> None:
+        """
+        Add the content of the INFO file to the Metaflow content
+
+        Parameters
+        ----------
+        info: Dict[str, Any]
+            The content of the INFO file
+        """
+        info_file_path = os.path.join(self._other_dir, self._info_file)
+        if info_file_path in self._other_content:
+            raise MetaflowException("INFO file already present in the MF environment")
+        self._other_content[info_file_path] = json.dumps(info).encode("utf-8")
+
+    def add_config(self, config: Dict[str, Any]) -> None:
+        """
+        Add the content of the CONFIG file to the Metaflow content
+
+        Parameters
+        ----------
+        config: Dict[str, Any]
+            The content of the CONFIG file
+        """
+        config_file_path = os.path.join(self._other_dir, self._config_file)
+        if config_file_path in self._other_content:
+            raise MetaflowException("CONFIG file already present in the MF environment")
+        self._other_content[config_file_path] = json.dumps(config).encode("utf-8")
+
+    def add_module(self, module: ModuleType) -> None:
+        """
+        Add a python module to the Metaflow content
+
+        Parameters
+        ----------
+        module_path: ModuleType
+            The module to add
+        """
+        name = module.__name__
+        debug.package_exec(f"Adding module {name} to the MF content")
+        # If the module is a single file, we handle this here by looking at __file__
+        # which will point to the single file. If it is an actual module, __path__
+        # will contain the path(s) to the module
+        self._modules[name] = _ModuleInfo(
+            name,
+            set(
+                Path(p).resolve().as_posix()
+                for p in getattr(module, "__path__", [module.__file__])
+            ),
+            module,
+            False,  # This is not a Metaflow module (added by the user manually)
+        )
+        self._files_from_modules.update(
+            self._module_files(name, self._modules[name].root_paths)
+        )
+
+    def add_code_file(self, file_path: str, file_name: str) -> None:
+        """
+        Add a code file to the Metaflow content
+
+        Parameters
+        ----------
+        file_path: str
+            The path to the code file to add (on the filesystem)
+        file_name: str
+            The path in the archive to add the code file to
+        """
+        file_path = os.path.realpath(file_path)
+        debug.package_exec(
+            f"Adding code file {file_path} as {file_name} to the MF content"
+        )
+
+        if file_path in self._files and self._files[file_path] != os.path.join(
+            self._code_dir, file_name.lstrip("/")
+        ):
+            raise MetaflowException(
+                "File '%s' is already present in the MF content with a different name: '%s'"
+                % (file_path, self._files[file_path])
+            )
+        self._files[file_path] = os.path.join(self._code_dir, file_name.lstrip("/"))
+
+    def add_other_file(self, file_path: str, file_name: str) -> None:
+        """
+        Add a non-python file to the Metaflow content
+
+        Parameters
+        ----------
+        file_path: str
+            The path to the file to add (on the filesystem)
+        file_name: str
+            The path in the archive to add the file to
+        """
+        file_path = os.path.realpath(file_path)
+        debug.package_exec(
+            f"Adding other file {file_path} as {file_name} to the MF content"
+        )
+        if file_path in self._other_files and self._other_files[
+            file_path
+        ] != os.path.join(self._other_dir, file_name.lstrip("/")):
+            raise MetaflowException(
+                "File %s is already present in the MF content with a different name: %s"
+                % (file_path, self._other_files[file_path])
+            )
+        self._other_files[file_path] = os.path.join(
+            self._other_dir, file_name.lstrip("/")
+        )
+
+    def _content(
+        self, content_types: Optional[int] = None, generate_value: bool = False
+    ) -> Generator[Tuple[Union[str, bytes], str], None, None]:
+        from ..package import MetaflowPackage  # Prevent circular dependency
+
+        if content_types is None:
+            content_types = ContentType.ALL_CONTENT.value
+
+        if content_types & ContentType.CODE_CONTENT.value:
+            yield from self._cached_metaflow_files
+            yield from self._files.items()
+        if content_types & ContentType.MODULE_CONTENT.value:
+            yield from self._files_from_modules.items()
+        if content_types & ContentType.OTHER_CONTENT.value:
+            yield from self._other_files.items()
+            if generate_value:
+                for k, v in self._other_content.items():
+                    yield v, k
+                # Include the distribution file too
+                yield json.dumps(self._distmetainfo).encode("utf-8"), os.path.join(
+                    self._other_dir, self._dist_info_file
+                )
+                yield json.dumps(self.create_mfcontent_info()).encode(
+                    "utf-8"
+                ), os.path.join(self._code_dir, MFCONTENT_MARKER)
+            else:
+                for k in self._other_content.keys():
+                    yield "" % (os.path.basename(k)), k
+                yield "" % (
+                    os.path.basename(self._dist_info_file)
+                ), os.path.join(self._other_dir, self._dist_info_file)
+                yield "" % MFCONTENT_MARKER, os.path.join(
+                    self._code_dir, MFCONTENT_MARKER
+                )
+
+    def _metaflow_distribution_files(self) -> Generator[Tuple[str, str], None, None]:
+        debug.package_exec("Including Metaflow from '%s'" % self._metaflow_root)
+        for path_tuple in walk(
+            os.path.join(self._metaflow_root, "metaflow"),
+            exclude_hidden=False,
+            file_filter=suffix_filter(self.METAFLOW_SUFFIXES_LIST),
+        ):
+            yield path_tuple[0], os.path.join(self._code_dir, path_tuple[1])
+
+    def _metaflow_extension_files(self) -> Generator[Tuple[str, str], None, None]:
+        # Metaflow extensions; for now, we package *all* extensions but this may change
+        # at a later date; it is possible to call `package_mfext_package` instead of
+        # `package_mfext_all` but in that case, make sure to also add a
+        # metaflow_extensions/__init__.py file to properly "close" the metaflow_extensions
+        # package and prevent other extensions from being loaded that may be
+        # present in the rest of the system
+        for path_tuple in package_mfext_all():
+            yield path_tuple[0], os.path.join(self._code_dir, path_tuple[1])
+        if debug.package:
+            ext_info = package_mfext_all_descriptions()
+            ext_info = {
+                k: {k1: v1 for k1, v1 in v.items() if k1 in ("root_paths",)}
+                for k, v in ext_info.items()
+            }
+            debug.package_exec(f"Metaflow extensions packaged: {ext_info}")
+
+    def _module_files(
+        self, name: str, paths: Set[str]
+    ) -> Generator[Tuple[str, str], None, None]:
+        debug.package_exec(
+            "    Looking for distributions for module %s in %s" % (name, paths)
+        )
+        paths = set(paths)  # Do not modify external paths
+        has_init = False
+        distributions = modules_to_distributions().get(name)
+        prefix_parts = tuple(name.split("."))
+
+        seen_distributions = set()
+        if distributions:
+            for dist in distributions:
+                dist_name = dist.metadata["Name"]  # dist.name not always present
+                if dist_name in seen_distributions:
+                    continue
+                # For some reason, sometimes the same distribution appears twice. We
+                # don't need to process twice.
+                seen_distributions.add(dist_name)
+                debug.package_exec(
+                    "    Including distribution '%s' for module '%s'"
+                    % (dist_name, name)
+                )
+                dist_root = str(dist.locate_file(name))
+                if dist_root not in paths:
+                    # This is an error because it means that this distribution is
+                    # not contributing to the module.
+                    raise RuntimeError(
+                        "Distribution '%s' is not contributing to module '%s' as "
+                        "expected (got '%s' when expected one of %s)"
+                        % (dist.metadata["Name"], name, dist_root, paths)
+                    )
+                paths.discard(dist_root)
+                if dist_name not in self._distmetainfo:
+                    # Possible that a distribution contributes to multiple modules
+                    self._distmetainfo[dist_name] = {
+                        # We can add more if needed but these are likely the most
+                        # useful (captures, name, version, etc and files which can
+                        # be used to find non-python files in the distribution).
+                        "METADATA": dist.read_text("METADATA") or "",
+                        "RECORD": dist.read_text("RECORD") or "",
+                    }
+                for file in dist.files or []:
+                    # Skip files that do not belong to this module (distribution may
+                    # provide multiple modules)
+                    if file.parts[: len(prefix_parts)] != prefix_parts:
+                        continue
+                    if file.parts[len(prefix_parts)] == "__init__.py":
+                        has_init = True
+                    yield str(
+                        dist.locate_file(file).resolve().as_posix()
+                    ), os.path.join(self._code_dir, *prefix_parts, *file.parts[1:])
+
+        # Now if there are more paths left in paths, it means there is a non-distribution
+        # component to this package which we also include.
+        debug.package_exec(
+            "    Looking for non-distribution files for module '%s' in %s"
+            % (name, paths)
+        )
+        for path in paths:
+            if not Path(path).is_dir():
+                # Single file for the module -- this will be something like .py
+                yield path, os.path.join(
+                    self._code_dir, *prefix_parts[:-1], f"{prefix_parts[-1]}.py"
+                )
+                has_init = True
+            else:
+                for root, _, files in os.walk(path):
+                    for file in files:
+                        if any(file.endswith(x) for x in EXT_EXCLUDE_SUFFIXES):
+                            continue
+                        rel_path = os.path.relpath(os.path.join(root, file), path)
+                        if rel_path == "__init__.py":
+                            has_init = True
+                        yield os.path.join(root, file), os.path.join(
+                            self._code_dir,
+                            name,
+                            rel_path,
+                        )
+        # We now include an empty __init__.py file to close the module and prevent
+        # leaks from possible namespace packages
+        if not has_init:
+            yield os.path.join(
+                self._metaflow_root, "metaflow", "extension_support", "_empty_file.py"
+            ), os.path.join(self._code_dir, *prefix_parts, "__init__.py")
diff --git a/metaflow/parameters.py b/metaflow/parameters.py
index a32a6c2e475..d33e917bd4d 100644
--- a/metaflow/parameters.py
+++ b/metaflow/parameters.py
@@ -1,5 +1,9 @@
 import json
-from typing import Any, Callable, Dict, NamedTuple, Optional, Type, Union
+
+from contextlib import contextmanager
+from threading import local
+
+from typing import Any, Callable, Dict, NamedTuple, Optional, TYPE_CHECKING, Type, Union
 
 from metaflow._vendor import click
 
@@ -10,6 +14,9 @@
     MetaflowException,
 )
 
+if TYPE_CHECKING:
+    from .user_configs.config_parameters import ConfigValue
+
 try:
     # Python2
     strtype = basestring
@@ -28,17 +35,57 @@
         ("parameter_name", str),
         ("logger", Callable[..., None]),
         ("ds_type", str),
+        ("configs", Optional["ConfigValue"]),
     ],
 )
 
-# currently we execute only one flow per process, so we can treat
-# Parameters globally. If this was to change, it should/might be
-# possible to move these globals in a FlowSpec (instance) specific
-# closure.
-parameters = []
+
+# When we launch a flow, we need to know the parameters so we can
+# attach them with add_custom_parameters to commands. This used to be a global
+# but causes problems when multiple FlowSpec are loaded (as can happen when using
+# the Runner or just if multiple Flows are defined and instantiated). To minimally
+# impact code, we now create the CLI with a thread local value of the FlowSpec
+# that is being used to create the CLI which enables us to extract the parameters
+# directly from the Flow.
+current_flow = local()
+
+
+@contextmanager
+def flow_context(flow_cls):
+    """
+    Context manager to set the current flow for the thread. This is used
+    to extract the parameters from the FlowSpec that is being used to create
+    the CLI.
+    """
+    # Use a stack because with the runner this can get called multiple times in
+    # a nested fashion
+    current_flow.flow_cls_stack = getattr(current_flow, "flow_cls_stack", [])
+    current_flow.flow_cls_stack.insert(0, flow_cls)
+    current_flow.flow_cls = current_flow.flow_cls_stack[0]
+    try:
+        yield
+    finally:
+        current_flow.flow_cls_stack = current_flow.flow_cls_stack[1:]
+        if len(current_flow.flow_cls_stack) == 0:
+            del current_flow.flow_cls_stack
+            del current_flow.flow_cls
+        else:
+            current_flow.flow_cls = current_flow.flow_cls_stack[0]
+
+
 context_proto = None
 
 
+def replace_flow_context(flow_cls):
+    """
+    Replace the current flow context with a new flow class. This is used
+    when we change the current flow class after having run user configuration functions
+    """
+    current_flow.flow_cls_stack = current_flow.flow_cls_stack[1:]
+    current_flow.flow_cls_stack.insert(0, flow_cls)
+    current_flow.flow_cls = current_flow.flow_cls_stack[0]
+
+
 class JSONTypeClass(click.ParamType):
     name = "JSON"
 
@@ -77,15 +124,14 @@ def __init__(
         return_str=True,
         print_representation=None,
     ):
-
         self.fun = fun
         self.field = field
         self.parameter_name = parameter_name
         self.parameter_type = parameter_type
         self.return_str = return_str
-        self.print_representation = (
-            self.user_print_representation
-        ) = print_representation
+        self.print_representation = self.user_print_representation = (
+            print_representation
+        )
         if self.print_representation is None:
             self.print_representation = str(self.fun)
 
@@ -119,6 +165,7 @@ def __call__(self, deploy_time=False):
             return self._check_type(val, deploy_time)
 
     def _check_type(self, val, deploy_time):
+
         # it is easy to introduce a deploy-time function that accidentally
         # returns a value whose type is not compatible with what is defined
         # in Parameter. Let's catch those mistakes early here, instead of
@@ -126,7 +173,7 @@ def _check_type(self, val, deploy_time):
 
         # note: this doesn't work with long in Python2 or types defined as
         # click types, e.g. click.INT
-        TYPES = {bool: "bool", int: "int", float: "float", list: "list"}
+        TYPES = {bool: "bool", int: "int", float: "float", list: "list", dict: "dict"}
 
         msg = (
             "The value returned by the deploy-time function for "
@@ -134,7 +181,12 @@ def _check_type(self, val, deploy_time):
             % (self.parameter_name, self.field)
         )
 
-        if self.parameter_type in TYPES:
+        if isinstance(self.parameter_type, list):
+            if not any(isinstance(val, x) for x in self.parameter_type):
+                msg += "Expected one of the following %s." % TYPES[self.parameter_type]
+                raise ParameterFieldTypeMismatch(msg)
+            return str(val) if self.return_str else val
+        elif self.parameter_type in TYPES:
             if type(val) != self.parameter_type:
                 msg += "Expected a %s." % TYPES[self.parameter_type]
                 raise ParameterFieldTypeMismatch(msg)
@@ -172,12 +224,18 @@ def __repr__(self):
 def deploy_time_eval(value):
     if isinstance(value, DeployTimeField):
         return value(deploy_time=True)
+    elif isinstance(value, DelayedEvaluationParameter):
+        return value(return_str=True)
     else:
         return value
 
 
 # this is called by cli.main
-def set_parameter_context(flow_name, echo, datastore):
+def set_parameter_context(flow_name, echo, datastore, configs):
+    from .user_configs.config_parameters import (
+        ConfigValue,
+    )  # Prevent circular dependency
+
     global context_proto
     context_proto = ParameterContext(
         flow_name=flow_name,
@@ -185,6 +243,7 @@ def set_parameter_context(flow_name, echo, datastore):
         parameter_name=None,
         logger=echo,
         ds_type=datastore.TYPE,
+        configs=ConfigValue(dict(configs)),
     )
 
 
@@ -241,24 +300,32 @@ class MyFlow(FlowSpec):
     ----------
     name : str
         User-visible parameter name.
-    default : str or float or int or bool or `JSONType` or a function.
+    default : Union[str, float, int, bool, Dict[str, Any],
+                Callable[
+                    [ParameterContext], Union[str, float, int, bool, Dict[str, Any]]
+                ],
+            ], optional, default None
         Default value for the parameter. Use a special `JSONType` class to
         indicate that the value must be a valid JSON object. A function
         implies that the parameter corresponds to a *deploy-time parameter*.
         The type of the default value is used as the parameter `type`.
-    type : Type, default: None
+    type : Type, default None
         If `default` is not specified, define the parameter type. Specify
         one of `str`, `float`, `int`, `bool`, or `JSONType`. If None, defaults
         to the type of `default` or `str` if none specified.
-    help : str, optional
+    help : str, optional, default None
         Help text to show in `run --help`.
-    required : bool, default: False
-        Require that the user specified a value for the parameter.
-        `required=True` implies that the `default` is not used.
-    show_default : bool, default: True
-        If True, show the default value in the help text.
+    required : bool, optional, default None
+        Require that the user specifies a value for the parameter. Note that if
+        a default is provide, the required flag is ignored.
+        A value of None is equivalent to False.
+    show_default : bool, optional, default None
+        If True, show the default value in the help text. A value of None is equivalent
+        to True.
     """
 
+    IS_CONFIG_PARAMETER = False
+
     def __init__(
         self,
         name: str,
@@ -269,62 +336,107 @@ def __init__(
                 int,
                 bool,
                 Dict[str, Any],
-                Callable[[], Union[str, float, int, bool, Dict[str, Any]]],
+                Callable[
+                    [ParameterContext], Union[str, float, int, bool, Dict[str, Any]]
+                ],
             ]
         ] = None,
         type: Optional[
             Union[Type[str], Type[float], Type[int], Type[bool], JSONTypeClass]
         ] = None,
         help: Optional[str] = None,
-        required: bool = False,
-        show_default: bool = True,
+        required: Optional[bool] = None,
+        show_default: Optional[bool] = None,
         **kwargs: Dict[str, Any]
     ):
         self.name = name
         self.kwargs = kwargs
-        for k, v in {
+        self._override_kwargs = {
             "default": default,
             "type": type,
             "help": help,
             "required": required,
             "show_default": show_default,
-        }.items():
-            if v is not None:
-                self.kwargs[k] = v
+        }
+
+    def init(self, ignore_errors=False):
+        # Prevent circular import
+        from .user_configs.config_parameters import (
+            resolve_delayed_evaluator,
+            unpack_delayed_evaluator,
+        )
+
+        # Resolve any value from configurations
+        self.kwargs, _ = unpack_delayed_evaluator(
+            self.kwargs, ignore_errors=ignore_errors
+        )
+        # Do it one item at a time so errors are ignored at that level (as opposed to
+        # at the entire kwargs level)
+        self.kwargs = {
+            k: resolve_delayed_evaluator(v, ignore_errors=ignore_errors, to_dict=True)
+            for k, v in self.kwargs.items()
+        }
+
+        # This was the behavior before configs: values specified in args would override
+        # stuff in kwargs which is what we implement here as well
+        for key, value in self._override_kwargs.items():
+            if value is not None:
+                self.kwargs[key] = resolve_delayed_evaluator(
+                    value, ignore_errors=ignore_errors, to_dict=True
+                )
+        # Set two default values if no-one specified them
+        self.kwargs.setdefault("required", False)
+        self.kwargs.setdefault("show_default", True)
+
+        # Continue processing kwargs free of any configuration values :)
 
         # TODO: check that the type is one of the supported types
-        param_type = self.kwargs["type"] = self._get_type(kwargs)
+        param_type = self.kwargs["type"] = self._get_type(self.kwargs)
+
         reserved_params = [
             "params",
             "with",
-            "max-num-splits",
-            "max-workers",
             "tag",
-            "run-id-file",
             "namespace",
+            "obj",
+            "tags",
+            "decospecs",
+            "run-id-file",
+            "max-num-splits",
+            "max-workers",
+            "max-log-size",
+            "user-namespace",
         ]
+        reserved = set(reserved_params)
+        # due to the way Click maps cli args to function args we also want to add underscored params to the set
+        for param in reserved_params:
+            reserved.add(param.replace("-", "_"))
 
-        if self.name in reserved_params:
+        if self.name in reserved:
             raise MetaflowException(
                 "Parameter name '%s' is a reserved "
                 "word. Please use a different "
-                "name for your parameter." % (name)
+                "name for your parameter." % (self.name)
             )
 
         # make sure the user is not trying to pass a function in one of the
         # fields that don't support function-values yet
         for field in ("show_default", "separator", "required"):
-            if callable(kwargs.get(field)):
+            if callable(self.kwargs.get(field)):
                 raise MetaflowException(
                     "Parameter *%s*: Field '%s' cannot "
-                    "have a function as its value" % (name, field)
+                    "have a function as its value" % (self.name, field)
                 )
 
         # default can be defined as a function
         default_field = self.kwargs.get("default")
         if callable(default_field) and not isinstance(default_field, DeployTimeField):
             self.kwargs["default"] = DeployTimeField(
-                name, param_type, "default", self.kwargs["default"], return_str=True
+                self.name,
+                param_type,
+                "default",
+                self.kwargs["default"],
+                return_str=True,
             )
 
         # note that separator doesn't work with DeployTimeFields unless you
@@ -333,9 +445,14 @@ def __init__(
         if self.separator and not self.is_string_type:
             raise MetaflowException(
                 "Parameter *%s*: Separator is only allowed "
-                "for string parameters." % name
+                "for string parameters." % self.name
             )
-        parameters.append(self)
+
+    def __repr__(self):
+        return "metaflow.Parameter(name=%s, kwargs=%s)" % (self.name, self.kwargs)
+
+    def __str__(self):
+        return "metaflow.Parameter(name=%s, kwargs=%s)" % (self.name, self.kwargs)
 
     def option_kwargs(self, deploy_mode):
         kwargs = self.kwargs
@@ -378,11 +495,21 @@ def add_custom_parameters(deploy_mode=False):
     # deploy_mode determines whether deploy-time functions should or should
     # not be evaluated for this command
     def wrapper(cmd):
+        cmd.has_flow_params = True
         # Iterate over parameters in reverse order so cmd.params lists options
         # in the order they are defined in the FlowSpec subclass
+        flow_cls = getattr(current_flow, "flow_cls", None)
+        if flow_cls is None:
+            return cmd
+        parameters = [
+            p for _, p in flow_cls._get_parameters() if not p.IS_CONFIG_PARAMETER
+        ]
         for arg in parameters[::-1]:
             kwargs = arg.option_kwargs(deploy_mode)
             cmd.params.insert(0, click.Option(("--" + arg.name,), **kwargs))
         return cmd
 
     return wrapper
+
+
+JSONType = JSONTypeClass()
diff --git a/metaflow/plugins/__init__.py b/metaflow/plugins/__init__.py
index 06572f6776f..bbab35885e8 100644
--- a/metaflow/plugins/__init__.py
+++ b/metaflow/plugins/__init__.py
@@ -1,3 +1,5 @@
+import sys
+
 from metaflow.extension_support.plugins import (
     merge_lists,
     process_plugins,
@@ -14,8 +16,15 @@
     ("argo-workflows", ".argo.argo_workflows_cli.cli"),
     ("card", ".cards.card_cli.cli"),
     ("tag", ".tag_cli.cli"),
+    ("spot-metadata", ".kubernetes.spot_metadata_cli.cli"),
+    ("logs", ".logs_cli.cli"),
 ]
 
+# Add additional commands to the runner here
+# These will be accessed using Runner().()
+RUNNER_CLIS_DESC = []
+
+
 from .test_unbounded_foreach_decorator import InternalTestUnboundedForeachInput
 
 # Add new step decorators here
@@ -41,10 +50,11 @@
         "unbounded_test_foreach_internal",
         ".test_unbounded_foreach_decorator.InternalTestUnboundedForeachDecorator",
     ),
-    ("conda", ".conda.conda_step_decorator.CondaStepDecorator"),
     ("card", ".cards.card_decorator.CardDecorator"),
     ("pytorch_parallel", ".frameworks.pytorch.PytorchParallelDecorator"),
     ("airflow_internal", ".airflow.airflow_decorator.AirflowInternalDecorator"),
+    ("pypi", ".pypi.pypi_decorator.PyPIStepDecorator"),
+    ("conda", ".pypi.conda_decorator.CondaStepDecorator"),
 ]
 
 # Add new flow decorators here
@@ -53,20 +63,26 @@
 # careful with the choice of name though - they become top-level
 # imports from the metaflow package.
 FLOW_DECORATORS_DESC = [
-    ("conda_base", ".conda.conda_flow_decorator.CondaFlowDecorator"),
     ("schedule", ".aws.step_functions.schedule_decorator.ScheduleDecorator"),
     ("project", ".project_decorator.ProjectDecorator"),
     ("trigger", ".events_decorator.TriggerDecorator"),
     ("trigger_on_finish", ".events_decorator.TriggerOnFinishDecorator"),
+    ("pypi_base", ".pypi.pypi_decorator.PyPIFlowDecorator"),
+    ("conda_base", ".pypi.conda_decorator.CondaFlowDecorator"),
+    ("exit_hook", ".exit_hook.exit_hook_decorator.ExitHookDecorator"),
 ]
 
 # Add environments here
-ENVIRONMENTS_DESC = [("conda", ".conda.conda_environment.CondaEnvironment")]
+ENVIRONMENTS_DESC = [
+    ("conda", ".pypi.conda_environment.CondaEnvironment"),
+    ("pypi", ".pypi.pypi_environment.PyPIEnvironment"),
+    ("uv", ".uv.uv_environment.UVEnvironment"),
+]
 
 # Add metadata providers here
 METADATA_PROVIDERS_DESC = [
-    ("service", ".metadata.service.ServiceMetadataProvider"),
-    ("local", ".metadata.local.LocalMetadataProvider"),
+    ("service", ".metadata_providers.service.ServiceMetadataProvider"),
+    ("local", ".metadata_providers.local.LocalMetadataProvider"),
 ]
 
 # Add datastore here
@@ -77,13 +93,25 @@
     ("gs", ".datastores.gs_storage.GSStorage"),
 ]
 
+# Dataclients are used for IncludeFile
+DATACLIENTS_DESC = [
+    ("local", ".datatools.Local"),
+    ("s3", ".datatools.S3"),
+    ("azure", ".azure.includefile_support.Azure"),
+    ("gs", ".gcp.includefile_support.GS"),
+]
+
 # Add non monitoring/logging sidecars here
 SIDECARS_DESC = [
     (
         "save_logs_periodically",
         "..mflog.save_logs_periodically.SaveLogsPeriodicallySidecar",
     ),
-    ("heartbeat", "metaflow.metadata.heartbeat.MetadataHeartBeat"),
+    (
+        "spot_termination_monitor",
+        ".kubernetes.spot_monitor_sidecar.SpotTerminationMonitorSidecar",
+    ),
+    ("heartbeat", "metaflow.metadata_provider.heartbeat.MetadataHeartBeat"),
 ]
 
 # Add logging sidecars here
@@ -115,6 +143,36 @@
         "aws-secrets-manager",
         ".aws.secrets_manager.aws_secrets_manager_secrets_provider.AwsSecretsManagerSecretsProvider",
     ),
+    (
+        "gcp-secret-manager",
+        ".gcp.gcp_secret_manager_secrets_provider.GcpSecretManagerSecretsProvider",
+    ),
+    (
+        "az-key-vault",
+        ".azure.azure_secret_manager_secrets_provider.AzureKeyVaultSecretsProvider",
+    ),
+]
+
+GCP_CLIENT_PROVIDERS_DESC = [
+    ("gcp-default", ".gcp.gs_storage_client_factory.GcpDefaultClientProvider")
+]
+
+AZURE_CLIENT_PROVIDERS_DESC = [
+    ("azure-default", ".azure.azure_credential.AzureDefaultClientProvider")
+]
+
+DEPLOYER_IMPL_PROVIDERS_DESC = [
+    ("argo-workflows", ".argo.argo_workflows_deployer.ArgoWorkflowsDeployer"),
+    (
+        "step-functions",
+        ".aws.step_functions.step_functions_deployer.StepFunctionsDeployer",
+    ),
+]
+
+TL_PLUGINS_DESC = [
+    ("requirements_txt_parser", ".pypi.parsers.requirements_txt_parser"),
+    ("pyproject_toml_parser", ".pypi.parsers.pyproject_toml_parser"),
+    ("conda_environment_yml_parser", ".pypi.parsers.conda_environment_yml_parser"),
 ]
 
 process_plugins(globals())
@@ -124,11 +182,24 @@ def get_plugin_cli():
     return resolve_plugins("cli")
 
 
+def get_plugin_cli_path():
+    return resolve_plugins("cli", path_only=True)
+
+
+def get_runner_cli():
+    return resolve_plugins("runner_cli")
+
+
+def get_runner_cli_path():
+    return resolve_plugins("runner_cli", path_only=True)
+
+
 STEP_DECORATORS = resolve_plugins("step_decorator")
 FLOW_DECORATORS = resolve_plugins("flow_decorator")
 ENVIRONMENTS = resolve_plugins("environment")
 METADATA_PROVIDERS = resolve_plugins("metadata_provider")
 DATASTORES = resolve_plugins("datastore")
+DATACLIENTS = resolve_plugins("dataclient")
 SIDECARS = resolve_plugins("sidecar")
 LOGGING_SIDECARS = resolve_plugins("logging_sidecar")
 MONITOR_SIDECARS = resolve_plugins("monitor_sidecar")
@@ -138,6 +209,13 @@ def get_plugin_cli():
 
 AWS_CLIENT_PROVIDERS = resolve_plugins("aws_client_provider")
 SECRETS_PROVIDERS = resolve_plugins("secrets_provider")
+AZURE_CLIENT_PROVIDERS = resolve_plugins("azure_client_provider")
+GCP_CLIENT_PROVIDERS = resolve_plugins("gcp_client_provider")
+
+if sys.version_info >= (3, 7):
+    DEPLOYER_IMPL_PROVIDERS = resolve_plugins("deployer_impl_provider")
+
+TL_PLUGINS = resolve_plugins("tl_plugin")
 
 from .cards.card_modules import MF_EXTERNAL_CARDS
 
@@ -161,6 +239,9 @@ def get_plugin_cli():
     TestNonEditableCard,
     TestPathSpecCard,
     TestTimeoutCard,
+    TestRefreshCard,
+    TestRefreshComponentCard,
+    TestImageCard,
 )
 
 CARDS = [
@@ -177,5 +258,14 @@ def get_plugin_cli():
     TestNonEditableCard,
     BlankCard,
     DefaultCardJSON,
+    TestRefreshCard,
+    TestRefreshComponentCard,
+    TestImageCard,
 ]
 merge_lists(CARDS, MF_EXTERNAL_CARDS, "type")
+
+
+def _import_tl_plugins(globals_dict):
+
+    for name, p in TL_PLUGINS.items():
+        globals_dict[name] = p
diff --git a/metaflow/plugins/airflow/airflow.py b/metaflow/plugins/airflow/airflow.py
index 8c5c759e37b..a2c39899599 100644
--- a/metaflow/plugins/airflow/airflow.py
+++ b/metaflow/plugins/airflow/airflow.py
@@ -17,6 +17,7 @@
     AIRFLOW_KUBERNETES_KUBECONFIG_FILE,
     AIRFLOW_KUBERNETES_STARTUP_TIMEOUT_SECONDS,
     AWS_SECRETS_MANAGER_DEFAULT_REGION,
+    GCP_SECRET_MANAGER_PREFIX,
     AZURE_STORAGE_BLOB_SERVICE_ENDPOINT,
     CARD_AZUREROOT,
     CARD_GSROOT,
@@ -31,7 +32,11 @@
     S3_ENDPOINT_URL,
     SERVICE_HEADERS,
     SERVICE_INTERNAL_URL,
+    AZURE_KEY_VAULT_PREFIX,
 )
+
+from metaflow.metaflow_config_funcs import config_values
+
 from metaflow.parameters import (
     DelayedEvaluationParameter,
     JSONTypeClass,
@@ -41,6 +46,7 @@
 # TODO: Move chevron to _vendor
 from metaflow.plugins.cards.card_modules import chevron
 from metaflow.plugins.kubernetes.kubernetes import Kubernetes
+from metaflow.plugins.kubernetes.kube_utils import qos_requests_and_limits
 from metaflow.plugins.timeout_decorator import get_run_time_limit_for_task
 from metaflow.util import compress_list, dict_to_cli_options, get_username
 
@@ -60,6 +66,7 @@ def __init__(
         name,
         graph,
         flow,
+        code_package_metadata,
         code_package_sha,
         code_package_url,
         metadata,
@@ -81,6 +88,7 @@ def __init__(
         self.name = name
         self.graph = graph
         self.flow = flow
+        self.code_package_metadata = code_package_metadata
         self.code_package_sha = code_package_sha
         self.code_package_url = code_package_url
         self.metadata = metadata
@@ -277,7 +285,7 @@ def _to_job(self, node):
         env_deco = [deco for deco in node.decorators if deco.name == "environment"]
         env = {}
         if env_deco:
-            env = env_deco[0].attributes["vars"]
+            env = env_deco[0].attributes["vars"].copy()
 
         # The below if/else block handles "input paths".
         # Input Paths help manage dataflow across the graph.
@@ -335,6 +343,16 @@ def _to_job(self, node):
         metaflow_version["production_token"] = self.production_token
         env["METAFLOW_VERSION"] = json.dumps(metaflow_version)
 
+        # Temporary passing of *some* environment variables. Do not rely on this
+        # mechanism as it will be removed in the near future
+        env.update(
+            {
+                k: v
+                for k, v in config_values()
+                if k.startswith("METAFLOW_CONDA_") or k.startswith("METAFLOW_DEBUG_")
+            }
+        )
+
         # Extract the k8s decorators for constructing the arguments of the K8s Pod Operator on Airflow.
         k8s_deco = [deco for deco in node.decorators if deco.name == "kubernetes"][0]
         user_code_retries, _ = self._get_retries(node)
@@ -356,6 +374,7 @@ def _to_job(self, node):
             # Technically the "user" is the stakeholder but should these labels be present.
         }
         additional_mf_variables = {
+            "METAFLOW_CODE_METADATA": self.code_package_metadata,
             "METAFLOW_CODE_SHA": self.code_package_sha,
             "METAFLOW_CODE_URL": self.code_package_url,
             "METAFLOW_CODE_DS": self.flow_datastore.TYPE,
@@ -384,17 +403,22 @@ def _to_job(self, node):
             "METAFLOW_CARD_GSROOT": CARD_GSROOT,
             "METAFLOW_S3_ENDPOINT_URL": S3_ENDPOINT_URL,
         }
-        env[
-            "METAFLOW_AZURE_STORAGE_BLOB_SERVICE_ENDPOINT"
-        ] = AZURE_STORAGE_BLOB_SERVICE_ENDPOINT
+        env["METAFLOW_AZURE_STORAGE_BLOB_SERVICE_ENDPOINT"] = (
+            AZURE_STORAGE_BLOB_SERVICE_ENDPOINT
+        )
         env["METAFLOW_DATASTORE_SYSROOT_AZURE"] = DATASTORE_SYSROOT_AZURE
         env["METAFLOW_CARD_AZUREROOT"] = CARD_AZUREROOT
         if DEFAULT_SECRETS_BACKEND_TYPE:
             env["METAFLOW_DEFAULT_SECRETS_BACKEND_TYPE"] = DEFAULT_SECRETS_BACKEND_TYPE
         if AWS_SECRETS_MANAGER_DEFAULT_REGION:
-            env[
-                "METAFLOW_AWS_SECRETS_MANAGER_DEFAULT_REGION"
-            ] = AWS_SECRETS_MANAGER_DEFAULT_REGION
+            env["METAFLOW_AWS_SECRETS_MANAGER_DEFAULT_REGION"] = (
+                AWS_SECRETS_MANAGER_DEFAULT_REGION
+            )
+        if GCP_SECRET_MANAGER_PREFIX:
+            env["METAFLOW_GCP_SECRET_MANAGER_PREFIX"] = GCP_SECRET_MANAGER_PREFIX
+
+        if AZURE_KEY_VAULT_PREFIX:
+            env["METAFLOW_AZURE_KEY_VAULT_PREFIX"] = AZURE_KEY_VAULT_PREFIX
 
         env.update(additional_mf_variables)
 
@@ -408,25 +432,25 @@ def _to_job(self, node):
             if k8s_deco.attributes["namespace"] is not None
             else "default"
         )
-
+        qos_requests, qos_limits = qos_requests_and_limits(
+            k8s_deco.attributes["qos"],
+            k8s_deco.attributes["cpu"],
+            k8s_deco.attributes["memory"],
+            k8s_deco.attributes["disk"],
+        )
         resources = dict(
-            requests={
-                "cpu": k8s_deco.attributes["cpu"],
-                "memory": "%sM" % str(k8s_deco.attributes["memory"]),
-                "ephemeral-storage": str(k8s_deco.attributes["disk"]),
-            }
+            requests=qos_requests,
+            limits={
+                **qos_limits,
+                **{
+                    "%s.com/gpu".lower()
+                    % k8s_deco.attributes["gpu_vendor"]: str(k8s_deco.attributes["gpu"])
+                    for k in [0]
+                    # Don't set GPU limits if gpu isn't specified.
+                    if k8s_deco.attributes["gpu"] is not None
+                },
+            },
         )
-        if k8s_deco.attributes["gpu"] is not None:
-            resources.update(
-                dict(
-                    limits={
-                        "%s.com/gpu".lower()
-                        % k8s_deco.attributes["gpu_vendor"]: str(
-                            k8s_deco.attributes["gpu"]
-                        )
-                    }
-                )
-            )
 
         annotations = {
             "metaflow/production_token": self.production_token,
@@ -455,6 +479,7 @@ def _to_job(self, node):
                 node.name,
                 AIRFLOW_MACROS.create_task_id(self.contains_foreach),
                 AIRFLOW_MACROS.ATTEMPT,
+                code_package_metadata=self.code_package_metadata,
                 code_package_url=self.code_package_url,
                 step_cmds=self._step_cli(
                     node, input_paths, self.code_package_url, user_code_retries
@@ -513,13 +538,13 @@ def _step_cli(self, node, paths, code_package_url, user_code_retries):
             "with": [
                 decorator.make_decorator_spec()
                 for decorator in node.decorators
-                if not decorator.statically_defined
+                if not decorator.statically_defined and decorator.inserted_by is None
             ]
         }
         # FlowDecorators can define their own top-level options. They are
         # responsible for adding their own top-level options and values through
         # the get_top_level_options() hook. See similar logic in runtime.py.
-        for deco in flow_decorators():
+        for deco in flow_decorators(self.flow):
             top_opts_dict.update(deco.get_top_level_options())
 
         top_opts = list(dict_to_cli_options(top_opts_dict))
@@ -633,6 +658,12 @@ def compile(self):
                 "to Airflow is not supported currently."
             )
 
+        if self.flow._flow_decorators.get("exit_hook"):
+            raise AirflowException(
+                "Deploying flows with the @exit_hook decorator "
+                "to Airflow is not currently supported."
+            )
+
         # Visit every node of the flow and recursively build the state machine.
         def _visit(node, workflow, exit_node=None):
             kube_deco = dict(
@@ -642,10 +673,15 @@ def _visit(node, workflow, exit_node=None):
             )
             if kube_deco:
                 # Only guard against use_tmpfs and tmpfs_size as these determine if tmpfs is enabled.
-                for attr in ["use_tmpfs", "tmpfs_size"]:
+                for attr in [
+                    "use_tmpfs",
+                    "tmpfs_size",
+                    "persistent_volume_claims",
+                    "image_pull_policy",
+                ]:
                     if kube_deco[attr]:
                         raise AirflowException(
-                            "tmpfs attribute *%s* is currently not supported on Airflow "
+                            "The decorator attribute *%s* is currently not supported on Airflow "
                             "for the @kubernetes decorator on step *%s*"
                             % (attr, node.name)
                         )
diff --git a/metaflow/plugins/airflow/airflow_cli.py b/metaflow/plugins/airflow/airflow_cli.py
index 55cedb1fcc4..6ba7e2253cb 100644
--- a/metaflow/plugins/airflow/airflow_cli.py
+++ b/metaflow/plugins/airflow/airflow_cli.py
@@ -7,6 +7,7 @@
 from metaflow import current, decorators
 from metaflow._vendor import click
 from metaflow.exception import MetaflowException, MetaflowInternalError
+from metaflow.metaflow_config import FEAT_ALWAYS_UPLOAD_CODE_PACKAGE
 from metaflow.package import MetaflowPackage
 from metaflow.plugins.aws.step_functions.production_token import (
     load_token,
@@ -24,7 +25,7 @@ class IncorrectProductionToken(MetaflowException):
     headline = "Incorrect production token"
 
 
-VALID_NAME = re.compile("[^a-zA-Z0-9_\-\.]")
+VALID_NAME = re.compile(r"[^a-zA-Z0-9_\-\.]")
 
 
 def resolve_token(
@@ -283,24 +284,35 @@ def make_flow(
 ):
     # Attach @kubernetes.
     decorators._attach_decorators(obj.flow, [KubernetesDecorator.name])
+    decorators._init(obj.flow)
 
     decorators._init_step_decorators(
         obj.flow, obj.graph, obj.environment, obj.flow_datastore, obj.logger
     )
-
+    obj.graph = obj.flow._graph
     # Save the code package in the flow datastore so that both user code and
     # metaflow package can be retrieved during workflow execution.
     obj.package = MetaflowPackage(
-        obj.flow, obj.environment, obj.echo, obj.package_suffixes
+        obj.flow,
+        obj.environment,
+        obj.echo,
+        suffixes=obj.package_suffixes,
+        flow_datastore=obj.flow_datastore if FEAT_ALWAYS_UPLOAD_CODE_PACKAGE else None,
     )
-    package_url, package_sha = obj.flow_datastore.save_data(
-        [obj.package.blob], len_hint=1
-    )[0]
+    # This blocks until the package is created
+    if FEAT_ALWAYS_UPLOAD_CODE_PACKAGE:
+        package_url = obj.package.package_url()
+        package_sha = obj.package.package_sha()
+    else:
+        package_url, package_sha = obj.flow_datastore.save_data(
+            [obj.package.blob], len_hint=1
+        )[0]
 
     return Airflow(
         dag_name,
         obj.graph,
         obj.flow,
+        obj.package.package_metadata,
         package_sha,
         package_url,
         obj.metadata,
@@ -389,6 +401,11 @@ def _validate_workflow(flow, graph, flow_datastore, metadata, workflow_timeout):
                 "Step *%s* is marked for execution on AWS Batch with Airflow which isn't currently supported."
                 % node.name
             )
+        if any([d.name == "slurm" for d in node.decorators]):
+            raise NotSupportedException(
+                "Step *%s* is marked for execution on Slurm with Airflow which isn't currently supported."
+                % node.name
+            )
     SUPPORTED_DATASTORES = ("azure", "s3", "gs")
     if flow_datastore.TYPE not in SUPPORTED_DATASTORES:
         raise AirflowException(
diff --git a/metaflow/plugins/airflow/airflow_decorator.py b/metaflow/plugins/airflow/airflow_decorator.py
index 11bdecaaa8b..01400867de8 100644
--- a/metaflow/plugins/airflow/airflow_decorator.py
+++ b/metaflow/plugins/airflow/airflow_decorator.py
@@ -1,7 +1,7 @@
 import json
 import os
 from metaflow.decorators import StepDecorator
-from metaflow.metadata import MetaDatum
+from metaflow.metadata_provider import MetaDatum
 
 from .airflow_utils import (
     TASK_ID_XCOM_KEY,
diff --git a/metaflow/plugins/airflow/airflow_utils.py b/metaflow/plugins/airflow/airflow_utils.py
index 39ce14b3ecc..d0574ad7401 100644
--- a/metaflow/plugins/airflow/airflow_utils.py
+++ b/metaflow/plugins/airflow/airflow_utils.py
@@ -567,9 +567,11 @@ def from_dict(cls, task_dict, flow_name=None, flow_contains_foreach=False):
         return cls(
             task_dict["name"],
             is_mapper_node=is_mapper_node,
-            operator_type=task_dict["operator_type"]
-            if "operator_type" in task_dict
-            else "kubernetes",
+            operator_type=(
+                task_dict["operator_type"]
+                if "operator_type" in task_dict
+                else "kubernetes"
+            ),
             flow_name=flow_name,
             flow_contains_foreach=flow_contains_foreach,
         ).set_operator_args(**op_args)
diff --git a/metaflow/plugins/airflow/sensors/base_sensor.py b/metaflow/plugins/airflow/sensors/base_sensor.py
index 9412072cd23..8a4608033b9 100644
--- a/metaflow/plugins/airflow/sensors/base_sensor.py
+++ b/metaflow/plugins/airflow/sensors/base_sensor.py
@@ -1,5 +1,5 @@
 import uuid
-from metaflow.decorators import FlowDecorator
+from metaflow.decorators import FlowDecorator, flow_decorators
 from ..exception import AirflowException
 from ..airflow_utils import AirflowTask, id_creator, TASK_ID_HASH_LEN
 
@@ -49,7 +49,7 @@ def create_task(self):
             operator_type=self.operator_type,
         ).set_operator_args(**{k: v for k, v in task_args.items() if v is not None})
 
-    def validate(self):
+    def validate(self, flow):
         """
         Validate if the arguments for the sensor are correct.
         """
@@ -58,7 +58,7 @@ def validate(self):
         if self.attributes["name"] is None:
             deco_index = [
                 d._id
-                for d in self._flow_decorators
+                for d in flow_decorators(flow)
                 if issubclass(d.__class__, AirflowSensorDecorator)
             ].index(self._id)
             self._airflow_task_name = "%s-%s" % (
@@ -71,4 +71,4 @@ def validate(self):
     def flow_init(
         self, flow, graph, environment, flow_datastore, metadata, logger, echo, options
     ):
-        self.validate()
+        self.validate(flow)
diff --git a/metaflow/plugins/airflow/sensors/external_task_sensor.py b/metaflow/plugins/airflow/sensors/external_task_sensor.py
index c599a05b4a0..264e47f18bc 100644
--- a/metaflow/plugins/airflow/sensors/external_task_sensor.py
+++ b/metaflow/plugins/airflow/sensors/external_task_sensor.py
@@ -28,27 +28,27 @@ class ExternalTaskSensorDecorator(AirflowSensorDecorator):
         Time, in seconds before the task times out and fails. (Default: 3600)
     poke_interval : int
         Time in seconds that the job should wait in between each try. (Default: 60)
-    mode : string
+    mode : str
         How the sensor operates. Options are: { poke | reschedule }. (Default: "poke")
     exponential_backoff : bool
         allow progressive longer waits between pokes by using exponential backoff algorithm. (Default: True)
-    pool : string
+    pool : str
         the slot pool this task should run in,
         slot pools are a way to limit concurrency for certain tasks. (Default:None)
     soft_fail : bool
         Set to true to mark the task as SKIPPED on failure. (Default: False)
-    name : string
+    name : str
         Name of the sensor on Airflow
-    description : string
+    description : str
         Description of sensor in the Airflow UI
-    external_dag_id : string
+    external_dag_id : str
         The dag_id that contains the task you want to wait for.
-    external_task_ids : List[string]
+    external_task_ids : List[str]
         The list of task_ids that you want to wait for.
         If None (default value) the sensor waits for the DAG. (Default: None)
-    allowed_states : List[string]
+    allowed_states : List[str]
         Iterable of allowed states, (Default: ['success'])
-    failed_states : List[string]
+    failed_states : List[str]
         Iterable of failed or dis-allowed states. (Default: None)
     execution_delta : datetime.timedelta
         time difference with the previous execution to look at,
@@ -85,7 +85,7 @@ def serialize_operator_args(self):
             )
         return task_args
 
-    def validate(self):
+    def validate(self, flow):
         if self.attributes["external_dag_id"] is None:
             raise AirflowException(
                 "`%s` argument of `@%s`cannot be `None`."
@@ -131,4 +131,4 @@ def validate(self):
                         self.name,
                     )
                 )
-        super().validate()
+        super().validate(flow)
diff --git a/metaflow/plugins/airflow/sensors/s3_sensor.py b/metaflow/plugins/airflow/sensors/s3_sensor.py
index 6fac6725d20..557dd00061b 100644
--- a/metaflow/plugins/airflow/sensors/s3_sensor.py
+++ b/metaflow/plugins/airflow/sensors/s3_sensor.py
@@ -17,20 +17,20 @@ class S3KeySensorDecorator(AirflowSensorDecorator):
         Time, in seconds before the task times out and fails. (Default: 3600)
     poke_interval : int
         Time in seconds that the job should wait in between each try. (Default: 60)
-    mode : string
+    mode : str
         How the sensor operates. Options are: { poke | reschedule }. (Default: "poke")
     exponential_backoff : bool
         allow progressive longer waits between pokes by using exponential backoff algorithm. (Default: True)
-    pool : string
+    pool : str
         the slot pool this task should run in,
         slot pools are a way to limit concurrency for certain tasks. (Default:None)
     soft_fail : bool
         Set to true to mark the task as SKIPPED on failure. (Default: False)
-    name : string
+    name : str
         Name of the sensor on Airflow
-    description : string
+    description : str
         Description of sensor in the Airflow UI
-    bucket_key : str | List[str]
+    bucket_key : Union[str, List[str]]
         The key(s) being waited on. Supports full s3:// style url or relative path from root level.
         When it's specified as a full s3:// url, please leave `bucket_name` as None
     bucket_name : str
@@ -38,7 +38,7 @@ class S3KeySensorDecorator(AirflowSensorDecorator):
         When specified, all the keys passed to bucket_key refers to this bucket. (Default:None)
     wildcard_match : bool
         whether the bucket_key should be interpreted as a Unix wildcard pattern. (Default: False)
-    aws_conn_id : string
+    aws_conn_id : str
         a reference to the s3 connection on Airflow. (Default: None)
     verify : bool
         Whether or not to verify SSL certificates for S3 connection. (Default: None)
@@ -58,9 +58,9 @@ class S3KeySensorDecorator(AirflowSensorDecorator):
         #  `verify` is a airflow variable.
     )
 
-    def validate(self):
+    def validate(self, flow):
         if self.attributes["bucket_key"] is None:
             raise AirflowException(
                 "`bucket_key` for `@%s`cannot be empty." % (self.name)
             )
-        super().validate()
+        super().validate(flow)
diff --git a/metaflow/plugins/argo/argo_client.py b/metaflow/plugins/argo/argo_client.py
index 0a1a2d73a7f..f8e96f89161 100644
--- a/metaflow/plugins/argo/argo_client.py
+++ b/metaflow/plugins/argo/argo_client.py
@@ -1,6 +1,4 @@
 import json
-import os
-import sys
 
 from metaflow.exception import MetaflowException
 from metaflow.plugins.kubernetes.kubernetes_client import KubernetesClient
@@ -10,6 +8,14 @@ class ArgoClientException(MetaflowException):
     headline = "Argo Client error"
 
 
+class ArgoResourceNotFound(MetaflowException):
+    headline = "Resource not found"
+
+
+class ArgoNotPermitted(MetaflowException):
+    headline = "Operation not permitted"
+
+
 class ArgoClient(object):
     def __init__(self, namespace=None):
         self._client = KubernetesClient()
@@ -17,6 +23,24 @@ def __init__(self, namespace=None):
         self._group = "argoproj.io"
         self._version = "v1alpha1"
 
+    def get_workflow(self, name):
+        client = self._client.get()
+        try:
+            workflow = client.CustomObjectsApi().get_namespaced_custom_object(
+                group=self._group,
+                version=self._version,
+                namespace=self._namespace,
+                plural="workflows",
+                name=name,
+            )
+        except client.rest.ApiException as e:
+            if e.status == 404:
+                return None
+            raise ArgoClientException(
+                json.loads(e.body)["message"] if e.body is not None else e.reason
+            )
+        return workflow
+
     def get_workflow_template(self, name):
         client = self._client.get()
         try:
@@ -34,6 +58,22 @@ def get_workflow_template(self, name):
                 json.loads(e.body)["message"] if e.body is not None else e.reason
             )
 
+    def get_workflow_templates(self):
+        client = self._client.get()
+        try:
+            return client.CustomObjectsApi().list_namespaced_custom_object(
+                group=self._group,
+                version=self._version,
+                namespace=self._namespace,
+                plural="workflowtemplates",
+            )["items"]
+        except client.rest.ApiException as e:
+            if e.status == 404:
+                return None
+            raise ArgoClientException(
+                json.loads(e.body)["message"] if e.body is not None else e.reason
+            )
+
     def register_workflow_template(self, name, workflow_template):
         # Unfortunately, Kubernetes client does not handle optimistic
         # concurrency control by itself unlike kubectl
@@ -86,12 +126,149 @@ def register_workflow_template(self, name, workflow_template):
                 json.loads(e.body)["message"] if e.body is not None else e.reason
             )
 
-    def trigger_workflow_template(self, name, parameters={}):
+    def delete_cronworkflow(self, name):
+        """
+        Issues an API call for deleting a cronworkflow
+
+        Returns either the successful API response, or None in case the resource was not found.
+        """
+        client = self._client.get()
+
+        try:
+            return client.CustomObjectsApi().delete_namespaced_custom_object(
+                group=self._group,
+                version=self._version,
+                namespace=self._namespace,
+                plural="cronworkflows",
+                name=name,
+            )
+        except client.rest.ApiException as e:
+            if e.status == 404:
+                return None
+            else:
+                raise wrap_api_error(e)
+
+    def delete_workflow_template(self, name):
+        """
+        Issues an API call for deleting a cronworkflow
+
+        Returns either the successful API response, or None in case the resource was not found.
+        """
+        client = self._client.get()
+
+        try:
+            return client.CustomObjectsApi().delete_namespaced_custom_object(
+                group=self._group,
+                version=self._version,
+                namespace=self._namespace,
+                plural="workflowtemplates",
+                name=name,
+            )
+        except client.rest.ApiException as e:
+            if e.status == 404:
+                return None
+            else:
+                raise wrap_api_error(e)
+
+    def terminate_workflow(self, name):
+        client = self._client.get()
+        try:
+            workflow = client.CustomObjectsApi().get_namespaced_custom_object(
+                group=self._group,
+                version=self._version,
+                namespace=self._namespace,
+                plural="workflows",
+                name=name,
+            )
+        except client.rest.ApiException as e:
+            raise ArgoClientException(
+                json.loads(e.body)["message"] if e.body is not None else e.reason
+            )
+
+        if workflow["status"]["finishedAt"] is not None:
+            raise ArgoClientException(
+                "Cannot terminate an execution that has already finished."
+            )
+        if workflow["spec"].get("shutdown") == "Terminate":
+            raise ArgoClientException("Execution has already been terminated.")
+
+        try:
+            body = {"spec": workflow["spec"]}
+            body["spec"]["shutdown"] = "Terminate"
+            return client.CustomObjectsApi().patch_namespaced_custom_object(
+                group=self._group,
+                version=self._version,
+                namespace=self._namespace,
+                plural="workflows",
+                name=name,
+                body=body,
+            )
+        except client.rest.ApiException as e:
+            raise ArgoClientException(
+                json.loads(e.body)["message"] if e.body is not None else e.reason
+            )
+
+    def suspend_workflow(self, name):
+        workflow = self.get_workflow(name)
+        if workflow is None:
+            raise ArgoClientException("Execution argo-%s was not found" % name)
+
+        if workflow["status"]["finishedAt"] is not None:
+            raise ArgoClientException(
+                "Cannot suspend an execution that has already finished."
+            )
+        if workflow["spec"].get("suspend") is True:
+            raise ArgoClientException("Execution has already been suspended.")
+
+        body = {"spec": workflow["spec"]}
+        body["spec"]["suspend"] = True
+        return self._patch_workflow(name, body)
+
+    def unsuspend_workflow(self, name):
+        workflow = self.get_workflow(name)
+        if workflow is None:
+            raise ArgoClientException("Execution argo-%s was not found" % name)
+
+        if workflow["status"]["finishedAt"] is not None:
+            raise ArgoClientException(
+                "Cannot unsuspend an execution that has already finished."
+            )
+        if not workflow["spec"].get("suspend", False):
+            raise ArgoClientException("Execution is already proceeding.")
+
+        body = {"spec": workflow["spec"]}
+        body["spec"]["suspend"] = False
+        return self._patch_workflow(name, body)
+
+    def _patch_workflow(self, name, body):
+        client = self._client.get()
+        try:
+            return client.CustomObjectsApi().patch_namespaced_custom_object(
+                group=self._group,
+                version=self._version,
+                namespace=self._namespace,
+                plural="workflows",
+                name=name,
+                body=body,
+            )
+        except client.rest.ApiException as e:
+            raise ArgoClientException(
+                json.loads(e.body)["message"] if e.body is not None else e.reason
+            )
+
+    def trigger_workflow_template(self, name, usertype, username, parameters={}):
         client = self._client.get()
         body = {
             "apiVersion": "argoproj.io/v1alpha1",
             "kind": "Workflow",
-            "metadata": {"generateName": name + "-"},
+            "metadata": {
+                "generateName": name + "-",
+                "annotations": {
+                    "metaflow/triggered_by_user": json.dumps(
+                        {"type": usertype, "name": username}
+                    )
+                },
+            },
             "spec": {
                 "workflowTemplateRef": {"name": name},
                 "arguments": {
@@ -127,6 +304,8 @@ def schedule_workflow_template(self, name, schedule=None, timezone=None):
                 "suspend": schedule is None,
                 "schedule": schedule,
                 "timezone": timezone,
+                "failedJobsHistoryLimit": 10000,  # default is unfortunately 1
+                "successfulJobsHistoryLimit": 10000,  # default is unfortunately 3
                 "workflowSpec": {"workflowTemplateRef": {"name": name}},
             },
         }
@@ -207,8 +386,6 @@ def register_sensor(self, name, sensor=None):
         except client.rest.ApiException as e:
             # Sensor does not exist and we want to add one
             if e.status == 404:
-                if sensor.get("kind") is None:
-                    return
                 try:
                     return client.CustomObjectsApi().create_namespaced_custom_object(
                         group=self._group,
@@ -227,20 +404,6 @@ def register_sensor(self, name, sensor=None):
                 raise ArgoClientException(
                     json.loads(e.body)["message"] if e.body is not None else e.reason
                 )
-        # Since sensors occupy real resources, delete existing sensor if needed
-        if sensor.get("kind") is None:
-            try:
-                return client.CustomObjectsApi().delete_namespaced_custom_object(
-                    group=self._group,
-                    version=self._version,
-                    namespace=self._namespace,
-                    plural="sensors",
-                    name=name,
-                )
-            except client.rest.ApiException as e:
-                raise ArgoClientException(
-                    json.loads(e.body)["message"] if e.body is not None else e.reason
-                )
         try:
             return client.CustomObjectsApi().replace_namespaced_custom_object(
                 group=self._group,
@@ -254,3 +417,38 @@ def register_sensor(self, name, sensor=None):
             raise ArgoClientException(
                 json.loads(e.body)["message"] if e.body is not None else e.reason
             )
+
+    def delete_sensor(self, name):
+        """
+        Issues an API call for deleting a sensor
+
+        Returns either the successful API response, or None in case the resource was not found.
+        """
+        client = self._client.get()
+
+        try:
+            return client.CustomObjectsApi().delete_namespaced_custom_object(
+                group=self._group,
+                version=self._version,
+                namespace=self._namespace,
+                plural="sensors",
+                name=name,
+            )
+        except client.rest.ApiException as e:
+            if e.status == 404:
+                return None
+            raise wrap_api_error(e)
+
+
+def wrap_api_error(error):
+    message = (
+        json.loads(error.body)["message"] if error.body is not None else error.reason
+    )
+    # catch all
+    ex = ArgoClientException(message)
+    if error.status == 404:
+        # usually handled outside this function as most cases want to return None instead.
+        ex = ArgoResourceNotFound(message)
+    if error.status == 403:
+        ex = ArgoNotPermitted(message)
+    return ex
diff --git a/metaflow/plugins/argo/argo_events.py b/metaflow/plugins/argo/argo_events.py
index 94c7c0d5d15..8bacd0b3e9f 100644
--- a/metaflow/plugins/argo/argo_events.py
+++ b/metaflow/plugins/argo/argo_events.py
@@ -7,7 +7,11 @@
 from datetime import datetime
 
 from metaflow.exception import MetaflowException
-from metaflow.metaflow_config import ARGO_EVENTS_WEBHOOK_URL
+from metaflow.metaflow_config import (
+    ARGO_EVENTS_WEBHOOK_AUTH,
+    ARGO_EVENTS_WEBHOOK_URL,
+    SERVICE_HEADERS,
+)
 
 
 class ArgoEventException(MetaflowException):
@@ -15,6 +19,21 @@ class ArgoEventException(MetaflowException):
 
 
 class ArgoEvent(object):
+    """
+    ArgoEvent is a small event, a message, that can be published to Argo Workflows. The
+    event will eventually start all flows which have been previously deployed with `@trigger`
+    to wait for this particular named event.
+
+    Parameters
+    ----------
+    name : str,
+        Name of the event
+    url : str, optional
+        Override the event endpoint from `ARGO_EVENTS_WEBHOOK_URL`.
+    payload : Dict, optional
+        A set of key-value pairs delivered in this event. Used to set parameters of triggered flows.
+    """
+
     def __init__(
         self, name, url=ARGO_EVENTS_WEBHOOK_URL, payload=None, access_token=None
     ):
@@ -25,10 +44,56 @@ def __init__(
         self._access_token = access_token
 
     def add_to_payload(self, key, value):
+        """
+        Add a key-value pair in the payload. This is typically used to set parameters
+        of triggered flows. Often, `key` is the parameter name you want to set to
+        `value`. Overrides any existing value of `key`.
+
+        Parameters
+        ----------
+        key : str
+            Key
+        value : str
+            Value
+        """
+
         self._payload[key] = str(value)
         return self
 
-    def publish(self, payload=None, force=False, ignore_errors=True):
+    def safe_publish(self, payload=None, ignore_errors=True):
+        """
+        Publishes an event when called inside a deployed workflow. Outside a deployed workflow
+        this function does nothing.
+
+        Use this function inside flows to create events safely. As this function is a no-op
+        for local runs, you can safely call it during local development without causing unintended
+        side-effects. It takes effect only when deployed on Argo Workflows.
+
+        Parameters
+        ----------
+        payload : dict
+            Additional key-value pairs to add to the payload.
+        ignore_errors : bool, default True
+            If True, events are created on a best effort basis - errors are silently ignored.
+        """
+
+        return self.publish(payload=payload, force=False, ignore_errors=ignore_errors)
+
+    def publish(self, payload=None, force=True, ignore_errors=True):
+        """
+        Publishes an event.
+
+        Note that the function returns immediately after the event has been sent. It
+        does not wait for flows to start, nor it guarantees that any flows will start.
+
+        Parameters
+        ----------
+        payload : dict
+            Additional key-value pairs to add to the payload.
+        ignore_errors : bool, default True
+            If True, events are created on a best effort basis - errors are silently ignored.
+        """
+
         if payload == None:
             payload = {}
         # Publish event iff forced or running on Argo Workflows
@@ -38,28 +103,29 @@ def publish(self, payload=None, force=False, ignore_errors=True):
                 if self._access_token:
                     # TODO: Test with bearer tokens
                     headers = {"Authorization": "Bearer {}".format(self._access_token)}
+                if ARGO_EVENTS_WEBHOOK_AUTH == "service":
+                    headers.update(SERVICE_HEADERS)
                 # TODO: do we need to worry about certs?
 
                 # Use urllib to avoid introducing any dependency in Metaflow
+                data = {
+                    "name": self._name,
+                    "payload": {
+                        # Add default fields here...
+                        "name": self._name,
+                        "id": str(uuid.uuid4()),
+                        "timestamp": int(time.time()),
+                        "utc_date": datetime.utcnow().strftime("%Y%m%d"),
+                        "generated-by-metaflow": True,
+                        **self._payload,
+                        **payload,
+                    },
+                }
                 request = urllib.request.Request(
                     self._url,
                     method="POST",
                     headers={"Content-Type": "application/json", **headers},
-                    data=json.dumps(
-                        {
-                            "name": self._name,
-                            "payload": {
-                                # Add default fields here...
-                                "name": self._name,
-                                "id": str(uuid.uuid4()),
-                                "timestamp": int(time.time()),
-                                "utc_date": datetime.utcnow().strftime("%Y%m%d"),
-                                "generated-by-metaflow": True,
-                                **self._payload,
-                                **payload,
-                            },
-                        }
-                    ).encode("utf-8"),
+                    data=json.dumps(data).encode("utf-8"),
                 )
                 retries = 3
                 backoff_factor = 2
@@ -70,7 +136,7 @@ def publish(self, payload=None, force=False, ignore_errors=True):
                         print(
                             "Argo Event (%s) published." % self._name, file=sys.stderr
                         )
-                        break
+                        return data["payload"]["id"]
                     except urllib.error.HTTPError as e:
                         # TODO: Retry retryable HTTP error codes
                         raise e
@@ -88,7 +154,7 @@ def publish(self, payload=None, force=False, ignore_errors=True):
         else:
             msg = (
                 "Argo Event (%s) was not published. Use "
-                + "ArgoEvent(...).publish(..., force=True) "
+                + "ArgoEvent(...).publish(...) "
                 + "to force publish."
             ) % self._name
 
diff --git a/metaflow/plugins/argo/argo_workflows.py b/metaflow/plugins/argo/argo_workflows.py
index 51e2aeff0e6..3e6df554d88 100644
--- a/metaflow/plugins/argo/argo_workflows.py
+++ b/metaflow/plugins/argo/argo_workflows.py
@@ -6,19 +6,27 @@
 import sys
 from collections import defaultdict
 from hashlib import sha1
+from math import inf
+from typing import List
 
-from metaflow import current
+from metaflow import JSONType, current
 from metaflow.decorators import flow_decorators
 from metaflow.exception import MetaflowException
+from metaflow.graph import FlowGraph
+from metaflow.includefile import FilePathClass
 from metaflow.metaflow_config import (
     ARGO_EVENTS_EVENT,
     ARGO_EVENTS_EVENT_BUS,
     ARGO_EVENTS_EVENT_SOURCE,
+    ARGO_EVENTS_INTERNAL_WEBHOOK_URL,
     ARGO_EVENTS_SERVICE_ACCOUNT,
-    ARGO_EVENTS_WEBHOOK_URL,
+    ARGO_EVENTS_WEBHOOK_AUTH,
+    ARGO_WORKFLOWS_CAPTURE_ERROR_SCRIPT,
     ARGO_WORKFLOWS_ENV_VARS_TO_SKIP,
     ARGO_WORKFLOWS_KUBERNETES_SECRETS,
+    ARGO_WORKFLOWS_UI_URL,
     AWS_SECRETS_MANAGER_DEFAULT_REGION,
+    AZURE_KEY_VAULT_PREFIX,
     AZURE_STORAGE_BLOB_SERVICE_ENDPOINT,
     CARD_AZUREROOT,
     CARD_GSROOT,
@@ -29,17 +37,25 @@
     DATATOOLS_S3ROOT,
     DEFAULT_METADATA,
     DEFAULT_SECRETS_BACKEND_TYPE,
+    GCP_SECRET_MANAGER_PREFIX,
     KUBERNETES_FETCH_EC2_METADATA,
     KUBERNETES_NAMESPACE,
-    KUBERNETES_NODE_SELECTOR,
     KUBERNETES_SANDBOX_INIT_SCRIPT,
     KUBERNETES_SECRETS,
     S3_ENDPOINT_URL,
+    S3_SERVER_SIDE_ENCRYPTION,
     SERVICE_HEADERS,
     SERVICE_INTERNAL_URL,
+    UI_URL,
 )
+from metaflow.metaflow_config_funcs import config_values
 from metaflow.mflog import BASH_SAVE_LOGS, bash_capture_logs, export_mflog_env_vars
 from metaflow.parameters import deploy_time_eval
+from metaflow.plugins.kubernetes.kube_utils import qos_requests_and_limits
+
+from metaflow.plugins.kubernetes.kubernetes_jobsets import KubernetesArgoJobSet
+from metaflow.unbounded_foreach import UBF_CONTROL, UBF_TASK
+from metaflow.user_configs.config_options import ConfigInput
 from metaflow.util import (
     compress_list,
     dict_to_cli_options,
@@ -49,6 +65,8 @@
 )
 
 from .argo_client import ArgoClient
+from .exit_hooks import ExitHookHack, HttpExitHook, ContainerHook
+from metaflow.util import resolve_identity
 
 
 class ArgoWorkflowsException(MetaflowException):
@@ -62,22 +80,18 @@ class ArgoWorkflowsSchedulingException(MetaflowException):
 # List of future enhancements -
 #     1. Configure Argo metrics.
 #     2. Support resuming failed workflows within Argo Workflows.
-#     3. Support gang-scheduled clusters for distributed PyTorch/TF - One option is to
-#        use volcano - https://github.com/volcano-sh/volcano/tree/master/example/integrations/argo
-#     4. Support GitOps workflows.
-#     5. Add Metaflow tags to labels/annotations.
-#     6. Support Multi-cluster scheduling - https://github.com/argoproj/argo-workflows/issues/3523#issuecomment-792307297
-#     7. Support for workflow notifications.
-#     8. Support R lang.
-#     9. Ping @savin at slack.outerbounds.co for any feature request.
+#     3. Add Metaflow tags to labels/annotations.
+#     4. Support R lang.
+#     5. Ping @savin at slack.outerbounds.co for any feature request
 
 
 class ArgoWorkflows(object):
     def __init__(
         self,
         name,
-        graph,
+        graph: FlowGraph,
         flow,
+        code_package_metadata,
         code_package_sha,
         code_package_url,
         production_token,
@@ -93,6 +107,15 @@ def __init__(
         workflow_timeout=None,
         workflow_priority=None,
         auto_emit_argo_events=False,
+        notify_on_error=False,
+        notify_on_success=False,
+        notify_slack_webhook_url=None,
+        notify_pager_duty_integration_key=None,
+        notify_incident_io_api_key=None,
+        incident_io_alert_source_config_id=None,
+        incident_io_metadata: List[str] = None,
+        enable_heartbeat_daemon=True,
+        enable_error_msg_capture=False,
     ):
         # Some high-level notes -
         #
@@ -121,6 +144,7 @@ def __init__(
         self.name = name
         self.graph = graph
         self.flow = flow
+        self.code_package_metadata = code_package_metadata
         self.code_package_sha = code_package_sha
         self.code_package_url = code_package_url
         self.production_token = production_token
@@ -136,11 +160,24 @@ def __init__(
         self.workflow_timeout = workflow_timeout
         self.workflow_priority = workflow_priority
         self.auto_emit_argo_events = auto_emit_argo_events
-
+        self.notify_on_error = notify_on_error
+        self.notify_on_success = notify_on_success
+        self.notify_slack_webhook_url = notify_slack_webhook_url
+        self.notify_pager_duty_integration_key = notify_pager_duty_integration_key
+        self.notify_incident_io_api_key = notify_incident_io_api_key
+        self.incident_io_alert_source_config_id = incident_io_alert_source_config_id
+        self.incident_io_metadata = self.parse_incident_io_metadata(
+            incident_io_metadata
+        )
+        self.enable_heartbeat_daemon = enable_heartbeat_daemon
+        self.enable_error_msg_capture = enable_error_msg_capture
         self.parameters = self._process_parameters()
+        self.config_parameters = self._process_config_parameters()
         self.triggers, self.trigger_options = self._process_triggers()
         self._schedule, self._timezone = self._get_schedule()
 
+        self._base_labels = self._base_kubernetes_labels()
+        self._base_annotations = self._base_kubernetes_annotations()
         self._workflow_template = self._compile_workflow_template()
         self._sensor = self._compile_sensor()
 
@@ -163,6 +200,116 @@ def _sanitize(name):
         # allowed by Metaflow - guaranteeing uniqueness.
         return name.replace("_", "-")
 
+    @staticmethod
+    def _sensor_name(name):
+        # Unfortunately, Argo Events Sensor names don't allow for
+        # dots (sensors run into an error) which rules out self.name :(
+        return name.replace(".", "-")
+
+    @staticmethod
+    def list_templates(flow_name, all=False):
+        client = ArgoClient(namespace=KUBERNETES_NAMESPACE)
+
+        templates = client.get_workflow_templates()
+        if templates is None:
+            return []
+
+        template_names = [
+            template["metadata"]["name"]
+            for template in templates
+            if all
+            or flow_name
+            == template["metadata"]
+            .get("annotations", {})
+            .get("metaflow/flow_name", None)
+        ]
+        return template_names
+
+    @staticmethod
+    def delete(name):
+        client = ArgoClient(namespace=KUBERNETES_NAMESPACE)
+
+        # Always try to delete the schedule. Failure in deleting the schedule should not
+        # be treated as an error, due to any of the following reasons
+        # - there might not have been a schedule, or it was deleted by some other means
+        # - retaining these resources should have no consequences as long as the workflow deletion succeeds.
+        # - regarding cost and compute, the significant resources are part of the workflow teardown, not the schedule.
+        schedule_deleted = client.delete_cronworkflow(name)
+
+        # The workflow might have sensors attached to it, which consume actual resources.
+        # Try to delete these as well.
+        sensor_deleted = client.delete_sensor(ArgoWorkflows._sensor_name(name))
+
+        # After cleaning up related resources, delete the workflow in question.
+        # Failure in deleting is treated as critical and will be made visible to the user
+        # for further action.
+        workflow_deleted = client.delete_workflow_template(name)
+        if workflow_deleted is None:
+            raise ArgoWorkflowsException(
+                "The workflow *%s* doesn't exist on Argo Workflows." % name
+            )
+
+        return schedule_deleted, sensor_deleted, workflow_deleted
+
+    @classmethod
+    def terminate(cls, flow_name, name):
+        client = ArgoClient(namespace=KUBERNETES_NAMESPACE)
+
+        response = client.terminate_workflow(name)
+        if response is None:
+            raise ArgoWorkflowsException(
+                "No execution found for {flow_name}/{run_id} in Argo Workflows.".format(
+                    flow_name=flow_name, run_id=name
+                )
+            )
+
+    @staticmethod
+    def get_workflow_status(flow_name, name):
+        client = ArgoClient(namespace=KUBERNETES_NAMESPACE)
+        # TODO: Only look for workflows for the specified flow
+        workflow = client.get_workflow(name)
+        if workflow:
+            # return workflow phase for now
+            status = workflow.get("status", {}).get("phase")
+            return status
+        else:
+            raise ArgoWorkflowsException(
+                "No execution found for {flow_name}/{run_id} in Argo Workflows.".format(
+                    flow_name=flow_name, run_id=name
+                )
+            )
+
+    @staticmethod
+    def suspend(name):
+        client = ArgoClient(namespace=KUBERNETES_NAMESPACE)
+
+        client.suspend_workflow(name)
+
+        return True
+
+    @staticmethod
+    def unsuspend(name):
+        client = ArgoClient(namespace=KUBERNETES_NAMESPACE)
+
+        client.unsuspend_workflow(name)
+
+        return True
+
+    @staticmethod
+    def parse_incident_io_metadata(metadata: List[str] = None):
+        "parse key value pairs into a dict for incident.io metadata if given"
+        parsed_metadata = None
+        if metadata is not None:
+            parsed_metadata = {}
+            for kv in metadata:
+                key, value = kv.split("=", 1)
+                if key in parsed_metadata:
+                    raise MetaflowException(
+                        "Incident.io Metadata *%s* provided multiple times" % key
+                    )
+                parsed_metadata[key] = value
+        return parsed_metadata
+
     @classmethod
     def trigger(cls, name, parameters=None):
         if parameters is None:
@@ -182,7 +329,7 @@ def trigger(cls, name, parameters=None):
             try:
                 # Check that the workflow was deployed through Metaflow
                 workflow_template["metadata"]["annotations"]["metaflow/owner"]
-            except KeyError as e:
+            except KeyError:
                 raise ArgoWorkflowsException(
                     "An existing non-metaflow workflow with the same name as "
                     "*%s* already exists in Argo Workflows. \nPlease modify the "
@@ -190,12 +337,57 @@ def trigger(cls, name, parameters=None):
                     "Workflows before proceeding." % name
                 )
         try:
+            id_parts = resolve_identity().split(":")
+            parts_size = len(id_parts)
+            usertype = id_parts[0] if parts_size > 0 else "unknown"
+            username = id_parts[1] if parts_size > 1 else "unknown"
+
             return ArgoClient(namespace=KUBERNETES_NAMESPACE).trigger_workflow_template(
-                name, parameters
+                name,
+                usertype,
+                username,
+                parameters,
             )
         except Exception as e:
             raise ArgoWorkflowsException(str(e))
 
+    def _base_kubernetes_labels(self):
+        """
+        Get shared Kubernetes labels for Argo resources.
+        """
+        # TODO: Add configuration through an environment variable or Metaflow config in the future if required.
+        labels = {"app.kubernetes.io/part-of": "metaflow"}
+
+        return labels
+
+    def _base_kubernetes_annotations(self):
+        """
+        Get shared Kubernetes annotations for Argo resources.
+        """
+        from datetime import datetime, timezone
+
+        # TODO: Add configuration through an environment variable or Metaflow config in the future if required.
+        # base annotations
+        annotations = {
+            "metaflow/production_token": self.production_token,
+            "metaflow/owner": self.username,
+            "metaflow/user": "argo-workflows",
+            "metaflow/flow_name": self.flow.name,
+            "metaflow/deployment_timestamp": str(
+                datetime.now(timezone.utc).isoformat()
+            ),
+        }
+
+        if current.get("project_name"):
+            annotations.update(
+                {
+                    "metaflow/project_name": current.project_name,
+                    "metaflow/branch_name": current.branch_name,
+                    "metaflow/project_flow_name": current.project_flow_name,
+                }
+            )
+        return annotations
+
     def _get_schedule(self):
         schedule = self.flow._flow_decorators.get("schedule")
         if schedule:
@@ -206,16 +398,19 @@ def _get_schedule(self):
 
     def schedule(self):
         try:
-            ArgoClient(namespace=KUBERNETES_NAMESPACE).schedule_workflow_template(
+            argo_client = ArgoClient(namespace=KUBERNETES_NAMESPACE)
+            argo_client.schedule_workflow_template(
                 self.name, self._schedule, self._timezone
             )
-            # Register sensor. Unfortunately, Argo Events Sensor names don't allow for
-            # dots (sensors run into an error) which rules out self.name :(
+            # Register sensor.
             # Metaflow will overwrite any existing sensor.
-            ArgoClient(namespace=KUBERNETES_NAMESPACE).register_sensor(
-                self.name.replace(".", "-"),
-                self._sensor.to_json() if self._sensor else {},
-            )
+            sensor_name = ArgoWorkflows._sensor_name(self.name)
+            if self._sensor:
+                argo_client.register_sensor(sensor_name, self._sensor.to_json())
+            else:
+                # Since sensors occupy real resources, delete existing sensor if needed
+                # Deregister sensors that might have existed before this deployment
+                argo_client.delete_sensor(sensor_name)
         except Exception as e:
             raise ArgoWorkflowsSchedulingException(str(e))
 
@@ -267,7 +462,7 @@ def get_existing_deployment(cls, name):
                         "metaflow/production_token"
                     ],
                 )
-            except KeyError as e:
+            except KeyError:
                 raise ArgoWorkflowsException(
                     "An existing non-metaflow workflow with the same name as "
                     "*%s* already exists in Argo Workflows. \nPlease modify the "
@@ -276,6 +471,29 @@ def get_existing_deployment(cls, name):
                 )
         return None
 
+    @classmethod
+    def get_execution(cls, name):
+        workflow = ArgoClient(namespace=KUBERNETES_NAMESPACE).get_workflow(name)
+        if workflow is not None:
+            try:
+                return (
+                    workflow["metadata"]["annotations"]["metaflow/owner"],
+                    workflow["metadata"]["annotations"]["metaflow/production_token"],
+                    workflow["metadata"]["annotations"]["metaflow/flow_name"],
+                    workflow["metadata"]["annotations"].get(
+                        "metaflow/branch_name", None
+                    ),
+                    workflow["metadata"]["annotations"].get(
+                        "metaflow/project_name", None
+                    ),
+                )
+            except KeyError:
+                raise ArgoWorkflowsException(
+                    "A non-metaflow workflow *%s* already exists in Argo Workflows."
+                    % name
+                )
+        return None
+
     def _process_parameters(self):
         parameters = {}
         has_schedule = self.flow._flow_decorators.get("schedule") is not None
@@ -290,6 +508,24 @@ def _process_parameters(self):
                     "case-insensitive." % param.name
                 )
             seen.add(norm)
+            # NOTE: We skip config parameters as these do not have dynamic values,
+            # and need to be treated differently.
+            if param.IS_CONFIG_PARAMETER:
+                continue
+
+            extra_attrs = {}
+            if param.kwargs.get("type") == JSONType:
+                param_type = str(param.kwargs.get("type").name)
+            elif isinstance(param.kwargs.get("type"), FilePathClass):
+                param_type = str(param.kwargs.get("type").name)
+                extra_attrs["is_text"] = getattr(
+                    param.kwargs.get("type"), "_is_text", True
+                )
+                extra_attrs["encoding"] = getattr(
+                    param.kwargs.get("type"), "_encoding", "utf-8"
+                )
+            else:
+                param_type = str(param.kwargs.get("type").__name__)
 
             is_required = param.kwargs.get("required", False)
             # Throw an exception if a schedule is set for a flow with required
@@ -302,18 +538,43 @@ def _process_parameters(self):
                     "Scheduling such parameters via Argo CronWorkflows is not "
                     "currently supported." % param.name
                 )
-            value = deploy_time_eval(param.kwargs.get("default"))
+            default_value = deploy_time_eval(param.kwargs.get("default"))
             # If the value is not required and the value is None, we set the value to
             # the JSON equivalent of None to please argo-workflows. Unfortunately it
             # has the side effect of casting the parameter value to string null during
             # execution - which needs to be fixed imminently.
-            if not is_required or value is not None:
-                value = json.dumps(value)
+            if not is_required or default_value is not None:
+                default_value = json.dumps(default_value)
+
             parameters[param.name] = dict(
+                python_var_name=var,
                 name=param.name,
-                value=value,
+                value=default_value,
+                type=param_type,
                 description=param.kwargs.get("help"),
                 is_required=is_required,
+                **extra_attrs,
+            )
+        return parameters
+
+    def _process_config_parameters(self):
+        parameters = []
+        seen = set()
+        for var, param in self.flow._get_parameters():
+            if not param.IS_CONFIG_PARAMETER:
+                continue
+            # Throw an exception if the parameter is specified twice.
+            norm = param.name.lower()
+            if norm in seen:
+                raise MetaflowException(
+                    "Parameter *%s* is specified twice. "
+                    "Note that parameter names are "
+                    "case-insensitive." % param.name
+                )
+            seen.add(norm)
+
+            parameters.append(
+                dict(name=param.name, kv_name=ConfigInput.make_key_name(param.name))
             )
         return parameters
 
@@ -335,14 +596,21 @@ def _process_triggers(self):
 
         # @trigger decorator
         if self.flow._flow_decorators.get("trigger"):
-            # Parameters are not duplicated, and exist in the flow.
-            # additionally, convert them to lower case since metaflow parameters are
-            # case insensitive.
+            # Parameters are not duplicated, and exist in the flow. Additionally,
+            # convert them to lower case since Metaflow parameters are case
+            # insensitive.
             seen = set()
+            # NOTE: We skip config parameters as their values can not be set through event payloads
             params = set(
-                [param.name.lower() for var, param in self.flow._get_parameters()]
+                [
+                    param.name.lower()
+                    for var, param in self.flow._get_parameters()
+                    if not param.IS_CONFIG_PARAMETER
+                ]
             )
-            for event in self.flow._flow_decorators.get("trigger")[0].triggers:
+            trigger_deco = self.flow._flow_decorators.get("trigger")[0]
+            trigger_deco.format_deploytime_value()
+            for event in trigger_deco.triggers:
                 parameters = {}
                 # TODO: Add a check to guard against names starting with numerals(?)
                 if not re.match(r"^[A-Za-z0-9_.-]+$", event["name"]):
@@ -375,36 +643,48 @@ def _process_triggers(self):
             triggers.extend(self.flow._flow_decorators.get("trigger")[0].triggers)
 
             # Set automatic parameter mapping iff only a single event dependency is
-            # specified with no explicit parameter mapping
+            # specified with no explicit parameter mapping.
             if len(triggers) == 1 and not triggers[0].get("parameters"):
                 triggers[0]["parameters"] = dict(zip(params, params))
             options = self.flow._flow_decorators.get("trigger")[0].options
 
         # @trigger_on_finish decorator
         if self.flow._flow_decorators.get("trigger_on_finish"):
-            for event in self.flow._flow_decorators.get("trigger_on_finish")[
-                0
-            ].triggers:
+            trigger_on_finish_deco = self.flow._flow_decorators.get(
+                "trigger_on_finish"
+            )[0]
+            trigger_on_finish_deco.format_deploytime_value()
+            for event in trigger_on_finish_deco.triggers:
                 # Actual filters are deduced here since we don't have access to
                 # the current object in the @trigger_on_finish decorator.
+                project_name = event.get("project") or current.get("project_name")
+                branch_name = event.get("branch") or current.get("branch_name")
+                # validate that we have complete project info for an event name
+                if project_name or branch_name:
+                    if not (project_name and branch_name):
+                        # if one of the two is missing, we would end up listening to an event that will never be broadcast.
+                        raise ArgoWorkflowsException(
+                            "Incomplete project info. Please specify both 'project' and 'project_branch' or use the @project decorator"
+                        )
+
                 triggers.append(
                     {
+                        # Make sure this remains consistent with the event name format
+                        # in ArgoWorkflowsInternalDecorator.
                         "name": "metaflow.%s.end"
                         % ".".join(
                             v
                             for v in [
-                                event.get("project") or current.get("project_name"),
-                                event.get("branch") or current.get("branch_name"),
+                                project_name,
+                                branch_name,
                                 event["flow"],
                             ]
                             if v
                         ),
                         "filters": {
                             "auto-generated-by-metaflow": True,
-                            "project_name": event.get("project")
-                            or current.get("project_name"),
-                            "branch_name": event.get("branch")
-                            or current.get("branch_name"),
+                            "project_name": project_name,
+                            "branch_name": branch_name,
                             # TODO: Add a time filters to guard against cached events
                         },
                         "type": "run",
@@ -417,14 +697,16 @@ def _process_triggers(self):
             # Assign a sanitized name since we need this at many places to please
             # Argo Events sensors. There is a slight possibility of name collision
             # but quite unlikely for us to worry about at this point.
-            event["sanitized_name"] = event["name"]
-            if any([x in event["name"] for x in [".", "-"]]):
-                event["sanitized_name"] = "%s_%s" % (
-                    event["name"].replace(".", "").replace("-", ""),
-                    to_unicode(
-                        base64.b32encode(sha1(to_bytes(event["name"])).digest())
-                    )[:4].lower(),
-                )
+            event["sanitized_name"] = "%s_%s" % (
+                event["name"]
+                .replace(".", "")
+                .replace("-", "")
+                .replace("@", "")
+                .replace("+", ""),
+                to_unicode(base64.b32encode(sha1(to_bytes(event["name"])).digest()))[
+                    :4
+                ].lower(),
+            )
         return triggers, options
 
     def _compile_workflow_template(self):
@@ -454,21 +736,69 @@ def _compile_workflow_template(self):
         # generate container templates at the top level (in WorkflowSpec) and maintain
         # references to them within the DAGTask.
 
-        annotations = {
-            "metaflow/production_token": self.production_token,
-            "metaflow/owner": self.username,
-            "metaflow/user": "argo-workflows",
-            "metaflow/flow_name": self.flow.name,
-        }
-        if current.get("project_name"):
+        annotations = {}
+        if self._schedule is not None:
+            # timezone is an optional field and json dumps on None will result in null
+            # hence configuring it to an empty string
+            if self._timezone is None:
+                self._timezone = ""
+            cron_info = {"schedule": self._schedule, "tz": self._timezone}
+            annotations.update({"metaflow/cron": json.dumps(cron_info)})
+
+        if self.parameters:
+            annotations.update({"metaflow/parameters": json.dumps(self.parameters)})
+
+        # Some more annotations to populate the Argo UI nicely
+        if self.tags:
+            annotations.update({"metaflow/tags": json.dumps(self.tags)})
+        if self.triggers:
             annotations.update(
                 {
-                    "metaflow/project_name": current.project_name,
-                    "metaflow/branch_name": current.branch_name,
-                    "metaflow/project_flow_name": current.project_flow_name,
+                    "metaflow/triggers": json.dumps(
+                        [
+                            {key: trigger.get(key) for key in ["name", "type"]}
+                            for trigger in self.triggers
+                        ]
+                    )
+                }
+            )
+        if self.notify_on_error:
+            annotations.update(
+                {
+                    "metaflow/notify_on_error": json.dumps(
+                        {
+                            "slack": bool(self.notify_slack_webhook_url),
+                            "pager_duty": bool(self.notify_pager_duty_integration_key),
+                            "incident_io": bool(self.notify_incident_io_api_key),
+                        }
+                    )
+                }
+            )
+        if self.notify_on_success:
+            annotations.update(
+                {
+                    "metaflow/notify_on_success": json.dumps(
+                        {
+                            "slack": bool(self.notify_slack_webhook_url),
+                            "pager_duty": bool(self.notify_pager_duty_integration_key),
+                            "incident_io": bool(self.notify_incident_io_api_key),
+                        }
+                    )
                 }
             )
+        try:
+            # Build the DAG based on the DAGNodes given by the FlowGraph for the found FlowSpec class.
+            _steps_info, graph_structure = self.graph.output_steps()
+            graph_info = {
+                # for the time being, we only need the graph_structure. Being mindful of annotation size limits we do not include anything extra.
+                "graph_structure": graph_structure
+            }
+        except Exception:
+            graph_info = None
 
+        dag_annotation = {"metaflow/dag": json.dumps(graph_info)}
+
+        lifecycle_hooks = self._lifecycle_hooks()
         return (
             WorkflowTemplate()
             .metadata(
@@ -479,9 +809,11 @@ def _compile_workflow_template(self):
                 # is released, we should be able to support multi-namespace /
                 # multi-cluster scheduling.
                 .namespace(KUBERNETES_NAMESPACE)
-                .label("app.kubernetes.io/name", "metaflow-flow")
-                .label("app.kubernetes.io/part-of", "metaflow")
                 .annotations(annotations)
+                .annotations(self._base_annotations)
+                .labels(self._base_labels)
+                .label("app.kubernetes.io/name", "metaflow-flow")
+                .annotations(dag_annotation)
             )
             .spec(
                 WorkflowSpec()
@@ -511,10 +843,14 @@ def _compile_workflow_template(self):
                 # Set workflow metadata
                 .workflow_metadata(
                     Metadata()
+                    .labels(self._base_labels)
                     .label("app.kubernetes.io/name", "metaflow-run")
-                    .label("app.kubernetes.io/part-of", "metaflow")
                     .annotations(
-                        {**annotations, **{"metaflow/run_id": "argo-{{workflow.name}}"}}
+                        {
+                            **annotations,
+                            **self._base_annotations,
+                            **{"metaflow/run_id": "argo-{{workflow.name}}"},
+                        }
                     )
                     # TODO: Set dynamic labels using labels_from. Ideally, we would
                     #       want to expose run_id as a label. It's easy to add labels,
@@ -527,7 +863,11 @@ def _compile_workflow_template(self):
                     Arguments().parameters(
                         [
                             Parameter(parameter["name"])
-                            .value(parameter["value"])
+                            .value(
+                                "'%s'" % parameter["value"]
+                                if parameter["type"] == "JSON"
+                                else parameter["value"]
+                            )
                             .description(parameter.get("description"))
                             # TODO: Better handle IncludeFile in Argo Workflows UI.
                             for parameter in self.parameters.values()
@@ -547,22 +887,57 @@ def _compile_workflow_template(self):
                 # Set common pod metadata.
                 .pod_metadata(
                     Metadata()
+                    .labels(self._base_labels)
                     .label("app.kubernetes.io/name", "metaflow-task")
-                    .label("app.kubernetes.io/part-of", "metaflow")
-                    .annotations(annotations)
+                    .annotations(
+                        {
+                            **annotations,
+                            **self._base_annotations,
+                            **{
+                                "metaflow/run_id": "argo-{{workflow.name}}"
+                            },  # we want pods of the workflow to have the run_id as an annotation as well
+                        }
+                    )
                 )
                 # Set the entrypoint to flow name
                 .entrypoint(self.flow.name)
+                # OnExit hooks
+                .onExit(
+                    "capture-error-hook-fn-preflight"
+                    if self.enable_error_msg_capture
+                    else None
+                )
+                # Set lifecycle hooks if notifications are enabled
+                .hooks(
+                    {
+                        lifecycle.name: lifecycle
+                        for hook in lifecycle_hooks
+                        for lifecycle in hook.lifecycle_hooks
+                    }
+                )
                 # Top-level DAG template(s)
                 .templates(self._dag_templates())
                 # Container templates
                 .templates(self._container_templates())
+                # Lifecycle hook template(s)
+                .templates([hook.template for hook in lifecycle_hooks])
+                # Exit hook template(s)
+                .templates(self._exit_hook_templates())
+                # Sidecar templates (Daemon Containers)
+                .templates(self._daemon_templates())
             )
         )
 
     # Visit every node and yield the uber DAGTemplate(s).
     def _dag_templates(self):
-        def _visit(node, exit_node=None, templates=None, dag_tasks=None):
+        def _visit(
+            node,
+            exit_node=None,
+            templates=None,
+            dag_tasks=None,
+            parent_foreach=None,
+        ):  # Returns Tuple[List[Template], List[DAGTask]]
+            """ """
             # Every for-each node results in a separate subDAG and an equivalent
             # DAGTemplate rooted at the child of the for-each node. Each DAGTemplate
             # has a unique name - the top-level DAGTemplate is named as the name of
@@ -576,7 +951,6 @@ def _visit(node, exit_node=None, templates=None, dag_tasks=None):
                 templates = []
             if exit_node is not None and exit_node is node.name:
                 return templates, dag_tasks
-
             if node.name == "start":
                 # Start node has no dependencies.
                 dag_task = DAGTask(self._sanitize(node.name)).template(
@@ -585,13 +959,86 @@ def _visit(node, exit_node=None, templates=None, dag_tasks=None):
             elif (
                 node.is_inside_foreach
                 and self.graph[node.in_funcs[0]].type == "foreach"
+                and not self.graph[node.in_funcs[0]].parallel_foreach
+                # We need to distinguish what is a "regular" foreach (i.e something that doesn't care about to gang semantics)
+                # vs what is a "num_parallel" based foreach (i.e. something that follows gang semantics.)
+                # A `regular` foreach is basically any arbitrary kind of foreach.
             ):
                 # Child of a foreach node needs input-paths as well as split-index
                 # This child is the first node of the sub workflow and has no dependency
+
+                parameters = [
+                    Parameter("input-paths").value("{{inputs.parameters.input-paths}}"),
+                    Parameter("split-index").value("{{inputs.parameters.split-index}}"),
+                ]
+                dag_task = (
+                    DAGTask(self._sanitize(node.name))
+                    .template(self._sanitize(node.name))
+                    .arguments(Arguments().parameters(parameters))
+                )
+            elif node.parallel_step:
+                # This is the step where the @parallel decorator is defined.
+                # Since this DAGTask will call the for the `resource` [based templates]
+                # (https://argo-workflows.readthedocs.io/en/stable/walk-through/kubernetes-resources/)
+                # we have certain constraints on the way we can pass information inside the Jobset manifest
+                # [All templates will have access](https://argo-workflows.readthedocs.io/en/stable/variables/#all-templates)
+                # to the `inputs.parameters` so we will pass down ANY/ALL information using the
+                # input parameters.
+                # We define the usual parameters like input-paths/split-index etc. but we will also
+                # define the following:
+                # - `workerCount`:  parameter which will be used to determine the number of
+                #                   parallel worker jobs
+                # - `jobset-name`:  parameter which will be used to determine the name of the jobset.
+                #                   This parameter needs to be dynamic so that when we have retries we don't
+                #                   end up using the name of the jobset again (if we do, it will crash since k8s wont allow duplicated job names)
+                # - `retryCount`:   parameter which will be used to determine the number of retries
+                #                   This parameter will *only* be available within the container templates like we
+                #                   have it for all other DAGTasks and NOT for custom kubernetes resource templates.
+                #                   So as a work-around, we will set it as the `retryCount` parameter instead of
+                #                   setting it as a {{ retries }} in the CLI code. Once set as a input parameter,
+                #                   we can use it in the Jobset Manifest templates as `{{inputs.parameters.retryCount}}`
+                # - `task-id-entropy`: This is a parameter which will help derive task-ids and jobset names. This parameter
+                #                   contains the relevant amount of entropy to ensure that task-ids and jobset names
+                #                   are uniquish. We will also use this in the join task to construct the task-ids of
+                #                   all parallel tasks since the task-ids for parallel task are minted formulaically.
                 parameters = [
                     Parameter("input-paths").value("{{inputs.parameters.input-paths}}"),
+                    Parameter("num-parallel").value(
+                        "{{inputs.parameters.num-parallel}}"
+                    ),
                     Parameter("split-index").value("{{inputs.parameters.split-index}}"),
+                    Parameter("task-id-entropy").value(
+                        "{{inputs.parameters.task-id-entropy}}"
+                    ),
+                    # we cant just use hyphens with sprig.
+                    # https://github.com/argoproj/argo-workflows/issues/10567#issuecomment-1452410948
+                    Parameter("workerCount").value(
+                        "{{=sprig.int(sprig.sub(sprig.int(inputs.parameters['num-parallel']),1))}}"
+                    ),
                 ]
+                if any(d.name == "retry" for d in node.decorators):
+                    parameters.extend(
+                        [
+                            Parameter("retryCount").value("{{retries}}"),
+                            # The job-setname needs to be unique for each retry
+                            # and we cannot use the `generateName` field in the
+                            # Jobset Manifest since we need to construct the subdomain
+                            # and control pod domain name pre-hand. So we will use
+                            # the retry count to ensure that the jobset name is unique
+                            Parameter("jobset-name").value(
+                                "js-{{inputs.parameters.task-id-entropy}}{{retries}}",
+                            ),
+                        ]
+                    )
+                else:
+                    parameters.extend(
+                        [
+                            Parameter("jobset-name").value(
+                                "js-{{inputs.parameters.task-id-entropy}}",
+                            )
+                        ]
+                    )
+
                 dag_task = (
                     DAGTask(self._sanitize(node.name))
                     .template(self._sanitize(node.name))
@@ -606,10 +1053,43 @@ def _visit(node, exit_node=None, templates=None, dag_tasks=None):
                                 "argo-{{workflow.name}}/%s/{{tasks.%s.outputs.parameters.task-id}}"
                                 % (n, self._sanitize(n))
                                 for n in node.in_funcs
-                            ]
+                            ],
+                            # NOTE: We set zlibmin to infinite because zlib compression for the Argo input-paths breaks template value substitution.
+                            zlibmin=inf,
                         )
                     )
                 ]
+                # NOTE: Due to limitations with Argo Workflows Parameter size we
+                #       can not pass arbitrarily large lists of task id's to join tasks.
+                #       Instead we ensure that task id's for foreach tasks can be
+                #       deduced deterministically and pass the relevant information to
+                #       the join task.
+                #
+                #       We need to add the split-index and root-input-path for the last
+                #       step in any foreach scope and use these to generate the task id,
+                #       as the join step uses the root and the cardinality of the
+                #       foreach scope to generate the required id's.
+                if (
+                    node.is_inside_foreach
+                    and self.graph[node.out_funcs[0]].type == "join"
+                ):
+                    if any(
+                        self.graph[parent].matching_join
+                        == self.graph[node.out_funcs[0]].name
+                        and self.graph[parent].type == "foreach"
+                        for parent in self.graph[node.out_funcs[0]].split_parents
+                    ):
+                        parameters.extend(
+                            [
+                                Parameter("split-index").value(
+                                    "{{inputs.parameters.split-index}}"
+                                ),
+                                Parameter("root-input-path").value(
+                                    "{{inputs.parameters.input-paths}}"
+                                ),
+                            ]
+                        )
+
                 dag_task = (
                     DAGTask(self._sanitize(node.name))
                     .dependencies(
@@ -618,8 +1098,8 @@ def _visit(node, exit_node=None, templates=None, dag_tasks=None):
                     .template(self._sanitize(node.name))
                     .arguments(Arguments().parameters(parameters))
                 )
-            dag_tasks.append(dag_task)
 
+            dag_tasks.append(dag_task)
             # End the workflow if we have reached the end of the flow
             if node.type == "end":
                 return [
@@ -630,19 +1110,45 @@ def _visit(node, exit_node=None, templates=None, dag_tasks=None):
             # For split nodes traverse all the children
             if node.type == "split":
                 for n in node.out_funcs:
-                    _visit(self.graph[n], node.matching_join, templates, dag_tasks)
+                    _visit(
+                        self.graph[n],
+                        node.matching_join,
+                        templates,
+                        dag_tasks,
+                        parent_foreach,
+                    )
                 return _visit(
-                    self.graph[node.matching_join], exit_node, templates, dag_tasks
+                    self.graph[node.matching_join],
+                    exit_node,
+                    templates,
+                    dag_tasks,
+                    parent_foreach,
                 )
             # For foreach nodes generate a new sub DAGTemplate
+            # We do this for "regular" foreaches (ie. `self.next(self.a, foreach=)`)
             elif node.type == "foreach":
                 foreach_template_name = self._sanitize(
                     "%s-foreach-%s"
                     % (
                         node.name,
-                        node.foreach_param,
+                        "parallel" if node.parallel_foreach else node.foreach_param,
+                        # Since foreach's are derived based on `self.next(self.a, foreach="")`
+                        # vs @parallel foreach are done based on `self.next(self.a, num_parallel="")`,
+                        # we need to ensure that `foreach_template_name` suffix is appropriately set based on the kind
+                        # of foreach.
                     )
                 )
+
+                # There are two separate "DAGTask"s created for the foreach node.
+                # - The first one is a "jump-off" DAGTask where we propagate the
+                # input-paths and split-index. This thing doesn't create
+                # any actual containers and it responsible for only propagating
+                # the parameters.
+                # - The DAGTask that follows first DAGTask is the one
+                # that uses the ContainerTemplate. This DAGTask is named the same
+                # thing as the foreach node. We will leverage a similar pattern for the
+                # @parallel tasks.
+                #
                 foreach_task = (
                     DAGTask(foreach_template_name)
                     .dependencies([self._sanitize(node.name)])
@@ -656,27 +1162,76 @@ def _visit(node, exit_node=None, templates=None, dag_tasks=None):
                                 ),
                                 Parameter("split-index").value("{{item}}"),
                             ]
+                            + (
+                                [
+                                    Parameter("root-input-path").value(
+                                        "argo-{{workflow.name}}/%s/{{tasks.%s.outputs.parameters.task-id}}"
+                                        % (node.name, self._sanitize(node.name))
+                                    ),
+                                ]
+                                if parent_foreach
+                                else []
+                            )
+                            + (
+                                # Disabiguate parameters for a regular `foreach` vs a `@parallel` foreach
+                                [
+                                    Parameter("num-parallel").value(
+                                        "{{tasks.%s.outputs.parameters.num-parallel}}"
+                                        % self._sanitize(node.name)
+                                    ),
+                                    Parameter("task-id-entropy").value(
+                                        "{{tasks.%s.outputs.parameters.task-id-entropy}}"
+                                        % self._sanitize(node.name)
+                                    ),
+                                ]
+                                if node.parallel_foreach
+                                else []
+                            )
                         )
                     )
                     .with_param(
+                        # For @parallel workloads `num-splits` will be explicitly set to one so that
+                        # we can piggyback on the current mechanism with which we leverage argo.
                         "{{tasks.%s.outputs.parameters.num-splits}}"
                         % self._sanitize(node.name)
                     )
                 )
                 dag_tasks.append(foreach_task)
                 templates, dag_tasks_1 = _visit(
-                    self.graph[node.out_funcs[0]], node.matching_join, templates, []
+                    self.graph[node.out_funcs[0]],
+                    node.matching_join,
+                    templates,
+                    [],
+                    node.name,
                 )
+
+                # How do foreach's work on Argo:
+                # Lets say you have the following dag: (start[sets `foreach="x"`]) --> (task-a [actual foreach]) --> (join) --> (end)
+                # With argo we will :
+                # (start [sets num-splits]) --> (task-a-foreach-(0,0) [dummy task]) --> (task-a) --> (join) --> (end)
+                # The (task-a-foreach-(0,0) [dummy task]) propagates the values of the `split-index` and the input paths.
+                # to the actual foreach task.
                 templates.append(
                     Template(foreach_template_name)
                     .inputs(
                         Inputs().parameters(
                             [Parameter("input-paths"), Parameter("split-index")]
+                            + ([Parameter("root-input-path")] if parent_foreach else [])
+                            + (
+                                [
+                                    Parameter("num-parallel"),
+                                    Parameter("task-id-entropy"),
+                                    # Parameter("workerCount")
+                                ]
+                                if node.parallel_foreach
+                                else []
+                            )
                         )
                     )
                     .outputs(
                         Outputs().parameters(
                             [
+                                # non @parallel tasks set task-ids as outputs
                                 Parameter("task-id").valueFrom(
                                     {
                                         "parameter": "{{tasks.%s.outputs.parameters.task-id}}"
@@ -686,25 +1241,76 @@ def _visit(node, exit_node=None, templates=None, dag_tasks=None):
                                     }
                                 )
                             ]
+                            if not node.parallel_foreach
+                            else [
+                                # @parallel tasks set `task-id-entropy` and `num-parallel`
+                                # as outputs so task-ids can be derived in the join step.
+                                # Both of these values should be propagated from the
+                                # jobset labels.
+                                Parameter("num-parallel").valueFrom(
+                                    {
+                                        "parameter": "{{tasks.%s.outputs.parameters.num-parallel}}"
+                                        % self._sanitize(
+                                            self.graph[node.matching_join].in_funcs[0]
+                                        )
+                                    }
+                                ),
+                                Parameter("task-id-entropy").valueFrom(
+                                    {
+                                        "parameter": "{{tasks.%s.outputs.parameters.task-id-entropy}}"
+                                        % self._sanitize(
+                                            self.graph[node.matching_join].in_funcs[0]
+                                        )
+                                    }
+                                ),
+                            ]
                         )
                     )
                     .dag(DAGTemplate().fail_fast().tasks(dag_tasks_1))
                 )
+
                 join_foreach_task = (
                     DAGTask(self._sanitize(self.graph[node.matching_join].name))
                     .template(self._sanitize(self.graph[node.matching_join].name))
                     .dependencies([foreach_template_name])
                     .arguments(
                         Arguments().parameters(
-                            [
-                                Parameter("input-paths").value(
-                                    "argo-{{workflow.name}}/%s/{{tasks.%s.outputs.parameters}}"
-                                    % (
-                                        self.graph[node.matching_join].in_funcs[-1],
-                                        foreach_template_name,
-                                    )
-                                )
-                            ]
+                            (
+                                [
+                                    Parameter("input-paths").value(
+                                        "argo-{{workflow.name}}/%s/{{tasks.%s.outputs.parameters.task-id}}"
+                                        % (node.name, self._sanitize(node.name))
+                                    ),
+                                    Parameter("split-cardinality").value(
+                                        "{{tasks.%s.outputs.parameters.split-cardinality}}"
+                                        % self._sanitize(node.name)
+                                    ),
+                                ]
+                                if not node.parallel_foreach
+                                else [
+                                    Parameter("num-parallel").value(
+                                        "{{tasks.%s.outputs.parameters.num-parallel}}"
+                                        % self._sanitize(node.name)
+                                    ),
+                                    Parameter("task-id-entropy").value(
+                                        "{{tasks.%s.outputs.parameters.task-id-entropy}}"
+                                        % self._sanitize(node.name)
+                                    ),
+                                ]
+                            )
+                            + (
+                                [
+                                    Parameter("split-index").value(
+                                        # TODO : Pass down these parameters to the jobset stuff.
+                                        "{{inputs.parameters.split-index}}"
+                                    ),
+                                    Parameter("root-input-path").value(
+                                        "{{inputs.parameters.input-paths}}"
+                                    ),
+                                ]
+                                if parent_foreach
+                                else []
+                            )
                         )
                     )
                 )
@@ -714,11 +1320,16 @@ def _visit(node, exit_node=None, templates=None, dag_tasks=None):
                     exit_node,
                     templates,
                     dag_tasks,
+                    parent_foreach,
                 )
             # For linear nodes continue traversing to the next node
             if node.type in ("linear", "join", "start"):
                 return _visit(
-                    self.graph[node.out_funcs[0]], exit_node, templates, dag_tasks
+                    self.graph[node.out_funcs[0]],
+                    exit_node,
+                    templates,
+                    dag_tasks,
+                    parent_foreach,
                 )
             else:
                 raise ArgoWorkflowsException(
@@ -726,7 +1337,13 @@ def _visit(node, exit_node=None, templates=None, dag_tasks=None):
                     "Argo Workflows." % (node.type, node.name)
                 )
 
-        templates, _ = _visit(node=self.graph["start"])
+        # Generate daemon tasks
+        daemon_tasks = [
+            DAGTask("%s-task" % daemon_template.name).template(daemon_template.name)
+            for daemon_template in self._daemon_templates()
+        ]
+
+        templates, _ = _visit(node=self.graph["start"], dag_tasks=daemon_tasks)
         return templates
 
     # Visit every node and yield ContainerTemplates.
@@ -761,17 +1378,69 @@ def _container_templates(self):
             # Ideally, we would like these task ids to be the same as node name
             # (modulo retry suffix) on Argo Workflows but that doesn't seem feasible
             # right now.
-            task_str = node.name + "-{{workflow.creationTimestamp}}"
-            if node.name != "start":
-                task_str += "-{{inputs.parameters.input-paths}}"
+
+            task_idx = ""
+            input_paths = ""
+            root_input = None
+            # export input_paths as it is used multiple times in the container script
+            # and we do not want to repeat the values.
+            input_paths_expr = "export INPUT_PATHS=''"
+            # If node is not a start step or a @parallel join then we will set the input paths.
+            # To set the input-paths as a parameter, we need to ensure that the node
+            # is not (a start node or a parallel join node). Start nodes will have no
+            # input paths and parallel join will derive input paths based on a
+            # formulaic approach using `num-parallel` and `task-id-entropy`.
+            if not (
+                node.name == "start"
+                or (node.type == "join" and self.graph[node.in_funcs[0]].parallel_step)
+            ):
+                # For parallel joins we don't pass the INPUT_PATHS but are dynamically constructed.
+                # So we don't need to set the input paths.
+                input_paths_expr = (
+                    "export INPUT_PATHS={{inputs.parameters.input-paths}}"
+                )
+                input_paths = "$(echo $INPUT_PATHS)"
             if any(self.graph[n].type == "foreach" for n in node.in_funcs):
-                task_str += "-{{inputs.parameters.split-index}}"
-            # Generated task_ids need to be non-numeric - see register_task_id in
-            # service.py. We do so by prefixing `t-`
-            task_id_expr = (
-                "export METAFLOW_TASK_ID="
-                "(t-$(echo %s | md5sum | cut -d ' ' -f 1 | tail -c 9))" % task_str
+                task_idx = "{{inputs.parameters.split-index}}"
+            if node.is_inside_foreach and self.graph[node.out_funcs[0]].type == "join":
+                if any(
+                    self.graph[parent].matching_join
+                    == self.graph[node.out_funcs[0]].name
+                    for parent in self.graph[node.out_funcs[0]].split_parents
+                    if self.graph[parent].type == "foreach"
+                ) and any(not self.graph[f].type == "foreach" for f in node.in_funcs):
+                    # we need to propagate the split-index and root-input-path info for
+                    # the last step inside a foreach for correctly joining nested
+                    # foreaches
+                    task_idx = "{{inputs.parameters.split-index}}"
+                    root_input = "{{inputs.parameters.root-input-path}}"
+
+            # Task string to be hashed into an ID
+            task_str = "-".join(
+                [
+                    node.name,
+                    "{{workflow.creationTimestamp}}",
+                    root_input or input_paths,
+                    task_idx,
+                ]
             )
+            if node.parallel_step:
+                task_str = "-".join(
+                    [
+                        "$TASK_ID_PREFIX",
+                        "{{inputs.parameters.task-id-entropy}}",
+                        "$TASK_ID_SUFFIX",
+                    ]
+                )
+            else:
+                # Generated task_ids need to be non-numeric - see register_task_id in
+                # service.py. We do so by prefixing `t-`
+                _task_id_base = (
+                    "$(echo %s | md5sum | cut -d ' ' -f 1 | tail -c 9)" % task_str
+                )
+                task_str = "(t-%s)" % _task_id_base
+
+            task_id_expr = "export METAFLOW_TASK_ID=" "%s" % task_str
             task_id = "$METAFLOW_TASK_ID"
 
             # Resolve retry strategy.
@@ -790,9 +1459,18 @@ def _container_templates(self):
             user_code_retries = max_user_code_retries
             total_retries = max_user_code_retries + max_error_retries
             # {{retries}} is only available if retryStrategy is specified
+            # For custom kubernetes manifests, we will pass the retryCount as a parameter
+            # and use that in the manifest.
             retry_count = (
-                "{{retries}}" if max_user_code_retries + max_error_retries else 0
+                (
+                    "{{retries}}"
+                    if not node.parallel_step
+                    else "{{inputs.parameters.retryCount}}"
+                )
+                if total_retries
+                else 0
             )
+
             minutes_between_retries = int(minutes_between_retries)
 
             # Configure log capture.
@@ -814,30 +1492,32 @@ def _container_templates(self):
                     # env var.
                     '${METAFLOW_INIT_SCRIPT:+eval \\"${METAFLOW_INIT_SCRIPT}\\"}',
                     "mkdir -p $PWD/.logs",
+                    input_paths_expr,
                     task_id_expr,
                     mflog_expr,
                 ]
                 + self.environment.get_package_commands(
-                    self.code_package_url, self.flow_datastore.TYPE
+                    self.code_package_url,
+                    self.flow_datastore.TYPE,
+                    self.code_package_metadata,
                 )
             )
             step_cmds = self.environment.bootstrap_commands(
                 node.name, self.flow_datastore.TYPE
             )
 
-            input_paths = "{{inputs.parameters.input-paths}}"
-
             top_opts_dict = {
                 "with": [
                     decorator.make_decorator_spec()
                     for decorator in node.decorators
                     if not decorator.statically_defined
+                    and decorator.inserted_by is None
                 ]
             }
             # FlowDecorators can define their own top-level options. They are
             # responsible for adding their own top-level options and values through
             # the get_top_level_options() hook. See similar logic in runtime.py.
-            for deco in flow_decorators():
+            for deco in flow_decorators(self.flow):
                 top_opts_dict.update(deco.get_top_level_options())
 
             top_level = list(dict_to_cli_options(top_opts_dict)) + [
@@ -849,7 +1529,7 @@ def _container_templates(self):
                 "--event-logger=%s" % self.event_logger.TYPE,
                 "--monitor=%s" % self.monitor.TYPE,
                 "--no-pylint",
-                "--with=argo_workflows_internal:auto-emit-argo-events=%s"
+                "--with=argo_workflows_internal:auto-emit-argo-events=%i"
                 % self.auto_emit_argo_events,
             ]
 
@@ -866,7 +1546,9 @@ def _container_templates(self):
                     ]
                     + [
                         # Parameter names can be hyphenated, hence we use
-                        # {{foo.bar['param_name']}}
+                        # {{foo.bar['param_name']}}.
+                        # https://argoproj.github.io/argo-events/tutorials/02-parameterization/
+                        # http://masterminds.github.io/sprig/strings.html
                         "--%s={{workflow.parameters.%s}}"
                         % (parameter["name"], parameter["name"])
                         for parameter in self.parameters.values()
@@ -893,11 +1575,27 @@ def _container_templates(self):
                 node.type == "join"
                 and self.graph[node.split_parents[-1]].type == "foreach"
             ):
-                # Set aggregated input-paths for a foreach-join
-                input_paths = (
-                    "$(python -m metaflow.plugins.argo.process_input_paths %s)"
-                    % input_paths
+                # Set aggregated input-paths for a for-each join
+                foreach_step = next(
+                    n for n in node.in_funcs if self.graph[n].is_inside_foreach
                 )
+                if not self.graph[node.split_parents[-1]].parallel_foreach:
+                    input_paths = (
+                        "$(python -m metaflow.plugins.argo.generate_input_paths %s {{workflow.creationTimestamp}} %s {{inputs.parameters.split-cardinality}})"
+                        % (
+                            foreach_step,
+                            input_paths,
+                        )
+                    )
+                else:
+                    # Handle @parallel where output from volume mount isn't accessible
+                    input_paths = (
+                        "$(python -m metaflow.plugins.argo.jobset_input_paths %s %s {{inputs.parameters.task-id-entropy}} {{inputs.parameters.num-parallel}})"
+                        % (
+                            run_id,
+                            foreach_step,
+                        )
+                    )
             step = [
                 "step",
                 node.name,
@@ -907,7 +1605,14 @@ def _container_templates(self):
                 "--max-user-code-retries %d" % user_code_retries,
                 "--input-paths %s" % input_paths,
             ]
-            if any(self.graph[n].type == "foreach" for n in node.in_funcs):
+            if node.parallel_step:
+                step.append(
+                    "--split-index ${MF_CONTROL_INDEX:-$((MF_WORKER_REPLICA_INDEX + 1))}"
+                )
+                # This is needed for setting the value of the UBF context in the CLI.
+                step.append("--ubf-context $UBF_CONTEXT")
+
+            elif any(self.graph[n].type == "foreach" for n in node.in_funcs):
                 # Pass split-index to a foreach task
                 step.append("--split-index {{inputs.parameters.split-index}}")
             if self.tags:
@@ -956,11 +1661,24 @@ def _container_templates(self):
                     0
                 ].attributes["vars"]
             )
+
+            # Temporary passing of *some* environment variables. Do not rely on this
+            # mechanism as it will be removed in the near future
+            env.update(
+                {
+                    k: v
+                    for k, v in config_values()
+                    if k.startswith("METAFLOW_CONDA_")
+                    or k.startswith("METAFLOW_DEBUG_")
+                }
+            )
+
             env.update(
                 {
                     **{
                         # These values are needed by Metaflow to set it's internal
-                        # state appropriately
+                        # state appropriately.
+                        "METAFLOW_CODE_METADATA": self.code_package_metadata,
                         "METAFLOW_CODE_URL": self.code_package_url,
                         "METAFLOW_CODE_SHA": self.code_package_sha,
                         "METAFLOW_CODE_DS": self.flow_datastore.TYPE,
@@ -978,16 +1696,18 @@ def _container_templates(self):
                         "METAFLOW_OWNER": self.username,
                     },
                     **{
-                        # Configuration for Argo Events
-                        # TODO: Move this to @kubernetes decorator instead.
+                        # Configuration for Argo Events. Keep these in sync with the
+                        # environment variables for @kubernetes decorator.
                         "METAFLOW_ARGO_EVENTS_EVENT": ARGO_EVENTS_EVENT,
                         "METAFLOW_ARGO_EVENTS_EVENT_BUS": ARGO_EVENTS_EVENT_BUS,
                         "METAFLOW_ARGO_EVENTS_EVENT_SOURCE": ARGO_EVENTS_EVENT_SOURCE,
                         "METAFLOW_ARGO_EVENTS_SERVICE_ACCOUNT": ARGO_EVENTS_SERVICE_ACCOUNT,
-                        "METAFLOW_ARGO_EVENTS_WEBHOOK_URL": ARGO_EVENTS_WEBHOOK_URL,
+                        "METAFLOW_ARGO_EVENTS_WEBHOOK_URL": ARGO_EVENTS_INTERNAL_WEBHOOK_URL,
+                        "METAFLOW_ARGO_EVENTS_WEBHOOK_AUTH": ARGO_EVENTS_WEBHOOK_AUTH,
                     },
                     **{
                         # Some optional values for bookkeeping
+                        "METAFLOW_FLOW_FILENAME": os.path.basename(sys.argv[0]),
                         "METAFLOW_FLOW_NAME": self.flow.name,
                         "METAFLOW_STEP_NAME": node.name,
                         "METAFLOW_RUN_ID": run_id,
@@ -1006,19 +1726,30 @@ def _container_templates(self):
 
             # support Metaflow sandboxes
             env["METAFLOW_INIT_SCRIPT"] = KUBERNETES_SANDBOX_INIT_SCRIPT
+            env["METAFLOW_KUBERNETES_SANDBOX_INIT_SCRIPT"] = (
+                KUBERNETES_SANDBOX_INIT_SCRIPT
+            )
 
             # support for @secret
             env["METAFLOW_DEFAULT_SECRETS_BACKEND_TYPE"] = DEFAULT_SECRETS_BACKEND_TYPE
-            env[
-                "METAFLOW_AWS_SECRETS_MANAGER_DEFAULT_REGION"
-            ] = AWS_SECRETS_MANAGER_DEFAULT_REGION
+            env["METAFLOW_AWS_SECRETS_MANAGER_DEFAULT_REGION"] = (
+                AWS_SECRETS_MANAGER_DEFAULT_REGION
+            )
+            env["METAFLOW_GCP_SECRET_MANAGER_PREFIX"] = GCP_SECRET_MANAGER_PREFIX
+            env["METAFLOW_AZURE_KEY_VAULT_PREFIX"] = AZURE_KEY_VAULT_PREFIX
 
             # support for Azure
-            env[
-                "METAFLOW_AZURE_STORAGE_BLOB_SERVICE_ENDPOINT"
-            ] = AZURE_STORAGE_BLOB_SERVICE_ENDPOINT
+            env["METAFLOW_AZURE_STORAGE_BLOB_SERVICE_ENDPOINT"] = (
+                AZURE_STORAGE_BLOB_SERVICE_ENDPOINT
+            )
             env["METAFLOW_DATASTORE_SYSROOT_AZURE"] = DATASTORE_SYSROOT_AZURE
             env["METAFLOW_CARD_AZUREROOT"] = CARD_AZUREROOT
+            env["METAFLOW_ARGO_WORKFLOWS_KUBERNETES_SECRETS"] = (
+                ARGO_WORKFLOWS_KUBERNETES_SECRETS
+            )
+            env["METAFLOW_ARGO_WORKFLOWS_ENV_VARS_TO_SKIP"] = (
+                ARGO_WORKFLOWS_ENV_VARS_TO_SKIP
+            )
 
             # support for GCP
             env["METAFLOW_DATASTORE_SYSROOT_GS"] = DATASTORE_SYSROOT_GS
@@ -1032,11 +1763,22 @@ def _container_templates(self):
                         % (event["type"], event["sanitized_name"])
                     ] = ("{{workflow.parameters.%s}}" % event["sanitized_name"])
 
+            # Map S3 upload headers to environment variables
+            if S3_SERVER_SIDE_ENCRYPTION is not None:
+                env["METAFLOW_S3_SERVER_SIDE_ENCRYPTION"] = S3_SERVER_SIDE_ENCRYPTION
+
             metaflow_version = self.environment.get_environment_info()
             metaflow_version["flow_name"] = self.graph.name
             metaflow_version["production_token"] = self.production_token
             env["METAFLOW_VERSION"] = json.dumps(metaflow_version)
 
+            # map config values
+            cfg_env = {
+                param["name"]: param["kv_name"] for param in self.config_parameters
+            }
+            if cfg_env:
+                env["METAFLOW_FLOW_CONFIG_VALUE"] = json.dumps(cfg_env)
+
             # Set the template inputs and outputs for passing state. Very simply,
             # the container template takes in input-paths as input and outputs
             # the task-id (which feeds in as input-paths to the subsequent task).
@@ -1045,22 +1787,94 @@ def _container_templates(self):
             # input. Analogously, if the node under consideration is a foreach
             # node, then we emit split cardinality as an extra output. I would like
             # to thank the designers of Argo Workflows for making this so
-            # straightforward!
+            # straightforward! Things become a bit more complicated to support very
+            # wide foreaches where we have to resort to passing a root-input-path
+            # so that we can compute the task ids for each parent task of a for-each
+            # join task deterministically inside the join task without resorting to
+            # passing a rather long list of (albiet compressed)
             inputs = []
-            if node.name != "start":
+            # To set the input-paths as a parameter, we need to ensure that the node
+            # is not (a start node or a parallel join node). Start nodes will have no
+            # input paths and parallel join will derive input paths based on a
+            # formulaic approach.
+            if not (
+                node.name == "start"
+                or (node.type == "join" and self.graph[node.in_funcs[0]].parallel_step)
+            ):
                 inputs.append(Parameter("input-paths"))
             if any(self.graph[n].type == "foreach" for n in node.in_funcs):
                 # Fetch split-index from parent
                 inputs.append(Parameter("split-index"))
 
+            if (
+                node.type == "join"
+                and self.graph[node.split_parents[-1]].type == "foreach"
+            ):
+                # @parallel join tasks require `num-parallel` and `task-id-entropy`
+                # to construct the input paths, so we pass them down as input parameters.
+                if self.graph[node.split_parents[-1]].parallel_foreach:
+                    inputs.extend(
+                        [Parameter("num-parallel"), Parameter("task-id-entropy")]
+                    )
+                else:
+                    # append this only for joins of foreaches, not static splits
+                    inputs.append(Parameter("split-cardinality"))
+            # check if the node is a @parallel node.
+            elif node.parallel_step:
+                inputs.extend(
+                    [
+                        Parameter("num-parallel"),
+                        Parameter("task-id-entropy"),
+                        Parameter("jobset-name"),
+                        Parameter("workerCount"),
+                    ]
+                )
+                if any(d.name == "retry" for d in node.decorators):
+                    inputs.append(Parameter("retryCount"))
+
+            if node.is_inside_foreach and self.graph[node.out_funcs[0]].type == "join":
+                if any(
+                    self.graph[parent].matching_join
+                    == self.graph[node.out_funcs[0]].name
+                    for parent in self.graph[node.out_funcs[0]].split_parents
+                    if self.graph[parent].type == "foreach"
+                ) and any(not self.graph[f].type == "foreach" for f in node.in_funcs):
+                    # we need to propagate the split-index and root-input-path info for
+                    # the last step inside a foreach for correctly joining nested
+                    # foreaches
+                    if not any(self.graph[n].type == "foreach" for n in node.in_funcs):
+                        # Don't add duplicate split index parameters.
+                        inputs.append(Parameter("split-index"))
+                    inputs.append(Parameter("root-input-path"))
+
             outputs = []
-            if node.name != "end":
+            # @parallel steps will not have a task-id as an output parameter since task-ids
+            # are derived at runtime.
+            if not (node.name == "end" or node.parallel_step):
                 outputs = [Parameter("task-id").valueFrom({"path": "/mnt/out/task_id"})]
             if node.type == "foreach":
                 # Emit split cardinality from foreach task
                 outputs.append(
                     Parameter("num-splits").valueFrom({"path": "/mnt/out/splits"})
                 )
+                outputs.append(
+                    Parameter("split-cardinality").valueFrom(
+                        {"path": "/mnt/out/split_cardinality"}
+                    )
+                )
+
+            if node.parallel_foreach:
+                outputs.extend(
+                    [
+                        Parameter("num-parallel").valueFrom(
+                            {"path": "/mnt/out/num_parallel"}
+                        ),
+                        Parameter("task-id-entropy").valueFrom(
+                            {"path": "/mnt/out/task_id_entropy"}
+                        ),
+                    ]
+                )
+            # Outputs should be defined over here and not in the _dag_template for @parallel.
 
             # It makes no sense to set env vars to None (shows up as "None" string)
             # Also we skip some env vars (e.g. in case we want to pull them from KUBERNETES_SECRETS)
@@ -1076,143 +1890,1195 @@ def _container_templates(self):
             tmpfs_size = resources["tmpfs_size"]
             tmpfs_path = resources["tmpfs_path"]
             tmpfs_tempdir = resources["tmpfs_tempdir"]
+            # Set shared_memory to 0 if it isn't specified. This results
+            # in Kubernetes using it's default value when the pod is created.
+            shared_memory = resources.get("shared_memory", 0)
+            port = resources.get("port", None)
+            if port:
+                port = int(port)
 
             tmpfs_enabled = use_tmpfs or (tmpfs_size and not use_tmpfs)
 
             if tmpfs_enabled and tmpfs_tempdir:
                 env["METAFLOW_TEMPDIR"] = tmpfs_path
 
+            qos_requests, qos_limits = qos_requests_and_limits(
+                resources["qos"],
+                resources["cpu"],
+                resources["memory"],
+                resources["disk"],
+            )
+
+            security_context = resources.get("security_context", None)
+            _security_context = {}
+            if security_context is not None and len(security_context) > 0:
+                _security_context = {
+                    "security_context": kubernetes_sdk.V1SecurityContext(
+                        **security_context
+                    )
+                }
+
             # Create a ContainerTemplate for this node. Ideally, we would have
             # liked to inline this ContainerTemplate and avoid scanning the workflow
             # twice, but due to issues with variable substitution, we will have to
             # live with this routine.
-            yield (
-                Template(self._sanitize(node.name))
-                # Set @timeout values
-                .active_deadline_seconds(run_time_limit)
-                # Set service account
-                .service_account_name(resources["service_account"])
-                # Configure template input
-                .inputs(Inputs().parameters(inputs))
-                # Configure template output
-                .outputs(Outputs().parameters(outputs))
-                # Fail fast!
-                .fail_fast()
-                # Set @retry/@catch values
-                .retry_strategy(
-                    times=total_retries,
-                    minutes_between_retries=minutes_between_retries,
+            if node.parallel_step:
+                jobset_name = "{{inputs.parameters.jobset-name}}"
+                jobset = KubernetesArgoJobSet(
+                    kubernetes_sdk=kubernetes_sdk,
+                    name=jobset_name,
+                    flow_name=self.flow.name,
+                    run_id=run_id,
+                    step_name=self._sanitize(node.name),
+                    task_id=task_id,
+                    attempt=retry_count,
+                    user=self.username,
+                    subdomain=jobset_name,
+                    command=cmds,
+                    namespace=resources["namespace"],
+                    image=resources["image"],
+                    image_pull_policy=resources["image_pull_policy"],
+                    image_pull_secrets=resources["image_pull_secrets"],
+                    service_account=resources["service_account"],
+                    secrets=(
+                        [
+                            k
+                            for k in (
+                                list(
+                                    []
+                                    if not resources.get("secrets")
+                                    else (
+                                        [resources.get("secrets")]
+                                        if isinstance(resources.get("secrets"), str)
+                                        else resources.get("secrets")
+                                    )
+                                )
+                                + KUBERNETES_SECRETS.split(",")
+                                + ARGO_WORKFLOWS_KUBERNETES_SECRETS.split(",")
+                            )
+                            if k
+                        ]
+                    ),
+                    node_selector=resources.get("node_selector"),
+                    cpu=str(resources["cpu"]),
+                    memory=str(resources["memory"]),
+                    disk=str(resources["disk"]),
+                    gpu=resources["gpu"],
+                    gpu_vendor=str(resources["gpu_vendor"]),
+                    tolerations=resources["tolerations"],
+                    use_tmpfs=use_tmpfs,
+                    tmpfs_tempdir=tmpfs_tempdir,
+                    tmpfs_size=tmpfs_size,
+                    tmpfs_path=tmpfs_path,
+                    timeout_in_seconds=run_time_limit,
+                    persistent_volume_claims=resources["persistent_volume_claims"],
+                    shared_memory=shared_memory,
+                    port=port,
+                    qos=resources["qos"],
+                    security_context=security_context,
+                )
+
+                for k, v in env.items():
+                    jobset.environment_variable(k, v)
+
+                # Set labels. Do not allow user-specified task labels to override internal ones.
+                #
+                # Explicitly add the task-id-hint label. This is important because this label
+                # is returned as an Output parameter of this step and is used subsequently as an
+                # an input in the join step.
+                kubernetes_labels = {
+                    "task_id_entropy": "{{inputs.parameters.task-id-entropy}}",
+                    "num_parallel": "{{inputs.parameters.num-parallel}}",
+                    "metaflow/argo-workflows-name": "{{workflow.name}}",
+                    "workflows.argoproj.io/workflow": "{{workflow.name}}",
+                }
+                jobset.labels(
+                    {
+                        **resources["labels"],
+                        **self._base_labels,
+                        **kubernetes_labels,
+                    }
+                )
+
+                jobset.environment_variable(
+                    "MF_MASTER_ADDR", jobset.jobset_control_addr
+                )
+                jobset.environment_variable("MF_MASTER_PORT", str(port))
+                jobset.environment_variable(
+                    "MF_WORLD_SIZE", "{{inputs.parameters.num-parallel}}"
+                )
+                # We need this task-id set so that all the nodes are aware of the control
+                # task's task-id. These "MF_" variables populate the `current.parallel` namedtuple
+                jobset.environment_variable(
+                    "MF_PARALLEL_CONTROL_TASK_ID",
+                    "control-{{inputs.parameters.task-id-entropy}}-0",
                 )
-                .metadata(
-                    ObjectMeta().annotation("metaflow/step_name", node.name)
-                    # Unfortunately, we can't set the task_id since it is generated
-                    # inside the pod. However, it can be inferred from the annotation
-                    # set by argo-workflows - `workflows.argoproj.io/outputs` - refer
-                    # the field 'task-id' in 'parameters'
-                    # .annotation("metaflow/task_id", ...)
-                    .annotation("metaflow/attempt", retry_count)
+                # for k, v in .items():
+                jobset.environment_variables_from_selectors(
+                    {
+                        "MF_WORKER_REPLICA_INDEX": "metadata.annotations['jobset.sigs.k8s.io/job-index']",
+                        "JOBSET_RESTART_ATTEMPT": "metadata.annotations['jobset.sigs.k8s.io/restart-attempt']",
+                        "METAFLOW_KUBERNETES_JOBSET_NAME": "metadata.annotations['jobset.sigs.k8s.io/jobset-name']",
+                        "METAFLOW_KUBERNETES_POD_NAMESPACE": "metadata.namespace",
+                        "METAFLOW_KUBERNETES_POD_NAME": "metadata.name",
+                        "METAFLOW_KUBERNETES_POD_ID": "metadata.uid",
+                        "METAFLOW_KUBERNETES_SERVICE_ACCOUNT_NAME": "spec.serviceAccountName",
+                        "METAFLOW_KUBERNETES_NODE_IP": "status.hostIP",
+                        "TASK_ID_SUFFIX": "metadata.annotations['jobset.sigs.k8s.io/job-index']",
+                    }
                 )
-                # Set emptyDir volume for state management
-                .empty_dir_volume("out")
-                # Set tmpfs emptyDir volume if enabled
-                .empty_dir_volume(
-                    "tmpfs-ephemeral-volume",
-                    medium="Memory",
-                    size_limit=tmpfs_size if tmpfs_enabled else 0,
+
+                # Set annotations. Do not allow user-specified task-specific annotations to override internal ones.
+                annotations = {
+                    # setting annotations explicitly as they wont be
+                    # passed down from WorkflowTemplate level
+                    "metaflow/step_name": node.name,
+                    "metaflow/attempt": str(retry_count),
+                    "metaflow/run_id": run_id,
+                }
+
+                jobset.annotations(
+                    {
+                        **resources["annotations"],
+                        **self._base_annotations,
+                        **annotations,
+                    }
                 )
-                # Set node selectors
-                .node_selectors(resources.get("node_selector"))
-                .tolerations(resources.get("tolerations"))
-                # Set container
-                .container(
-                    # TODO: Unify the logic with kubernetes.py
-                    # Important note - Unfortunately, V1Container uses snakecase while
-                    # Argo Workflows uses camel. For most of the attributes, both cases
-                    # are indistinguishable, but unfortunately, not for all - (
-                    # env_from, value_from, etc.) - so we need to handle the conversion
-                    # ourselves using to_camelcase. We need to be vigilant about
-                    # resources attributes in particular where the keys maybe user
-                    # defined.
-                    to_camelcase(
-                        kubernetes_sdk.V1Container(
-                            name=self._sanitize(node.name),
-                            command=cmds,
-                            env=[
-                                kubernetes_sdk.V1EnvVar(name=k, value=str(v))
-                                for k, v in env.items()
+
+                jobset.control.replicas(1)
+                jobset.worker.replicas("{{=asInt(inputs.parameters.workerCount)}}")
+                jobset.control.environment_variable("UBF_CONTEXT", UBF_CONTROL)
+                jobset.worker.environment_variable("UBF_CONTEXT", UBF_TASK)
+                jobset.control.environment_variable("MF_CONTROL_INDEX", "0")
+                # `TASK_ID_PREFIX` needs to explicitly be `control` or `worker`
+                # because the join task uses a formulaic approach to infer the task-ids
+                jobset.control.environment_variable("TASK_ID_PREFIX", "control")
+                jobset.worker.environment_variable("TASK_ID_PREFIX", "worker")
+
+                yield (
+                    Template(ArgoWorkflows._sanitize(node.name))
+                    .resource(
+                        "create",
+                        jobset.dump(),
+                        "status.terminalState == Completed",
+                        "status.terminalState == Failed",
+                    )
+                    .inputs(Inputs().parameters(inputs))
+                    .outputs(
+                        Outputs().parameters(
+                            [
+                                Parameter("task-id-entropy").valueFrom(
+                                    {"jsonPath": "{.metadata.labels.task_id_entropy}"}
+                                ),
+                                Parameter("num-parallel").valueFrom(
+                                    {"jsonPath": "{.metadata.labels.num_parallel}"}
+                                ),
                             ]
-                            # Add environment variables for book-keeping.
-                            # https://argoproj.github.io/argo-workflows/fields/#fields_155
-                            + [
-                                kubernetes_sdk.V1EnvVar(
-                                    name=k,
-                                    value_from=kubernetes_sdk.V1EnvVarSource(
-                                        field_ref=kubernetes_sdk.V1ObjectFieldSelector(
-                                            field_path=str(v)
+                        )
+                    )
+                    .retry_strategy(
+                        times=total_retries,
+                        minutes_between_retries=minutes_between_retries,
+                    )
+                )
+            else:
+                yield (
+                    Template(self._sanitize(node.name))
+                    # Set @timeout values
+                    .active_deadline_seconds(run_time_limit)
+                    # Set service account
+                    .service_account_name(resources["service_account"])
+                    # Configure template input
+                    .inputs(Inputs().parameters(inputs))
+                    # Configure template output
+                    .outputs(Outputs().parameters(outputs))
+                    # Fail fast!
+                    .fail_fast()
+                    # Set @retry/@catch values
+                    .retry_strategy(
+                        times=total_retries,
+                        minutes_between_retries=minutes_between_retries,
+                    )
+                    .metadata(
+                        ObjectMeta()
+                        .annotation("metaflow/step_name", node.name)
+                        # Unfortunately, we can't set the task_id since it is generated
+                        # inside the pod. However, it can be inferred from the annotation
+                        # set by argo-workflows - `workflows.argoproj.io/outputs` - refer
+                        # the field 'task-id' in 'parameters'
+                        # .annotation("metaflow/task_id", ...)
+                        .annotation("metaflow/attempt", retry_count)
+                        .annotations(resources["annotations"])
+                        .labels(resources["labels"])
+                    )
+                    # Set emptyDir volume for state management
+                    .empty_dir_volume("out")
+                    # Set tmpfs emptyDir volume if enabled
+                    .empty_dir_volume(
+                        "tmpfs-ephemeral-volume",
+                        medium="Memory",
+                        size_limit=tmpfs_size if tmpfs_enabled else 0,
+                    )
+                    .empty_dir_volume("dhsm", medium="Memory", size_limit=shared_memory)
+                    .pvc_volumes(resources.get("persistent_volume_claims"))
+                    # Set node selectors
+                    .node_selectors(resources.get("node_selector"))
+                    # Set tolerations
+                    .tolerations(resources.get("tolerations"))
+                    # Set image pull secrets if present. We need to use pod_spec_patch due to Argo not supporting this on a template level.
+                    .pod_spec_patch(
+                        {
+                            "imagePullSecrets": [
+                                {"name": secret}
+                                for secret in resources["image_pull_secrets"]
+                            ]
+                        }
+                        if resources["image_pull_secrets"]
+                        else None
+                    )
+                    # Set container
+                    .container(
+                        # TODO: Unify the logic with kubernetes.py
+                        # Important note - Unfortunately, V1Container uses snakecase while
+                        # Argo Workflows uses camel. For most of the attributes, both cases
+                        # are indistinguishable, but unfortunately, not for all - (
+                        # env_from, value_from, etc.) - so we need to handle the conversion
+                        # ourselves using to_camelcase. We need to be vigilant about
+                        # resources attributes in particular where the keys maybe user
+                        # defined.
+                        to_camelcase(
+                            kubernetes_sdk.V1Container(
+                                name=self._sanitize(node.name),
+                                command=cmds,
+                                termination_message_policy="FallbackToLogsOnError",
+                                ports=(
+                                    [
+                                        kubernetes_sdk.V1ContainerPort(
+                                            container_port=port
                                         )
-                                    ),
-                                )
-                                for k, v in {
-                                    "METAFLOW_KUBERNETES_POD_NAMESPACE": "metadata.namespace",
-                                    "METAFLOW_KUBERNETES_POD_NAME": "metadata.name",
-                                    "METAFLOW_KUBERNETES_POD_ID": "metadata.uid",
-                                    "METAFLOW_KUBERNETES_SERVICE_ACCOUNT_NAME": "spec.serviceAccountName",
-                                    "METAFLOW_KUBERNETES_NODE_IP": "status.hostIP",
-                                }.items()
-                            ],
-                            image=resources["image"],
-                            resources=kubernetes_sdk.V1ResourceRequirements(
-                                requests={
-                                    "cpu": str(resources["cpu"]),
-                                    "memory": "%sM" % str(resources["memory"]),
-                                    "ephemeral-storage": "%sM" % str(resources["disk"]),
-                                },
-                                limits={
-                                    "%s.com/gpu".lower()
-                                    % resources["gpu_vendor"]: str(resources["gpu"])
-                                    for k in [0]
-                                    if resources["gpu"] is not None
-                                },
-                            ),
-                            # Configure secrets
-                            env_from=[
-                                kubernetes_sdk.V1EnvFromSource(
-                                    secret_ref=kubernetes_sdk.V1SecretEnvSource(
-                                        name=str(k),
-                                        # optional=True
+                                    ]
+                                    if port
+                                    else None
+                                ),
+                                env=[
+                                    kubernetes_sdk.V1EnvVar(name=k, value=str(v))
+                                    for k, v in env.items()
+                                ]
+                                # Add environment variables for book-keeping.
+                                # https://argoproj.github.io/argo-workflows/fields/#fields_155
+                                + [
+                                    kubernetes_sdk.V1EnvVar(
+                                        name=k,
+                                        value_from=kubernetes_sdk.V1EnvVarSource(
+                                            field_ref=kubernetes_sdk.V1ObjectFieldSelector(
+                                                field_path=str(v)
+                                            )
+                                        ),
+                                    )
+                                    for k, v in {
+                                        "METAFLOW_KUBERNETES_NAMESPACE": "metadata.namespace",
+                                        "METAFLOW_KUBERNETES_POD_NAMESPACE": "metadata.namespace",
+                                        "METAFLOW_KUBERNETES_POD_NAME": "metadata.name",
+                                        "METAFLOW_KUBERNETES_POD_ID": "metadata.uid",
+                                        "METAFLOW_KUBERNETES_SERVICE_ACCOUNT_NAME": "spec.serviceAccountName",
+                                        "METAFLOW_KUBERNETES_NODE_IP": "status.hostIP",
+                                    }.items()
+                                ],
+                                image=resources["image"],
+                                image_pull_policy=resources["image_pull_policy"],
+                                resources=kubernetes_sdk.V1ResourceRequirements(
+                                    requests=qos_requests,
+                                    limits={
+                                        **qos_limits,
+                                        **{
+                                            "%s.com/gpu".lower()
+                                            % resources["gpu_vendor"]: str(
+                                                resources["gpu"]
+                                            )
+                                            for k in [0]
+                                            if resources["gpu"] is not None
+                                        },
+                                    },
+                                ),
+                                # Configure secrets
+                                env_from=[
+                                    kubernetes_sdk.V1EnvFromSource(
+                                        secret_ref=kubernetes_sdk.V1SecretEnvSource(
+                                            name=str(k),
+                                            # optional=True
+                                        )
+                                    )
+                                    for k in list(
+                                        []
+                                        if not resources.get("secrets")
+                                        else (
+                                            [resources.get("secrets")]
+                                            if isinstance(resources.get("secrets"), str)
+                                            else resources.get("secrets")
+                                        )
+                                    )
+                                    + KUBERNETES_SECRETS.split(",")
+                                    + ARGO_WORKFLOWS_KUBERNETES_SECRETS.split(",")
+                                    if k
+                                ],
+                                volume_mounts=[
+                                    # Assign a volume mount to pass state to the next task.
+                                    kubernetes_sdk.V1VolumeMount(
+                                        name="out", mount_path="/mnt/out"
                                     )
+                                ]
+                                # Support tmpfs.
+                                + (
+                                    [
+                                        kubernetes_sdk.V1VolumeMount(
+                                            name="tmpfs-ephemeral-volume",
+                                            mount_path=tmpfs_path,
+                                        )
+                                    ]
+                                    if tmpfs_enabled
+                                    else []
                                 )
-                                for k in list(
-                                    []
-                                    if not resources.get("secrets")
-                                    else [resources.get("secrets")]
+                                # Support shared_memory
+                                + (
+                                    [
+                                        kubernetes_sdk.V1VolumeMount(
+                                            name="dhsm",
+                                            mount_path="/dev/shm",
+                                        )
+                                    ]
+                                    if shared_memory
+                                    else []
+                                )
+                                # Support persistent volume claims.
+                                + (
+                                    [
+                                        kubernetes_sdk.V1VolumeMount(
+                                            name=claim, mount_path=path
+                                        )
+                                        for claim, path in resources.get(
+                                            "persistent_volume_claims"
+                                        ).items()
+                                    ]
+                                    if resources.get("persistent_volume_claims")
+                                    is not None
+                                    else []
+                                ),
+                                **_security_context,
+                            ).to_dict()
+                        )
+                    )
+                )
+
+    # Return daemon container templates for workflow execution notifications.
+    def _daemon_templates(self):
+        templates = []
+        if self.enable_heartbeat_daemon:
+            templates.append(self._heartbeat_daemon_template())
+        return templates
+
+    # Return lifecycle hooks for workflow execution notifications.
+    def _lifecycle_hooks(self):
+        hooks = []
+        if self.notify_on_error:
+            hooks.append(self._slack_error_template())
+            hooks.append(self._pager_duty_alert_template())
+            hooks.append(self._incident_io_alert_template())
+        if self.notify_on_success:
+            hooks.append(self._slack_success_template())
+            hooks.append(self._pager_duty_change_template())
+            hooks.append(self._incident_io_change_template())
+
+        exit_hook_decos = self.flow._flow_decorators.get("exit_hook", [])
+
+        for deco in exit_hook_decos:
+            hooks.extend(self._lifecycle_hook_from_deco(deco))
+
+        # Clean up None values from templates.
+        hooks = list(filter(None, hooks))
+
+        if hooks:
+            hooks.append(
+                ExitHookHack(
+                    url=(
+                        self.notify_slack_webhook_url
+                        or "https://events.pagerduty.com/v2/enqueue"
+                    )
+                )
+            )
+        return hooks
+
+    def _lifecycle_hook_from_deco(self, deco):
+        from kubernetes import client as kubernetes_sdk
+
+        start_step = [step for step in self.graph if step.name == "start"][0]
+        # We want to grab the base image used by the start step, as this is known to be pullable from within the cluster,
+        # and it might contain the required libraries, allowing us to start up faster.
+        start_kube_deco = [
+            deco for deco in start_step.decorators if deco.name == "kubernetes"
+        ][0]
+        resources = dict(start_kube_deco.attributes)
+        kube_defaults = dict(start_kube_deco.defaults)
+
+        run_id_template = "argo-{{workflow.name}}"
+        metaflow_version = self.environment.get_environment_info()
+        metaflow_version["flow_name"] = self.graph.name
+        metaflow_version["production_token"] = self.production_token
+        env = {
+            # These values are needed by Metaflow to set it's internal
+            # state appropriately.
+            "METAFLOW_CODE_URL": self.code_package_url,
+            "METAFLOW_CODE_SHA": self.code_package_sha,
+            "METAFLOW_CODE_DS": self.flow_datastore.TYPE,
+            "METAFLOW_SERVICE_URL": SERVICE_INTERNAL_URL,
+            "METAFLOW_SERVICE_HEADERS": json.dumps(SERVICE_HEADERS),
+            "METAFLOW_USER": "argo-workflows",
+            "METAFLOW_DEFAULT_DATASTORE": self.flow_datastore.TYPE,
+            "METAFLOW_DEFAULT_METADATA": DEFAULT_METADATA,
+            "METAFLOW_OWNER": self.username,
+        }
+        # pass on the Run pathspec for script
+        env["RUN_PATHSPEC"] = f"{self.graph.name}/{run_id_template}"
+
+        # support Metaflow sandboxes
+        env["METAFLOW_INIT_SCRIPT"] = KUBERNETES_SANDBOX_INIT_SCRIPT
+
+        env["METAFLOW_WORKFLOW_NAME"] = "{{workflow.name}}"
+        env["METAFLOW_WORKFLOW_NAMESPACE"] = "{{workflow.namespace}}"
+        env = {
+            k: v
+            for k, v in env.items()
+            if v is not None
+            and k not in set(ARGO_WORKFLOWS_ENV_VARS_TO_SKIP.split(","))
+        }
+
+        def _cmd(fn_name):
+            mflog_expr = export_mflog_env_vars(
+                datastore_type=self.flow_datastore.TYPE,
+                stdout_path="$PWD/.logs/mflog_stdout",
+                stderr_path="$PWD/.logs/mflog_stderr",
+                flow_name=self.flow.name,
+                run_id=run_id_template,
+                step_name=f"_hook_{fn_name}",
+                task_id="1",
+                retry_count="0",
+            )
+            cmds = " && ".join(
+                [
+                    # For supporting sandboxes, ensure that a custom script is executed
+                    # before anything else is executed. The script is passed in as an
+                    # env var.
+                    '${METAFLOW_INIT_SCRIPT:+eval \\"${METAFLOW_INIT_SCRIPT}\\"}',
+                    "mkdir -p $PWD/.logs",
+                    mflog_expr,
+                ]
+                + self.environment.get_package_commands(
+                    self.code_package_url, self.flow_datastore.TYPE
+                )[:-1]
+                # Replace the line 'Task in starting'
+                + [f"mflog 'Lifecycle hook {fn_name} is starting.'"]
+                + [
+                    f"python -m metaflow.plugins.exit_hook.exit_hook_script {metaflow_version['script']} {fn_name} $RUN_PATHSPEC"
+                ]
+            )
+
+            cmds = shlex.split('bash -c "%s"' % cmds)
+            return cmds
+
+        def _container(cmds):
+            return to_camelcase(
+                kubernetes_sdk.V1Container(
+                    name="main",
+                    command=cmds,
+                    image=deco.attributes["options"].get("image", None)
+                    or resources["image"],
+                    env=[
+                        kubernetes_sdk.V1EnvVar(name=k, value=str(v))
+                        for k, v in env.items()
+                    ],
+                    env_from=[
+                        kubernetes_sdk.V1EnvFromSource(
+                            secret_ref=kubernetes_sdk.V1SecretEnvSource(
+                                name=str(k),
+                                # optional=True
+                            )
+                        )
+                        for k in list(
+                            []
+                            if not resources.get("secrets")
+                            else (
+                                [resources.get("secrets")]
+                                if isinstance(resources.get("secrets"), str)
+                                else resources.get("secrets")
+                            )
+                        )
+                        + KUBERNETES_SECRETS.split(",")
+                        + ARGO_WORKFLOWS_KUBERNETES_SECRETS.split(",")
+                        if k
+                    ],
+                    resources=kubernetes_sdk.V1ResourceRequirements(
+                        requests={
+                            "cpu": str(kube_defaults["cpu"]),
+                            "memory": "%sM" % str(kube_defaults["memory"]),
+                        }
+                    ),
+                ).to_dict()
+            )
+
+        # create lifecycle hooks from deco
+        hooks = []
+        for success_fn_name in deco.success_hooks:
+            hook = ContainerHook(
+                name=f"success-{success_fn_name.replace('_', '-')}",
+                container=_container(cmds=_cmd(success_fn_name)),
+                service_account_name=resources["service_account"],
+                on_success=True,
+            )
+            hooks.append(hook)
+
+        for error_fn_name in deco.error_hooks:
+            hook = ContainerHook(
+                name=f"error-{error_fn_name.replace('_', '-')}",
+                service_account_name=resources["service_account"],
+                container=_container(cmds=_cmd(error_fn_name)),
+                on_error=True,
+            )
+            hooks.append(hook)
+
+        return hooks
+
+    def _exit_hook_templates(self):
+        templates = []
+        if self.enable_error_msg_capture:
+            templates.extend(self._error_msg_capture_hook_templates())
+
+        return templates
+
+    def _error_msg_capture_hook_templates(self):
+        from kubernetes import client as kubernetes_sdk
+
+        start_step = [step for step in self.graph if step.name == "start"][0]
+        # We want to grab the base image used by the start step, as this is known to be pullable from within the cluster,
+        # and it might contain the required libraries, allowing us to start up faster.
+        resources = dict(
+            [deco for deco in start_step.decorators if deco.name == "kubernetes"][
+                0
+            ].attributes
+        )
+
+        run_id_template = "argo-{{workflow.name}}"
+        metaflow_version = self.environment.get_environment_info()
+        metaflow_version["flow_name"] = self.graph.name
+        metaflow_version["production_token"] = self.production_token
+
+        mflog_expr = export_mflog_env_vars(
+            datastore_type=self.flow_datastore.TYPE,
+            stdout_path="$PWD/.logs/mflog_stdout",
+            stderr_path="$PWD/.logs/mflog_stderr",
+            flow_name=self.flow.name,
+            run_id=run_id_template,
+            step_name="_run_capture_error",
+            task_id="1",
+            retry_count="0",
+        )
+
+        cmds = " && ".join(
+            [
+                # For supporting sandboxes, ensure that a custom script is executed
+                # before anything else is executed. The script is passed in as an
+                # env var.
+                '${METAFLOW_INIT_SCRIPT:+eval \\"${METAFLOW_INIT_SCRIPT}\\"}',
+                "mkdir -p $PWD/.logs",
+                mflog_expr,
+            ]
+            + self.environment.get_package_commands(
+                self.code_package_url,
+                self.flow_datastore.TYPE,
+                self.code_package_metadata,
+            )[:-1]
+            # Replace the line 'Task in starting'
+            # FIXME: this can be brittle.
+            + ["mflog 'Error capture hook is starting.'"]
+            + ["argo_error=$(python -m 'metaflow.plugins.argo.capture_error')"]
+            + ["export METAFLOW_ARGO_ERROR=$argo_error"]
+            + [
+                """python -c 'import json, os; error_obj=os.getenv(\\"METAFLOW_ARGO_ERROR\\");data=json.loads(error_obj); print(data[\\"message\\"])'"""
+            ]
+            + [
+                'if [ -n \\"${METAFLOW_ARGO_WORKFLOWS_CAPTURE_ERROR_SCRIPT}\\" ]; then eval \\"${METAFLOW_ARGO_WORKFLOWS_CAPTURE_ERROR_SCRIPT}\\"; fi'
+            ]
+        )
+
+        # TODO: Also capture the first failed task id
+        cmds = shlex.split('bash -c "%s"' % cmds)
+        env = {
+            # These values are needed by Metaflow to set it's internal
+            # state appropriately.
+            "METAFLOW_CODE_METADATA": self.code_package_metadata,
+            "METAFLOW_CODE_URL": self.code_package_url,
+            "METAFLOW_CODE_SHA": self.code_package_sha,
+            "METAFLOW_CODE_DS": self.flow_datastore.TYPE,
+            "METAFLOW_SERVICE_URL": SERVICE_INTERNAL_URL,
+            "METAFLOW_SERVICE_HEADERS": json.dumps(SERVICE_HEADERS),
+            "METAFLOW_USER": "argo-workflows",
+            "METAFLOW_DEFAULT_DATASTORE": self.flow_datastore.TYPE,
+            "METAFLOW_DEFAULT_METADATA": DEFAULT_METADATA,
+            "METAFLOW_OWNER": self.username,
+        }
+        # support Metaflow sandboxes
+        env["METAFLOW_INIT_SCRIPT"] = KUBERNETES_SANDBOX_INIT_SCRIPT
+        env["METAFLOW_ARGO_WORKFLOWS_CAPTURE_ERROR_SCRIPT"] = (
+            ARGO_WORKFLOWS_CAPTURE_ERROR_SCRIPT
+        )
+
+        env["METAFLOW_WORKFLOW_NAME"] = "{{workflow.name}}"
+        env["METAFLOW_WORKFLOW_NAMESPACE"] = "{{workflow.namespace}}"
+        env["METAFLOW_ARGO_WORKFLOW_FAILURES"] = "{{workflow.failures}}"
+        env = {
+            k: v
+            for k, v in env.items()
+            if v is not None
+            and k not in set(ARGO_WORKFLOWS_ENV_VARS_TO_SKIP.split(","))
+        }
+        return [
+            Template("error-msg-capture-hook")
+            .service_account_name(resources["service_account"])
+            .container(
+                to_camelcase(
+                    kubernetes_sdk.V1Container(
+                        name="main",
+                        command=cmds,
+                        image=resources["image"],
+                        env=[
+                            kubernetes_sdk.V1EnvVar(name=k, value=str(v))
+                            for k, v in env.items()
+                        ],
+                        env_from=[
+                            kubernetes_sdk.V1EnvFromSource(
+                                secret_ref=kubernetes_sdk.V1SecretEnvSource(
+                                    name=str(k),
+                                    # optional=True
+                                )
+                            )
+                            for k in list(
+                                []
+                                if not resources.get("secrets")
+                                else (
+                                    [resources.get("secrets")]
                                     if isinstance(resources.get("secrets"), str)
                                     else resources.get("secrets")
                                 )
-                                + KUBERNETES_SECRETS.split(",")
-                                + ARGO_WORKFLOWS_KUBERNETES_SECRETS.split(",")
-                                if k
-                            ],
-                            # Assign a volume point to pass state to the next task.
-                            volume_mounts=[
-                                kubernetes_sdk.V1VolumeMount(
-                                    name="out", mount_path="/mnt/out"
+                            )
+                            + KUBERNETES_SECRETS.split(",")
+                            + ARGO_WORKFLOWS_KUBERNETES_SECRETS.split(",")
+                            if k
+                        ],
+                        resources=kubernetes_sdk.V1ResourceRequirements(
+                            # NOTE: base resources for this are kept to a minimum to save on running costs.
+                            # This has an adverse effect on startup time for the daemon, which can be completely
+                            # alleviated by using a base image that has the required dependencies pre-installed
+                            requests={
+                                "cpu": "200m",
+                                "memory": "100Mi",
+                            },
+                            limits={
+                                "cpu": "200m",
+                                "memory": "500Mi",
+                            },
+                        ),
+                    ).to_dict()
+                )
+            ),
+            Template("capture-error-hook-fn-preflight").steps(
+                [
+                    WorkflowStep()
+                    .name("capture-error-hook-fn-preflight")
+                    .template("error-msg-capture-hook")
+                    .when("{{workflow.status}} != Succeeded")
+                ]
+            ),
+        ]
+
+    def _pager_duty_alert_template(self):
+        # https://developer.pagerduty.com/docs/ZG9jOjExMDI5NTgx-send-an-alert-event
+        if self.notify_pager_duty_integration_key is None:
+            return None
+        return HttpExitHook(
+            name="notify-pager-duty-on-error",
+            method="POST",
+            url="https://events.pagerduty.com/v2/enqueue",
+            headers={"Content-Type": "application/json"},
+            body=json.dumps(
+                {
+                    "event_action": "trigger",
+                    "routing_key": self.notify_pager_duty_integration_key,
+                    # "dedup_key": self.flow.name,  # TODO: Do we need deduplication?
+                    "payload": {
+                        "source": "{{workflow.name}}",
+                        "severity": "info",
+                        "summary": "Metaflow run %s/argo-{{workflow.name}} failed!"
+                        % self.flow.name,
+                        "custom_details": {
+                            "Flow": self.flow.name,
+                            "Run ID": "argo-{{workflow.name}}",
+                        },
+                    },
+                    "links": self._pager_duty_notification_links(),
+                }
+            ),
+            on_error=True,
+        )
+
+    def _incident_io_alert_template(self):
+        if self.notify_incident_io_api_key is None:
+            return None
+        if self.incident_io_alert_source_config_id is None:
+            raise MetaflowException(
+                "Creating alerts for errors requires a alert source config ID."
+            )
+        ui_links = self._incident_io_ui_urls_for_run()
+        return HttpExitHook(
+            name="notify-incident-io-on-error",
+            method="POST",
+            url=(
+                "https://api.incident.io/v2/alert_events/http/%s"
+                % self.incident_io_alert_source_config_id
+            ),
+            headers={
+                "Content-Type": "application/json",
+                "Authorization": "Bearer %s" % self.notify_incident_io_api_key,
+            },
+            body=json.dumps(
+                {
+                    "idempotency_key": "argo-{{workflow.name}}",  # use run id to deduplicate alerts.
+                    "status": "firing",
+                    "title": "Flow %s has failed." % self.flow.name,
+                    "description": "Metaflow run {run_pathspec} failed!{urls}".format(
+                        run_pathspec="%s/argo-{{workflow.name}}" % self.flow.name,
+                        urls=(
+                            "\n\nSee details for the run at:\n\n"
+                            + "\n\n".join(ui_links)
+                            if ui_links
+                            else ""
+                        ),
+                    ),
+                    "source_url": (
+                        "%s/%s/%s"
+                        % (
+                            UI_URL.rstrip("/"),
+                            self.flow.name,
+                            "argo-{{workflow.name}}",
+                        )
+                        if UI_URL
+                        else None
+                    ),
+                    "metadata": {
+                        **(self.incident_io_metadata or {}),
+                        **{
+                            "run_status": "failed",
+                            "flow_name": self.flow.name,
+                            "run_id": "argo-{{workflow.name}}",
+                        },
+                    },
+                }
+            ),
+            on_error=True,
+        )
+
+    def _incident_io_change_template(self):
+        if self.notify_incident_io_api_key is None:
+            return None
+        if self.incident_io_alert_source_config_id is None:
+            raise MetaflowException(
+                "Creating alerts for successes requires an alert source config ID."
+            )
+        ui_links = self._incident_io_ui_urls_for_run()
+        return HttpExitHook(
+            name="notify-incident-io-on-success",
+            method="POST",
+            url=(
+                "https://api.incident.io/v2/alert_events/http/%s"
+                % self.incident_io_alert_source_config_id
+            ),
+            headers={
+                "Content-Type": "application/json",
+                "Authorization": "Bearer %s" % self.notify_incident_io_api_key,
+            },
+            body=json.dumps(
+                {
+                    "idempotency_key": "argo-{{workflow.name}}",  # use run id to deduplicate alerts.
+                    "status": "firing",
+                    "title": "Flow %s has succeeded." % self.flow.name,
+                    "description": "Metaflow run {run_pathspec} succeeded!{urls}".format(
+                        run_pathspec="%s/argo-{{workflow.name}}" % self.flow.name,
+                        urls=(
+                            "\n\nSee details for the run at:\n\n"
+                            + "\n\n".join(ui_links)
+                            if ui_links
+                            else ""
+                        ),
+                    ),
+                    "source_url": (
+                        "%s/%s/%s"
+                        % (
+                            UI_URL.rstrip("/"),
+                            self.flow.name,
+                            "argo-{{workflow.name}}",
+                        )
+                        if UI_URL
+                        else None
+                    ),
+                    "metadata": {
+                        **(self.incident_io_metadata or {}),
+                        **{
+                            "run_status": "succeeded",
+                            "flow_name": self.flow.name,
+                            "run_id": "argo-{{workflow.name}}",
+                        },
+                    },
+                }
+            ),
+            on_success=True,
+        )
+
+    def _incident_io_ui_urls_for_run(self):
+        links = []
+        if UI_URL:
+            url = "[Metaflow UI](%s/%s/%s)" % (
+                UI_URL.rstrip("/"),
+                self.flow.name,
+                "argo-{{workflow.name}}",
+            )
+            links.append(url)
+        if ARGO_WORKFLOWS_UI_URL:
+            url = "[Argo UI](%s/workflows/%s/%s)" % (
+                ARGO_WORKFLOWS_UI_URL.rstrip("/"),
+                "{{workflow.namespace}}",
+                "{{workflow.name}}",
+            )
+            links.append(url)
+        return links
+
+    def _pager_duty_change_template(self):
+        # https://developer.pagerduty.com/docs/ZG9jOjExMDI5NTgy-send-a-change-event
+        if self.notify_pager_duty_integration_key is None:
+            return None
+        return HttpExitHook(
+            name="notify-pager-duty-on-success",
+            method="POST",
+            url="https://events.pagerduty.com/v2/change/enqueue",
+            headers={"Content-Type": "application/json"},
+            body=json.dumps(
+                {
+                    "routing_key": self.notify_pager_duty_integration_key,
+                    "payload": {
+                        "summary": "Metaflow run %s/argo-{{workflow.name}} Succeeded"
+                        % self.flow.name,
+                        "source": "{{workflow.name}}",
+                        "custom_details": {
+                            "Flow": self.flow.name,
+                            "Run ID": "argo-{{workflow.name}}",
+                        },
+                    },
+                    "links": self._pager_duty_notification_links(),
+                }
+            ),
+            on_success=True,
+        )
+
+    def _pager_duty_notification_links(self):
+        links = []
+        if UI_URL:
+            links.append(
+                {
+                    "href": "%s/%s/%s"
+                    % (UI_URL.rstrip("/"), self.flow.name, "argo-{{workflow.name}}"),
+                    "text": "Metaflow UI",
+                }
+            )
+        if ARGO_WORKFLOWS_UI_URL:
+            links.append(
+                {
+                    "href": "%s/workflows/%s/%s"
+                    % (
+                        ARGO_WORKFLOWS_UI_URL.rstrip("/"),
+                        "{{workflow.namespace}}",
+                        "{{workflow.name}}",
+                    ),
+                    "text": "Argo UI",
+                }
+            )
+
+        return links
+
+    def _get_slack_blocks(self, message):
+        """
+        Use Slack's Block Kit to add general information about the environment and
+        execution metadata, including a link to the UI and an optional message.
+        """
+        ui_link = "%s/%s/argo-{{workflow.name}}" % (UI_URL.rstrip("/"), self.flow.name)
+        # fmt: off
+        if getattr(current, "project_name", None):
+            # Add @project metadata when available.
+            environment_details_block = {
+                "type": "section",
+                "text": {
+                    "type": "mrkdwn",
+                    "text": "Environment details"
+                },
+                "fields": [
+                    {
+                        "type": "mrkdwn",
+                        "text": "*Project:* %s" % current.project_name
+                    },
+                    {
+                        "type": "mrkdwn",
+                        "text": "*Project Branch:* %s" % current.branch_name
+                    }
+                ]
+            }
+        else:
+            environment_details_block = {
+                "type": "section",
+                "text": {
+                    "type": "mrkdwn",
+                    "text": "Environment details"
+                }
+            }
+
+        blocks = [
+            environment_details_block,
+            {
+                "type": "context",
+                "elements": [
+                    {
+                        "type": "mrkdwn",
+                        "text": " :information_source: *<%s>*" % ui_link,
+                    }
+                ],
+            },
+            {
+                "type": "divider"
+            },
+        ]
+
+        if message:
+            blocks += [
+                {
+                    "type": "section",
+                    "text": {
+                        "type": "mrkdwn",
+                        "text": message
+                    }
+                }
+            ]
+        # fmt: on
+        return blocks
+
+    def _slack_error_template(self):
+        if self.notify_slack_webhook_url is None:
+            return None
+
+        message = (
+            ":rotating_light: _%s/argo-{{workflow.name}}_ failed!" % self.flow.name
+        )
+        payload = {"text": message}
+        if UI_URL:
+            blocks = self._get_slack_blocks(message)
+            payload = {"text": message, "blocks": blocks}
+
+        return HttpExitHook(
+            name="notify-slack-on-error",
+            method="POST",
+            url=self.notify_slack_webhook_url,
+            body=json.dumps(payload),
+            on_error=True,
+        )
+
+    def _slack_success_template(self):
+        if self.notify_slack_webhook_url is None:
+            return None
+
+        message = (
+            ":white_check_mark: _%s/argo-{{workflow.name}}_ succeeded!" % self.flow.name
+        )
+        payload = {"text": message}
+        if UI_URL:
+            blocks = self._get_slack_blocks(message)
+            payload = {"text": message, "blocks": blocks}
+
+        return HttpExitHook(
+            name="notify-slack-on-success",
+            method="POST",
+            url=self.notify_slack_webhook_url,
+            body=json.dumps(payload),
+            on_success=True,
+        )
+
+    def _heartbeat_daemon_template(self):
+        # Use all the affordances available to _parameters task
+        executable = self.environment.executable("_parameters")
+        run_id = "argo-{{workflow.name}}"
+        script_name = os.path.basename(sys.argv[0])
+        entrypoint = [executable, script_name]
+        # FlowDecorators can define their own top-level options. These might affect run level information
+        # so it is important to pass these to the heartbeat process as well, as it might be the first task to register a run.
+        top_opts_dict = {}
+        for deco in flow_decorators(self.flow):
+            top_opts_dict.update(deco.get_top_level_options())
+
+        top_level = list(dict_to_cli_options(top_opts_dict)) + [
+            "--quiet",
+            "--metadata=%s" % self.metadata.TYPE,
+            "--environment=%s" % self.environment.TYPE,
+            "--datastore=%s" % self.flow_datastore.TYPE,
+            "--datastore-root=%s" % self.flow_datastore.datastore_root,
+            "--event-logger=%s" % self.event_logger.TYPE,
+            "--monitor=%s" % self.monitor.TYPE,
+            "--no-pylint",
+            "--with=argo_workflows_internal:auto-emit-argo-events=%i"
+            % self.auto_emit_argo_events,
+        ]
+        heartbeat_cmds = "{entrypoint} {top_level} argo-workflows heartbeat --run_id {run_id} {tags}".format(
+            entrypoint=" ".join(entrypoint),
+            top_level=" ".join(top_level) if top_level else "",
+            run_id=run_id,
+            tags=" ".join(["--tag %s" % t for t in self.tags]) if self.tags else "",
+        )
+
+        # TODO: we do not really need MFLOG logging for the daemon at the moment, but might be good for the future.
+        # Consider if we can do without this setup.
+        # Configure log capture.
+        mflog_expr = export_mflog_env_vars(
+            datastore_type=self.flow_datastore.TYPE,
+            stdout_path="$PWD/.logs/mflog_stdout",
+            stderr_path="$PWD/.logs/mflog_stderr",
+            flow_name=self.flow.name,
+            run_id=run_id,
+            step_name="_run_heartbeat_daemon",
+            task_id="1",
+            retry_count="0",
+        )
+        # TODO: Can the init be trimmed down?
+        # Can we do without get_package_commands fetching the whole code package?
+        init_cmds = " && ".join(
+            [
+                # For supporting sandboxes, ensure that a custom script is executed
+                # before anything else is executed. The script is passed in as an
+                # env var.
+                '${METAFLOW_INIT_SCRIPT:+eval \\"${METAFLOW_INIT_SCRIPT}\\"}',
+                "mkdir -p $PWD/.logs",
+                mflog_expr,
+            ]
+            + self.environment.get_package_commands(
+                self.code_package_url,
+                self.flow_datastore.TYPE,
+            )[:-1]
+            # Replace the line 'Task in starting'
+            # FIXME: this can be brittle.
+            + ["mflog 'Heartbeat daemon is starting.'"]
+        )
+
+        cmd_str = " && ".join([init_cmds, heartbeat_cmds])
+        cmds = shlex.split('bash -c "%s"' % cmd_str)
+
+        # Env required for sending heartbeats to the metadata service, nothing extra.
+        # prod token / runtime info is required to correctly register flow branches
+        env = {
+            # These values are needed by Metaflow to set it's internal
+            # state appropriately.
+            "METAFLOW_CODE_METADATA": self.code_package_metadata,
+            "METAFLOW_CODE_URL": self.code_package_url,
+            "METAFLOW_CODE_SHA": self.code_package_sha,
+            "METAFLOW_CODE_DS": self.flow_datastore.TYPE,
+            "METAFLOW_SERVICE_URL": SERVICE_INTERNAL_URL,
+            "METAFLOW_SERVICE_HEADERS": json.dumps(SERVICE_HEADERS),
+            "METAFLOW_USER": "argo-workflows",
+            "METAFLOW_DATASTORE_SYSROOT_S3": DATASTORE_SYSROOT_S3,
+            "METAFLOW_DATATOOLS_S3ROOT": DATATOOLS_S3ROOT,
+            "METAFLOW_DEFAULT_DATASTORE": self.flow_datastore.TYPE,
+            "METAFLOW_DEFAULT_METADATA": DEFAULT_METADATA,
+            "METAFLOW_CARD_S3ROOT": CARD_S3ROOT,
+            "METAFLOW_KUBERNETES_WORKLOAD": 1,
+            "METAFLOW_KUBERNETES_FETCH_EC2_METADATA": KUBERNETES_FETCH_EC2_METADATA,
+            "METAFLOW_RUNTIME_ENVIRONMENT": "kubernetes",
+            "METAFLOW_OWNER": self.username,
+            "METAFLOW_PRODUCTION_TOKEN": self.production_token,  # Used in identity resolving. This affects system tags.
+        }
+        # support Metaflow sandboxes
+        env["METAFLOW_INIT_SCRIPT"] = KUBERNETES_SANDBOX_INIT_SCRIPT
+
+        # cleanup env values
+        env = {
+            k: v
+            for k, v in env.items()
+            if v is not None
+            and k not in set(ARGO_WORKFLOWS_ENV_VARS_TO_SKIP.split(","))
+        }
+
+        # We want to grab the base image used by the start step, as this is known to be pullable from within the cluster,
+        # and it might contain the required libraries, allowing us to start up faster.
+        start_step = next(step for step in self.flow if step.name == "start")
+        resources = dict(
+            [deco for deco in start_step.decorators if deco.name == "kubernetes"][
+                0
+            ].attributes
+        )
+        from kubernetes import client as kubernetes_sdk
+
+        return (
+            DaemonTemplate("heartbeat-daemon")
+            # NOTE: Even though a retry strategy does not work for Argo daemon containers,
+            # this has the side-effect of protecting the exit hooks of the workflow from failing in case the daemon container errors out.
+            .retry_strategy(10, 1)
+            .service_account_name(resources["service_account"])
+            .container(
+                to_camelcase(
+                    kubernetes_sdk.V1Container(
+                        name="main",
+                        # TODO: Make the image configurable
+                        image=resources["image"],
+                        command=cmds,
+                        env=[
+                            kubernetes_sdk.V1EnvVar(name=k, value=str(v))
+                            for k, v in env.items()
+                        ],
+                        env_from=[
+                            kubernetes_sdk.V1EnvFromSource(
+                                secret_ref=kubernetes_sdk.V1SecretEnvSource(
+                                    name=str(k),
+                                    # optional=True
                                 )
-                            ]
-                            + (
-                                [
-                                    kubernetes_sdk.V1VolumeMount(
-                                        name="tmpfs-ephemeral-volume",
-                                        mount_path=tmpfs_path,
-                                    )
-                                ]
-                                if tmpfs_enabled
-                                else []
-                            ),
-                        ).to_dict()
+                            )
+                            for k in list(
+                                []
+                                if not resources.get("secrets")
+                                else (
+                                    [resources.get("secrets")]
+                                    if isinstance(resources.get("secrets"), str)
+                                    else resources.get("secrets")
+                                )
+                            )
+                            + KUBERNETES_SECRETS.split(",")
+                            + ARGO_WORKFLOWS_KUBERNETES_SECRETS.split(",")
+                            if k
+                        ],
+                        resources=kubernetes_sdk.V1ResourceRequirements(
+                            # NOTE: base resources for this are kept to a minimum to save on running costs.
+                            # This has an adverse effect on startup time for the daemon, which can be completely
+                            # alleviated by using a base image that has the required dependencies pre-installed
+                            requests={
+                                "cpu": "200m",
+                                "memory": "100Mi",
+                            },
+                            limits={
+                                "cpu": "200m",
+                                "memory": "100Mi",
+                            },
+                        ),
                     )
-                )
+                ).to_dict()
             )
+        )
 
     def _compile_sensor(self):
         # This method compiles a Metaflow @trigger decorator into Argo Events Sensor.
@@ -1274,7 +3140,7 @@ def _compile_sensor(self):
                 "https://argoproj.github.io/argo-events/eventsources/naming/. "
                 "It is very likely that all events for your deployment share the "
                 "same name. You can configure it by executing "
-                "`metaflow configure events` or setting METAFLOW_ARGO_EVENTS_EVENT "
+                "`metaflow configure kubernetes` or setting METAFLOW_ARGO_EVENTS_EVENT "
                 "in your configuration. If in doubt, reach out for support at "
                 "http://chat.metaflow.org"
             )
@@ -1286,7 +3152,7 @@ def _compile_sensor(self):
                 "An Argo Event Source name hasn't been configured for your deployment "
                 "yet. Please see this article for more details on event names - "
                 "https://argoproj.github.io/argo-events/eventsources/naming/. "
-                "You can configure it by executing `metaflow configure events` or "
+                "You can configure it by executing `metaflow configure kubernetes` or "
                 "setting METAFLOW_ARGO_EVENTS_EVENT_SOURCE in your configuration. If "
                 "in doubt, reach out for support at http://chat.metaflow.org"
             )
@@ -1297,7 +3163,7 @@ def _compile_sensor(self):
                 "An Argo Event service account hasn't been configured for your "
                 "deployment yet. Please see this article for more details on event "
                 "names - https://argoproj.github.io/argo-events/service-accounts/. "
-                "You can configure it by executing `metaflow configure events` or "
+                "You can configure it by executing `metaflow configure kubernetes` or "
                 "setting METAFLOW_ARGO_EVENTS_SERVICE_ACCOUNT in your configuration. "
                 "If in doubt, reach out for support at http://chat.metaflow.org"
             )
@@ -1313,33 +3179,16 @@ def _compile_sensor(self):
                 "sdk (https://pypi.org/project/kubernetes/) first."
             )
 
-        labels = {"app.kubernetes.io/part-of": "metaflow"}
-
-        annotations = {
-            "metaflow/production_token": self.production_token,
-            "metaflow/owner": self.username,
-            "metaflow/user": "argo-workflows",
-            "metaflow/flow_name": self.flow.name,
-        }
-        if current.get("project_name"):
-            annotations.update(
-                {
-                    "metaflow/project_name": current.project_name,
-                    "metaflow/branch_name": current.branch_name,
-                    "metaflow/project_flow_name": current.project_flow_name,
-                }
-            )
-
         return (
             Sensor()
             .metadata(
                 # Sensor metadata.
                 ObjectMeta()
-                .name(self.name.replace(".", "-"))
+                .name(ArgoWorkflows._sensor_name(self.name))
                 .namespace(KUBERNETES_NAMESPACE)
+                .labels(self._base_labels)
                 .label("app.kubernetes.io/name", "metaflow-sensor")
-                .label("app.kubernetes.io/part-of", "metaflow")
-                .annotations(annotations)
+                .annotations(self._base_annotations)
             )
             .spec(
                 SensorSpec().template(
@@ -1349,7 +3198,7 @@ def _compile_sensor(self):
                         ObjectMeta()
                         .label("app.kubernetes.io/name", "metaflow-sensor")
                         .label("app.kubernetes.io/part-of", "metaflow")
-                        .annotations(annotations)
+                        .annotations(self._base_annotations)
                     )
                     .container(
                         # Run sensor in guaranteed QoS. The sensor isn't doing a lot
@@ -1369,7 +3218,7 @@ def _compile_sensor(self):
                                         "memory": "250Mi",
                                     },
                                 ),
-                            )
+                            ).to_dict()
                         )
                     )
                     .service_account_name(ARGO_EVENTS_SERVICE_ACCOUNT)
@@ -1396,6 +3245,18 @@ def _compile_sensor(self):
                                         "metadata": {
                                             "generateName": "%s-" % self.name,
                                             "namespace": KUBERNETES_NAMESPACE,
+                                            # Useful to paint the UI
+                                            "annotations": {
+                                                "metaflow/triggered_by": json.dumps(
+                                                    [
+                                                        {
+                                                            key: trigger.get(key)
+                                                            for key in ["name", "type"]
+                                                        }
+                                                        for trigger in self.triggers
+                                                    ]
+                                                )
+                                            },
                                         },
                                         "spec": {
                                             "arguments": {
@@ -1431,14 +3292,40 @@ def _compile_sensor(self):
                                                 # Technically, we don't need to create
                                                 # a payload carry-on and can stuff
                                                 # everything within the body.
-                                                data_key="body.payload.%s" % v,
+                                                # NOTE: We need the conditional logic in order to successfully fall back to the default value
+                                                # when the event payload does not contain a key for a parameter.
+                                                # NOTE: Keys might contain dashes, so use the safer 'get' for fetching the value
+                                                data_template='{{ if (hasKey $.Input.body.payload "%s") }}{{- (get $.Input.body.payload "%s" %s) -}}{{- else -}}{{ (fail "use-default-instead") }}{{- end -}}'
+                                                % (
+                                                    v,
+                                                    v,
+                                                    (
+                                                        "| toRawJson | squote"
+                                                        if self.parameters[
+                                                            parameter_name
+                                                        ]["type"]
+                                                        == "JSON"
+                                                        else "| toRawJson"
+                                                    ),
+                                                ),
                                                 # Unfortunately the sensor needs to
                                                 # record the default values for
                                                 # the parameters - there doesn't seem
                                                 # to be any way for us to skip
-                                                value=self.parameters[parameter_name][
-                                                    "value"
-                                                ],
+                                                value=(
+                                                    json.dumps(
+                                                        self.parameters[parameter_name][
+                                                            "value"
+                                                        ]
+                                                    )
+                                                    if self.parameters[parameter_name][
+                                                        "type"
+                                                    ]
+                                                    == "JSON"
+                                                    else self.parameters[
+                                                        parameter_name
+                                                    ]["value"]
+                                                ),
                                             )
                                             .dest(
                                                 # this undocumented (mis?)feature in
@@ -1624,7 +3511,7 @@ def label(self, key, value):
     def labels(self, labels):
         if "labels" not in self.payload:
             self.payload["labels"] = {}
-        self.payload["labels"].update(labels)
+        self.payload["labels"].update(labels or {})
         return self
 
     def name(self, name):
@@ -1642,6 +3529,34 @@ def __str__(self):
         return json.dumps(self.to_json(), indent=4)
 
 
+class WorkflowStep(object):
+    def __init__(self):
+        tree = lambda: defaultdict(tree)
+        self.payload = tree()
+
+    def name(self, name):
+        self.payload["name"] = str(name)
+        return self
+
+    def template(self, template):
+        self.payload["template"] = str(template)
+        return self
+
+    def when(self, condition):
+        self.payload["when"] = str(condition)
+        return self
+
+    def step(self, expression):
+        self.payload["expression"] = str(expression)
+        return self
+
+    def to_json(self):
+        return self.payload
+
+    def __str__(self):
+        return json.dumps(self.to_json(), indent=4)
+
+
 class WorkflowSpec(object):
     # https://argoproj.github.io/argo-workflows/fields/#workflowspec
     # This object sets all Workflow level properties.
@@ -1672,6 +3587,11 @@ def entrypoint(self, entrypoint):
         self.payload["entrypoint"] = entrypoint
         return self
 
+    def onExit(self, on_exit_template):
+        if on_exit_template:
+            self.payload["onExit"] = on_exit_template
+        return self
+
     def parallelism(self, parallelism):
         # Set parallelism at Workflow level
         self.payload["parallelism"] = int(parallelism)
@@ -1702,6 +3622,14 @@ def templates(self, templates):
             self.payload["templates"].append(template.to_json())
         return self
 
+    def hooks(self, hooks):
+        # https://argoproj.github.io/argo-workflows/fields/#lifecyclehook
+        if "hooks" not in self.payload:
+            self.payload["hooks"] = {}
+        for k, v in hooks.items():
+            self.payload["hooks"].update({k: v.to_json()})
+        return self
+
     def to_json(self):
         return self.payload
 
@@ -1733,7 +3661,7 @@ def label(self, key, value):
     def labels(self, labels):
         if "labels" not in self.payload:
             self.payload["labels"] = {}
-        self.payload["labels"].update(labels)
+        self.payload["labels"].update(labels or {})
         return self
 
     def labels_from(self, labels_from):
@@ -1752,6 +3680,38 @@ def __str__(self):
         return json.dumps(self.to_json(), indent=4)
 
 
+class DaemonTemplate(object):
+    def __init__(self, name):
+        tree = lambda: defaultdict(tree)
+        self.name = name
+        self.payload = tree()
+        self.payload["daemon"] = True
+        self.payload["name"] = name
+
+    def container(self, container):
+        self.payload["container"] = container
+        return self
+
+    def service_account_name(self, service_account_name):
+        self.payload["serviceAccountName"] = service_account_name
+        return self
+
+    def retry_strategy(self, times, minutes_between_retries):
+        if times > 0:
+            self.payload["retryStrategy"] = {
+                "retryPolicy": "Always",
+                "limit": times,
+                "backoff": {"duration": "%sm" % minutes_between_retries},
+            }
+        return self
+
+    def to_json(self):
+        return self.payload
+
+    def __str__(self):
+        return json.dumps(self.payload, indent=4)
+
+
 class Template(object):
     # https://argoproj.github.io/argo-workflows/fields/#template
 
@@ -1770,12 +3730,28 @@ def dag(self, dag_template):
         self.payload["dag"] = dag_template.to_json()
         return self
 
+    def steps(self, steps):
+        if "steps" not in self.payload:
+            self.payload["steps"] = []
+        # steps is a list of lists.
+        # hence we go over every item in the incoming list
+        # serialize it and then append the list to the payload
+        step_list = []
+        for step in steps:
+            step_list.append(step.to_json())
+        self.payload["steps"].append(step_list)
+        return self
+
     def container(self, container):
         # Luckily this can simply be V1Container and we are spared from writing more
         # boilerplate - https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1Container.md.
         self.payload["container"] = container
         return self
 
+    def http(self, http):
+        self.payload["http"] = http.to_json()
+        return self
+
     def inputs(self, inputs):
         self.payload["inputs"] = inputs.to_json()
         return self
@@ -1838,6 +3814,34 @@ def empty_dir_volume(self, name, medium=None, size_limit=None):
         )
         return self
 
+    def pvc_volumes(self, pvcs=None):
+        """
+        Create and attach Persistent Volume Claims as volumes.
+
+        Parameters:
+        -----------
+        pvcs: Optional[Dict]
+            a dictionary of pvc's and the paths they should be mounted to. e.g.
+            {"pv-claim-1": "/mnt/path1", "pv-claim-2": "/mnt/path2"}
+        """
+        if pvcs is None:
+            return self
+        if "volumes" not in self.payload:
+            self.payload["volumes"] = []
+        for claim in pvcs.keys():
+            self.payload["volumes"].append(
+                {"name": claim, "persistentVolumeClaim": {"claimName": claim}}
+            )
+        return self
+
+    def pod_spec_patch(self, pod_spec_patch=None):
+        if pod_spec_patch is None:
+            return self
+
+        self.payload["podSpecPatch"] = json.dumps(pod_spec_patch)
+
+        return self
+
     def node_selectors(self, node_selectors):
         if "nodeSelector" not in self.payload:
             self.payload["nodeSelector"] = {}
@@ -1852,6 +3856,15 @@ def tolerations(self, tolerations):
     def to_json(self):
         return self.payload
 
+    def resource(self, action, manifest, success_criteria, failure_criteria):
+        self.payload["resource"] = {}
+        self.payload["resource"]["action"] = action
+        self.payload["resource"]["setOwnerReference"] = True
+        self.payload["resource"]["successCondition"] = success_criteria
+        self.payload["resource"]["failureCondition"] = failure_criteria
+        self.payload["resource"]["manifest"] = manifest
+        return self
+
     def __str__(self):
         return json.dumps(self.payload, indent=4)
 
@@ -2255,10 +4268,11 @@ def __init__(self):
         tree = lambda: defaultdict(tree)
         self.payload = tree()
 
-    def src(self, dependency_name, data_key, value):
+    def src(self, dependency_name, value, data_key=None, data_template=None):
         self.payload["src"] = {
             "dependencyName": dependency_name,
             "dataKey": data_key,
+            "dataTemplate": data_template,
             "value": value,
             # explicitly set it to false to ensure proper deserialization
             "useRawData": False,
diff --git a/metaflow/plugins/argo/argo_workflows_cli.py b/metaflow/plugins/argo/argo_workflows_cli.py
index 0ef28b5f339..c4573fa2f10 100644
--- a/metaflow/plugins/argo/argo_workflows_cli.py
+++ b/metaflow/plugins/argo/argo_workflows_cli.py
@@ -3,13 +3,23 @@
 import platform
 import re
 import sys
-from distutils.version import LooseVersion
 from hashlib import sha1
+from time import sleep
 
-from metaflow import JSONType, current, decorators, parameters
+from metaflow import JSONType, Run, current, decorators, parameters
 from metaflow._vendor import click
-from metaflow.exception import MetaflowException, MetaflowInternalError
-from metaflow.metaflow_config import SERVICE_VERSION_CHECK, UI_URL
+from metaflow.exception import (
+    MetaflowException,
+    MetaflowInternalError,
+    MetaflowNotFound,
+)
+from metaflow.metaflow_config import (
+    ARGO_WORKFLOWS_UI_URL,
+    FEAT_ALWAYS_UPLOAD_CODE_PACKAGE,
+    KUBERNETES_NAMESPACE,
+    SERVICE_VERSION_CHECK,
+    UI_URL,
+)
 from metaflow.package import MetaflowPackage
 
 # TODO: Move production_token to utils
@@ -21,17 +31,28 @@
 from metaflow.plugins.environment_decorator import EnvironmentDecorator
 from metaflow.plugins.kubernetes.kubernetes_decorator import KubernetesDecorator
 from metaflow.tagging_util import validate_tags
-from metaflow.util import get_username, to_bytes, to_unicode
+from metaflow.util import get_username, to_bytes, to_unicode, version_parse
 
 from .argo_workflows import ArgoWorkflows
 
-VALID_NAME = re.compile("^[a-z0-9]([a-z0-9\.\-]*[a-z0-9])?$")
+VALID_NAME = re.compile(r"^[a-z0-9]([a-z0-9\.\-]*[a-z0-9])?$")
+
+unsupported_decorators = {
+    "snowpark": "Step *%s* is marked for execution on Snowpark with Argo Workflows which isn't currently supported.",
+    "slurm": "Step *%s* is marked for execution on Slurm with Argo Workflows which isn't currently supported.",
+    "nvidia": "Step *%s* is marked for execution on Nvidia with Argo Workflows which isn't currently supported.",
+    "nvct": "Step *%s* is marked for execution on Nvct with Argo Workflows which isn't currently supported.",
+}
 
 
 class IncorrectProductionToken(MetaflowException):
     headline = "Incorrect production token"
 
 
+class RunIdMismatch(MetaflowException):
+    headline = "Run ID mismatch"
+
+
 class IncorrectMetadataServiceVersion(MetaflowException):
     headline = "Incorrect version for metaflow service"
 
@@ -111,6 +132,7 @@ def argo_workflows(obj, name=None):
     is_flag=True,
     default=False,
     help="Only print out JSON sent to Argo Workflows. Do not deploy anything.",
+    hidden=True,
 )
 @click.option(
     "--max-workers",
@@ -126,14 +148,73 @@ def argo_workflows(obj, name=None):
     default=None,
     type=int,
     help="Workflow priority as an integer. Workflows with higher priority "
-    "are processed first if Argo Workflows controller is configured to process limited "
-    "number of workflows in parallel",
+    "are processed first if Argo Workflows controller is configured to process "
+    "limited number of workflows in parallel",
 )
 @click.option(
     "--auto-emit-argo-events/--no-auto-emit-argo-events",
-    default=False,  # TODO: Default to a value from config
+    default=True,  # TODO: Default to a value from config
+    show_default=True,
+    help="Auto emits Argo Events when the run completes successfully.",
+)
+@click.option(
+    "--notify-on-error/--no-notify-on-error",
+    default=False,
+    show_default=True,
+    help="Notify if the workflow fails.",
+)
+@click.option(
+    "--notify-on-success/--no-notify-on-success",
+    default=False,
+    show_default=True,
+    help="Notify if the workflow succeeds.",
+)
+@click.option(
+    "--notify-slack-webhook-url",
+    default=None,
+    help="Slack incoming webhook url for workflow success/failure notifications.",
+)
+@click.option(
+    "--notify-pager-duty-integration-key",
+    default=None,
+    help="PagerDuty Events API V2 Integration key for workflow success/failure notifications.",
+)
+@click.option(
+    "--notify-incident-io-api-key",
+    default=None,
+    help="Incident.io API V2 key for workflow success/failure notifications.",
+)
+@click.option(
+    "--incident-io-alert-source-config-id",
+    default=None,
+    help="Incident.io Alert source config ID. Example '01GW2G3V0S59R238FAHPDS1R66'",
+)
+@click.option(
+    "--incident-io-metadata",
+    default=None,
+    type=str,
+    multiple=True,
+    help="Incident.io Alert Custom Metadata field in the form of Key=Value",
+)
+@click.option(
+    "--enable-heartbeat-daemon/--no-enable-heartbeat-daemon",
+    default=False,
+    show_default=True,
+    help="Use a daemon container to broadcast heartbeats.",
+)
+@click.option(
+    "--deployer-attribute-file",
+    default=None,
+    show_default=True,
+    type=str,
+    help="Write the workflow name to the file specified. Used internally for Metaflow's Deployer API.",
+    hidden=True,
+)
+@click.option(
+    "--enable-error-msg-capture/--no-enable-error-msg-capture",
+    default=True,
     show_default=True,
-    help="Auto emits Argo Events when the run completes successfully",
+    help="Capture stack trace of first failed task in exit hook.",
 )
 @click.pass_obj
 def create(
@@ -148,9 +229,35 @@ def create(
     workflow_timeout=None,
     workflow_priority=None,
     auto_emit_argo_events=False,
+    notify_on_error=False,
+    notify_on_success=False,
+    notify_slack_webhook_url=None,
+    notify_pager_duty_integration_key=None,
+    notify_incident_io_api_key=None,
+    incident_io_alert_source_config_id=None,
+    incident_io_metadata=None,
+    enable_heartbeat_daemon=True,
+    deployer_attribute_file=None,
+    enable_error_msg_capture=False,
 ):
+    for node in obj.graph:
+        for decorator, error_message in unsupported_decorators.items():
+            if any([d.name == decorator for d in node.decorators]):
+                raise MetaflowException(error_message % node.name)
+
     validate_tags(tags)
 
+    if deployer_attribute_file:
+        with open(deployer_attribute_file, "w", encoding="utf-8") as f:
+            json.dump(
+                {
+                    "name": obj.workflow_name,
+                    "flow_name": obj.flow.name,
+                    "metadata": obj.metadata.metadata_str(),
+                },
+                f,
+            )
+
     obj.echo("Deploying *%s* to Argo Workflows..." % obj.workflow_name, bold=True)
 
     if SERVICE_VERSION_CHECK:
@@ -180,6 +287,15 @@ def create(
         workflow_timeout,
         workflow_priority,
         auto_emit_argo_events,
+        notify_on_error,
+        notify_on_success,
+        notify_slack_webhook_url,
+        notify_pager_duty_integration_key,
+        notify_incident_io_api_key,
+        incident_io_alert_source_config_id,
+        incident_io_metadata,
+        enable_heartbeat_daemon,
+        enable_error_msg_capture,
     )
 
     if only_json:
@@ -201,6 +317,17 @@ def create(
                 "due to Kubernetes naming conventions\non Argo Workflows. The "
                 "original flow name is stored in the workflow annotation.\n"
             )
+
+        if ARGO_WORKFLOWS_UI_URL:
+            obj.echo("See the deployed workflow here:", bold=True)
+            argo_workflowtemplate_link = "%s/workflow-templates/%s" % (
+                ARGO_WORKFLOWS_UI_URL.rstrip("/"),
+                KUBERNETES_NAMESPACE,
+            )
+            obj.echo(
+                "%s/%s\n\n" % (argo_workflowtemplate_link, obj.workflow_name),
+                indent=True,
+            )
         flow.schedule()
         obj.echo("What will trigger execution of the workflow:", bold=True)
         obj.echo(flow.trigger_explanation(), indent=True)
@@ -218,20 +345,20 @@ def create(
 
 
 def check_python_version(obj):
-    # argo-workflows integration for Metaflow isn't supported for Py versions below 3.5.
+    # argo-workflows integration for Metaflow isn't supported for Py versions below 3.6.
     # This constraint can very well be lifted if desired.
-    if sys.version_info < (3, 5):
+    if sys.version_info < (3, 6):
         obj.echo("")
         obj.echo(
             "Metaflow doesn't support Argo Workflows for Python %s right now."
             % platform.python_version()
         )
         obj.echo(
-            "Please upgrade your Python interpreter to version 3.5 (or higher) or "
+            "Please upgrade your Python interpreter to version 3.6 (or higher) or "
             "reach out to us at slack.outerbounds.co for more help."
         )
         raise UnsupportedPythonVersion(
-            "Try again with a more recent version of Python (>=3.5)."
+            "Try again with a more recent version of Python (>=3.6)."
         )
 
 
@@ -240,7 +367,7 @@ def check_metadata_service_version(obj):
     version = metadata.version()
     if version == "local":
         return
-    elif version is not None and LooseVersion(version) >= LooseVersion("2.0.2"):
+    elif version is not None and version_parse(version) >= version_parse("2.0.2"):
         # Metaflow metadata service needs to be at least at version 2.0.2
         # since prior versions did not support strings as object ids.
         return
@@ -287,6 +414,8 @@ def resolve_workflow_name(obj, name):
         # by default. Also, while project and branch allow for underscores, Argo
         # Workflows doesn't (DNS Subdomain names as defined in RFC 1123) - so we will
         # remove any underscores as well as convert the name to lower case.
+        # Also remove + and @ as not allowed characters, which can be part of the
+        # project branch due to using email addresses as user names.
         if len(workflow_name) > 253:
             name_hash = to_unicode(
                 base64.b32encode(sha1(to_bytes(workflow_name)).digest())
@@ -294,12 +423,7 @@ def resolve_workflow_name(obj, name):
             workflow_name = "%s-%s" % (workflow_name[:242], name_hash)
             obj._is_workflow_name_modified = True
         if not VALID_NAME.search(workflow_name):
-            workflow_name = (
-                re.compile(r"^[^A-Za-z0-9]+")
-                .sub("", workflow_name)
-                .replace("_", "")
-                .lower()
-            )
+            workflow_name = sanitize_for_argo(workflow_name)
             obj._is_workflow_name_modified = True
     else:
         if name and not VALID_NAME.search(name):
@@ -324,12 +448,7 @@ def resolve_workflow_name(obj, name):
             raise ArgoWorkflowsNameTooLong(msg)
 
         if not VALID_NAME.search(workflow_name):
-            workflow_name = (
-                re.compile(r"^[^A-Za-z0-9]+")
-                .sub("", workflow_name)
-                .replace("_", "")
-                .lower()
-            )
+            workflow_name = sanitize_for_argo(workflow_name)
             obj._is_workflow_name_modified = True
 
     return workflow_name, token_prefix.lower(), is_project
@@ -345,6 +464,15 @@ def make_flow(
     workflow_timeout,
     workflow_priority,
     auto_emit_argo_events,
+    notify_on_error,
+    notify_on_success,
+    notify_slack_webhook_url,
+    notify_pager_duty_integration_key,
+    notify_incident_io_api_key,
+    incident_io_alert_source_config_id,
+    incident_io_metadata,
+    enable_heartbeat_daemon,
+    enable_error_msg_capture,
 ):
     # TODO: Make this check less specific to Amazon S3 as we introduce
     #       support for more cloud object stores.
@@ -353,29 +481,66 @@ def make_flow(
             "Argo Workflows requires --datastore=s3 or --datastore=azure or --datastore=gs"
         )
 
+    if (notify_on_error or notify_on_success) and not (
+        notify_slack_webhook_url
+        or notify_pager_duty_integration_key
+        or notify_incident_io_api_key
+    ):
+        raise MetaflowException(
+            "Notifications require specifying an incoming Slack webhook url via --notify-slack-webhook-url, PagerDuty events v2 integration key via --notify-pager-duty-integration-key or\n"
+            "Incident.io integration API key via --notify-incident-io-api-key.\n"
+            " If you would like to set up notifications for your Slack workspace, follow the instructions at "
+            "https://api.slack.com/messaging/webhooks to generate a webhook url.\n"
+            " For notifications through PagerDuty, generate an integration key by following the instructions at "
+            "https://support.pagerduty.com/docs/services-and-integrations#create-a-generic-events-api-integration\n"
+            " For notifications through Incident.io, generate an alert source config."
+        )
+
+    if (
+        (notify_on_error or notify_on_success)
+        and notify_incident_io_api_key
+        and incident_io_alert_source_config_id is None
+    ):
+        raise MetaflowException(
+            "Incident.io alerts require an alert source configuration ID. Please set one with --incident-io-alert-source-config-id"
+        )
+
     # Attach @kubernetes and @environment decorator to the flow to
     # ensure that the related decorator hooks are invoked.
     decorators._attach_decorators(
         obj.flow, [KubernetesDecorator.name, EnvironmentDecorator.name]
     )
+    decorators._init(obj.flow)
 
     decorators._init_step_decorators(
         obj.flow, obj.graph, obj.environment, obj.flow_datastore, obj.logger
     )
+    obj.graph = obj.flow._graph
 
     # Save the code package in the flow datastore so that both user code and
     # metaflow package can be retrieved during workflow execution.
     obj.package = MetaflowPackage(
-        obj.flow, obj.environment, obj.echo, obj.package_suffixes
+        obj.flow,
+        obj.environment,
+        obj.echo,
+        suffixes=obj.package_suffixes,
+        flow_datastore=obj.flow_datastore if FEAT_ALWAYS_UPLOAD_CODE_PACKAGE else None,
     )
-    package_url, package_sha = obj.flow_datastore.save_data(
-        [obj.package.blob], len_hint=1
-    )[0]
+
+    # This blocks until the package is created
+    if FEAT_ALWAYS_UPLOAD_CODE_PACKAGE:
+        package_url = obj.package.package_url()
+        package_sha = obj.package.package_sha()
+    else:
+        package_url, package_sha = obj.flow_datastore.save_data(
+            [obj.package.blob], len_hint=1
+        )[0]
 
     return ArgoWorkflows(
         name,
         obj.graph,
         obj.flow,
+        obj.package.package_metadata,
         package_sha,
         package_url,
         token,
@@ -391,6 +556,15 @@ def make_flow(
         workflow_timeout=workflow_timeout,
         workflow_priority=workflow_priority,
         auto_emit_argo_events=auto_emit_argo_events,
+        notify_on_error=notify_on_error,
+        notify_on_success=notify_on_success,
+        notify_slack_webhook_url=notify_slack_webhook_url,
+        notify_pager_duty_integration_key=notify_pager_duty_integration_key,
+        notify_incident_io_api_key=notify_incident_io_api_key,
+        incident_io_alert_source_config_id=incident_io_alert_source_config_id,
+        incident_io_metadata=incident_io_metadata,
+        enable_heartbeat_daemon=enable_heartbeat_daemon,
+        enable_error_msg_capture=enable_error_msg_capture,
     )
 
 
@@ -502,8 +676,16 @@ def resolve_token(
     type=str,
     help="Write the ID of this run to the file specified.",
 )
+@click.option(
+    "--deployer-attribute-file",
+    default=None,
+    show_default=True,
+    type=str,
+    help="Write the metadata and pathspec of this run to the file specified.\nUsed internally for Metaflow's Deployer API.",
+    hidden=True,
+)
 @click.pass_obj
-def trigger(obj, run_id_file=None, **kwargs):
+def trigger(obj, run_id_file=None, deployer_attribute_file=None, **kwargs):
     def _convert_value(param):
         # Swap `-` with `_` in parameter name to match click's behavior
         val = kwargs.get(param.name.replace("-", "_").lower())
@@ -526,6 +708,17 @@ def _convert_value(param):
         with open(run_id_file, "w") as f:
             f.write(str(run_id))
 
+    if deployer_attribute_file:
+        with open(deployer_attribute_file, "w") as f:
+            json.dump(
+                {
+                    "name": obj.workflow_name,
+                    "metadata": obj.metadata.metadata_str(),
+                    "pathspec": "/".join((obj.flow.name, run_id)),
+                },
+                f,
+            )
+
     obj.echo(
         "Workflow *{name}* triggered on Argo Workflows "
         "(run-id *{run_id}*).".format(name=obj.workflow_name, run_id=run_id),
@@ -541,3 +734,391 @@ def _convert_value(param):
             "See the run in the UI at %s" % run_url,
             bold=True,
         )
+
+
+@argo_workflows.command(help="Delete the flow on Argo Workflows.")
+@click.option(
+    "--authorize",
+    default=None,
+    type=str,
+    help="Authorize the deletion with a production token",
+)
+@click.pass_obj
+def delete(obj, authorize=None):
+    def _token_instructions(flow_name, prev_user):
+        obj.echo(
+            "There is an existing version of *%s* on Argo Workflows which was "
+            "deployed by the user *%s*." % (flow_name, prev_user)
+        )
+        obj.echo(
+            "To delete this flow, you need to use the same production token that they used."
+        )
+        obj.echo(
+            "Please reach out to them to get the token. Once you have it, call "
+            "this command:"
+        )
+        obj.echo("    argo-workflows delete --authorize MY_TOKEN", fg="green")
+        obj.echo(
+            'See "Organizing Results" at docs.metaflow.org for more information '
+            "about production tokens."
+        )
+
+    validate_token(obj.workflow_name, obj.token_prefix, authorize, _token_instructions)
+    obj.echo("Deleting workflow *{name}*...".format(name=obj.workflow_name), bold=True)
+
+    schedule_deleted, sensor_deleted, workflow_deleted = ArgoWorkflows.delete(
+        obj.workflow_name
+    )
+
+    if schedule_deleted:
+        obj.echo(
+            "Deleting cronworkflow *{name}*...".format(name=obj.workflow_name),
+            bold=True,
+        )
+
+    if sensor_deleted:
+        obj.echo(
+            "Deleting sensor *{name}*...".format(name=obj.workflow_name),
+            bold=True,
+        )
+
+    if workflow_deleted:
+        obj.echo(
+            "Deleting Kubernetes resources may take a while. "
+            "Deploying the flow again to Argo Workflows while the delete is in-flight will fail."
+        )
+        obj.echo(
+            "In-flight executions will not be affected. "
+            "If necessary, terminate them manually."
+        )
+
+
+@argo_workflows.command(help="Suspend flow execution on Argo Workflows.")
+@click.option(
+    "--authorize",
+    default=None,
+    type=str,
+    help="Authorize the suspension with a production token",
+)
+@click.argument("run-id", required=True, type=str)
+@click.pass_obj
+def suspend(obj, run_id, authorize=None):
+    def _token_instructions(flow_name, prev_user):
+        obj.echo(
+            "There is an existing version of *%s* on Argo Workflows which was "
+            "deployed by the user *%s*." % (flow_name, prev_user)
+        )
+        obj.echo(
+            "To suspend this flow, you need to use the same production token that they used."
+        )
+        obj.echo(
+            "Please reach out to them to get the token. Once you have it, call "
+            "this command:"
+        )
+        obj.echo("    argo-workflows suspend RUN_ID --authorize MY_TOKEN", fg="green")
+        obj.echo(
+            'See "Organizing Results" at docs.metaflow.org for more information '
+            "about production tokens."
+        )
+
+    validate_run_id(
+        obj.workflow_name, obj.token_prefix, authorize, run_id, _token_instructions
+    )
+
+    # Trim prefix from run_id
+    name = run_id[5:]
+
+    workflow_suspended = ArgoWorkflows.suspend(name)
+
+    if workflow_suspended:
+        obj.echo("Suspended execution of *%s*" % run_id)
+
+
+@argo_workflows.command(help="Unsuspend flow execution on Argo Workflows.")
+@click.option(
+    "--authorize",
+    default=None,
+    type=str,
+    help="Authorize the unsuspend with a production token",
+)
+@click.argument("run-id", required=True, type=str)
+@click.pass_obj
+def unsuspend(obj, run_id, authorize=None):
+    def _token_instructions(flow_name, prev_user):
+        obj.echo(
+            "There is an existing version of *%s* on Argo Workflows which was "
+            "deployed by the user *%s*." % (flow_name, prev_user)
+        )
+        obj.echo(
+            "To unsuspend this flow, you need to use the same production token that they used."
+        )
+        obj.echo(
+            "Please reach out to them to get the token. Once you have it, call "
+            "this command:"
+        )
+        obj.echo(
+            "    argo-workflows unsuspend RUN_ID --authorize MY_TOKEN",
+            fg="green",
+        )
+        obj.echo(
+            'See "Organizing Results" at docs.metaflow.org for more information '
+            "about production tokens."
+        )
+
+    validate_run_id(
+        obj.workflow_name, obj.token_prefix, authorize, run_id, _token_instructions
+    )
+
+    # Trim prefix from run_id
+    name = run_id[5:]
+
+    workflow_suspended = ArgoWorkflows.unsuspend(name)
+
+    if workflow_suspended:
+        obj.echo("Unsuspended execution of *%s*" % run_id)
+
+
+def validate_token(name, token_prefix, authorize, instructions_fn=None):
+    """
+    Validate that the production token matches that of the deployed flow.
+    In case both the user and token do not match, raises an error.
+    Optionally outputs instructions on token usage via the provided instruction_fn(flow_name, prev_user)
+    """
+    # TODO: Unify this with the existing resolve_token implementation.
+
+    # 1) retrieve the previous deployment, if one exists
+    workflow = ArgoWorkflows.get_existing_deployment(name)
+    if workflow is None:
+        prev_token = None
+    else:
+        prev_user, prev_token = workflow
+
+    # 2) authorize this deployment
+    if prev_token is not None:
+        if authorize is None:
+            authorize = load_token(token_prefix)
+        elif authorize.startswith("production:"):
+            authorize = authorize[11:]
+
+        # we allow the user who deployed the previous version to re-deploy,
+        # even if they don't have the token
+        # NOTE: The username is visible in multiple sources, and can be set by the user.
+        # Should we consider being stricter here?
+        if prev_user != get_username() and authorize != prev_token:
+            if instructions_fn:
+                instructions_fn(flow_name=name, prev_user=prev_user)
+            raise IncorrectProductionToken(
+                "Try again with the correct production token."
+            )
+
+    # 3) all validations passed, store the previous token for future use
+    token = prev_token
+
+    store_token(token_prefix, token)
+    return True
+
+
+def get_run_object(pathspec: str):
+    try:
+        return Run(pathspec, _namespace_check=False)
+    except MetaflowNotFound:
+        return None
+
+
+def get_status_considering_run_object(status, run_obj):
+    remapped_status = remap_status(status)
+    if remapped_status == "Running" and run_obj is None:
+        return "Pending"
+    return remapped_status
+
+
+@argo_workflows.command(help="Fetch flow execution status on Argo Workflows.")
+@click.argument("run-id", required=True, type=str)
+@click.pass_obj
+def status(obj, run_id):
+    if not run_id.startswith("argo-"):
+        raise RunIdMismatch(
+            "Run IDs for flows executed through Argo Workflows begin with 'argo-'"
+        )
+    obj.echo(
+        "Fetching status for run *{run_id}* for {flow_name} ...".format(
+            run_id=run_id, flow_name=obj.flow.name
+        ),
+        bold=True,
+    )
+    # Trim prefix from run_id
+    name = run_id[5:]
+    status = ArgoWorkflows.get_workflow_status(obj.flow.name, name)
+    run_obj = get_run_object("/".join((obj.flow.name, run_id)))
+    if status is not None:
+        status = get_status_considering_run_object(status, run_obj)
+        obj.echo_always(status)
+
+
+@argo_workflows.command(help="Terminate flow execution on Argo Workflows.")
+@click.option(
+    "--authorize",
+    default=None,
+    type=str,
+    help="Authorize the termination with a production token",
+)
+@click.argument("run-id", required=True, type=str)
+@click.pass_obj
+def terminate(obj, run_id, authorize=None):
+    def _token_instructions(flow_name, prev_user):
+        obj.echo(
+            "There is an existing version of *%s* on Argo Workflows which was "
+            "deployed by the user *%s*." % (flow_name, prev_user)
+        )
+        obj.echo(
+            "To terminate this flow, you need to use the same production token that they used."
+        )
+        obj.echo(
+            "Please reach out to them to get the token. Once you have it, call "
+            "this command:"
+        )
+        obj.echo("    argo-workflows terminate --authorize MY_TOKEN RUN_ID", fg="green")
+        obj.echo(
+            'See "Organizing Results" at docs.metaflow.org for more information '
+            "about production tokens."
+        )
+
+    validate_run_id(
+        obj.workflow_name, obj.token_prefix, authorize, run_id, _token_instructions
+    )
+
+    # Trim prefix from run_id
+    name = run_id[5:]
+    obj.echo(
+        "Terminating run *{run_id}* for {flow_name} ...".format(
+            run_id=run_id, flow_name=obj.flow.name
+        ),
+        bold=True,
+    )
+
+    terminated = ArgoWorkflows.terminate(obj.flow.name, name)
+    if terminated:
+        obj.echo("\nRun terminated.")
+
+
+@argo_workflows.command(help="List Argo Workflow templates for the flow.")
+@click.option(
+    "--all",
+    default=False,
+    is_flag=True,
+    type=bool,
+    help="list all Argo Workflow Templates (not just limited to this flow)",
+)
+@click.pass_obj
+def list_workflow_templates(obj, all=None):
+    templates = ArgoWorkflows.list_templates(obj.flow.name, all)
+    for template_name in templates:
+        obj.echo_always(template_name)
+
+
+# Internal CLI command to run a heartbeat daemon in an Argo Workflows Daemon container.
+@argo_workflows.command(hidden=True, help="start heartbeat process for a run")
+@click.option("--run_id", required=True)
+@click.option(
+    "--tag",
+    "tags",
+    multiple=True,
+    default=None,
+    help="Annotate all objects produced by Argo Workflows runs "
+    "with the given tag. You can specify this option multiple "
+    "times to attach multiple tags.",
+)
+@click.pass_obj
+def heartbeat(obj, run_id, tags=None):
+    # Try to register a run in case the start task has not taken care of it yet.
+    obj.metadata.register_run_id(run_id, tags)
+    # Start run heartbeat
+    obj.metadata.start_run_heartbeat(obj.flow.name, run_id)
+    # Keepalive loop
+    while True:
+        # Do not pollute daemon logs with anything unnecessary,
+        # as they might be extremely long running.
+        sleep(10)
+
+
+def validate_run_id(
+    workflow_name, token_prefix, authorize, run_id, instructions_fn=None
+):
+    """
+    Validates that a run_id adheres to the Argo Workflows naming rules, and
+    that it belongs to the current flow (accounting for project branch as well).
+    """
+    # Verify that user is trying to change an Argo workflow
+    if not run_id.startswith("argo-"):
+        raise RunIdMismatch(
+            "Run IDs for flows executed through Argo Workflows begin with 'argo-'"
+        )
+
+    # Verify that run_id belongs to the Flow, and that branches match
+    name = run_id[5:]
+    workflow = ArgoWorkflows.get_execution(name)
+    if workflow is None:
+        raise MetaflowException("Could not find workflow *%s* on Argo Workflows" % name)
+
+    owner, token, flow_name, branch_name, project_name = workflow
+
+    # Verify we are operating on the correct Flow file compared to the running one.
+    # Without this check, using --name could be used to run commands for arbitrary run_id's, disregarding the Flow in the file.
+    if current.flow_name != flow_name:
+        raise RunIdMismatch(
+            "The workflow with the run_id *%s* belongs to the flow *%s*, not for the flow *%s*."
+            % (run_id, flow_name, current.flow_name)
+        )
+
+    if project_name is not None:
+        # Verify we are operating on the correct project.
+        if current.get("project_name") != project_name:
+            raise RunIdMismatch(
+                "The workflow belongs to the project *%s*. "
+                "Please use the project decorator or --name to target the correct project"
+                % project_name
+            )
+
+        # Verify we are operating on the correct branch.
+        if current.get("branch_name") != branch_name:
+            raise RunIdMismatch(
+                "The workflow belongs to the branch *%s*. "
+                "Please use --branch, --production or --name to target the correct branch"
+                % branch_name
+            )
+
+    # Verify that the production tokens match. We do not want to cache the token that was used though,
+    # as the operations that require run_id validation can target runs not authored from the local environment
+    if authorize is None:
+        authorize = load_token(token_prefix)
+    elif authorize.startswith("production:"):
+        authorize = authorize[11:]
+
+    if owner != get_username() and authorize != token:
+        if instructions_fn:
+            instructions_fn(flow_name=name, prev_user=owner)
+        raise IncorrectProductionToken("Try again with the correct production token.")
+
+    return True
+
+
+def sanitize_for_argo(text):
+    """
+    Sanitizes a string so it does not contain characters that are not permitted in Argo Workflow resource names.
+    """
+    return (
+        re.compile(r"^[^A-Za-z0-9]+")
+        .sub("", text)
+        .replace("_", "")
+        .replace("@", "")
+        .replace("+", "")
+        .lower()
+    )
+
+
+def remap_status(status):
+    """
+    Group similar Argo Workflow statuses together in order to have similar output to step functions statuses.
+    """
+    STATUS_MAP = {"Error": "Failed"}
+    return STATUS_MAP.get(status, status)
diff --git a/metaflow/plugins/argo/argo_workflows_decorator.py b/metaflow/plugins/argo/argo_workflows_decorator.py
index 8c0abede698..ce92d34b5b4 100644
--- a/metaflow/plugins/argo/argo_workflows_decorator.py
+++ b/metaflow/plugins/argo/argo_workflows_decorator.py
@@ -1,13 +1,13 @@
 import json
 import os
-import time
+
 
 from metaflow import current
 from metaflow.decorators import StepDecorator
 from metaflow.events import Trigger
-from metaflow.metadata import MetaDatum
-from metaflow.metaflow_config import ARGO_EVENTS_WEBHOOK_URL
-
+from metaflow.metadata_provider import MetaDatum
+from metaflow.graph import FlowGraph
+from metaflow.flowspec import FlowSpec
 from .argo_events import ArgoEvent
 
 
@@ -40,7 +40,7 @@ def task_pre_step(
                 if payload != "null":  # Argo-Workflow's None
                     try:
                         payload = json.loads(payload)
-                    except (TypeError, ValueError) as e:
+                    except (TypeError, ValueError):
                         # There could be arbitrary events that Metaflow doesn't know of
                         payload = {}
                     triggers.append(
@@ -52,7 +52,7 @@ def task_pre_step(
                                 "_", 1
                             )[
                                 0
-                            ]  # infer type from env var key
+                            ],  # infer type from env var key
                             # Add more event metadata here in the future
                         }
                     )
@@ -72,6 +72,7 @@ def task_pre_step(
         meta["argo-workflow-name"] = os.environ["ARGO_WORKFLOW_NAME"]
         meta["argo-workflow-namespace"] = os.environ["ARGO_WORKFLOW_NAMESPACE"]
         meta["auto-emit-argo-events"] = self.attributes["auto-emit-argo-events"]
+        meta["argo-workflow-template-owner"] = os.environ["METAFLOW_OWNER"]
         entries = [
             MetaDatum(
                 field=k, value=v, type=k, tags=["attempt_id:{0}".format(retry_count)]
@@ -82,7 +83,13 @@ def task_pre_step(
         metadata.register_metadata(run_id, step_name, task_id, entries)
 
     def task_finished(
-        self, step_name, flow, graph, is_task_ok, retry_count, max_user_code_retries
+        self,
+        step_name,
+        flow: FlowSpec,
+        graph: FlowGraph,
+        is_task_ok,
+        retry_count,
+        max_user_code_retries,
     ):
         if not is_task_ok:
             # The task finished with an exception - execution won't
@@ -99,13 +106,32 @@ def task_finished(
         # we run pods with a security context. We work around this constraint by
         # mounting an emptyDir volume.
         if graph[step_name].type == "foreach":
+            if graph[step_name].parallel_foreach:
+                # If a node is marked as a `parallel_foreach`, pass down the value of
+                # `num_parallel` to the subsequent steps.
+                with open("/mnt/out/num_parallel", "w") as f:
+                    json.dump(flow._parallel_ubf_iter.num_parallel, f)
+                # Set splits to 1 since parallelism is handled by JobSet.
+                flow._foreach_num_splits = 1
+                with open("/mnt/out/task_id_entropy", "w") as file:
+                    import uuid
+
+                    file.write(uuid.uuid4().hex[:6])
+
             with open("/mnt/out/splits", "w") as file:
                 json.dump(list(range(flow._foreach_num_splits)), file)
-        # Unfortunately, we can't always use pod names as task-ids since the pod names
-        # are not static across retries. We write the task-id to a file that is read
-        # by the next task here.
-        with open("/mnt/out/task_id", "w") as file:
-            file.write(self.task_id)
+            with open("/mnt/out/split_cardinality", "w") as file:
+                json.dump(flow._foreach_num_splits, file)
+
+        # For steps that have a `@parallel` decorator set to them, we will be relying on Jobsets
+        # to run the task. In this case, we cannot set anything in the
+        # `/mnt/out` directory, since such form of output mounts are not available to Jobset executions.
+        if not graph[step_name].parallel_step:
+            # Unfortunately, we can't always use pod names as task-ids since the pod names
+            # are not static across retries. We write the task-id to a file that is read
+            # by the next task here.
+            with open("/mnt/out/task_id", "w") as file:
+                file.write(self.task_id)
 
         # Emit Argo Events given that the flow has succeeded. Given that we only
         # emit events when the task succeeds, we can piggy back on this decorator
@@ -146,4 +172,4 @@ def task_finished(
             # Keep in mind that any errors raised here will fail the run but the task
             # will still be marked as success. That's why we explicitly swallow any
             # errors and instead print them to std.err.
-            event.publish(ignore_errors=True)
+            event.safe_publish(ignore_errors=True)
diff --git a/metaflow/plugins/argo/argo_workflows_deployer.py b/metaflow/plugins/argo/argo_workflows_deployer.py
new file mode 100644
index 00000000000..0c9da919633
--- /dev/null
+++ b/metaflow/plugins/argo/argo_workflows_deployer.py
@@ -0,0 +1,106 @@
+from typing import Any, ClassVar, Dict, Optional, TYPE_CHECKING, Type
+
+from metaflow.runner.deployer_impl import DeployerImpl
+
+if TYPE_CHECKING:
+    import metaflow.plugins.argo.argo_workflows_deployer_objects
+
+
+class ArgoWorkflowsDeployer(DeployerImpl):
+    """
+    Deployer implementation for Argo Workflows.
+
+    Parameters
+    ----------
+    name : str, optional, default None
+        Argo workflow name. The flow name is used instead if this option is not specified.
+    """
+
+    TYPE: ClassVar[Optional[str]] = "argo-workflows"
+
+    def __init__(self, deployer_kwargs: Dict[str, str], **kwargs):
+        """
+        Initialize the ArgoWorkflowsDeployer.
+
+        Parameters
+        ----------
+        deployer_kwargs : Dict[str, str]
+            The deployer-specific keyword arguments.
+        **kwargs : Any
+            Additional arguments to pass to the superclass constructor.
+        """
+        self._deployer_kwargs = deployer_kwargs
+        super().__init__(**kwargs)
+
+    @property
+    def deployer_kwargs(self) -> Dict[str, Any]:
+        return self._deployer_kwargs
+
+    @staticmethod
+    def deployed_flow_type() -> (
+        Type[
+            "metaflow.plugins.argo.argo_workflows_deployer_objects.ArgoWorkflowsDeployedFlow"
+        ]
+    ):
+        from .argo_workflows_deployer_objects import ArgoWorkflowsDeployedFlow
+
+        return ArgoWorkflowsDeployedFlow
+
+    def create(
+        self, **kwargs
+    ) -> "metaflow.plugins.argo.argo_workflows_deployer_objects.ArgoWorkflowsDeployedFlow":
+        """
+        Create a new ArgoWorkflow deployment.
+
+        Parameters
+        ----------
+        authorize : str, optional, default None
+            Authorize using this production token. Required when re-deploying an existing flow
+            for the first time. The token is cached in METAFLOW_HOME.
+        generate_new_token : bool, optional, default False
+            Generate a new production token for this flow. Moves the production flow to a new namespace.
+        given_token : str, optional, default None
+            Use the given production token for this flow. Moves the production flow to the given namespace.
+        tags : List[str], optional, default None
+            Annotate all objects produced by Argo Workflows runs with these tags.
+        user_namespace : str, optional, default None
+            Change the namespace from the default (production token) to the given tag.
+        only_json : bool, optional, default False
+            Only print out JSON sent to Argo Workflows without deploying anything.
+        max_workers : int, optional, default 100
+            Maximum number of parallel processes.
+        workflow_timeout : int, optional, default None
+            Workflow timeout in seconds.
+        workflow_priority : int, optional, default None
+            Workflow priority as an integer. Higher priority workflows are processed first
+            if Argo Workflows controller is configured to process limited parallel workflows.
+        auto_emit_argo_events : bool, optional, default True
+            Auto emits Argo Events when the run completes successfully.
+        notify_on_error : bool, optional, default False
+            Notify if the workflow fails.
+        notify_on_success : bool, optional, default False
+            Notify if the workflow succeeds.
+        notify_slack_webhook_url : str, optional, default ''
+            Slack incoming webhook url for workflow success/failure notifications.
+        notify_pager_duty_integration_key : str, optional, default ''
+            PagerDuty Events API V2 Integration key for workflow success/failure notifications.
+        enable_heartbeat_daemon : bool, optional, default False
+            Use a daemon container to broadcast heartbeats.
+        deployer_attribute_file : str, optional, default None
+            Write the workflow name to the specified file. Used internally for Metaflow's Deployer API.
+        enable_error_msg_capture : bool, optional, default True
+            Capture stack trace of first failed task in exit hook.
+
+        Returns
+        -------
+        ArgoWorkflowsDeployedFlow
+            The Flow deployed to Argo Workflows.
+        """
+
+        # Prevent circular import
+        from .argo_workflows_deployer_objects import ArgoWorkflowsDeployedFlow
+
+        return self._create(ArgoWorkflowsDeployedFlow, **kwargs)
+
+
+_addl_stubgen_modules = ["metaflow.plugins.argo.argo_workflows_deployer_objects"]
diff --git a/metaflow/plugins/argo/argo_workflows_deployer_objects.py b/metaflow/plugins/argo/argo_workflows_deployer_objects.py
new file mode 100644
index 00000000000..f5d119e4165
--- /dev/null
+++ b/metaflow/plugins/argo/argo_workflows_deployer_objects.py
@@ -0,0 +1,384 @@
+import sys
+import json
+import time
+import tempfile
+from typing import ClassVar, Optional
+
+from metaflow.client.core import get_metadata
+from metaflow.exception import MetaflowException
+from metaflow.plugins.argo.argo_client import ArgoClient
+from metaflow.metaflow_config import KUBERNETES_NAMESPACE
+from metaflow.plugins.argo.argo_workflows import ArgoWorkflows
+from metaflow.runner.deployer import (
+    Deployer,
+    DeployedFlow,
+    TriggeredRun,
+    generate_fake_flow_file_contents,
+)
+
+from metaflow.runner.utils import get_lower_level_group, handle_timeout, temporary_fifo
+
+
+class ArgoWorkflowsTriggeredRun(TriggeredRun):
+    """
+    A class representing a triggered Argo Workflow execution.
+    """
+
+    def suspend(self, **kwargs) -> bool:
+        """
+        Suspend the running workflow.
+
+        Parameters
+        ----------
+        authorize : str, optional, default None
+            Authorize the suspension with a production token.
+
+        Returns
+        -------
+        bool
+            True if the command was successful, False otherwise.
+        """
+        _, run_id = self.pathspec.split("/")
+
+        # every subclass needs to have `self.deployer_kwargs`
+        command = get_lower_level_group(
+            self.deployer.api,
+            self.deployer.top_level_kwargs,
+            self.deployer.TYPE,
+            self.deployer.deployer_kwargs,
+        ).suspend(run_id=run_id, **kwargs)
+
+        pid = self.deployer.spm.run_command(
+            [sys.executable, *command],
+            env=self.deployer.env_vars,
+            cwd=self.deployer.cwd,
+            show_output=self.deployer.show_output,
+        )
+
+        command_obj = self.deployer.spm.get(pid)
+        command_obj.sync_wait()
+        return command_obj.process.returncode == 0
+
+    def unsuspend(self, **kwargs) -> bool:
+        """
+        Unsuspend the suspended workflow.
+
+        Parameters
+        ----------
+        authorize : str, optional, default None
+            Authorize the unsuspend with a production token.
+
+        Returns
+        -------
+        bool
+            True if the command was successful, False otherwise.
+        """
+        _, run_id = self.pathspec.split("/")
+
+        # every subclass needs to have `self.deployer_kwargs`
+        command = get_lower_level_group(
+            self.deployer.api,
+            self.deployer.top_level_kwargs,
+            self.deployer.TYPE,
+            self.deployer.deployer_kwargs,
+        ).unsuspend(run_id=run_id, **kwargs)
+
+        pid = self.deployer.spm.run_command(
+            [sys.executable, *command],
+            env=self.deployer.env_vars,
+            cwd=self.deployer.cwd,
+            show_output=self.deployer.show_output,
+        )
+
+        command_obj = self.deployer.spm.get(pid)
+        command_obj.sync_wait()
+        return command_obj.process.returncode == 0
+
+    def terminate(self, **kwargs) -> bool:
+        """
+        Terminate the running workflow.
+
+        Parameters
+        ----------
+        authorize : str, optional, default None
+            Authorize the termination with a production token.
+
+        Returns
+        -------
+        bool
+            True if the command was successful, False otherwise.
+        """
+        _, run_id = self.pathspec.split("/")
+
+        # every subclass needs to have `self.deployer_kwargs`
+        command = get_lower_level_group(
+            self.deployer.api,
+            self.deployer.top_level_kwargs,
+            self.deployer.TYPE,
+            self.deployer.deployer_kwargs,
+        ).terminate(run_id=run_id, **kwargs)
+
+        pid = self.deployer.spm.run_command(
+            [sys.executable, *command],
+            env=self.deployer.env_vars,
+            cwd=self.deployer.cwd,
+            show_output=self.deployer.show_output,
+        )
+
+        command_obj = self.deployer.spm.get(pid)
+        command_obj.sync_wait()
+        return command_obj.process.returncode == 0
+
+    def wait_for_completion(
+        self, check_interval: int = 5, timeout: Optional[int] = None
+    ):
+        """
+        Wait for the workflow to complete or timeout.
+
+        Parameters
+        ----------
+        check_interval: int, default: 5
+            Frequency of checking for workflow completion, in seconds.
+        timeout : int, optional, default None
+            Maximum time in seconds to wait for workflow completion.
+            If None, waits indefinitely.
+
+        Raises
+        ------
+        TimeoutError
+            If the workflow does not complete within the specified timeout period.
+        """
+        start_time = time.time()
+        while self.is_running:
+            if timeout is not None and (time.time() - start_time) > timeout:
+                raise TimeoutError(
+                    "Workflow did not complete within specified timeout."
+                )
+            time.sleep(check_interval)
+
+    @property
+    def is_running(self):
+        """
+        Check if the workflow is currently running.
+
+        Returns
+        -------
+        bool
+            True if the workflow status is either 'Pending' or 'Running',
+            False otherwise.
+        """
+        workflow_status = self.status
+        # full list of all states present here:
+        # https://github.com/argoproj/argo-workflows/blob/main/pkg/apis/workflow/v1alpha1/workflow_types.go#L54
+        # we only consider non-terminal states to determine if the workflow has not finished
+        return workflow_status is not None and workflow_status in ["Pending", "Running"]
+
+    @property
+    def status(self) -> Optional[str]:
+        """
+        Get the status of the triggered run.
+
+        Returns
+        -------
+        str, optional
+            The status of the workflow considering the run object, or None if
+            the status could not be retrieved.
+        """
+        from metaflow.plugins.argo.argo_workflows_cli import (
+            get_status_considering_run_object,
+        )
+
+        flow_name, run_id = self.pathspec.split("/")
+        name = run_id[5:]
+        status = ArgoWorkflows.get_workflow_status(flow_name, name)
+        if status is not None:
+            return get_status_considering_run_object(status, self.run)
+        return None
+
+
+class ArgoWorkflowsDeployedFlow(DeployedFlow):
+    """
+    A class representing a deployed Argo Workflow template.
+    """
+
+    TYPE: ClassVar[Optional[str]] = "argo-workflows"
+
+    @classmethod
+    def from_deployment(cls, identifier: str, metadata: Optional[str] = None):
+        """
+        Retrieves a `ArgoWorkflowsDeployedFlow` object from an identifier and optional
+        metadata.
+
+        Parameters
+        ----------
+        identifier : str
+            Deployer specific identifier for the workflow to retrieve
+        metadata : str, optional, default None
+            Optional deployer specific metadata.
+
+        Returns
+        -------
+        ArgoWorkflowsDeployedFlow
+            A `ArgoWorkflowsDeployedFlow` object representing the
+            deployed flow on argo workflows.
+        """
+        client = ArgoClient(namespace=KUBERNETES_NAMESPACE)
+        workflow_template = client.get_workflow_template(identifier)
+
+        if workflow_template is None:
+            raise MetaflowException("No deployed flow found for: %s" % identifier)
+
+        metadata_annotations = workflow_template.get("metadata", {}).get(
+            "annotations", {}
+        )
+
+        flow_name = metadata_annotations.get("metaflow/flow_name", "")
+        username = metadata_annotations.get("metaflow/owner", "")
+        parameters = json.loads(metadata_annotations.get("metaflow/parameters", "{}"))
+
+        # these two only exist if @project decorator is used..
+        branch_name = metadata_annotations.get("metaflow/branch_name", None)
+        project_name = metadata_annotations.get("metaflow/project_name", None)
+
+        project_kwargs = {}
+        if branch_name is not None:
+            if branch_name.startswith("prod."):
+                project_kwargs["production"] = True
+                project_kwargs["branch"] = branch_name[len("prod.") :]
+            elif branch_name.startswith("test."):
+                project_kwargs["branch"] = branch_name[len("test.") :]
+            elif branch_name == "prod":
+                project_kwargs["production"] = True
+
+        fake_flow_file_contents = generate_fake_flow_file_contents(
+            flow_name=flow_name, param_info=parameters, project_name=project_name
+        )
+
+        with tempfile.NamedTemporaryFile(suffix=".py", delete=False) as fake_flow_file:
+            with open(fake_flow_file.name, "w") as fp:
+                fp.write(fake_flow_file_contents)
+
+            if branch_name is not None:
+                d = Deployer(
+                    fake_flow_file.name,
+                    env={"METAFLOW_USER": username},
+                    **project_kwargs,
+                ).argo_workflows()
+            else:
+                d = Deployer(
+                    fake_flow_file.name, env={"METAFLOW_USER": username}
+                ).argo_workflows(name=identifier)
+
+            d.name = identifier
+            d.flow_name = flow_name
+            if metadata is None:
+                d.metadata = get_metadata()
+            else:
+                d.metadata = metadata
+
+        return cls(deployer=d)
+
+    @property
+    def production_token(self) -> Optional[str]:
+        """
+        Get the production token for the deployed flow.
+
+        Returns
+        -------
+        str, optional
+            The production token, None if it cannot be retrieved.
+        """
+        try:
+            _, production_token = ArgoWorkflows.get_existing_deployment(
+                self.deployer.name
+            )
+            return production_token
+        except TypeError:
+            return None
+
+    def delete(self, **kwargs) -> bool:
+        """
+        Delete the deployed workflow template.
+
+        Parameters
+        ----------
+        authorize : str, optional, default None
+            Authorize the deletion with a production token.
+
+        Returns
+        -------
+        bool
+            True if the command was successful, False otherwise.
+        """
+        command = get_lower_level_group(
+            self.deployer.api,
+            self.deployer.top_level_kwargs,
+            self.deployer.TYPE,
+            self.deployer.deployer_kwargs,
+        ).delete(**kwargs)
+
+        pid = self.deployer.spm.run_command(
+            [sys.executable, *command],
+            env=self.deployer.env_vars,
+            cwd=self.deployer.cwd,
+            show_output=self.deployer.show_output,
+        )
+
+        command_obj = self.deployer.spm.get(pid)
+        command_obj.sync_wait()
+        return command_obj.process.returncode == 0
+
+    def trigger(self, **kwargs) -> ArgoWorkflowsTriggeredRun:
+        """
+        Trigger a new run for the deployed flow.
+
+        Parameters
+        ----------
+        **kwargs : Any
+            Additional arguments to pass to the trigger command,
+            `Parameters` in particular.
+
+        Returns
+        -------
+        ArgoWorkflowsTriggeredRun
+            The triggered run instance.
+
+        Raises
+        ------
+        Exception
+            If there is an error during the trigger process.
+        """
+        with temporary_fifo() as (attribute_file_path, attribute_file_fd):
+            # every subclass needs to have `self.deployer_kwargs`
+            command = get_lower_level_group(
+                self.deployer.api,
+                self.deployer.top_level_kwargs,
+                self.deployer.TYPE,
+                self.deployer.deployer_kwargs,
+            ).trigger(deployer_attribute_file=attribute_file_path, **kwargs)
+
+            pid = self.deployer.spm.run_command(
+                [sys.executable, *command],
+                env=self.deployer.env_vars,
+                cwd=self.deployer.cwd,
+                show_output=self.deployer.show_output,
+            )
+
+            command_obj = self.deployer.spm.get(pid)
+            content = handle_timeout(
+                attribute_file_fd, command_obj, self.deployer.file_read_timeout
+            )
+            command_obj.sync_wait()
+            if command_obj.process.returncode == 0:
+                return ArgoWorkflowsTriggeredRun(
+                    deployer=self.deployer, content=content
+                )
+
+        raise Exception(
+            "Error triggering %s on %s for %s"
+            % (
+                self.deployer.name,
+                self.deployer.TYPE,
+                self.deployer.flow_file,
+            )
+        )
diff --git a/metaflow/plugins/argo/capture_error.py b/metaflow/plugins/argo/capture_error.py
new file mode 100644
index 00000000000..8d93d5509b7
--- /dev/null
+++ b/metaflow/plugins/argo/capture_error.py
@@ -0,0 +1,70 @@
+import json
+import os
+from datetime import datetime
+
+###
+# Algorithm to determine 1st error:
+#   ignore the failures where message = ""
+#   group the failures via templateName
+#     sort each group by finishedAt
+#   find the group for which the last finishedAt is earliest
+#   if the earliest message is "No more retries left" then
+#     get the n-1th message from that group
+#   else
+#     return the last message.
+###
+
+
+def parse_workflow_failures():
+    failures = json.loads(
+        json.loads(os.getenv("METAFLOW_ARGO_WORKFLOW_FAILURES", "[]"), strict=False),
+        strict=False,
+    )
+    return [wf for wf in failures if wf.get("message")]
+
+
+def group_failures_by_template(failures):
+    groups = {}
+    for failure in failures:
+        groups.setdefault(failure["templateName"], []).append(failure)
+    return groups
+
+
+def sort_by_finished_at(items):
+    return sorted(
+        items, key=lambda x: datetime.strptime(x["finishedAt"], "%Y-%m-%dT%H:%M:%SZ")
+    )
+
+
+def find_earliest_last_finished_group(groups):
+    return min(
+        groups,
+        key=lambda k: datetime.strptime(
+            groups[k][-1]["finishedAt"], "%Y-%m-%dT%H:%M:%SZ"
+        ),
+    )
+
+
+def determine_first_error():
+    failures = parse_workflow_failures()
+    if not failures:
+        return None
+
+    grouped_failures = group_failures_by_template(failures)
+    for group in grouped_failures.values():
+        group.sort(
+            key=lambda x: datetime.strptime(x["finishedAt"], "%Y-%m-%dT%H:%M:%SZ")
+        )
+
+    earliest_group = grouped_failures[
+        find_earliest_last_finished_group(grouped_failures)
+    ]
+
+    if earliest_group[-1]["message"] == "No more retries left":
+        return earliest_group[-2]
+    return earliest_group[-1]
+
+
+if __name__ == "__main__":
+    first_err = determine_first_error()
+    print(json.dumps(first_err, indent=2))
diff --git a/metaflow/plugins/argo/exit_hooks.py b/metaflow/plugins/argo/exit_hooks.py
new file mode 100644
index 00000000000..59d17664a91
--- /dev/null
+++ b/metaflow/plugins/argo/exit_hooks.py
@@ -0,0 +1,209 @@
+from collections import defaultdict
+import json
+from typing import Dict, List, Optional
+
+
+class JsonSerializable(object):
+    def to_json(self):
+        return self.payload
+
+    def __str__(self):
+        return json.dumps(self.payload, indent=4)
+
+
+class _LifecycleHook(JsonSerializable):
+    # https://argoproj.github.io/argo-workflows/fields/#lifecyclehook
+
+    def __init__(self, name):
+        tree = lambda: defaultdict(tree)
+        self.name = name
+        self.payload = tree()
+
+    def expression(self, expression):
+        self.payload["expression"] = str(expression)
+        return self
+
+    def template(self, template):
+        self.payload["template"] = template
+        return self
+
+
+class _Template(JsonSerializable):
+    # https://argoproj.github.io/argo-workflows/fields/#template
+
+    def __init__(self, name):
+        tree = lambda: defaultdict(tree)
+        self.name = name
+        self.payload = tree()
+        self.payload["name"] = name
+
+    def http(self, http):
+        self.payload["http"] = http.to_json()
+        return self
+
+    def script(self, script):
+        self.payload["script"] = script.to_json()
+        return self
+
+    def container(self, container):
+        self.payload["container"] = container
+        return self
+
+    def service_account_name(self, service_account_name):
+        self.payload["serviceAccountName"] = service_account_name
+        return self
+
+
+class Hook(object):
+    """
+    Abstraction for Argo Workflows exit hooks.
+    A hook consists of a Template, and one or more LifecycleHooks that trigger the template
+    """
+
+    template: "_Template"
+    lifecycle_hooks: List["_LifecycleHook"]
+
+
+class _HttpSpec(JsonSerializable):
+    # https://argoproj.github.io/argo-workflows/fields/#http
+
+    def __init__(self, method):
+        tree = lambda: defaultdict(tree)
+        self.payload = tree()
+        self.payload["method"] = method
+        self.payload["headers"] = []
+
+    def header(self, header, value):
+        self.payload["headers"].append({"name": header, "value": value})
+        return self
+
+    def body(self, body):
+        self.payload["body"] = str(body)
+        return self
+
+    def url(self, url):
+        self.payload["url"] = url
+        return self
+
+    def success_condition(self, success_condition):
+        self.payload["successCondition"] = success_condition
+        return self
+
+
+# HTTP hook
+class HttpExitHook(Hook):
+    def __init__(
+        self,
+        name: str,
+        url: str,
+        method: str = "GET",
+        headers: Optional[Dict] = None,
+        body: Optional[str] = None,
+        on_success: bool = False,
+        on_error: bool = False,
+    ):
+        self.template = _Template(name)
+        http = _HttpSpec(method).url(url)
+        if headers is not None:
+            for header, value in headers.items():
+                http.header(header, value)
+
+        if body is not None:
+            http.body(body)
+
+        self.template.http(http)
+
+        self.lifecycle_hooks = []
+
+        if on_success and on_error:
+            raise Exception("Set only one of the on_success/on_error at a time.")
+
+        if on_success:
+            self.lifecycle_hooks.append(
+                _LifecycleHook(name)
+                .expression("workflow.status == 'Succeeded'")
+                .template(self.template.name)
+            )
+
+        if on_error:
+            self.lifecycle_hooks.append(
+                _LifecycleHook(name)
+                .expression("workflow.status == 'Error' || workflow.status == 'Failed'")
+                .template(self.template.name)
+            )
+
+        if not on_success and not on_error:
+            # add an expressionless lifecycle hook
+            self.lifecycle_hooks.append(_LifecycleHook(name).template(name))
+
+
+class ExitHookHack(Hook):
+    # Warning: terrible hack to workaround a bug in Argo Workflow where the
+    #          templates listed above do not execute unless there is an
+    #          explicit exit hook. as and when this bug is patched, we should
+    #          remove this effectively no-op template.
+    # Note: We use the Http template because changing this to an actual no-op container had the side-effect of
+    # leaving LifecycleHooks in a pending state even when they have finished execution.
+    def __init__(
+        self,
+        url,
+        headers=None,
+        body=None,
+    ):
+        self.template = _Template("exit-hook-hack")
+        http = _HttpSpec("GET").url(url)
+        if headers is not None:
+            for header, value in headers.items():
+                http.header(header, value)
+
+        if body is not None:
+            http.body(json.dumps(body))
+
+        http.success_condition("true == true")
+
+        self.template.http(http)
+
+        self.lifecycle_hooks = []
+
+        # add an expressionless lifecycle hook
+        self.lifecycle_hooks.append(_LifecycleHook("exit").template("exit-hook-hack"))
+
+
+class ContainerHook(Hook):
+    def __init__(
+        self,
+        name: str,
+        container: Dict,
+        service_account_name: str = None,
+        on_success: bool = False,
+        on_error: bool = False,
+    ):
+        self.template = _Template(name)
+
+        if service_account_name is not None:
+            self.template.service_account_name(service_account_name)
+
+        self.template.container(container)
+
+        self.lifecycle_hooks = []
+
+        if on_success and on_error:
+            raise Exception("Set only one of the on_success/on_error at a time.")
+
+        if on_success:
+            self.lifecycle_hooks.append(
+                _LifecycleHook(name)
+                .expression("workflow.status == 'Succeeded'")
+                .template(self.template.name)
+            )
+
+        if on_error:
+            self.lifecycle_hooks.append(
+                _LifecycleHook(name)
+                .expression("workflow.status == 'Error' || workflow.status == 'Failed'")
+                .template(self.template.name)
+            )
+
+        if not on_success and not on_error:
+            # add an expressionless lifecycle hook
+            self.lifecycle_hooks.append(_LifecycleHook(name).template(name))
diff --git a/metaflow/plugins/argo/generate_input_paths.py b/metaflow/plugins/argo/generate_input_paths.py
new file mode 100644
index 00000000000..447a54874d9
--- /dev/null
+++ b/metaflow/plugins/argo/generate_input_paths.py
@@ -0,0 +1,23 @@
+import sys
+from hashlib import md5
+
+
+def generate_input_paths(step_name, timestamp, input_paths, split_cardinality):
+    # => run_id/step/:foo,bar
+    run_id = input_paths.split("/")[0]
+    foreach_base_id = "{}-{}-{}".format(step_name, timestamp, input_paths)
+
+    ids = [_generate_task_id(foreach_base_id, i) for i in range(int(split_cardinality))]
+    return "{}/{}/:{}".format(run_id, step_name, ",".join(ids))
+
+
+def _generate_task_id(base, idx):
+    # For foreach splits generate the expected input-paths based on split_cardinality and base_id.
+    # newline required at the end due to 'echo' appending one in the shell side task_id creation.
+    task_str = "%s-%s\n" % (base, idx)
+    hash = md5(task_str.encode("utf-8")).hexdigest()[-8:]
+    return "t-" + hash
+
+
+if __name__ == "__main__":
+    print(generate_input_paths(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4]))
diff --git a/metaflow/plugins/argo/jobset_input_paths.py b/metaflow/plugins/argo/jobset_input_paths.py
new file mode 100644
index 00000000000..da28fc6dedc
--- /dev/null
+++ b/metaflow/plugins/argo/jobset_input_paths.py
@@ -0,0 +1,15 @@
+import sys
+
+
+def generate_input_paths(run_id, step_name, task_id_entropy, num_parallel):
+    # => run_id/step/:foo,bar
+    control_id = "control-{}-0".format(task_id_entropy)
+    worker_ids = [
+        "worker-{}-{}".format(task_id_entropy, i) for i in range(int(num_parallel) - 1)
+    ]
+    ids = [control_id] + worker_ids
+    return "{}/{}/:{}".format(run_id, step_name, ",".join(ids))
+
+
+if __name__ == "__main__":
+    print(generate_input_paths(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4]))
diff --git a/metaflow/plugins/argo/process_input_paths.py b/metaflow/plugins/argo/process_input_paths.py
deleted file mode 100644
index c74b8e73e6d..00000000000
--- a/metaflow/plugins/argo/process_input_paths.py
+++ /dev/null
@@ -1,19 +0,0 @@
-import re
-import sys
-
-
-def process_input_paths(input_paths):
-    # Convert Argo Workflows provided input-paths string to something that Metaflow
-    # understands
-    #
-    # flow/step/[{task-id:foo},{task-id:bar}] => flow/step/:foo,bar
-
-    flow, run_id, task_ids = input_paths.split("/")
-    task_ids = re.sub("[\[\]{}]", "", task_ids)
-    task_ids = task_ids.split(",")
-    tasks = [t.split(":")[1] for t in task_ids]
-    return "{}/{}/:{}".format(flow, run_id, ",".join(tasks))
-
-
-if __name__ == "__main__":
-    print(process_input_paths(sys.argv[1]))
diff --git a/metaflow/plugins/aws/aws_client.py b/metaflow/plugins/aws/aws_client.py
index eeee7c33698..71479b52ce1 100644
--- a/metaflow/plugins/aws/aws_client.py
+++ b/metaflow/plugins/aws/aws_client.py
@@ -14,6 +14,7 @@ def get_client(
             AWS_SANDBOX_ENABLED,
             AWS_SANDBOX_STS_ENDPOINT_URL,
             AWS_SANDBOX_API_KEY,
+            S3_CLIENT_RETRY_CONFIG,
         )
 
         if session_vars is None:
@@ -34,13 +35,19 @@ def get_client(
                 "Could not import module 'boto3'. Install boto3 first."
             )
 
+        # Convert dictionary config to Config object if needed
+        if "config" in client_params and not isinstance(
+            client_params["config"], Config
+        ):
+            client_params["config"] = Config(**client_params["config"])
+
         if module == "s3" and (
             "config" not in client_params or client_params["config"].retries is None
         ):
-            # Use the adaptive retry strategy by default -- do not set anything if
-            # the user has already set something
+            # do not set anything if the user has already set something
             config = client_params.get("config", Config())
-            config.retries = {"max_attempts": 10, "mode": "adaptive"}
+            config.retries = S3_CLIENT_RETRY_CONFIG
+            client_params["config"] = config
 
         if AWS_SANDBOX_ENABLED:
             # role is ignored in the sandbox
diff --git a/metaflow/plugins/aws/aws_utils.py b/metaflow/plugins/aws/aws_utils.py
index fd35d776714..c60cc2a6d45 100644
--- a/metaflow/plugins/aws/aws_utils.py
+++ b/metaflow/plugins/aws/aws_utils.py
@@ -1,9 +1,24 @@
 import re
-import requests
 
 from metaflow.exception import MetaflowException
 
 
+def parse_s3_full_path(s3_uri):
+    from urllib.parse import urlparse
+
+    #  :///;?#
+    scheme, netloc, path, _, _, _ = urlparse(s3_uri)
+    assert scheme == "s3"
+    assert netloc is not None
+
+    bucket = netloc
+    path = path.lstrip("/").rstrip("/")
+    if path == "":
+        path = None
+
+    return bucket, path
+
+
 def get_ec2_instance_metadata():
     """
     Fetches the EC2 instance metadata through AWS instance metadata service
@@ -14,19 +29,39 @@ def get_ec2_instance_metadata():
         - ec2-region
         - ec2-availability-zone
     """
+
+    # TODO: Remove dependency on requests
+    import requests
+
     meta = {}
     # Capture AWS instance identity metadata. This is best-effort only since
     # access to this end-point might be blocked on AWS and not available
     # for non-AWS deployments.
     # https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-identity-documents.html
+    # Set a very aggressive timeout, as the communication is happening in the same subnet,
+    # there should not be any significant delay in the response.
+    # Having a long default timeout here introduces unnecessary delay in launching tasks when the
+    # instance is unreachable.
+    timeout = (1, 10)
+    token = None
+    try:
+        # Try to get an IMDSv2 token.
+        token = requests.put(
+            url="http://169.254.169.254/latest/api/token",
+            headers={"X-aws-ec2-metadata-token-ttl-seconds": "100"},
+            timeout=timeout,
+        ).text
+    except:
+        pass
     try:
-        # Set a very aggressive timeout, as the communication is happening in the same subnet,
-        # there should not be any significant delay in the response.
-        # Having a long default timeout here introduces unnecessary delay in launching tasks when the
-        # instance is unreachable.
+        headers = {}
+        # Add IMDSv2 token if available, else fall back to IMDSv1.
+        if token:
+            headers["X-aws-ec2-metadata-token"] = token
         instance_meta = requests.get(
             url="http://169.254.169.254/latest/dynamic/instance-identity/document",
-            timeout=(1, 10),
+            headers=headers,
+            timeout=timeout,
         ).json()
         meta["ec2-instance-id"] = instance_meta.get("instanceId")
         meta["ec2-instance-type"] = instance_meta.get("instanceType")
@@ -40,20 +75,20 @@ def get_ec2_instance_metadata():
 def get_docker_registry(image_uri):
     """
     Explanation:
-        (.+?(?:[:.].+?)\/)? - [GROUP 0] REGISTRY
-            .+?                 - A registry must start with at least one character
-            (?:[:.].+?)\/       - A registry must have ":" or "." and end with "/"
-            ?                   - Make a registry optional
-        (.*?)               - [GROUP 1] REPOSITORY
-            .*?                 - Get repository name until separator
-        (?:[@:])?           - SEPARATOR
-            ?:                  - Don't capture separator
-            [@:]                - The separator must be either "@" or ":"
-            ?                   - The separator is optional
-        ((?<=[@:]).*)?      - [GROUP 2] TAG / DIGEST
-            (?<=[@:])           - A tag / digest must be preceded by "@" or ":"
-            .*                  - Capture rest of tag / digest
-            ?                   - A tag / digest is optional
+        (.+?(?:[:.].+?)\\/)? - [GROUP 0] REGISTRY
+            .+?                  - A registry must start with at least one character
+            (?:[:.].+?)\\/       - A registry must have ":" or "." and end with "/"
+            ?                    - Make a registry optional
+        (.*?)                - [GROUP 1] REPOSITORY
+            .*?                  - Get repository name until separator
+        (?:[@:])?            - SEPARATOR
+            ?:                   - Don't capture separator
+            [@:]                 - The separator must be either "@" or ":"
+            ?                    - The separator is optional
+        ((?<=[@:]).*)?       - [GROUP 2] TAG / DIGEST
+            (?<=[@:])            - A tag / digest must be preceded by "@" or ":"
+            .*                   - Capture rest of tag / digest
+            ?                    - A tag / digest is optional
     Examples:
         image
             - None
@@ -97,7 +132,7 @@ def compute_resource_attributes(decos, compute_deco, resource_defaults):
     Returns a dictionary of resource attr -> value (str).
     """
     assert compute_deco is not None
-
+    supported_keys = set([*resource_defaults.keys(), *compute_deco.attributes.keys()])
     # Use the value from resource_defaults by default (don't use None)
     result = {k: v for k, v in resource_defaults.items() if v is not None}
 
@@ -109,6 +144,11 @@ def compute_resource_attributes(decos, compute_deco, resource_defaults):
                 # We use the non None value if there is only one or the larger value
                 # if they are both non None. Note this considers "" to be equivalent to
                 # the value zero.
+                #
+                # Skip attributes that are not supported by the decorator.
+                if k not in supported_keys:
+                    continue
+
                 if my_val is None and v is None:
                     continue
                 if my_val is not None and v is not None:
@@ -122,6 +162,8 @@ def compute_resource_attributes(decos, compute_deco, resource_defaults):
                         # Here we don't have ints, so we compare the value and raise
                         # an exception if not equal
                         if my_val != v:
+                            # TODO: Throw a better exception since the user has no
+                            #       knowledge of 'compute' decorator
                             raise MetaflowException(
                                 "'resources' and compute decorator have conflicting "
                                 "values for '%s'. Please use consistent values or "
@@ -140,3 +182,15 @@ def compute_resource_attributes(decos, compute_deco, resource_defaults):
             result[k] = str(compute_deco.attributes[k] or "0")
 
     return result
+
+
+def sanitize_batch_tag(key, value):
+    """
+    Sanitize a key and value for use as a Batch tag.
+    """
+    # https://docs.aws.amazon.com/batch/latest/userguide/using-tags.html#tag-restrictions
+    RE_NOT_PERMITTED = r"[^A-Za-z0-9\s\+\-\=\.\_\:\/\@]"
+    _key = re.sub(RE_NOT_PERMITTED, "", key)[:128]
+    _value = re.sub(RE_NOT_PERMITTED, "", value)[:256]
+
+    return _key, _value
diff --git a/metaflow/plugins/aws/batch/batch.py b/metaflow/plugins/aws/batch/batch.py
index 03719c5575e..8064618e3e1 100644
--- a/metaflow/plugins/aws/batch/batch.py
+++ b/metaflow/plugins/aws/batch/batch.py
@@ -8,8 +8,10 @@
 
 from metaflow import util
 from metaflow.plugins.datatools.s3.s3tail import S3Tail
+from metaflow.plugins.aws.aws_utils import sanitize_batch_tag
 from metaflow.exception import MetaflowException
 from metaflow.metaflow_config import (
+    OTEL_ENDPOINT,
     SERVICE_INTERNAL_URL,
     DATATOOLS_S3ROOT,
     DATASTORE_SYSROOT_S3,
@@ -20,7 +22,11 @@
     S3_ENDPOINT_URL,
     DEFAULT_SECRETS_BACKEND_TYPE,
     AWS_SECRETS_MANAGER_DEFAULT_REGION,
+    S3_SERVER_SIDE_ENCRYPTION,
 )
+
+from metaflow.metaflow_config_funcs import config_values
+
 from metaflow.mflog import (
     export_mflog_env_vars,
     bash_capture_logs,
@@ -53,14 +59,24 @@ def __init__(self, metadata, environment):
         self._client = BatchClient()
         atexit.register(lambda: self.job.kill() if hasattr(self, "job") else None)
 
-    def _command(self, environment, code_package_url, step_name, step_cmds, task_spec):
+    def _command(
+        self,
+        environment,
+        code_package_metadata,
+        code_package_url,
+        step_name,
+        step_cmds,
+        task_spec,
+    ):
         mflog_expr = export_mflog_env_vars(
             datastore_type="s3",
             stdout_path=STDOUT_PATH,
             stderr_path=STDERR_PATH,
             **task_spec
         )
-        init_cmds = environment.get_package_commands(code_package_url, "s3")
+        init_cmds = environment.get_package_commands(
+            code_package_url, "s3", code_package_metadata
+        )
         init_expr = " && ".join(init_cmds)
         step_expr = bash_capture_logs(
             " && ".join(environment.bootstrap_commands(step_name, "s3") + step_cmds)
@@ -161,6 +177,7 @@ def create_job(
         step_name,
         step_cli,
         task_spec,
+        code_package_metadata,
         code_package_sha,
         code_package_url,
         code_package_ds,
@@ -176,14 +193,19 @@ def create_job(
         max_swap=None,
         swappiness=None,
         inferentia=None,
+        efa=None,
         env={},
         attrs={},
         host_volumes=None,
+        efs_volumes=None,
         use_tmpfs=None,
         tmpfs_tempdir=None,
         tmpfs_size=None,
         tmpfs_path=None,
         num_parallel=0,
+        ephemeral_storage=None,
+        log_driver=None,
+        log_options=None,
     ):
         job_name = self._job_name(
             attrs.get("metaflow.user"),
@@ -199,7 +221,12 @@ def create_job(
             .job_queue(queue)
             .command(
                 self._command(
-                    self.environment, code_package_url, step_name, [step_cli], task_spec
+                    self.environment,
+                    code_package_metadata,
+                    code_package_url,
+                    step_name,
+                    [step_cli],
+                    task_spec,
                 )
             )
             .image(image)
@@ -212,6 +239,7 @@ def create_job(
             .max_swap(max_swap)
             .swappiness(swappiness)
             .inferentia(inferentia)
+            .efa(efa)
             .timeout_in_secs(run_time_limit)
             .job_def(
                 image,
@@ -222,16 +250,22 @@ def create_job(
                 max_swap,
                 swappiness,
                 inferentia,
+                efa,
                 memory=memory,
                 host_volumes=host_volumes,
+                efs_volumes=efs_volumes,
                 use_tmpfs=use_tmpfs,
                 tmpfs_tempdir=tmpfs_tempdir,
                 tmpfs_size=tmpfs_size,
                 tmpfs_path=tmpfs_path,
                 num_parallel=num_parallel,
+                ephemeral_storage=ephemeral_storage,
+                log_driver=log_driver,
+                log_options=log_options,
             )
             .task_id(attrs.get("metaflow.task_id"))
             .environment_variable("AWS_DEFAULT_REGION", self._client.region())
+            .environment_variable("METAFLOW_CODE_METADATA", code_package_metadata)
             .environment_variable("METAFLOW_CODE_SHA", code_package_sha)
             .environment_variable("METAFLOW_CODE_URL", code_package_url)
             .environment_variable("METAFLOW_CODE_DS", code_package_ds)
@@ -245,8 +279,16 @@ def create_job(
             .environment_variable("METAFLOW_DEFAULT_DATASTORE", "s3")
             .environment_variable("METAFLOW_DEFAULT_METADATA", DEFAULT_METADATA)
             .environment_variable("METAFLOW_CARD_S3ROOT", CARD_S3ROOT)
+            .environment_variable("METAFLOW_OTEL_ENDPOINT", OTEL_ENDPOINT)
             .environment_variable("METAFLOW_RUNTIME_ENVIRONMENT", "aws-batch")
         )
+
+        # Temporary passing of *some* environment variables. Do not rely on this
+        # mechanism as it will be removed in the near future
+        for k, v in config_values():
+            if k.startswith("METAFLOW_CONDA_") or k.startswith("METAFLOW_DEBUG_"):
+                job.environment_variable(k, v)
+
         if DEFAULT_SECRETS_BACKEND_TYPE is not None:
             job.environment_variable(
                 "METAFLOW_DEFAULT_SECRETS_BACKEND_TYPE", DEFAULT_SECRETS_BACKEND_TYPE
@@ -262,6 +304,11 @@ def create_job(
         if tmpfs_enabled and tmpfs_tempdir:
             job.environment_variable("METAFLOW_TEMPDIR", tmpfs_path)
 
+        if S3_SERVER_SIDE_ENCRYPTION is not None:
+            job.environment_variable(
+                "METAFLOW_S3_SERVER_SIDE_ENCRYPTION", S3_SERVER_SIDE_ENCRYPTION
+            )
+
         # Skip setting METAFLOW_DATASTORE_SYSROOT_LOCAL because metadata sync between the local user
         # instance and the remote AWS Batch instance assumes metadata is stored in DATASTORE_LOCAL_DIR
         # on the remote AWS Batch instance; this happens when METAFLOW_DATASTORE_SYSROOT_LOCAL
@@ -283,14 +330,20 @@ def create_job(
                 "metaflow.flow_name",
                 "metaflow.run_id",
                 "metaflow.step_name",
-                "metaflow.version",
                 "metaflow.run_id.$",
-                "metaflow.user",
-                "metaflow.owner",
                 "metaflow.production_token",
             ]:
                 if key in attrs:
                     job.tag(key, attrs.get(key))
+            # As some values can be affected by users, sanitize them so they adhere to AWS tagging restrictions.
+            for key in [
+                "metaflow.version",
+                "metaflow.user",
+                "metaflow.owner",
+            ]:
+                if key in attrs:
+                    k, v = sanitize_batch_tag(key, attrs.get(key))
+                    job.tag(k, v)
         return job
 
     def launch_job(
@@ -298,6 +351,7 @@ def launch_job(
         step_name,
         step_cli,
         task_spec,
+        code_package_metadata,
         code_package_sha,
         code_package_url,
         code_package_ds,
@@ -313,7 +367,9 @@ def launch_job(
         max_swap=None,
         swappiness=None,
         inferentia=None,
+        efa=None,
         host_volumes=None,
+        efs_volumes=None,
         use_tmpfs=None,
         tmpfs_tempdir=None,
         tmpfs_size=None,
@@ -321,6 +377,9 @@ def launch_job(
         num_parallel=0,
         env={},
         attrs={},
+        ephemeral_storage=None,
+        log_driver=None,
+        log_options=None,
     ):
         if queue is None:
             queue = next(self._client.active_job_queues(), None)
@@ -333,6 +392,7 @@ def launch_job(
             step_name,
             step_cli,
             task_spec,
+            code_package_metadata,
             code_package_sha,
             code_package_url,
             code_package_ds,
@@ -348,14 +408,19 @@ def launch_job(
             max_swap,
             swappiness,
             inferentia,
+            efa,
             env=env,
             attrs=attrs,
             host_volumes=host_volumes,
+            efs_volumes=efs_volumes,
             use_tmpfs=use_tmpfs,
             tmpfs_tempdir=tmpfs_tempdir,
             tmpfs_size=tmpfs_size,
             tmpfs_path=tmpfs_path,
             num_parallel=num_parallel,
+            ephemeral_storage=ephemeral_storage,
+            log_driver=log_driver,
+            log_options=log_options,
         )
         self.num_parallel = num_parallel
         self.job = job.execute()
diff --git a/metaflow/plugins/aws/batch/batch_cli.py b/metaflow/plugins/aws/batch/batch_cli.py
index e82a647a3d0..9d3eb4cbaa0 100644
--- a/metaflow/plugins/aws/batch/batch_cli.py
+++ b/metaflow/plugins/aws/batch/batch_cli.py
@@ -4,15 +4,13 @@
 import time
 import traceback
 
-from distutils.dir_util import copy_tree
-
 from metaflow import util
 from metaflow import R
 from metaflow.exception import CommandException, METAFLOW_EXIT_DISALLOW_RETRY
-from metaflow.metadata.util import sync_local_metadata_from_datastore
+from metaflow.metadata_provider.util import sync_local_metadata_from_datastore
 from metaflow.metaflow_config import DATASTORE_LOCAL_DIR
 from metaflow.mflog import TASK_LOG_SOURCE
-
+from metaflow.unbounded_foreach import UBF_CONTROL, UBF_TASK
 from .batch import Batch, BatchKilledException
 
 
@@ -102,6 +100,7 @@ def kill(ctx, run_id, user, my_runs):
     "Metaflow."
 )
 @click.argument("step-name")
+@click.argument("code-package-metadata")
 @click.argument("code-package-sha")
 @click.argument("code-package-url")
 @click.option("--executable", help="Executable requirement for AWS Batch.")
@@ -141,13 +140,42 @@ def kill(ctx, run_id, user, my_runs):
 @click.option("--max-swap", help="Max Swap requirement for AWS Batch.")
 @click.option("--swappiness", help="Swappiness requirement for AWS Batch.")
 @click.option("--inferentia", help="Inferentia requirement for AWS Batch.")
+@click.option(
+    "--efa",
+    default=0,
+    type=int,
+    help="Activate designated number of elastic fabric adapter devices. "
+    "EFA driver must be installed and instance type compatible with EFA",
+)
 @click.option("--use-tmpfs", is_flag=True, help="tmpfs requirement for AWS Batch.")
 @click.option("--tmpfs-tempdir", is_flag=True, help="tmpfs requirement for AWS Batch.")
 @click.option("--tmpfs-size", help="tmpfs requirement for AWS Batch.")
 @click.option("--tmpfs-path", help="tmpfs requirement for AWS Batch.")
-# TODO: Maybe remove it altogether since it's not used here
-@click.option("--ubf-context", default=None, type=click.Choice([None, "ubf_control"]))
+# NOTE: ubf-context is not explicitly used, but @parallel decorator tries to pass this so keep it for now
+@click.option(
+    "--ubf-context", default=None, type=click.Choice(["none", UBF_CONTROL, UBF_TASK])
+)
 @click.option("--host-volumes", multiple=True)
+@click.option("--efs-volumes", multiple=True)
+@click.option(
+    "--ephemeral-storage",
+    default=None,
+    type=int,
+    help="Ephemeral storage (for AWS Batch only)",
+)
+@click.option(
+    "--log-driver",
+    default=None,
+    type=str,
+    help="Log driver for AWS ECS container",
+)
+@click.option(
+    "--log-options",
+    default=None,
+    type=str,
+    multiple=True,
+    help="Log options for the chosen log driver",
+)
 @click.option(
     "--num-parallel",
     default=0,
@@ -158,6 +186,7 @@ def kill(ctx, run_id, user, my_runs):
 def step(
     ctx,
     step_name,
+    code_package_metadata,
     code_package_sha,
     code_package_url,
     executable=None,
@@ -173,13 +202,18 @@ def step(
     max_swap=None,
     swappiness=None,
     inferentia=None,
+    efa=None,
     use_tmpfs=None,
     tmpfs_tempdir=None,
     tmpfs_size=None,
     tmpfs_path=None,
     host_volumes=None,
+    efs_volumes=None,
+    ephemeral_storage=None,
+    log_driver=None,
+    log_options=None,
     num_parallel=None,
-    **kwargs
+    **kwargs,
 ):
     def echo(msg, stream="stderr", batch_id=None, **kwargs):
         msg = util.to_unicode(msg)
@@ -190,8 +224,7 @@ def echo(msg, stream="stderr", batch_id=None, **kwargs):
     if R.use_r():
         entrypoint = R.entrypoint()
     else:
-        if executable is None:
-            executable = ctx.obj.environment.executable(step_name)
+        executable = ctx.obj.environment.executable(step_name, executable)
         entrypoint = "%s -u %s" % (executable, os.path.basename(sys.argv[0]))
 
     top_args = " ".join(util.dict_to_cli_options(ctx.parent.parent.params))
@@ -242,11 +275,11 @@ def echo(msg, stream="stderr", batch_id=None, **kwargs):
         "metaflow_version"
     ]
 
+    env = {"METAFLOW_FLOW_FILENAME": os.path.basename(sys.argv[0])}
+
     env_deco = [deco for deco in node.decorators if deco.name == "environment"]
     if env_deco:
-        env = env_deco[0].attributes["vars"]
-    else:
-        env = {}
+        env.update(env_deco[0].attributes["vars"])
 
     # Add the environment variables related to the input-paths argument
     if split_vars:
@@ -286,6 +319,7 @@ def _sync_metadata():
                 step_name,
                 step_cli,
                 task_spec,
+                code_package_metadata,
                 code_package_sha,
                 code_package_url,
                 ctx.obj.flow_datastore.TYPE,
@@ -301,16 +335,21 @@ def _sync_metadata():
                 max_swap=max_swap,
                 swappiness=swappiness,
                 inferentia=inferentia,
+                efa=efa,
                 env=env,
                 attrs=attrs,
                 host_volumes=host_volumes,
+                efs_volumes=efs_volumes,
                 use_tmpfs=use_tmpfs,
                 tmpfs_tempdir=tmpfs_tempdir,
                 tmpfs_size=tmpfs_size,
                 tmpfs_path=tmpfs_path,
+                ephemeral_storage=ephemeral_storage,
+                log_driver=log_driver,
+                log_options=log_options,
                 num_parallel=num_parallel,
             )
-    except Exception as e:
+    except Exception:
         traceback.print_exc()
         _sync_metadata()
         sys.exit(METAFLOW_EXIT_DISALLOW_RETRY)
diff --git a/metaflow/plugins/aws/batch/batch_client.py b/metaflow/plugins/aws/batch/batch_client.py
index 8ef26070f61..bf0f6a824e7 100644
--- a/metaflow/plugins/aws/batch/batch_client.py
+++ b/metaflow/plugins/aws/batch/batch_client.py
@@ -1,9 +1,7 @@
 # -*- coding: utf-8 -*-
-from collections import defaultdict, deque
+from collections import defaultdict
 import copy
 import random
-import select
-import sys
 import time
 import hashlib
 
@@ -89,6 +87,9 @@ def execute(self):
         # Multinode
         if getattr(self, "num_parallel", 0) >= 1:
             num_nodes = self.num_parallel
+            # We need this task-id set so that all the nodes are aware of the control
+            # task's task-id. These "MF_" variables populate the `current.parallel` namedtuple
+            self.environment_variable("MF_PARALLEL_CONTROL_TASK_ID", self._task_id)
             main_task_override = copy.deepcopy(self.payload["containerOverrides"])
 
             # main
@@ -149,13 +150,18 @@ def _register_job_definition(
         max_swap,
         swappiness,
         inferentia,
+        efa,
         memory,
         host_volumes,
+        efs_volumes,
         use_tmpfs,
         tmpfs_tempdir,
         tmpfs_size,
         tmpfs_path,
         num_parallel,
+        ephemeral_storage,
+        log_driver,
+        log_options,
     ):
         # identify platform from any compute environment associated with the
         # queue
@@ -193,6 +199,25 @@ def _register_job_definition(
             "propagateTags": True,
         }
 
+        log_options_dict = {}
+        if log_options:
+            if isinstance(log_options, str):
+                log_options = [log_options]
+            for each_log_option in log_options:
+                k, v = each_log_option.split(":", 1)
+                log_options_dict[k] = v
+
+        if log_driver or log_options:
+            job_definition["containerProperties"]["logConfiguration"] = {}
+        if log_driver:
+            job_definition["containerProperties"]["logConfiguration"][
+                "logDriver"
+            ] = log_driver
+        if log_options:
+            job_definition["containerProperties"]["logConfiguration"][
+                "options"
+            ] = log_options_dict
+
         if platform == "FARGATE" or platform == "FARGATE_SPOT":
             if num_parallel > 1:
                 raise BatchJobException("Fargate does not support multinode jobs.")
@@ -208,6 +233,10 @@ def _register_job_definition(
             job_definition["containerProperties"]["networkConfiguration"] = {
                 "assignPublicIp": "ENABLED"
             }
+            if ephemeral_storage:
+                job_definition["containerProperties"]["ephemeralStorage"] = {
+                    "sizeInGiB": ephemeral_storage
+                }
 
         if platform == "EC2" or platform == "SPOT":
             if "linuxParameters" not in job_definition["containerProperties"]:
@@ -215,7 +244,7 @@ def _register_job_definition(
             if shared_memory is not None:
                 if not (
                     isinstance(shared_memory, (int, unicode, basestring))
-                    and int(shared_memory) > 0
+                    and int(float(shared_memory)) > 0
                 ):
                     raise BatchJobException(
                         "Invalid shared memory size value ({}); "
@@ -224,7 +253,7 @@ def _register_job_definition(
                 else:
                     job_definition["containerProperties"]["linuxParameters"][
                         "sharedMemorySize"
-                    ] = int(shared_memory)
+                    ] = int(float(shared_memory))
             if swappiness is not None:
                 if not (
                     isinstance(swappiness, (int, unicode, basestring))
@@ -252,6 +281,10 @@ def _register_job_definition(
                     job_definition["containerProperties"]["linuxParameters"][
                         "maxSwap"
                     ] = int(max_swap)
+            if ephemeral_storage:
+                raise BatchJobException(
+                    "The ephemeral_storage parameter is only available for FARGATE compute environments"
+                )
 
         if inferentia:
             if not (isinstance(inferentia, (int, unicode, basestring))):
@@ -269,24 +302,54 @@ def _register_job_definition(
                         {
                             "containerPath": "/dev/neuron{}".format(i),
                             "hostPath": "/dev/neuron{}".format(i),
-                            "permissions": ["read", "write"],
+                            "permissions": ["READ", "WRITE"],
                         }
                     )
 
-        if host_volumes:
+        if host_volumes or efs_volumes:
             job_definition["containerProperties"]["volumes"] = []
             job_definition["containerProperties"]["mountPoints"] = []
-            if isinstance(host_volumes, str):
-                host_volumes = [host_volumes]
-            for host_path in host_volumes:
-                name = host_path.replace("/", "_").replace(".", "_")
-                job_definition["containerProperties"]["volumes"].append(
-                    {"name": name, "host": {"sourcePath": host_path}}
-                )
-                job_definition["containerProperties"]["mountPoints"].append(
-                    {"sourceVolume": name, "containerPath": host_path}
-                )
 
+            if host_volumes:
+                if isinstance(host_volumes, str):
+                    host_volumes = [host_volumes]
+                for host_path in host_volumes:
+                    container_path = host_path
+                    if ":" in host_path:
+                        host_path, container_path = host_path.split(":", 1)
+                    name = host_path.replace("/", "_").replace(".", "_")
+                    job_definition["containerProperties"]["volumes"].append(
+                        {"name": name, "host": {"sourcePath": host_path}}
+                    )
+                    job_definition["containerProperties"]["mountPoints"].append(
+                        {"sourceVolume": name, "containerPath": container_path}
+                    )
+
+            if efs_volumes:
+                if isinstance(efs_volumes, str):
+                    efs_volumes = [efs_volumes]
+                for efs_id in efs_volumes:
+                    container_path = "/mnt/" + efs_id
+                    if ":" in efs_id:
+                        efs_id, container_path = efs_id.split(":", 1)
+                    name = "efs_" + efs_id
+                    job_definition["containerProperties"]["volumes"].append(
+                        {
+                            "name": name,
+                            "efsVolumeConfiguration": {
+                                "fileSystemId": efs_id,
+                                "transitEncryption": "ENABLED",
+                            },
+                        }
+                    )
+                    job_definition["containerProperties"]["mountPoints"].append(
+                        {"sourceVolume": name, "containerPath": container_path}
+                    )
+
+        if use_tmpfs and (platform == "FARGATE" or platform == "FARGATE_SPOT"):
+            raise BatchJobException(
+                "tmpfs is not available for Fargate compute resources"
+            )
         if use_tmpfs or (tmpfs_size and not use_tmpfs):
             if tmpfs_size:
                 if not (isinstance(tmpfs_size, (int, unicode, basestring))):
@@ -297,7 +360,7 @@ def _register_job_definition(
                     )
             else:
                 # default tmpfs behavior - https://man7.org/linux/man-pages/man5/tmpfs.5.html
-                tmpfs_size = int(memory) / 2
+                tmpfs_size = int(float(memory)) / 2
 
             job_definition["containerProperties"]["linuxParameters"]["tmpfs"] = [
                 {
@@ -310,6 +373,39 @@ def _register_job_definition(
                 }
             ]
 
+        if efa:
+            if not (isinstance(efa, (int, unicode, basestring))):
+                raise BatchJobException(
+                    "Invalid efa value: ({}) (should be 0 or greater)".format(efa)
+                )
+            else:
+                if "linuxParameters" not in job_definition["containerProperties"]:
+                    job_definition["containerProperties"]["linuxParameters"] = {}
+                if (
+                    "devices"
+                    not in job_definition["containerProperties"]["linuxParameters"]
+                ):
+                    job_definition["containerProperties"]["linuxParameters"][
+                        "devices"
+                    ] = []
+                if (num_parallel or 0) > 1:
+                    # Multi-node parallel jobs require the container path and permissions explicitly specified in Job definition
+                    for i in range(int(efa)):
+                        job_definition["containerProperties"]["linuxParameters"][
+                            "devices"
+                        ].append(
+                            {
+                                "hostPath": "/dev/infiniband/uverbs{}".format(i),
+                                "containerPath": "/dev/infiniband/uverbs{}".format(i),
+                                "permissions": ["READ", "WRITE", "MKNOD"],
+                            }
+                        )
+                else:
+                    # Single-node container jobs only require host path in job definition
+                    job_definition["containerProperties"]["linuxParameters"][
+                        "devices"
+                    ].append({"hostPath": "/dev/infiniband/uverbs0"})
+
         self.num_parallel = num_parallel or 0
         if self.num_parallel >= 1:
             job_definition["type"] = "multinode"
@@ -332,6 +428,7 @@ def _register_job_definition(
                         "container": job_definition["containerProperties"],
                     }
                 )
+
             del job_definition["containerProperties"]  # not used for multi-node
 
         # check if job definition already exists
@@ -371,13 +468,18 @@ def job_def(
         max_swap,
         swappiness,
         inferentia,
+        efa,
         memory,
         host_volumes,
+        efs_volumes,
         use_tmpfs,
         tmpfs_tempdir,
         tmpfs_size,
         tmpfs_path,
         num_parallel,
+        ephemeral_storage,
+        log_driver,
+        log_options,
     ):
         self.payload["jobDefinition"] = self._register_job_definition(
             image,
@@ -388,13 +490,18 @@ def job_def(
             max_swap,
             swappiness,
             inferentia,
+            efa,
             memory,
             host_volumes,
+            efs_volumes,
             use_tmpfs,
             tmpfs_tempdir,
             tmpfs_size,
             tmpfs_path,
             num_parallel,
+            ephemeral_storage,
+            log_driver,
+            log_options,
         )
         return self
 
@@ -438,6 +545,10 @@ def inferentia(self, inferentia):
         self._inferentia = inferentia
         return self
 
+    def efa(self, efa):
+        self._efa = efa
+        return self
+
     def command(self, command):
         if "command" not in self.payload["containerOverrides"]:
             self.payload["containerOverrides"]["command"] = []
diff --git a/metaflow/plugins/aws/batch/batch_decorator.py b/metaflow/plugins/aws/batch/batch_decorator.py
index 6dcb6e8e996..7eac3a602f1 100644
--- a/metaflow/plugins/aws/batch/batch_decorator.py
+++ b/metaflow/plugins/aws/batch/batch_decorator.py
@@ -1,34 +1,31 @@
 import os
-import sys
 import platform
-import requests
+import sys
 import time
 
-from metaflow import util
 from metaflow import R, current
-
 from metaflow.decorators import StepDecorator
-from metaflow.plugins.resources_decorator import ResourcesDecorator
-from metaflow.plugins.timeout_decorator import get_run_time_limit_for_task
-from metaflow.metadata import MetaDatum
-from metaflow.metadata.util import sync_local_metadata_to_datastore
+from metaflow.metadata_provider import MetaDatum
+from metaflow.metadata_provider.util import sync_local_metadata_to_datastore
 from metaflow.metaflow_config import (
-    ECS_S3_ACCESS_IAM_ROLE,
-    BATCH_JOB_QUEUE,
     BATCH_CONTAINER_IMAGE,
     BATCH_CONTAINER_REGISTRY,
-    ECS_FARGATE_EXECUTION_ROLE,
+    BATCH_JOB_QUEUE,
     DATASTORE_LOCAL_DIR,
+    ECS_FARGATE_EXECUTION_ROLE,
+    ECS_S3_ACCESS_IAM_ROLE,
+    FEAT_ALWAYS_UPLOAD_CODE_PACKAGE,
 )
+from metaflow.plugins.timeout_decorator import get_run_time_limit_for_task
 from metaflow.sidecar import Sidecar
 from metaflow.unbounded_foreach import UBF_CONTROL
 
-from .batch import BatchException
 from ..aws_utils import (
     compute_resource_attributes,
     get_docker_registry,
     get_ec2_instance_metadata,
 )
+from .batch import BatchException
 
 
 class BatchDecorator(StepDecorator):
@@ -37,52 +34,66 @@ class BatchDecorator(StepDecorator):
 
     Parameters
     ----------
-    cpu : int, default: 1
+    cpu : int, default 1
         Number of CPUs required for this step. If `@resources` is
         also present, the maximum value from all decorators is used.
-    gpu : int, default: 0
+    gpu : int, default 0
         Number of GPUs required for this step. If `@resources` is
         also present, the maximum value from all decorators is used.
-    memory : int, default: 4096
+    memory : int, default 4096
         Memory size (in MB) required for this step. If
         `@resources` is also present, the maximum value from all decorators is
         used.
-    image : str, optional
+    image : str, optional, default None
         Docker image to use when launching on AWS Batch. If not specified, and
         METAFLOW_BATCH_CONTAINER_IMAGE is specified, that image is used. If
         not, a default Docker image mapping to the current version of Python is used.
-    queue : str, default: METAFLOW_BATCH_JOB_QUEUE
+    queue : str, default METAFLOW_BATCH_JOB_QUEUE
         AWS Batch Job Queue to submit the job to.
-    iam_role : str, default: METAFLOW_ECS_S3_ACCESS_IAM_ROLE
+    iam_role : str, default METAFLOW_ECS_S3_ACCESS_IAM_ROLE
         AWS IAM role that AWS Batch container uses to access AWS cloud resources.
-    execution_role : str, default: METAFLOW_ECS_FARGATE_EXECUTION_ROLE
+    execution_role : str, default METAFLOW_ECS_FARGATE_EXECUTION_ROLE
         AWS IAM role that AWS Batch can use [to trigger AWS Fargate tasks]
         (https://docs.aws.amazon.com/batch/latest/userguide/execution-IAM-role.html).
-    shared_memory : int, optional
+    shared_memory : int, optional, default None
         The value for the size (in MiB) of the /dev/shm volume for this step.
         This parameter maps to the `--shm-size` option in Docker.
-    max_swap : int, optional
+    max_swap : int, optional, default None
         The total amount of swap memory (in MiB) a container can use for this
         step. This parameter is translated to the `--memory-swap` option in
         Docker where the value is the sum of the container memory plus the
         `max_swap` value.
-    swappiness : int, optional
+    swappiness : int, optional, default None
         This allows you to tune memory swappiness behavior for this step.
         A swappiness value of 0 causes swapping not to happen unless absolutely
         necessary. A swappiness value of 100 causes pages to be swapped very
         aggressively. Accepted values are whole numbers between 0 and 100.
-    use_tmpfs: bool, default: False
-        This enables an explicit tmpfs mount for this step.
-    tmpfs_tempdir: bool, default: True
+    use_tmpfs : bool, default False
+        This enables an explicit tmpfs mount for this step. Note that tmpfs is
+        not available on Fargate compute environments
+    tmpfs_tempdir : bool, default True
         sets METAFLOW_TEMPDIR to tmpfs_path if set for this step.
-    tmpfs_size: int, optional
+    tmpfs_size : int, optional, default None
         The value for the size (in MiB) of the tmpfs mount for this step.
         This parameter maps to the `--tmpfs` option in Docker. Defaults to 50% of the
         memory allocated for this step.
-    tmpfs_path: string, optional
+    tmpfs_path : str, optional, default None
         Path to tmpfs mount for this step. Defaults to /metaflow_temp.
-    inferentia : int, default: 0
+    inferentia : int, default 0
         Number of Inferentia chips required for this step.
+    trainium : int, default None
+        Alias for inferentia. Use only one of the two.
+    efa : int, default 0
+        Number of elastic fabric adapter network devices to attach to container
+    ephemeral_storage : int, default None
+        The total amount, in GiB, of ephemeral storage to set for the task, 21-200GiB.
+        This is only relevant for Fargate compute environments
+    log_driver: str, optional, default None
+        The log driver to use for the Amazon ECS container.
+    log_options: List[str], optional, default None
+        List of strings containing options for the chosen log driver. The configurable values
+        depend on the `log driver` chosen. Validation of these options is not supported yet.
+        Example: [`awslogs-group:aws/batch/job`]
     """
 
     name = "batch"
@@ -98,24 +109,34 @@ class BatchDecorator(StepDecorator):
         "max_swap": None,
         "swappiness": None,
         "inferentia": None,
+        "trainium": None,  # alias for inferentia
+        "efa": None,
         "host_volumes": None,
+        "efs_volumes": None,
         "use_tmpfs": False,
         "tmpfs_tempdir": True,
         "tmpfs_size": None,
         "tmpfs_path": "/metaflow_temp",
+        "ephemeral_storage": None,
+        "log_driver": None,
+        "log_options": None,
+        "executable": None,
     }
     resource_defaults = {
         "cpu": "1",
         "gpu": "0",
         "memory": "4096",
     }
+    package_metadata = None
     package_url = None
     package_sha = None
     run_time_limit = None
 
-    def __init__(self, attributes=None, statically_defined=False):
-        super(BatchDecorator, self).__init__(attributes, statically_defined)
+    # Conda environment support
+    supports_conda_environment = True
+    target_platform = "linux-64"
 
+    def init(self):
         # If no docker image is explicitly specified, impute a default image.
         if not self.attributes["image"]:
             # If metaflow-config specifies a docker image, just use that.
@@ -142,6 +163,21 @@ def __init__(self, attributes=None, statically_defined=False):
                     self.attributes["image"],
                 )
 
+        # Alias trainium to inferentia and check that both are not in use.
+        if (
+            self.attributes["inferentia"] is not None
+            and self.attributes["trainium"] is not None
+        ):
+            raise BatchException(
+                "only specify a value for 'inferentia' or 'trainium', not both."
+            )
+
+        if self.attributes["trainium"] is not None:
+            self.attributes["inferentia"] = self.attributes["trainium"]
+
+        # clean up the alias attribute so it is not passed on.
+        self.attributes.pop("trainium", None)
+
     # Refer https://github.com/Netflix/metaflow/blob/master/docs/lifecycle.png
     # to understand where these functions are invoked in the lifecycle of a
     # Metaflow flow.
@@ -192,6 +228,7 @@ def runtime_step_cli(
             # to execute on AWS Batch anymore. We can execute possible fallback
             # code locally.
             cli_args.commands = ["batch", "step"]
+            cli_args.command_args.append(self.package_metadata)
             cli_args.command_args.append(self.package_sha)
             cli_args.command_args.append(self.package_url)
             cli_args.command_options.update(self.attributes)
@@ -226,8 +263,8 @@ def task_pre_step(
         # metadata. A rudimentary way to detect non-local execution is to
         # check for the existence of AWS_BATCH_JOB_ID environment variable.
 
+        meta = {}
         if "AWS_BATCH_JOB_ID" in os.environ:
-            meta = {}
             meta["aws-batch-job-id"] = os.environ["AWS_BATCH_JOB_ID"]
             meta["aws-batch-job-attempt"] = os.environ["AWS_BATCH_JOB_ATTEMPT"]
             meta["aws-batch-ce-name"] = os.environ["AWS_BATCH_CE_NAME"]
@@ -240,6 +277,10 @@ def task_pre_step(
             # Metaflow would be running the container agent compatible with
             # version V4.
             # https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-metadata-endpoint.html
+
+            # TODO: Remove dependency on requests
+            import requests
+
             try:
                 logs_meta = (
                     requests.get(url=os.environ["ECS_CONTAINER_METADATA_URI_V4"])
@@ -255,21 +296,16 @@ def task_pre_step(
             instance_meta = get_ec2_instance_metadata()
             meta.update(instance_meta)
 
-            entries = [
-                MetaDatum(
-                    field=k,
-                    value=v,
-                    type=k,
-                    tags=["attempt_id:{0}".format(retry_count)],
-                )
-                for k, v in meta.items()
-            ]
-            # Register book-keeping metadata for debugging.
-            metadata.register_metadata(run_id, step_name, task_id, entries)
-
             self._save_logs_sidecar = Sidecar("save_logs_periodically")
             self._save_logs_sidecar.start()
 
+            # Start spot termination monitor sidecar.
+            current._update_env(
+                {"spot_termination_notice": "/tmp/spot_termination_notice"}
+            )
+            self._spot_monitor_sidecar = Sidecar("spot_termination_monitor")
+            self._spot_monitor_sidecar.start()
+
         num_parallel = int(os.environ.get("AWS_BATCH_JOB_NUM_NODES", 0))
         if num_parallel >= 1 and ubf_context == UBF_CONTROL:
             # UBF handling for multinode case
@@ -287,6 +323,21 @@ def task_pre_step(
 
         if num_parallel >= 1:
             _setup_multinode_environment()
+            # current.parallel.node_index will be correctly available over here.
+            meta.update({"parallel-node-index": current.parallel.node_index})
+
+        if len(meta) > 0:
+            entries = [
+                MetaDatum(
+                    field=k,
+                    value=v,
+                    type=k,
+                    tags=["attempt_id:{0}".format(retry_count)],
+                )
+                for k, v in meta.items()
+            ]
+            # Register book-keeping metadata for debugging.
+            metadata.register_metadata(run_id, step_name, task_id, entries)
 
     def task_finished(
         self, step_name, flow, graph, is_task_ok, retry_count, max_retries
@@ -307,6 +358,7 @@ def task_finished(
 
         try:
             self._save_logs_sidecar.terminate()
+            self._spot_monitor_sidecar.terminate()
         except:
             # Best effort kill
             pass
@@ -343,7 +395,7 @@ def _wait_for_mapper_tasks(self, flow, step_name):
                             len(flow._control_mapper_tasks),
                         )
                     )
-            except Exception as e:
+            except Exception:
                 pass
         raise Exception(
             "Batch secondary workers did not finish in %s seconds" % TIMEOUT
@@ -352,9 +404,16 @@ def _wait_for_mapper_tasks(self, flow, step_name):
     @classmethod
     def _save_package_once(cls, flow_datastore, package):
         if cls.package_url is None:
-            cls.package_url, cls.package_sha = flow_datastore.save_data(
-                [package.blob], len_hint=1
-            )[0]
+            if not FEAT_ALWAYS_UPLOAD_CODE_PACKAGE:
+                cls.package_url, cls.package_sha = flow_datastore.save_data(
+                    [package.blob], len_hint=1
+                )[0]
+                cls.package_metadata = package.package_metadata
+            else:
+                # Blocks until the package is uploaded
+                cls.package_url = package.package_url()
+                cls.package_sha = package.package_sha()
+                cls.package_metadata = package.package_metadata
 
 
 def _setup_multinode_environment():
diff --git a/metaflow/plugins/aws/secrets_manager/aws_secrets_manager_secrets_provider.py b/metaflow/plugins/aws/secrets_manager/aws_secrets_manager_secrets_provider.py
index 6dfffd192a8..153bbb31d1e 100644
--- a/metaflow/plugins/aws/secrets_manager/aws_secrets_manager_secrets_provider.py
+++ b/metaflow/plugins/aws/secrets_manager/aws_secrets_manager_secrets_provider.py
@@ -37,36 +37,40 @@ def _sanitize_key_as_env_var(key):
     Also, it's TBD whether all possible providers will share the same sanitization logic.
     Therefore we will keep this function private for now
     """
-    return key.replace("-", "_").replace(".", "_")
+    return key.replace("-", "_").replace(".", "_").replace("/", "_")
 
 
 class AwsSecretsManagerSecretsProvider(SecretsProvider):
     TYPE = "aws-secrets-manager"
 
-    def get_secret_as_dict(self, secret_id, options={}):
+    def get_secret_as_dict(self, secret_id, options={}, role=None):
         """
         Reads a secret from AWS Secrets Manager and returns it as a dictionary of environment variables.
 
         The secret payload from AWS is EITHER a string OR a binary blob.
 
         If the secret contains a string payload ("SecretString"):
-        - if the `parse_secret_string_as_json` option is True (default):
+        - if the `json` option is True (default):
             {SecretString} will be parsed as a JSON. If successfully parsed, AND the JSON contains a
             top-level object, each entry K/V in the object will also be converted to an entry in the result. V will
             always be casted to a string (if not already a string).
-        - If `parse_secret_string_as_json` option is False:
-            {SecretString} will be returned as a single entry in the result, with the key being the secret_id.
+        - If `json` option is False:
+            {SecretString} will be returned as a single entry in the result, where the key is either:
+                - the `secret_id`, OR
+                - the value set by `options={"env_var_name": custom_env_var_name}`.
 
-        Otherwise, the secret contains a binary blob payload ("SecretBinary"). In this case
-        - The result dic contains '{SecretName}': '{SecretBinary}', where {SecretBinary} is a base64-encoded string
+        Otherwise, if the secret contains a binary blob payload ("SecretBinary"):
+        - The result dict contains '{SecretName}': '{SecretBinary}', where {SecretBinary} is a base64-encoded string.
 
-        All keys in the result are sanitized to be more valid environment variable names. This is done on a best effort
+        All keys in the result are sanitized to be more valid environment variable names. This is done on a best-effort
         basis. Further validation is expected to be done by the invoking @secrets decorator itself.
 
-        :param secret_id: ARN or friendly name of the secret
-        :param options: unused
-        :return: dict of environment variables. All keys and values are strings.
+        :param secret_id: ARN or friendly name of the secret.
+        :param options: Dictionary of additional options. E.g., `options={"env_var_name": custom_env_var_name}`.
+        :param role: AWS IAM Role ARN to assume before reading the secret.
+        :return: Dictionary of environment variables. All keys and values are strings.
         """
+
         import botocore
         from metaflow.plugins.aws.aws_client import get_aws_client
 
@@ -79,12 +83,15 @@ def get_secret_as_dict(self, secret_id, options={}):
             effective_aws_region = options["region"]
         else:
             effective_aws_region = AWS_SECRETS_MANAGER_DEFAULT_REGION
+
         # At the end of all that, `effective_aws_region` may still be None.
         # This might still be OK, if there is fallback AWS region info in environment like:
         # .aws/config or AWS_REGION env var or AWS_DEFAULT_REGION env var, etc.
         try:
             secrets_manager_client = get_aws_client(
-                "secretsmanager", client_params={"region_name": effective_aws_region}
+                "secretsmanager",
+                client_params={"region_name": effective_aws_region},
+                role_arn=role,
             )
         except botocore.exceptions.NoRegionError:
             # We try our best with a nice error message.
@@ -143,7 +150,11 @@ def _sanitize_and_add_entry_to_result(k, v):
                         "Secret string could not be parsed as JSON"
                     )
             else:
-                _sanitize_and_add_entry_to_result(secret_name, secret_str)
+                if options.get("env_var_name"):
+                    env_var_name = options["env_var_name"]
+                else:
+                    env_var_name = secret_name
+                _sanitize_and_add_entry_to_result(env_var_name, secret_str)
 
         elif "SecretBinary" in response:
             # boto3 docs say response gives base64 encoded, but it's wrong.
@@ -153,8 +164,12 @@ def _sanitize_and_add_entry_to_result(k, v):
             # bytes.
             #
             # The trailing decode gives us a final UTF-8 string.
+            if options.get("env_var_name"):
+                env_var_name = options["env_var_name"]
+            else:
+                env_var_name = secret_name
             _sanitize_and_add_entry_to_result(
-                secret_name, base64.b64encode(response["SecretBinary"]).decode()
+                env_var_name, base64.b64encode(response["SecretBinary"]).decode()
             )
         else:
             raise MetaflowAWSSecretsManagerBadResponse(
diff --git a/metaflow/plugins/aws/step_functions/dynamo_db_client.py b/metaflow/plugins/aws/step_functions/dynamo_db_client.py
index caff1b4a343..2f4859e626d 100644
--- a/metaflow/plugins/aws/step_functions/dynamo_db_client.py
+++ b/metaflow/plugins/aws/step_functions/dynamo_db_client.py
@@ -1,5 +1,5 @@
-import os
-import requests
+import time
+
 from metaflow.metaflow_config import SFN_DYNAMO_DB_TABLE
 
 
@@ -25,12 +25,31 @@ def save_foreach_cardinality(self, foreach_split_task_id, foreach_cardinality, t
     def save_parent_task_id_for_foreach_join(
         self, foreach_split_task_id, foreach_join_parent_task_id
     ):
-        return self._client.update_item(
-            TableName=self.name,
-            Key={"pathspec": {"S": foreach_split_task_id}},
-            UpdateExpression="ADD parent_task_ids_for_foreach_join :val",
-            ExpressionAttributeValues={":val": {"SS": [foreach_join_parent_task_id]}},
-        )
+        ex = None
+        for attempt in range(10):
+            try:
+                return self._client.update_item(
+                    TableName=self.name,
+                    Key={"pathspec": {"S": foreach_split_task_id}},
+                    UpdateExpression="ADD parent_task_ids_for_foreach_join :val",
+                    ExpressionAttributeValues={
+                        ":val": {"SS": [foreach_join_parent_task_id]}
+                    },
+                )
+            except self._client.exceptions.ClientError as error:
+                ex = error
+                if (
+                    error.response["Error"]["Code"]
+                    == "ProvisionedThroughputExceededException"
+                ):
+                    # hopefully, enough time for AWS to scale up! otherwise
+                    # ensure sufficient on-demand throughput for dynamo db
+                    # is provisioned ahead of time
+                    sleep_time = min((2**attempt) * 10, 60)
+                    time.sleep(sleep_time)
+                else:
+                    raise
+        raise ex
 
     def get_parent_task_ids_for_foreach_join(self, foreach_split_task_id):
         response = self._client.get_item(
diff --git a/metaflow/plugins/aws/step_functions/event_bridge_client.py b/metaflow/plugins/aws/step_functions/event_bridge_client.py
index 5620b1de039..7f2273ed559 100644
--- a/metaflow/plugins/aws/step_functions/event_bridge_client.py
+++ b/metaflow/plugins/aws/step_functions/event_bridge_client.py
@@ -60,6 +60,19 @@ def _set(self):
             ],
         )
 
+    def delete(self):
+        try:
+            response = self._client.remove_targets(
+                Rule=self.name,
+                Ids=[self.name],
+            )
+            if response.get("FailedEntryCount", 0) > 0:
+                raise RuntimeError("Failed to remove targets from rule %s" % self.name)
+            return self._client.delete_rule(Name=self.name)
+        except self._client.exceptions.ResourceNotFoundException:
+            # Ignore if the rule does not exist.
+            return None
+
 
 def format(name):
     # AWS Event Bridge has a limit of 64 chars for rule names.
diff --git a/metaflow/plugins/aws/step_functions/production_token.py b/metaflow/plugins/aws/step_functions/production_token.py
index 1561cba1ab5..83e5e760936 100644
--- a/metaflow/plugins/aws/step_functions/production_token.py
+++ b/metaflow/plugins/aws/step_functions/production_token.py
@@ -1,9 +1,9 @@
-import os
 import json
+import os
 import random
 import string
 import zlib
-from itertools import dropwhile, islice
+from itertools import dropwhile
 
 from metaflow.util import to_bytes
 
diff --git a/metaflow/plugins/aws/step_functions/schedule_decorator.py b/metaflow/plugins/aws/step_functions/schedule_decorator.py
index 90f0c5201ef..d5f977538f6 100644
--- a/metaflow/plugins/aws/step_functions/schedule_decorator.py
+++ b/metaflow/plugins/aws/step_functions/schedule_decorator.py
@@ -9,16 +9,16 @@ class ScheduleDecorator(FlowDecorator):
 
     Parameters
     ----------
-    hourly : bool, default: False
+    hourly : bool, default False
         Run the workflow hourly.
-    daily : bool, default: True
+    daily : bool, default True
         Run the workflow daily.
-    weekly : bool, default: False
+    weekly : bool, default False
         Run the workflow weekly.
-    cron : str, optional
+    cron : str, optional, default None
         Run the workflow at [a custom Cron schedule](https://docs.aws.amazon.com/eventbridge/latest/userguide/scheduled-events.html#cron-expressions)
         specified by this expression.
-    timezone : str, optional
+    timezone : str, optional, default None
         Timezone on which the schedule runs (default: None). Currently supported only for Argo workflows,
         which accepts timezones in [IANA format](https://nodatime.org/TimeZones).
     """
diff --git a/metaflow/plugins/aws/step_functions/step_functions.py b/metaflow/plugins/aws/step_functions/step_functions.py
index 5119e34e422..963a39d559c 100644
--- a/metaflow/plugins/aws/step_functions/step_functions.py
+++ b/metaflow/plugins/aws/step_functions/step_functions.py
@@ -4,27 +4,23 @@
 import random
 import string
 import sys
-import time
-import uuid
 from collections import defaultdict
 
 from metaflow import R
 from metaflow.decorators import flow_decorators
-from metaflow.exception import MetaflowException, MetaflowInternalError
+from metaflow.exception import MetaflowException
 from metaflow.metaflow_config import (
     EVENTS_SFN_ACCESS_IAM_ROLE,
     S3_ENDPOINT_URL,
     SFN_DYNAMO_DB_TABLE,
     SFN_EXECUTION_LOG_GROUP_ARN,
     SFN_IAM_ROLE,
+    SFN_S3_DISTRIBUTED_MAP_OUTPUT_PATH,
 )
 from metaflow.parameters import deploy_time_eval
-from metaflow.plugins.aws.batch.batch_decorator import BatchDecorator
-from metaflow.plugins.resources_decorator import ResourcesDecorator
-from metaflow.plugins.retry_decorator import RetryDecorator
-from metaflow.util import compress_list, dict_to_cli_options, to_pascalcase
+from metaflow.user_configs.config_options import ConfigInput
+from metaflow.util import dict_to_cli_options, to_pascalcase
 
-from ..aws_utils import compute_resource_attributes
 from ..batch.batch import Batch
 from .event_bridge_client import EventBridgeClient
 from .step_functions_client import StepFunctionsClient
@@ -44,6 +40,7 @@ def __init__(
         name,
         graph,
         flow,
+        code_package_metadata,
         code_package_sha,
         code_package_url,
         production_token,
@@ -58,10 +55,12 @@ def __init__(
         max_workers=None,
         workflow_timeout=None,
         is_project=False,
+        use_distributed_map=False,
     ):
         self.name = name
         self.graph = graph
         self.flow = flow
+        self.code_package_metadata = code_package_metadata
         self.code_package_sha = code_package_sha
         self.code_package_url = code_package_url
         self.production_token = production_token
@@ -75,6 +74,10 @@ def __init__(
         self.username = username
         self.max_workers = max_workers
         self.workflow_timeout = workflow_timeout
+        self.config_parameters = self._process_config_parameters()
+
+        # https://aws.amazon.com/blogs/aws/step-functions-distributed-map-a-serverless-solution-for-large-scale-parallel-data-processing/
+        self.use_distributed_map = use_distributed_map
 
         self._client = StepFunctionsClient()
         self._workflow = self._compile()
@@ -158,6 +161,27 @@ def schedule(self):
         except Exception as e:
             raise StepFunctionsSchedulingException(repr(e))
 
+    @classmethod
+    def delete(cls, name):
+        # Always attempt to delete the event bridge rule.
+        schedule_deleted = EventBridgeClient(name).delete()
+
+        sfn_deleted = StepFunctionsClient().delete(name)
+
+        if sfn_deleted is None:
+            raise StepFunctionsException(
+                "The workflow *%s* doesn't exist on AWS Step Functions." % name
+            )
+
+        return schedule_deleted, sfn_deleted
+
+    @classmethod
+    def terminate(cls, flow_name, name):
+        client = StepFunctionsClient()
+        execution_arn, _, _, _ = cls.get_execution(flow_name, name)
+        response = client.terminate_execution(execution_arn)
+        return response
+
     @classmethod
     def trigger(cls, name, parameters):
         try:
@@ -214,7 +238,7 @@ def get_existing_deployment(cls, name):
                 return parameters.get("metaflow.owner"), parameters.get(
                     "metaflow.production_token"
                 )
-            except KeyError as e:
+            except KeyError:
                 raise StepFunctionsException(
                     "An existing non-metaflow "
                     "workflow with the same name as "
@@ -226,6 +250,50 @@ def get_existing_deployment(cls, name):
                 )
         return None
 
+    @classmethod
+    def get_execution(cls, state_machine_name, name):
+        client = StepFunctionsClient()
+        try:
+            state_machine = client.get(state_machine_name)
+        except Exception as e:
+            raise StepFunctionsException(repr(e))
+        if state_machine is None:
+            raise StepFunctionsException(
+                "The state machine *%s* doesn't exist on AWS Step Functions."
+                % state_machine_name
+            )
+        try:
+            state_machine_arn = state_machine.get("stateMachineArn")
+            environment_vars = (
+                json.loads(state_machine.get("definition"))
+                .get("States")
+                .get("start")
+                .get("Parameters")
+                .get("ContainerOverrides")
+                .get("Environment")
+            )
+            parameters = {
+                item.get("Name"): item.get("Value") for item in environment_vars
+            }
+            executions = client.list_executions(state_machine_arn, states=["RUNNING"])
+            for execution in executions:
+                if execution.get("name") == name:
+                    try:
+                        return (
+                            execution.get("executionArn"),
+                            parameters.get("METAFLOW_OWNER"),
+                            parameters.get("METAFLOW_PRODUCTION_TOKEN"),
+                            parameters.get("SFN_STATE_MACHINE"),
+                        )
+                    except KeyError:
+                        raise StepFunctionsException(
+                            "A non-metaflow workflow *%s* already exists in AWS Step Functions."
+                            % name
+                        )
+            return None
+        except Exception as e:
+            raise StepFunctionsException(repr(e))
+
     def _compile(self):
         if self.flow._flow_decorators.get("trigger") or self.flow._flow_decorators.get(
             "trigger_on_finish"
@@ -234,6 +302,13 @@ def _compile(self):
                 "Deploying flows with @trigger or @trigger_on_finish decorator(s) "
                 "to AWS Step Functions is not supported currently."
             )
+
+        if self.flow._flow_decorators.get("exit_hook"):
+            raise StepFunctionsException(
+                "Deploying flows with the @exit_hook decorator "
+                "to AWS Step Functions is not currently supported."
+            )
+
         # Visit every node of the flow and recursively build the state machine.
         def _visit(node, workflow, exit_node=None):
             if node.parallel_foreach:
@@ -305,17 +380,80 @@ def _visit(node, workflow, exit_node=None):
                     .parameter("SplitParentTaskId.$", "$.JobId")
                     .parameter("Parameters.$", "$.Parameters")
                     .parameter("Index.$", "$$.Map.Item.Value")
-                    .next(node.matching_join)
+                    .next(
+                        "%s_*GetManifest" % iterator_name
+                        if self.use_distributed_map
+                        else node.matching_join
+                    )
                     .iterator(
                         _visit(
                             self.graph[node.out_funcs[0]],
-                            Workflow(node.out_funcs[0]).start_at(node.out_funcs[0]),
+                            Workflow(node.out_funcs[0])
+                            .start_at(node.out_funcs[0])
+                            .mode(
+                                "DISTRIBUTED" if self.use_distributed_map else "INLINE"
+                            ),
                             node.matching_join,
                         )
                     )
                     .max_concurrency(self.max_workers)
-                    .output_path("$.[0]")
+                    # AWS Step Functions has a short coming for DistributedMap at the
+                    # moment that does not allow us to subset the output of for-each
+                    # to just a single element. We have to rely on a rather terrible
+                    # hack and resort to using ResultWriter to write the state to
+                    # Amazon S3 and process it in another task. But, well what can we
+                    # do...
+                    .result_writer(
+                        *(
+                            (
+                                (
+                                    SFN_S3_DISTRIBUTED_MAP_OUTPUT_PATH[len("s3://") :]
+                                    if SFN_S3_DISTRIBUTED_MAP_OUTPUT_PATH.startswith(
+                                        "s3://"
+                                    )
+                                    else SFN_S3_DISTRIBUTED_MAP_OUTPUT_PATH
+                                ).split("/", 1)
+                                + [""]
+                            )[:2]
+                            if self.use_distributed_map
+                            else (None, None)
+                        )
+                    )
+                    .output_path("$" if self.use_distributed_map else "$.[0]")
                 )
+                if self.use_distributed_map:
+                    workflow.add_state(
+                        State("%s_*GetManifest" % iterator_name)
+                        .resource("arn:aws:states:::aws-sdk:s3:getObject")
+                        .parameter("Bucket.$", "$.ResultWriterDetails.Bucket")
+                        .parameter("Key.$", "$.ResultWriterDetails.Key")
+                        .next("%s_*Map" % iterator_name)
+                        .result_selector("Body.$", "States.StringToJson($.Body)")
+                    )
+                    workflow.add_state(
+                        Map("%s_*Map" % iterator_name)
+                        .iterator(
+                            Workflow("%s_*PassWorkflow" % iterator_name)
+                            .mode("DISTRIBUTED")
+                            .start_at("%s_*Pass" % iterator_name)
+                            .add_state(
+                                Pass("%s_*Pass" % iterator_name)
+                                .end()
+                                .parameter("Output.$", "States.StringToJson($.Output)")
+                                .output_path("$.Output")
+                            )
+                        )
+                        .next(node.matching_join)
+                        .max_concurrency(1000)
+                        .item_reader(
+                            JSONItemReader()
+                            .resource("arn:aws:states:::s3:getObject")
+                            .parameter("Bucket.$", "$.Body.DestinationBucket")
+                            .parameter("Key.$", "$.Body.ResultFiles.SUCCEEDED[0].Key")
+                        )
+                        .output_path("$.[0]")
+                    )
+
                 # Continue the traversal from the matching_join.
                 _visit(self.graph[node.matching_join], workflow, exit_node)
             # We shouldn't ideally ever get here.
@@ -357,6 +495,10 @@ def _process_parameters(self):
                     "case-insensitive." % param.name
                 )
             seen.add(norm)
+            # NOTE: We skip config parameters as these do not have dynamic values,
+            # and need to be treated differently.
+            if param.IS_CONFIG_PARAMETER:
+                continue
 
             is_required = param.kwargs.get("required", False)
             # Throw an exception if a schedule is set for a flow with required
@@ -373,6 +515,27 @@ def _process_parameters(self):
             parameters.append(dict(name=param.name, value=value))
         return parameters
 
+    def _process_config_parameters(self):
+        parameters = []
+        seen = set()
+        for var, param in self.flow._get_parameters():
+            if not param.IS_CONFIG_PARAMETER:
+                continue
+            # Throw an exception if the parameter is specified twice.
+            norm = param.name.lower()
+            if norm in seen:
+                raise MetaflowException(
+                    "Parameter *%s* is specified twice. "
+                    "Note that parameter names are "
+                    "case-insensitive." % param.name
+                )
+            seen.add(norm)
+
+            parameters.append(
+                dict(name=param.name, kv_name=ConfigInput.make_key_name(param.name))
+            )
+        return parameters
+
     def _batch(self, node):
         attrs = {
             # metaflow.user is only used for setting the AWS Job Name.
@@ -384,7 +547,6 @@ def _batch(self, node):
             "metaflow.owner": self.username,
             "metaflow.flow_name": self.flow.name,
             "metaflow.step_name": node.name,
-            "metaflow.run_id.$": "$$.Execution.Name",
             # Unfortunately we can't set the task id here since AWS Step
             # Functions lacks any notion of run-scoped task identifiers. We
             # instead co-opt the AWS Batch job id as the task id. This also
@@ -396,6 +558,10 @@ def _batch(self, node):
             # `$$.State.RetryCount` resolves to an int dynamically and
             # AWS Batch job specification only accepts strings. We handle
             # retries/catch within AWS Batch to get around this limitation.
+            # And, we also cannot set the run id here since the run id maps to
+            # the execution name of the AWS Step Functions State Machine, which
+            # is different when executing inside a distributed map. We set it once
+            # in the start step and move it along to be consumed by all the children.
             "metaflow.version": self.environment.get_environment_info()[
                 "metaflow_version"
             ],
@@ -432,6 +598,12 @@ def _batch(self, node):
             env["METAFLOW_S3_ENDPOINT_URL"] = S3_ENDPOINT_URL
 
         if node.name == "start":
+            # metaflow.run_id maps to AWS Step Functions State Machine Execution in all
+            # cases except for when within a for-each construct that relies on
+            # Distributed Map. To work around this issue, we pass the run id from the
+            # start step to all subsequent tasks.
+            attrs["metaflow.run_id.$"] = "$$.Execution.Name"
+
             # Initialize parameters for the flow in the `start` step.
             parameters = self._process_parameters()
             if parameters:
@@ -490,6 +662,8 @@ def _batch(self, node):
                 env["METAFLOW_SPLIT_PARENT_TASK_ID"] = (
                     "$.Parameters.split_parent_task_id_%s" % node.split_parents[-1]
                 )
+                # Inherit the run id from the parent and pass it along to children.
+                attrs["metaflow.run_id.$"] = "$.Parameters.['metaflow.run_id']"
             else:
                 # Set appropriate environment variables for runtime replacement.
                 if len(node.in_funcs) == 1:
@@ -498,6 +672,8 @@ def _batch(self, node):
                         % node.in_funcs[0]
                     )
                     env["METAFLOW_PARENT_TASK_ID"] = "$.JobId"
+                    # Inherit the run id from the parent and pass it along to children.
+                    attrs["metaflow.run_id.$"] = "$.Parameters.['metaflow.run_id']"
                 else:
                     # Generate the input paths in a quasi-compressed format.
                     # See util.decompress_list for why this is written the way
@@ -507,6 +683,8 @@ def _batch(self, node):
                         "${METAFLOW_PARENT_%s_TASK_ID}" % (idx, idx)
                         for idx, _ in enumerate(node.in_funcs)
                     )
+                    # Inherit the run id from the parent and pass it along to children.
+                    attrs["metaflow.run_id.$"] = "$.[0].Parameters.['metaflow.run_id']"
                     for idx, _ in enumerate(node.in_funcs):
                         env["METAFLOW_PARENT_%s_TASK_ID" % idx] = "$.[%s].JobId" % idx
                         env["METAFLOW_PARENT_%s_STEP" % idx] = (
@@ -521,9 +699,9 @@ def _batch(self, node):
                 # input to those descendent tasks. We set and propagate the
                 # task ids pointing to split_parents through every state.
                 if any(self.graph[n].type == "foreach" for n in node.in_funcs):
-                    attrs[
-                        "split_parent_task_id_%s.$" % node.split_parents[-1]
-                    ] = "$.SplitParentTaskId"
+                    attrs["split_parent_task_id_%s.$" % node.split_parents[-1]] = (
+                        "$.SplitParentTaskId"
+                    )
                     for parent in node.split_parents[:-1]:
                         if self.graph[parent].type == "foreach":
                             attrs["split_parent_task_id_%s.$" % parent] = (
@@ -604,6 +782,11 @@ def _batch(self, node):
         metaflow_version["production_token"] = self.production_token
         env["METAFLOW_VERSION"] = json.dumps(metaflow_version)
 
+        # map config values
+        cfg_env = {param["name"]: param["kv_name"] for param in self.config_parameters}
+        if cfg_env:
+            env["METAFLOW_FLOW_CONFIG_VALUE"] = json.dumps(cfg_env)
+
         # Set AWS DynamoDb Table Name for state tracking for for-eaches.
         # There are three instances when metaflow runtime directly interacts
         # with AWS DynamoDB.
@@ -649,11 +832,6 @@ def _batch(self, node):
         batch_deco = [deco for deco in node.decorators if deco.name == "batch"][0]
         resources = {}
         resources.update(batch_deco.attributes)
-        resources.update(
-            compute_resource_attributes(
-                node.decorators, batch_deco, batch_deco.resource_defaults
-            )
-        )
         # Resolve retry strategy.
         user_code_retries, total_retries = self._get_retries(node)
 
@@ -677,6 +855,7 @@ def _batch(self, node):
                     node, input_paths, self.code_package_url, user_code_retries
                 ),
                 task_spec=task_spec,
+                code_package_metadata=self.code_package_metadata,
                 code_package_sha=self.code_package_sha,
                 code_package_url=self.code_package_url,
                 code_package_ds=self.flow_datastore.TYPE,
@@ -691,6 +870,7 @@ def _batch(self, node):
                 shared_memory=resources["shared_memory"],
                 max_swap=resources["max_swap"],
                 swappiness=resources["swappiness"],
+                efa=resources["efa"],
                 use_tmpfs=resources["use_tmpfs"],
                 tmpfs_tempdir=resources["tmpfs_tempdir"],
                 tmpfs_size=resources["tmpfs_size"],
@@ -699,6 +879,10 @@ def _batch(self, node):
                 env=env,
                 attrs=attrs,
                 host_volumes=resources["host_volumes"],
+                efs_volumes=resources["efs_volumes"],
+                ephemeral_storage=resources["ephemeral_storage"],
+                log_driver=resources["log_driver"],
+                log_options=resources["log_options"],
             )
             .attempts(total_retries + 1)
         )
@@ -732,13 +916,13 @@ def _step_cli(self, node, paths, code_package_url, user_code_retries):
             "with": [
                 decorator.make_decorator_spec()
                 for decorator in node.decorators
-                if not decorator.statically_defined
+                if not decorator.statically_defined and decorator.inserted_by is None
             ]
         }
         # FlowDecorators can define their own top-level options. They are
         # responsible for adding their own top-level options and values through
         # the get_top_level_options() hook. See similar logic in runtime.py.
-        for deco in flow_decorators():
+        for deco in flow_decorators(self.flow):
             top_opts_dict.update(deco.get_top_level_options())
 
         top_opts = list(dict_to_cli_options(top_opts_dict))
@@ -836,6 +1020,12 @@ def __init__(self, name):
         tree = lambda: defaultdict(tree)
         self.payload = tree()
 
+    def mode(self, mode):
+        self.payload["ProcessorConfig"] = {"Mode": mode}
+        if mode == "DISTRIBUTED":
+            self.payload["ProcessorConfig"]["ExecutionType"] = "STANDARD"
+        return self
+
     def start_at(self, start_at):
         self.payload["StartAt"] = start_at
         return self
@@ -883,10 +1073,18 @@ def result_path(self, result_path):
         self.payload["ResultPath"] = result_path
         return self
 
+    def result_selector(self, name, value):
+        self.payload["ResultSelector"][name] = value
+        return self
+
     def _partition(self):
         # This is needed to support AWS Gov Cloud and AWS CN regions
         return SFN_IAM_ROLE.split(":")[1]
 
+    def retry_strategy(self, retry_strategy):
+        self.payload["Retry"] = [retry_strategy]
+        return self
+
     def batch(self, job):
         self.resource(
             "arn:%s:states:::batch:submitJob.sync" % self._partition()
@@ -906,6 +1104,19 @@ def batch(self, job):
         # tags may not be present in all scenarios
         if "tags" in job.payload:
             self.parameter("Tags", job.payload["tags"])
+        # set retry strategy for AWS Batch job submission to account for the
+        # measily 50 jobs / second queue admission limit which people can
+        # run into very quickly.
+        self.retry_strategy(
+            {
+                "ErrorEquals": ["Batch.AWSBatchException"],
+                "BackoffRate": 2,
+                "IntervalSeconds": 2,
+                "MaxDelaySeconds": 60,
+                "MaxAttempts": 10,
+                "JitterStrategy": "FULL",
+            }
+        )
         return self
 
     def dynamo_db(self, table_name, primary_key, values):
@@ -919,6 +1130,26 @@ def dynamo_db(self, table_name, primary_key, values):
         return self
 
 
+class Pass(object):
+    def __init__(self, name):
+        self.name = name
+        tree = lambda: defaultdict(tree)
+        self.payload = tree()
+        self.payload["Type"] = "Pass"
+
+    def end(self):
+        self.payload["End"] = True
+        return self
+
+    def parameter(self, name, value):
+        self.payload["Parameters"][name] = value
+        return self
+
+    def output_path(self, output_path):
+        self.payload["OutputPath"] = output_path
+        return self
+
+
 class Parallel(object):
     def __init__(self, name):
         self.name = name
@@ -980,3 +1211,37 @@ def output_path(self, output_path):
     def result_path(self, result_path):
         self.payload["ResultPath"] = result_path
         return self
+
+    def item_reader(self, item_reader):
+        self.payload["ItemReader"] = item_reader.payload
+        return self
+
+    def result_writer(self, bucket, prefix):
+        if bucket is not None and prefix is not None:
+            self.payload["ResultWriter"] = {
+                "Resource": "arn:aws:states:::s3:putObject",
+                "Parameters": {
+                    "Bucket": bucket,
+                    "Prefix": prefix,
+                },
+            }
+        return self
+
+
+class JSONItemReader(object):
+    def __init__(self):
+        tree = lambda: defaultdict(tree)
+        self.payload = tree()
+        self.payload["ReaderConfig"] = {"InputType": "JSON", "MaxItems": 1}
+
+    def resource(self, resource):
+        self.payload["Resource"] = resource
+        return self
+
+    def parameter(self, name, value):
+        self.payload["Parameters"][name] = value
+        return self
+
+    def output_path(self, output_path):
+        self.payload["OutputPath"] = output_path
+        return self
diff --git a/metaflow/plugins/aws/step_functions/step_functions_cli.py b/metaflow/plugins/aws/step_functions/step_functions_cli.py
index d11eea58966..1a6f4a56561 100644
--- a/metaflow/plugins/aws/step_functions/step_functions_cli.py
+++ b/metaflow/plugins/aws/step_functions/step_functions_cli.py
@@ -1,32 +1,36 @@
 import base64
-from metaflow._vendor import click
-from hashlib import sha1
 import json
 import re
-from distutils.version import LooseVersion
+from hashlib import sha1
 
-from metaflow import current, decorators, parameters, JSONType
+from metaflow import JSONType, current, decorators, parameters
+from metaflow._vendor import click
+from metaflow.exception import MetaflowException, MetaflowInternalError
 from metaflow.metaflow_config import (
+    FEAT_ALWAYS_UPLOAD_CODE_PACKAGE,
     SERVICE_VERSION_CHECK,
     SFN_STATE_MACHINE_PREFIX,
     UI_URL,
 )
-from metaflow.exception import MetaflowException, MetaflowInternalError
 from metaflow.package import MetaflowPackage
 from metaflow.plugins.aws.batch.batch_decorator import BatchDecorator
 from metaflow.tagging_util import validate_tags
-from metaflow.util import get_username, to_bytes, to_unicode
+from metaflow.util import get_username, to_bytes, to_unicode, version_parse
 
+from .production_token import load_token, new_token, store_token
 from .step_functions import StepFunctions
-from .production_token import load_token, store_token, new_token
 
-VALID_NAME = re.compile("[^a-zA-Z0-9_\-\.]")
+VALID_NAME = re.compile(r"[^a-zA-Z0-9_\-\.]")
 
 
 class IncorrectProductionToken(MetaflowException):
     headline = "Incorrect production token"
 
 
+class RunIdMismatch(MetaflowException):
+    headline = "Run ID mismatch"
+
+
 class IncorrectMetadataServiceVersion(MetaflowException):
     headline = "Incorrect version for metaflow service"
 
@@ -121,6 +125,20 @@ def step_functions(obj, name=None):
     help="Log AWS Step Functions execution history to AWS CloudWatch "
     "Logs log group.",
 )
+@click.option(
+    "--use-distributed-map/--no-use-distributed-map",
+    is_flag=True,
+    help="Use AWS Step Functions Distributed Map instead of Inline Map for "
+    "defining foreach tasks in Amazon State Language.",
+)
+@click.option(
+    "--deployer-attribute-file",
+    default=None,
+    show_default=True,
+    type=str,
+    help="Write the workflow name to the file specified. Used internally for Metaflow's Deployer API.",
+    hidden=True,
+)
 @click.pass_obj
 def create(
     obj,
@@ -133,9 +151,29 @@ def create(
     max_workers=None,
     workflow_timeout=None,
     log_execution_history=False,
+    use_distributed_map=False,
+    deployer_attribute_file=None,
 ):
+    for node in obj.graph:
+        if any([d.name == "slurm" for d in node.decorators]):
+            raise MetaflowException(
+                "Step *%s* is marked for execution on Slurm with AWS Step Functions which isn't currently supported."
+                % node.name
+            )
+
     validate_tags(tags)
 
+    if deployer_attribute_file:
+        with open(deployer_attribute_file, "w") as f:
+            json.dump(
+                {
+                    "name": obj.state_machine_name,
+                    "flow_name": obj.flow.name,
+                    "metadata": obj.metadata.metadata_str(),
+                },
+                f,
+            )
+
     obj.echo(
         "Deploying *%s* to AWS Step Functions..." % obj.state_machine_name, bold=True
     )
@@ -162,6 +200,7 @@ def create(
         max_workers,
         workflow_timeout,
         obj.is_project,
+        use_distributed_map,
     )
 
     if only_json:
@@ -192,7 +231,7 @@ def check_metadata_service_version(obj):
     version = metadata.version()
     if version == "local":
         return
-    elif version is not None and LooseVersion(version) >= LooseVersion("2.0.2"):
+    elif version is not None and version_parse(version) >= version_parse("2.0.2"):
         # Metaflow metadata service needs to be at least at version 2.0.2
         return
     else:
@@ -220,8 +259,10 @@ def check_metadata_service_version(obj):
 
 
 def resolve_state_machine_name(obj, name):
-    def attach_prefix(name):
-        if SFN_STATE_MACHINE_PREFIX is not None:
+    def attach_prefix(name: str):
+        if SFN_STATE_MACHINE_PREFIX is not None and (
+            not name.startswith(SFN_STATE_MACHINE_PREFIX)
+        ):
             return SFN_STATE_MACHINE_PREFIX + "_" + name
         return name
 
@@ -270,28 +311,48 @@ def attach_prefix(name):
 
 
 def make_flow(
-    obj, token, name, tags, namespace, max_workers, workflow_timeout, is_project
+    obj,
+    token,
+    name,
+    tags,
+    namespace,
+    max_workers,
+    workflow_timeout,
+    is_project,
+    use_distributed_map,
 ):
     if obj.flow_datastore.TYPE != "s3":
         raise MetaflowException("AWS Step Functions requires --datastore=s3.")
 
     # Attach AWS Batch decorator to the flow
     decorators._attach_decorators(obj.flow, [BatchDecorator.name])
+    decorators._init(obj.flow)
     decorators._init_step_decorators(
         obj.flow, obj.graph, obj.environment, obj.flow_datastore, obj.logger
     )
+    obj.graph = obj.flow._graph
 
     obj.package = MetaflowPackage(
-        obj.flow, obj.environment, obj.echo, obj.package_suffixes
+        obj.flow,
+        obj.environment,
+        obj.echo,
+        suffixes=obj.package_suffixes,
+        flow_datastore=obj.flow_datastore if FEAT_ALWAYS_UPLOAD_CODE_PACKAGE else None,
     )
-    package_url, package_sha = obj.flow_datastore.save_data(
-        [obj.package.blob], len_hint=1
-    )[0]
+    # This blocks until the package is created
+    if FEAT_ALWAYS_UPLOAD_CODE_PACKAGE:
+        package_url = obj.package.package_url()
+        package_sha = obj.package.package_sha()
+    else:
+        package_url, package_sha = obj.flow_datastore.save_data(
+            [obj.package.blob], len_hint=1
+        )[0]
 
     return StepFunctions(
         name,
         obj.graph,
         obj.flow,
+        obj.package.package_metadata,
         package_sha,
         package_url,
         token,
@@ -306,6 +367,7 @@ def make_flow(
         username=get_username(),
         workflow_timeout=workflow_timeout,
         is_project=is_project,
+        use_distributed_map=use_distributed_map,
     )
 
 
@@ -420,8 +482,16 @@ def resolve_token(
     type=str,
     help="Write the ID of this run to the file specified.",
 )
+@click.option(
+    "--deployer-attribute-file",
+    default=None,
+    show_default=True,
+    type=str,
+    help="Write the metadata and pathspec of this run to the file specified.\nUsed internally for Metaflow's Deployer API.",
+    hidden=True,
+)
 @click.pass_obj
-def trigger(obj, run_id_file=None, **kwargs):
+def trigger(obj, run_id_file=None, deployer_attribute_file=None, **kwargs):
     def _convert_value(param):
         # Swap `-` with `_` in parameter name to match click's behavior
         val = kwargs.get(param.name.replace("-", "_").lower())
@@ -446,6 +516,17 @@ def _convert_value(param):
         with open(run_id_file, "w") as f:
             f.write(str(run_id))
 
+    if deployer_attribute_file:
+        with open(deployer_attribute_file, "w") as f:
+            json.dump(
+                {
+                    "name": obj.state_machine_name,
+                    "metadata": obj.metadata.metadata_str(),
+                    "pathspec": "/".join((obj.flow.name, run_id)),
+                },
+                f,
+            )
+
     obj.echo(
         "Workflow *{name}* triggered on AWS Step Functions "
         "(run-id *{run_id}*).".format(name=obj.state_machine_name, run_id=run_id),
@@ -555,3 +636,179 @@ def list_runs(
                 "No executions for *%s* found on AWS Step Functions."
                 % (obj.state_machine_name)
             )
+
+
+@step_functions.command(help="Delete a workflow")
+@click.option(
+    "--authorize",
+    default=None,
+    type=str,
+    help="Authorize the deletion with a production token",
+)
+@click.pass_obj
+def delete(obj, authorize=None):
+    def _token_instructions(flow_name, prev_user):
+        obj.echo(
+            "There is an existing version of *%s* on AWS Step "
+            "Functions which was deployed by the user "
+            "*%s*." % (flow_name, prev_user)
+        )
+        obj.echo(
+            "To delete this flow, you need to use the same production token that they used."
+        )
+        obj.echo(
+            "Please reach out to them to get the token. Once you "
+            "have it, call this command:"
+        )
+        obj.echo("    step-functions delete --authorize MY_TOKEN", fg="green")
+        obj.echo(
+            'See "Organizing Results" at docs.metaflow.org for more '
+            "information about production tokens."
+        )
+
+    validate_token(
+        obj.state_machine_name, obj.token_prefix, authorize, _token_instructions
+    )
+
+    obj.echo(
+        "Deleting AWS Step Functions state machine *{name}*...".format(
+            name=obj.state_machine_name
+        ),
+        bold=True,
+    )
+    schedule_deleted, sfn_deleted = StepFunctions.delete(obj.state_machine_name)
+
+    if schedule_deleted:
+        obj.echo(
+            "Deleting Amazon EventBridge rule *{name}* as well...".format(
+                name=obj.state_machine_name
+            ),
+            bold=True,
+        )
+    if sfn_deleted:
+        obj.echo(
+            "Deleting the AWS Step Functions state machine may take a while. "
+            "Deploying the flow again to AWS Step Functions while the delete is in-flight will fail."
+        )
+        obj.echo(
+            "In-flight executions will not be affected. "
+            "If necessary, terminate them manually."
+        )
+
+
+@step_functions.command(help="Terminate flow execution on Step Functions.")
+@click.option(
+    "--authorize",
+    default=None,
+    type=str,
+    help="Authorize the termination with a production token",
+)
+@click.argument("run-id", required=True, type=str)
+@click.pass_obj
+def terminate(obj, run_id, authorize=None):
+    def _token_instructions(flow_name, prev_user):
+        obj.echo(
+            "There is an existing version of *%s* on AWS Step Functions which was "
+            "deployed by the user *%s*." % (flow_name, prev_user)
+        )
+        obj.echo(
+            "To terminate this flow, you need to use the same production token that they used."
+        )
+        obj.echo(
+            "Please reach out to them to get the token. Once you have it, call "
+            "this command:"
+        )
+        obj.echo("    step-functions terminate --authorize MY_TOKEN RUN_ID", fg="green")
+        obj.echo(
+            'See "Organizing Results" at docs.metaflow.org for more information '
+            "about production tokens."
+        )
+
+    validate_run_id(
+        obj.state_machine_name, obj.token_prefix, authorize, run_id, _token_instructions
+    )
+
+    # Trim prefix from run_id
+    name = run_id[4:]
+    obj.echo(
+        "Terminating run *{run_id}* for {flow_name} ...".format(
+            run_id=run_id, flow_name=obj.flow.name
+        ),
+        bold=True,
+    )
+
+    terminated = StepFunctions.terminate(obj.state_machine_name, name)
+    if terminated:
+        obj.echo("\nRun terminated at %s." % terminated.get("stopDate"))
+
+
+def validate_run_id(
+    state_machine_name, token_prefix, authorize, run_id, instructions_fn=None
+):
+    if not run_id.startswith("sfn-"):
+        raise RunIdMismatch(
+            "Run IDs for flows executed through AWS Step Functions begin with 'sfn-'"
+        )
+
+    name = run_id[4:]
+    execution = StepFunctions.get_execution(state_machine_name, name)
+    if execution is None:
+        raise MetaflowException(
+            "Could not find the execution *%s* (in RUNNING state) for the state machine *%s* on AWS Step Functions"
+            % (name, state_machine_name)
+        )
+
+    _, owner, token, _ = execution
+
+    if authorize is None:
+        authorize = load_token(token_prefix)
+    elif authorize.startswith("production:"):
+        authorize = authorize[11:]
+
+    if owner != get_username() and authorize != token:
+        if instructions_fn:
+            instructions_fn(flow_name=name, prev_user=owner)
+        raise IncorrectProductionToken("Try again with the correct production token.")
+
+    return True
+
+
+def validate_token(name, token_prefix, authorize, instruction_fn=None):
+    """
+    Validate that the production token matches that of the deployed flow.
+
+    In case both the user and token do not match, raises an error.
+    Optionally outputs instructions on token usage via the provided instruction_fn(flow_name, prev_user)
+    """
+    # TODO: Unify this with the existing resolve_token implementation.
+
+    # 1) retrieve the previous deployment, if one exists
+    workflow = StepFunctions.get_existing_deployment(name)
+    if workflow is None:
+        prev_token = None
+    else:
+        prev_user, prev_token = workflow
+
+    # 2) authorize this deployment
+    if prev_token is not None:
+        if authorize is None:
+            authorize = load_token(token_prefix)
+        elif authorize.startswith("production:"):
+            authorize = authorize[11:]
+
+        # we allow the user who deployed the previous version to re-deploy,
+        # even if they don't have the token
+        # NOTE: The username is visible in multiple sources, and can be set by the user.
+        # Should we consider being stricter here?
+        if prev_user != get_username() and authorize != prev_token:
+            if instruction_fn:
+                instruction_fn(flow_name=name, prev_user=prev_user)
+            raise IncorrectProductionToken(
+                "Try again with the correct production token."
+            )
+
+    # 3) all validations passed, store the previous token for future use
+    token = prev_token
+
+    store_token(token_prefix, token)
+    return True
diff --git a/metaflow/plugins/aws/step_functions/step_functions_client.py b/metaflow/plugins/aws/step_functions/step_functions_client.py
index c42bb0a8907..ceec8e4d0ce 100644
--- a/metaflow/plugins/aws/step_functions/step_functions_client.py
+++ b/metaflow/plugins/aws/step_functions/step_functions_client.py
@@ -81,9 +81,14 @@ def list_executions(self, state_machine_arn, states):
             for execution in page["executions"]
         )
 
-    def terminate_execution(self, state_machine_arn, execution_arn):
-        # TODO
-        pass
+    def terminate_execution(self, execution_arn):
+        try:
+            response = self._client.stop_execution(executionArn=execution_arn)
+            return response
+        except self._client.exceptions.ExecutionDoesNotExist:
+            raise ValueError("The execution ARN %s does not exist." % execution_arn)
+        except Exception as e:
+            raise e
 
     def _default_logging_configuration(self, log_execution_history):
         if log_execution_history:
@@ -117,3 +122,11 @@ def get_state_machine_arn(self, name):
             if state_machine:
                 return state_machine["stateMachineArn"]
             return None
+
+    def delete(self, name):
+        state_machine_arn = self.get_state_machine_arn(name)
+        if state_machine_arn is None:
+            return None
+        return self._client.delete_state_machine(
+            stateMachineArn=state_machine_arn,
+        )
diff --git a/metaflow/plugins/aws/step_functions/step_functions_decorator.py b/metaflow/plugins/aws/step_functions/step_functions_decorator.py
index ba71c0af8df..43c2de81dab 100644
--- a/metaflow/plugins/aws/step_functions/step_functions_decorator.py
+++ b/metaflow/plugins/aws/step_functions/step_functions_decorator.py
@@ -1,9 +1,8 @@
 import os
-import json
 import time
 
 from metaflow.decorators import StepDecorator
-from metaflow.metadata import MetaDatum
+from metaflow.metadata_provider import MetaDatum
 
 from .dynamo_db_client import DynamoDbClient
 
diff --git a/metaflow/plugins/aws/step_functions/step_functions_deployer.py b/metaflow/plugins/aws/step_functions/step_functions_deployer.py
new file mode 100644
index 00000000000..6e2f1d72151
--- /dev/null
+++ b/metaflow/plugins/aws/step_functions/step_functions_deployer.py
@@ -0,0 +1,94 @@
+from typing import Any, ClassVar, Dict, Optional, TYPE_CHECKING, Type
+
+from metaflow.runner.deployer_impl import DeployerImpl
+
+if TYPE_CHECKING:
+    import metaflow.plugins.aws.step_functions.step_functions_deployer_objects
+
+
+class StepFunctionsDeployer(DeployerImpl):
+    """
+    Deployer implementation for AWS Step Functions.
+
+    Parameters
+    ----------
+    name : str, optional, default None
+        State Machine name. The flow name is used instead if this option is not specified.
+    """
+
+    TYPE: ClassVar[Optional[str]] = "step-functions"
+
+    def __init__(self, deployer_kwargs: Dict[str, str], **kwargs):
+        """
+        Initialize the StepFunctionsDeployer.
+
+        Parameters
+        ----------
+        deployer_kwargs : Dict[str, str]
+            The deployer-specific keyword arguments.
+        **kwargs : Any
+            Additional arguments to pass to the superclass constructor.
+        """
+        self._deployer_kwargs = deployer_kwargs
+        super().__init__(**kwargs)
+
+    @property
+    def deployer_kwargs(self) -> Dict[str, Any]:
+        return self._deployer_kwargs
+
+    @staticmethod
+    def deployed_flow_type() -> (
+        Type[
+            "metaflow.plugins.aws.step_functions.step_functions_deployer_objects.StepFunctionsDeployedFlow"
+        ]
+    ):
+        from .step_functions_deployer_objects import StepFunctionsDeployedFlow
+
+        return StepFunctionsDeployedFlow
+
+    def create(
+        self, **kwargs
+    ) -> "metaflow.plugins.aws.step_functions.step_functions_deployer_objects.StepFunctionsDeployedFlow":
+        """
+        Create a new AWS Step Functions State Machine deployment.
+
+        Parameters
+        ----------
+        authorize : str, optional, default None
+            Authorize using this production token. Required when re-deploying an existing flow
+            for the first time. The token is cached in METAFLOW_HOME.
+        generate_new_token : bool, optional, default False
+            Generate a new production token for this flow. Moves the production flow to a new namespace.
+        given_token : str, optional, default None
+            Use the given production token for this flow. Moves the production flow to the given namespace.
+        tags : List[str], optional, default None
+            Annotate all objects produced by AWS Step Functions runs with these tags.
+        user_namespace : str, optional, default None
+            Change the namespace from the default (production token) to the given tag.
+        only_json : bool, optional, default False
+            Only print out JSON sent to AWS Step Functions without deploying anything.
+        max_workers : int, optional, default 100
+            Maximum number of parallel processes.
+        workflow_timeout : int, optional, default None
+            Workflow timeout in seconds.
+        log_execution_history : bool, optional, default False
+            Log AWS Step Functions execution history to AWS CloudWatch Logs log group.
+        use_distributed_map : bool, optional, default False
+            Use AWS Step Functions Distributed Map instead of Inline Map for defining foreach
+            tasks in Amazon State Language.
+        deployer_attribute_file : str, optional, default None
+            Write the workflow name to the specified file. Used internally for Metaflow's Deployer API.
+
+        Returns
+        -------
+        StepFunctionsDeployedFlow
+            The Flow deployed to AWS Step Functions.
+        """
+        from .step_functions_deployer_objects import StepFunctionsDeployedFlow
+
+        return self._create(StepFunctionsDeployedFlow, **kwargs)
+
+
+_addl_stubgen_modules = [
+    "metaflow.plugins.aws.step_functions.step_functions_deployer_objects"
+]
diff --git a/metaflow/plugins/aws/step_functions/step_functions_deployer_objects.py b/metaflow/plugins/aws/step_functions/step_functions_deployer_objects.py
new file mode 100644
index 00000000000..e6cf68078e5
--- /dev/null
+++ b/metaflow/plugins/aws/step_functions/step_functions_deployer_objects.py
@@ -0,0 +1,234 @@
+import sys
+import json
+from typing import ClassVar, Optional, List
+
+from metaflow.plugins.aws.step_functions.step_functions import StepFunctions
+from metaflow.runner.deployer import DeployedFlow, TriggeredRun
+
+from metaflow.runner.utils import get_lower_level_group, handle_timeout, temporary_fifo
+
+
+class StepFunctionsTriggeredRun(TriggeredRun):
+    """
+    A class representing a triggered AWS Step Functions state machine execution.
+    """
+
+    def terminate(self, **kwargs) -> bool:
+        """
+        Terminate the running state machine execution.
+
+        Parameters
+        ----------
+        authorize : str, optional, default None
+            Authorize the termination with a production token.
+
+        Returns
+        -------
+        bool
+            True if the command was successful, False otherwise.
+        """
+        _, run_id = self.pathspec.split("/")
+
+        # every subclass needs to have `self.deployer_kwargs`
+        command = get_lower_level_group(
+            self.deployer.api,
+            self.deployer.top_level_kwargs,
+            self.deployer.TYPE,
+            self.deployer.deployer_kwargs,
+        ).terminate(run_id=run_id, **kwargs)
+
+        pid = self.deployer.spm.run_command(
+            [sys.executable, *command],
+            env=self.deployer.env_vars,
+            cwd=self.deployer.cwd,
+            show_output=self.deployer.show_output,
+        )
+
+        command_obj = self.deployer.spm.get(pid)
+        command_obj.sync_wait()
+        return command_obj.process.returncode == 0
+
+
+class StepFunctionsDeployedFlow(DeployedFlow):
+    """
+    A class representing a deployed AWS Step Functions state machine.
+    """
+
+    TYPE: ClassVar[Optional[str]] = "step-functions"
+
+    @classmethod
+    def from_deployment(cls, identifier: str, metadata: Optional[str] = None):
+        """
+        This method is not currently implemented for Step Functions.
+
+        Raises
+        ------
+        NotImplementedError
+            This method is not implemented for Step Functions.
+        """
+        raise NotImplementedError(
+            "from_deployment is not implemented for StepFunctions"
+        )
+
+    @property
+    def production_token(self: DeployedFlow) -> Optional[str]:
+        """
+        Get the production token for the deployed flow.
+
+        Returns
+        -------
+        str, optional
+            The production token, None if it cannot be retrieved.
+        """
+        try:
+            _, production_token = StepFunctions.get_existing_deployment(
+                self.deployer.name
+            )
+            return production_token
+        except TypeError:
+            return None
+
+    def list_runs(
+        self, states: Optional[List[str]] = None
+    ) -> List[StepFunctionsTriggeredRun]:
+        """
+        List runs of the deployed flow.
+
+        Parameters
+        ----------
+        states : List[str], optional, default None
+            A list of states to filter the runs by. Allowed values are:
+            RUNNING, SUCCEEDED, FAILED, TIMED_OUT, ABORTED.
+            If not provided, all states will be considered.
+
+        Returns
+        -------
+        List[StepFunctionsTriggeredRun]
+            A list of TriggeredRun objects representing the runs of the deployed flow.
+
+        Raises
+        ------
+        ValueError
+            If any of the provided states are invalid or if there are duplicate states.
+        """
+        VALID_STATES = {"RUNNING", "SUCCEEDED", "FAILED", "TIMED_OUT", "ABORTED"}
+
+        if states is None:
+            states = []
+
+        unique_states = set(states)
+        if not unique_states.issubset(VALID_STATES):
+            invalid_states = unique_states - VALID_STATES
+            raise ValueError(
+                f"Invalid states found: {invalid_states}. Valid states are: {VALID_STATES}"
+            )
+
+        if len(states) != len(unique_states):
+            raise ValueError("Duplicate states are not allowed")
+
+        triggered_runs = []
+        executions = StepFunctions.list(self.deployer.name, states)
+
+        for e in executions:
+            run_id = "sfn-%s" % e["name"]
+            tr = StepFunctionsTriggeredRun(
+                deployer=self.deployer,
+                content=json.dumps(
+                    {
+                        "metadata": self.deployer.metadata,
+                        "pathspec": "/".join((self.deployer.flow_name, run_id)),
+                        "name": run_id,
+                    }
+                ),
+            )
+            triggered_runs.append(tr)
+
+        return triggered_runs
+
+    def delete(self, **kwargs) -> bool:
+        """
+        Delete the deployed state machine.
+
+        Parameters
+        ----------
+        authorize : str, optional, default None
+            Authorize the deletion with a production token.
+
+        Returns
+        -------
+        bool
+            True if the command was successful, False otherwise.
+        """
+        command = get_lower_level_group(
+            self.deployer.api,
+            self.deployer.top_level_kwargs,
+            self.deployer.TYPE,
+            self.deployer.deployer_kwargs,
+        ).delete(**kwargs)
+
+        pid = self.deployer.spm.run_command(
+            [sys.executable, *command],
+            env=self.deployer.env_vars,
+            cwd=self.deployer.cwd,
+            show_output=self.deployer.show_output,
+        )
+
+        command_obj = self.deployer.spm.get(pid)
+        command_obj.sync_wait()
+        return command_obj.process.returncode == 0
+
+    def trigger(self, **kwargs) -> StepFunctionsTriggeredRun:
+        """
+        Trigger a new run for the deployed flow.
+
+        Parameters
+        ----------
+        **kwargs : Any
+            Additional arguments to pass to the trigger command,
+            `Parameters` in particular
+
+        Returns
+        -------
+        StepFunctionsTriggeredRun
+            The triggered run instance.
+
+        Raises
+        ------
+        Exception
+            If there is an error during the trigger process.
+        """
+        with temporary_fifo() as (attribute_file_path, attribute_file_fd):
+            # every subclass needs to have `self.deployer_kwargs`
+            command = get_lower_level_group(
+                self.deployer.api,
+                self.deployer.top_level_kwargs,
+                self.deployer.TYPE,
+                self.deployer.deployer_kwargs,
+            ).trigger(deployer_attribute_file=attribute_file_path, **kwargs)
+
+            pid = self.deployer.spm.run_command(
+                [sys.executable, *command],
+                env=self.deployer.env_vars,
+                cwd=self.deployer.cwd,
+                show_output=self.deployer.show_output,
+            )
+
+            command_obj = self.deployer.spm.get(pid)
+            content = handle_timeout(
+                attribute_file_fd, command_obj, self.deployer.file_read_timeout
+            )
+
+            command_obj.sync_wait()
+            if command_obj.process.returncode == 0:
+                return StepFunctionsTriggeredRun(
+                    deployer=self.deployer, content=content
+                )
+
+        raise Exception(
+            "Error triggering %s on %s for %s"
+            % (
+                self.deployer.name,
+                self.deployer.TYPE,
+                self.deployer.flow_file,
+            )
+        )
diff --git a/metaflow/plugins/azure/__init__.py b/metaflow/plugins/azure/__init__.py
index e69de29bb2d..422dde8e7c1 100644
--- a/metaflow/plugins/azure/__init__.py
+++ b/metaflow/plugins/azure/__init__.py
@@ -0,0 +1,3 @@
+from .azure_credential import (
+    create_cacheable_azure_credential as create_azure_credential,
+)
diff --git a/metaflow/plugins/azure/azure_credential.py b/metaflow/plugins/azure/azure_credential.py
new file mode 100644
index 00000000000..e0bccb6423d
--- /dev/null
+++ b/metaflow/plugins/azure/azure_credential.py
@@ -0,0 +1,53 @@
+class AzureDefaultClientProvider(object):
+    name = "azure-default"
+
+    @staticmethod
+    def create_cacheable_azure_credential(*args, **kwargs):
+        """azure.identity.DefaultAzureCredential is not readily cacheable in a dictionary
+        because it does not have a content based hash and equality implementations.
+
+        We implement a subclass CacheableDefaultAzureCredential to add them.
+
+        We need this because credentials will be part of the cache key in _ClientCache.
+        """
+        from azure.identity import DefaultAzureCredential
+
+        class CacheableDefaultAzureCredential(DefaultAzureCredential):
+            def __init__(self, *args, **kwargs):
+                super(CacheableDefaultAzureCredential, self).__init__(*args, **kwargs)
+                # Just hashing all the kwargs works because they are all individually
+                # hashable as of 7/15/2022.
+                #
+                # What if Azure adds unhashable things to kwargs?
+                # - We will have CI to catch this (it will always install the latest Azure SDKs)
+                # - In Metaflow usage today we never specify any kwargs anyway. (see last line
+                #   of the outer function.
+                self._hash_code = hash((args, tuple(sorted(kwargs.items()))))
+
+            def __hash__(self):
+                return self._hash_code
+
+            def __eq__(self, other):
+                return hash(self) == hash(other)
+
+        return CacheableDefaultAzureCredential(*args, **kwargs)
+
+
+cached_provider_class = None
+
+
+def create_cacheable_azure_credential():
+    global cached_provider_class
+    if cached_provider_class is None:
+        from metaflow.metaflow_config import DEFAULT_AZURE_CLIENT_PROVIDER
+        from metaflow.plugins import AZURE_CLIENT_PROVIDERS
+
+        for p in AZURE_CLIENT_PROVIDERS:
+            if p.name == DEFAULT_AZURE_CLIENT_PROVIDER:
+                cached_provider_class = p
+                break
+        else:
+            raise ValueError(
+                "Cannot find Azure Client provider %s" % DEFAULT_AZURE_CLIENT_PROVIDER
+            )
+    return cached_provider_class.create_cacheable_azure_credential()
diff --git a/metaflow/plugins/azure/azure_exceptions.py b/metaflow/plugins/azure/azure_exceptions.py
index f4f500b1268..1a915f2c17d 100644
--- a/metaflow/plugins/azure/azure_exceptions.py
+++ b/metaflow/plugins/azure/azure_exceptions.py
@@ -10,4 +10,4 @@ class MetaflowAzureResourceError(MetaflowException):
 
 
 class MetaflowAzurePackageError(MetaflowException):
-    headline = "Missing required packages azure-identity and azure-storage-blob"
+    headline = "Missing required packages 'azure-identity' and 'azure-storage-blob' and 'azure-keyvault-secrets'"
diff --git a/metaflow/plugins/azure/azure_secret_manager_secrets_provider.py b/metaflow/plugins/azure/azure_secret_manager_secrets_provider.py
new file mode 100644
index 00000000000..d1567cbbd77
--- /dev/null
+++ b/metaflow/plugins/azure/azure_secret_manager_secrets_provider.py
@@ -0,0 +1,240 @@
+from metaflow.plugins.secrets import SecretsProvider
+import re
+import base64
+import codecs
+from urllib.parse import urlparse
+from metaflow.exception import MetaflowException
+import sys
+from metaflow.metaflow_config import AZURE_KEY_VAULT_PREFIX
+from metaflow.plugins.azure.azure_credential import (
+    create_cacheable_azure_credential,
+)
+
+
+class MetaflowAzureKeyVaultBadVault(MetaflowException):
+    """Raised when the secretid is fully qualified but does not have the right key vault domain"""
+
+
+class MetaflowAzureKeyVaultBadSecretType(MetaflowException):
+    """Raised when the secret type is anything except secrets"""
+
+
+class MetaflowAzureKeyVaultBadSecretPath(MetaflowException):
+    """Raised when the secret path does not match to expected length"""
+
+
+class MetaflowAzureKeyVaultBadSecretName(MetaflowException):
+    """Raised when the secret name does not match expected pattern"""
+
+
+class MetaflowAzureKeyVaultBadSecretVersion(MetaflowException):
+    """Raised when the secret version does not match expected pattern"""
+
+
+class MetaflowAzureKeyVaultBadSecret(MetaflowException):
+    """Raised when the secret does not match supported patterns in Metaflow"""
+
+
+class AzureKeyVaultSecretsProvider(SecretsProvider):
+    TYPE = "az-key-vault"
+    key_vault_domains = [
+        ".vault.azure.net",
+        ".vault.azure.cn",
+        ".vault.usgovcloudapi.net",
+        ".vault.microsoftazure.de",
+    ]
+    supported_vault_object_types = ["secrets"]
+
+    # https://learn.microsoft.com/en-us/azure/key-vault/general/about-keys-secrets-certificates has details on vault name structure
+    # Vault name and Managed HSM pool name must be a 3-24 character string, containing only 0-9, a-z, A-Z, and not consecutive -.
+    def _is_valid_vault_name(self, vault_name):
+        vault_name_pattern = r"^(?!.*--)[a-zA-Z0-9-]{3,24}$"
+        return re.match(vault_name_pattern, vault_name) is not None
+
+    # The type of the object can be, "keys", "secrets", or "certificates".
+    # Currently only secrets will be supported
+    def _is_valid_object_type(self, secret_type):
+        for type in self.supported_vault_object_types:
+            if secret_type == type:
+                return True
+        return False
+
+    # The secret name must be a 1-127 character string, starting with a letter and containing only 0-9, a-z, A-Z, and -.
+    def _is_valid_secret_name(self, secret_name):
+        secret_name_pattern = r"^[a-zA-Z][a-zA-Z0-9-]{0,126}$"
+        return re.match(secret_name_pattern, secret_name) is not None
+
+    # An object-version is a system-generated, 32 character string identifier that is optionally used to address a unique version of an object.
+    def _is_valid_object_version(self, secret_version):
+        object_version_pattern = r"^[a-zA-Z0-9]{32}$"
+        return re.match(object_version_pattern, secret_version) is not None
+
+    # This function will check if the secret_id is fully qualified url. It will return True iff the secret_id is of the form:
+    # https://myvault.vault.azure.net/secrets/mysecret/ec96f02080254f109c51a1f14cdb1931 OR
+    # https://myvault.vault.azure.net/secrets/mysecret/
+    # validating the above as per recommendations in https://devblogs.microsoft.com/azure-sdk/guidance-for-applications-using-the-key-vault-libraries/
+    def _is_secret_id_fully_qualified_url(self, secret_id):
+        # if the secret_id is None/empty/does not start with https then return false
+        if secret_id is None or secret_id == "" or not secret_id.startswith("https://"):
+            return False
+        try:
+            parsed_vault_url = urlparse(secret_id)
+        except ValueError:
+            print("invalid vault url", file=sys.stderr)
+            return False
+        hostname = parsed_vault_url.netloc
+
+        k_v_domain_found = False
+        actual_k_v_domain = ""
+        for k_v_domain in self.key_vault_domains:
+            if k_v_domain in hostname:
+                k_v_domain_found = True
+                actual_k_v_domain = k_v_domain
+                break
+        if not k_v_domain_found:
+            # the secret_id started with https:// however the key_vault_domains
+            # were not present in the secret_id which means
+            raise MetaflowAzureKeyVaultBadVault("bad key vault domain %s" % secret_id)
+
+        # given the secret_id seems to have a valid key vault domain
+        # lets verify that the vault name corresponds to its regex.
+        vault_name = hostname[: -len(actual_k_v_domain)]
+        # verify the vault name pattern
+        if not self._is_valid_vault_name(vault_name):
+            raise MetaflowAzureKeyVaultBadVault("bad key vault name %s" % vault_name)
+
+        path_parts = parsed_vault_url.path.strip("/").split("/")
+        total_path_parts = len(path_parts)
+        if total_path_parts < 2 or total_path_parts > 3:
+            raise MetaflowAzureKeyVaultBadSecretPath(
+                "bad secret uri path %s" % path_parts
+            )
+
+        object_type = path_parts[0]
+        if not self._is_valid_object_type(object_type):
+            raise MetaflowAzureKeyVaultBadSecretType("bad secret type %s" % object_type)
+
+        secret_name = path_parts[1]
+        if not self._is_valid_secret_name(secret_name=secret_name):
+            raise MetaflowAzureKeyVaultBadSecretName("bad secret name %s" % secret_name)
+
+        if total_path_parts == 3:
+            if not self._is_valid_object_version(path_parts[2]):
+                raise MetaflowAzureKeyVaultBadSecretVersion(
+                    "bad secret version %s" % path_parts[2]
+                )
+
+        return True
+
+    # This function will validate the correctness of the partial secret id.
+    # It will attempt to construct the fully qualified secret URL internally and
+    # call the _is_secret_id_fully_qualified_url to check validity
+    def _is_partial_secret_valid(self, secret_id):
+        secret_parts = secret_id.strip("/").split("/")
+        total_secret_parts = len(secret_parts)
+        if total_secret_parts < 1 or total_secret_parts > 2:
+            return False
+
+        # since the secret_id is supposedly a partial id, the AZURE_KEY_VAULT_PREFIX
+        # must be set.
+        if not AZURE_KEY_VAULT_PREFIX:
+            raise ValueError(
+                "cannot use simple secret id without setting METAFLOW_AZURE_KEY_VAULT_PREFIX. %s"
+                % AZURE_KEY_VAULT_PREFIX
+            )
+        domain = AZURE_KEY_VAULT_PREFIX.rstrip("/")
+        full_secret = "%s/secrets/%s" % (domain, secret_id)
+        if not self._is_secret_id_fully_qualified_url(full_secret):
+            return False
+
+        return True
+
+    def _sanitize_key_as_env_var(self, key):
+        """
+        Sanitize a key as an environment variable name.
+        This is purely a convenience trade-off to cover common cases well, vs. introducing
+        ambiguities (e.g. did the final '_' come from '.', or '-' or is original?).
+
+        1/27/2023(jackie):
+
+        We start with few rules and should *sparingly* add more over time.
+        Also, it's TBD whether all possible providers will share the same sanitization logic.
+        Therefore we will keep this function private for now
+        """
+        return key.replace("-", "_").replace(".", "_").replace("/", "_")
+
+    def get_secret_as_dict(self, secret_id, options={}, role=None):
+        # https://learn.microsoft.com/en-us/azure/app-service/app-service-key-vault-references?tabs=azure-cli has a lot of details on
+        # the patterns used in key vault
+        # Vault names and Managed HSM pool names are selected by the user and are globally unique.
+        # Vault name and Managed HSM pool name must be a 3-24 character string, containing only 0-9, a-z, A-Z, and not consecutive -.
+        # object-type	The type of the object. As of 05/08/24 only "secrets", are supported
+        # object-name	An object-name is a user provided name for and must be unique within a key vault. The name must be a 1-127 character string, starting with a letter and containing only 0-9, a-z, A-Z, and -.
+        # object-version	An object-version is a system-generated, 32 character string identifier that is optionally used to address a unique version of an object.
+
+        # We allow these forms of secret_id:
+        #
+        # 1. Full path like https://<.vault-domain>/secrets//. This is what you
+        # see in Azure portal and is easy to copy paste.
+        #
+        # 2. Full path but without the version like https://<.vault-domain>/secrets/
+        #
+        # 3. Simple string like mysecret. This corresponds to the SecretName.
+        #
+        # 4. Simple string with / suffix like mysecret/123
+
+        # The latter two forms require METAFLOW_AZURE_KEY_VAULT_PREFIX to be set.
+
+        # if the secret_id is None/empty/does not start with https then return false
+        if secret_id is None or secret_id == "":
+            raise MetaflowAzureKeyVaultBadSecret("empty secret id is not supported")
+
+        # check if the passed in secret is a short-form ( #3/#4 in the above comment)
+        if not secret_id.startswith("https://"):
+            # check if the secret_id is of form `secret_name` OR `secret_name/secret_version`
+            if not self._is_partial_secret_valid(secret_id=secret_id):
+                raise MetaflowAzureKeyVaultBadSecret(
+                    "unsupported partial secret %s" % secret_id
+                )
+
+            domain = AZURE_KEY_VAULT_PREFIX.rstrip("/")
+            full_secret = "%s/secrets/%s" % (domain, secret_id)
+
+        # if the secret id is passed as a URL - then check if the url is fully qualified
+        if secret_id.startswith("https://"):
+            if not self._is_secret_id_fully_qualified_url(secret_id=secret_id):
+                raise MetaflowException("unsupported secret %s" % secret_id)
+            full_secret = secret_id
+
+        # at this point I know that the secret URL is good so we can start creating the Secret Client
+        az_credentials = create_cacheable_azure_credential()
+        res = urlparse(full_secret)
+        az_vault_url = "%s://%s" % (
+            res.scheme,
+            res.netloc,
+        )  # https://myvault.vault.azure.net
+        secret_data = res.path.strip("/").split("/")[1:]
+        secret_name = secret_data[0]
+        secret_version = None
+        if len(secret_data) > 1:
+            secret_version = secret_data[1]
+
+        from azure.keyvault.secrets import SecretClient
+
+        client = SecretClient(vault_url=az_vault_url, credential=az_credentials)
+
+        key_vault_secret_val = client.get_secret(
+            name=secret_name, version=secret_version
+        )
+
+        result = {}
+
+        if options.get("env_var_name") is not None:
+            env_var_name = options["env_var_name"]
+            sanitized_key = self._sanitize_key_as_env_var(env_var_name)
+        else:
+            sanitized_key = self._sanitize_key_as_env_var(key_vault_secret_val.name)
+
+        response_payload = key_vault_secret_val.value
+        result[sanitized_key] = response_payload
+        return result
diff --git a/metaflow/plugins/azure/azure_tail.py b/metaflow/plugins/azure/azure_tail.py
index d07fe08e94a..1329e469931 100644
--- a/metaflow/plugins/azure/azure_tail.py
+++ b/metaflow/plugins/azure/azure_tail.py
@@ -70,7 +70,7 @@ def _fill_buf(self):
         if data is None:
             return None
         if data:
-            buf = BytesIO(data)
+            buf = BytesIO(self._tail + data)
             self._pos += len(data)
             self._tail = b""
             return buf
diff --git a/metaflow/plugins/azure/azure_utils.py b/metaflow/plugins/azure/azure_utils.py
index 0f3f465a171..633804d1df7 100644
--- a/metaflow/plugins/azure/azure_utils.py
+++ b/metaflow/plugins/azure/azure_utils.py
@@ -7,6 +7,7 @@
     MetaflowAzurePackageError,
 )
 from metaflow.exception import MetaflowInternalError, MetaflowException
+from metaflow.plugins.azure.azure_credential import create_cacheable_azure_credential
 
 
 def _check_and_init_azure_deps():
@@ -138,38 +139,6 @@ def _inner_func(*args, **kwargs):
     return _inner_func
 
 
-@check_azure_deps
-def create_cacheable_default_azure_credentials(*args, **kwargs):
-    """azure.identity.DefaultAzureCredential is not readily cacheable in a dictionary
-    because it does not have a content based hash and equality implementations.
-
-    We implement a subclass CacheableDefaultAzureCredential to add them.
-
-    We need this because credentials will be part of the cache key in _ClientCache.
-    """
-    from azure.identity import DefaultAzureCredential
-
-    class CacheableDefaultAzureCredential(DefaultAzureCredential):
-        def __init__(self, *args, **kwargs):
-            super(CacheableDefaultAzureCredential, self).__init__(*args, **kwargs)
-            # Just hashing all the kwargs works because they are all individually
-            # hashable as of 7/15/2022.
-            #
-            # What if Azure adds unhashable things to kwargs?
-            # - We will have CI to catch this (it will always install the latest Azure SDKs)
-            # - In Metaflow usage today we never specify any kwargs anyway. (see last line
-            #   of the outer function.
-            self._hash_code = hash((args, tuple(sorted(kwargs.items()))))
-
-        def __hash__(self):
-            return self._hash_code
-
-        def __eq__(self, other):
-            return hash(self) == hash(other)
-
-    return CacheableDefaultAzureCredential(*args, **kwargs)
-
-
 @check_azure_deps
 def create_static_token_credential(token_):
     from azure.core.credentials import TokenCredential
@@ -200,9 +169,7 @@ def __init__(self, token):
         def get_token(self, *_scopes, **_kwargs):
 
             if (self._cached_token.expires_on - time.time()) < 300:
-                from azure.identity import DefaultAzureCredential
-
-                self._credential = DefaultAzureCredential()
+                self._credential = create_cacheable_azure_credential()
             if self._credential:
                 return self._credential.get_token(*_scopes, **_kwargs)
             return self._cached_token
diff --git a/metaflow/plugins/azure/blob_service_client_factory.py b/metaflow/plugins/azure/blob_service_client_factory.py
index 64cd04ebe5d..4897a8cbb05 100644
--- a/metaflow/plugins/azure/blob_service_client_factory.py
+++ b/metaflow/plugins/azure/blob_service_client_factory.py
@@ -1,9 +1,11 @@
 from metaflow.exception import MetaflowException
 from metaflow.metaflow_config import AZURE_STORAGE_BLOB_SERVICE_ENDPOINT
 from metaflow.plugins.azure.azure_utils import (
-    create_cacheable_default_azure_credentials,
     check_azure_deps,
 )
+from metaflow.plugins.azure.azure_credential import (
+    create_cacheable_azure_credential,
+)
 
 import os
 import threading
@@ -125,7 +127,7 @@ def get_azure_blob_service_client(
     blob_service_endpoint = AZURE_STORAGE_BLOB_SERVICE_ENDPOINT
 
     if not credential:
-        credential = create_cacheable_default_azure_credentials()
+        credential = create_cacheable_azure_credential()
         credential_is_cacheable = True
 
     if not credential_is_cacheable:
diff --git a/metaflow/plugins/azure/includefile_support.py b/metaflow/plugins/azure/includefile_support.py
index e1a36b32c56..1db2fd0ed98 100644
--- a/metaflow/plugins/azure/includefile_support.py
+++ b/metaflow/plugins/azure/includefile_support.py
@@ -8,6 +8,8 @@
 
 
 class Azure(object):
+    TYPE = "azure"
+
     @classmethod
     def get_root_from_config(cls, echo, create_on_absent=True):
         from metaflow.metaflow_config import DATATOOLS_AZUREROOT
diff --git a/metaflow/plugins/cards/card_cli.py b/metaflow/plugins/cards/card_cli.py
index 2c0c8017744..9cb8b4bbb9d 100644
--- a/metaflow/plugins/cards/card_cli.py
+++ b/metaflow/plugins/cards/card_cli.py
@@ -1,16 +1,25 @@
 from metaflow.client import Task
-from metaflow import JSONType, namespace
-from metaflow.exception import CommandException
+from metaflow.parameters import JSONTypeClass
+from metaflow import namespace
+from metaflow.util import resolve_identity
+from metaflow.exception import (
+    CommandException,
+    MetaflowNotFound,
+    MetaflowNamespaceMismatch,
+)
 import webbrowser
 import re
 from metaflow._vendor import click
 import os
+import time
 import json
+import uuid
 import signal
 import random
 from contextlib import contextmanager
 from functools import wraps
 from metaflow.exception import MetaflowNamespaceMismatch
+
 from .card_datastore import CardDatastore, NUM_SHORT_HASH_CHARS
 from .exception import (
     CardClassFoundException,
@@ -19,11 +28,18 @@
     CardNotPresentException,
     TaskNotFoundException,
 )
-
+import traceback
+from collections import namedtuple
+from .metadata import _save_metadata
 from .card_resolver import resolve_paths_from_task, resumed_info
 
 id_func = id
 
+CardRenderInfo = namedtuple(
+    "CardRenderInfo",
+    ["mode", "is_implemented", "data", "timed_out", "timeout_stack_trace"],
+)
+
 
 def open_in_browser(card_path):
     url = "file://" + os.path.abspath(card_path)
@@ -151,8 +167,6 @@ def timeout(time):
 
     try:
         yield
-    except TimeoutError:
-        pass
     finally:
         # Unregister the signal so that it won't be triggered
         # if the timeout is not reached.
@@ -375,14 +389,154 @@ def wrapper(*args, **kwargs):
     return wrapper
 
 
-def render_card(mf_card, task, timeout_value=None):
-    rendered_info = None
+def _extract_reload_token(data, task, mf_card):
+    if "render_seq" not in data:
+        return "never"
+
+    if data["render_seq"] == "final":
+        # final data update should always trigger a card reload to show
+        # the final card, hence a different token for the final update
+        return "final"
+    elif mf_card.RELOAD_POLICY == mf_card.RELOAD_POLICY_ALWAYS:
+        return "render-seq-%s" % data["render_seq"]
+    elif mf_card.RELOAD_POLICY == mf_card.RELOAD_POLICY_NEVER:
+        return "never"
+    elif mf_card.RELOAD_POLICY == mf_card.RELOAD_POLICY_ONCHANGE:
+        return mf_card.reload_content_token(task, data)
+
+
+def update_card(mf_card, mode, task, data, timeout_value=None):
+    """
+    This method will be responsible for creating a card/data-update based on the `mode`.
+    There are three possible modes taken by this function.
+        - render :
+            - This will render the "final" card.
+            - This mode is passed at task completion.
+            - Setting this mode will call the `render` method of a MetaflowCard.
+            - It will result in the creation of an HTML page.
+        - render_runtime:
+            - Setting this mode will render a card during task "runtime".
+            - Setting this mode will call the `render_runtime` method of a MetaflowCard.
+            - It will result in the creation of an HTML page.
+        - refresh:
+            - Setting this mode will refresh the data update for a card.
+            - We support this mode because rendering a full card can be an expensive operation, but shipping tiny data updates can be cheap.
+            - Setting this mode will call the `refresh` method of a MetaflowCard.
+            - It will result in the creation of a JSON object.
+
+    Parameters
+    ----------
+    mf_card : MetaflowCard
+        MetaflowCard object which will be used to render the card.
+    mode : str
+        Mode of rendering the card.
+    task : Task
+        Task object which will be passed to render the card.
+    data : dict
+        object created and passed down from `current.card._get_latest_data` method.
+        For more information on this object's schema have a look at `current.card._get_latest_data` method.
+    timeout_value : int
+        Timeout value for rendering the card.
+
+    Returns
+    -------
+    CardRenderInfo
+        - NamedTuple which will contain:
+            - `mode`: The mode of rendering the card.
+            - `is_implemented`: whether the function was implemented or not.
+            - `data` : output from rendering the card (Can be string/dict)
+            - `timed_out` : whether the function timed out or not.
+            - `timeout_stack_trace` : stack trace of the function if it timed out.
+    """
+
+    def _add_token_html(html):
+        if html is None:
+            return None
+        return html.replace(
+            mf_card.RELOAD_POLICY_TOKEN,
+            _extract_reload_token(data=data, task=task, mf_card=mf_card),
+        )
+
+    def _add_token_json(json_msg):
+        if json_msg is None:
+            return None
+        return {
+            "reload_token": _extract_reload_token(
+                data=data, task=task, mf_card=mf_card
+            ),
+            "data": json_msg,
+            "created_on": time.time(),
+        }
+
+    def _safe_call_function(func, *args, **kwargs):
+        """
+        returns (data, is_implemented)
+        """
+        try:
+            return func(*args, **kwargs), True
+        except NotImplementedError as e:
+            return None, False
+
+    def _call():
+        if mode == "render":
+            setattr(
+                mf_card.__class__,
+                "runtime_data",
+                property(fget=lambda _, data=data: data),
+            )
+            output = _add_token_html(mf_card.render(task))
+            return CardRenderInfo(
+                mode=mode,
+                is_implemented=True,
+                data=output,
+                timed_out=False,
+                timeout_stack_trace=None,
+            )
+
+        elif mode == "render_runtime":
+            # Since many cards created by metaflow users may not have implemented a
+            # `render_time` / `refresh` methods, it can result in an exception and thereby
+            # creation of error cards (especially for the `render_runtime` method). So instead
+            # we will catch the NotImplementedError and return None if users have not implemented it.
+            # If there any any other exception from the user code, it should be bubbled to the top level.
+            output, is_implemented = _safe_call_function(
+                mf_card.render_runtime, task, data
+            )
+            return CardRenderInfo(
+                mode=mode,
+                is_implemented=is_implemented,
+                data=_add_token_html(output),
+                timed_out=False,
+                timeout_stack_trace=None,
+            )
+
+        elif mode == "refresh":
+            output, is_implemented = _safe_call_function(mf_card.refresh, task, data)
+            return CardRenderInfo(
+                mode=mode,
+                is_implemented=is_implemented,
+                data=_add_token_json(output),
+                timed_out=False,
+                timeout_stack_trace=None,
+            )
+
+    render_info = None
     if timeout_value is None or timeout_value < 0:
-        rendered_info = mf_card.render(task)
+        return _call()
     else:
-        with timeout(timeout_value):
-            rendered_info = mf_card.render(task)
-    return rendered_info
+        try:
+            with timeout(timeout_value):
+                render_info = _call()
+        except TimeoutError:
+            stack_trace = traceback.format_exc()
+            return CardRenderInfo(
+                mode=mode,
+                is_implemented=True,
+                data=None,
+                timed_out=True,
+                timeout_stack_trace=stack_trace,
+            )
+        return render_info
 
 
 @card.command(help="create a HTML card")
@@ -398,7 +552,7 @@ def render_card(mf_card, task, timeout_value=None):
     "--options",
     default=None,
     show_default=True,
-    type=JSONType,
+    type=JSONTypeClass(),
     help="arguments of the card being created.",
 )
 @click.option(
@@ -414,30 +568,74 @@ def render_card(mf_card, task, timeout_value=None):
     is_flag=True,
     help="Upon failing to render a card, render a card holding the stack trace",
 )
+@click.option(
+    "--id",
+    default=None,
+    show_default=True,
+    type=str,
+    help="ID of the card",
+)
 @click.option(
     "--component-file",
     default=None,
     show_default=True,
     type=str,
-    help="JSON File with Pre-rendered components.(internal)",
+    help="JSON File with pre-rendered components. (internal)",
 )
 @click.option(
-    "--id",
+    "--mode",
+    default="render",
+    show_default=True,
+    type=click.Choice(["render", "render_runtime", "refresh"]),
+    help="Rendering mode. (internal)",
+)
+@click.option(
+    "--data-file",
     default=None,
     show_default=True,
     type=str,
-    help="ID of the card",
+    hidden=True,
+    help="JSON file containing data to be updated. (internal)",
+)
+@click.option(
+    "--card-uuid",
+    default=None,
+    show_default=True,
+    type=str,
+    hidden=True,
+    help="Card UUID. (internal)",
+)
+@click.option(
+    "--delete-input-files",
+    default=False,
+    is_flag=True,
+    show_default=True,
+    hidden=True,
+    help="Delete data-file and component-file after reading. (internal)",
+)
+@click.option(
+    "--save-metadata",
+    default=None,
+    show_default=True,
+    type=JSONTypeClass(),
+    hidden=True,
+    help="JSON string containing metadata to be saved. (internal)",
 )
 @click.pass_context
 def create(
     ctx,
     pathspec,
+    mode=None,
     type=None,
     options=None,
     timeout=None,
     component_file=None,
+    data_file=None,
     render_error_card=False,
+    card_uuid=None,
+    delete_input_files=None,
     id=None,
+    save_metadata=None,
 ):
     card_id = id
     rendered_info = None  # Variable holding all the information which will be rendered
@@ -452,11 +650,26 @@ def create(
 
     graph_dict, _ = ctx.obj.graph.output_steps()
 
+    if card_uuid is None:
+        card_uuid = str(uuid.uuid4()).replace("-", "")
+
     # Components are rendered in a Step and added via `current.card.append` are added here.
     component_arr = []
     if component_file is not None:
         with open(component_file, "r") as f:
             component_arr = json.load(f)
+        # Component data used in card runtime is passed in as temporary files which can be deleted after use
+        if delete_input_files:
+            os.remove(component_file)
+
+    # Load data to be refreshed for runtime cards
+    data = {}
+    if data_file is not None:
+        with open(data_file, "r") as f:
+            data = json.load(f)
+        # data is passed in as temporary files which can be deleted after use
+        if delete_input_files:
+            os.remove(data_file)
 
     task = Task(full_pathspec)
     from metaflow.plugins import CARDS
@@ -487,10 +700,15 @@ def create(
         try:
             if options is not None:
                 mf_card = filtered_card(
-                    options=options, components=component_arr, graph=graph_dict
+                    options=options,
+                    components=component_arr,
+                    graph=graph_dict,
+                    flow=ctx.obj.flow,
                 )
             else:
-                mf_card = filtered_card(components=component_arr, graph=graph_dict)
+                mf_card = filtered_card(
+                    components=component_arr, graph=graph_dict, flow=ctx.obj.flow
+                )
         except TypeError as e:
             if render_error_card:
                 mf_card = None
@@ -498,23 +716,92 @@ def create(
             else:
                 raise IncorrectCardArgsException(type, options)
 
+        rendered_content = None
         if mf_card:
             try:
-                rendered_info = render_card(mf_card, task, timeout_value=timeout)
+                rendered_info = update_card(
+                    mf_card, mode, task, data, timeout_value=timeout
+                )
+                rendered_content = rendered_info.data
             except:
+                rendered_info = CardRenderInfo(
+                    mode=mode,
+                    is_implemented=True,
+                    data=None,
+                    timed_out=False,
+                    timeout_stack_trace=None,
+                )
                 if render_error_card:
                     error_stack_trace = str(UnrenderableCardException(type, options))
                 else:
                     raise UnrenderableCardException(type, options)
-        #
-
-    if error_stack_trace is not None:
-        rendered_info = error_card().render(task, stack_trace=error_stack_trace)
 
-    if rendered_info is None and render_error_card:
-        rendered_info = error_card().render(
-            task, stack_trace="No information rendered From card of type %s" % type
+    # In the entire card rendering process, there are a few cases we want to handle:
+    # - [mode == "render"]
+    #   1. Card is rendered successfully (We store it in the datastore as a HTML file)
+    #   2. Card is not rendered successfully and we have --save-error-card flag set to True
+    #      (We store it in the datastore as a HTML file with stack trace)
+    #   3. Card render timed-out and we have --save-error-card flag set to True
+    #      (We store it in the datastore as a HTML file with stack trace)
+    #   4. `render` returns nothing and we have --save-error-card flag set to True.
+    #       (We store it in the datastore as a HTML file with some message saying you returned nothing)
+    # - [mode == "render_runtime"]
+    #   1. Card is rendered successfully (We store it in the datastore as a HTML file)
+    #   2. `render_runtime` is not implemented but gets called and we have --save-error-card flag set to True.
+    #       (We store it in the datastore as a HTML file with some message saying the card should not be a runtime card if this method is not Implemented)
+    #   3. `render_runtime` is implemented and raises an exception and we have --save-error-card flag set to True.
+    #       (We store it in the datastore as a HTML file with stack trace)
+    #   4. `render_runtime` is implemented but returns nothing and we have --save-error-card flag set to True.
+    #       (We store it in the datastore as a HTML file with some message saying you returned nothing)
+    #   5. `render_runtime` is implemented but times out and we have --save-error-card flag set to True.
+    #       (We store it in the datastore as a HTML file with stack trace)
+    # - [mode == "refresh"]
+    #   1. Data update is created successfully (We store it in the datastore as a JSON file)
+    #   2. `refresh` is not implemented. (We do nothing. Don't store anything.)
+    #   3. `refresh` is implemented but it raises an exception. (We do nothing. Don't store anything.)
+    #   4. `refresh` is implemented but it times out. (We do nothing. Don't store anything.)
+
+    def _render_error_card(stack_trace):
+        _card = error_card()
+        token = _extract_reload_token(data, task, _card)
+        return _card.render(
+            task,
+            stack_trace=stack_trace,
+        ).replace(_card.RELOAD_POLICY_TOKEN, token)
+
+    if error_stack_trace is not None and mode != "refresh":
+        rendered_content = _render_error_card(error_stack_trace)
+    elif (
+        rendered_info.is_implemented
+        and rendered_info.timed_out
+        and mode != "refresh"
+        and render_error_card
+    ):
+        timeout_stack_trace = (
+            "\nCard rendering timed out after %s seconds. "
+            "To increase the timeout duration for card rendering, please set the `timeout` parameter in the @card decorator. "
+            "\nStack Trace : \n%s"
+        ) % (timeout, rendered_info.timeout_stack_trace)
+        rendered_content = _render_error_card(timeout_stack_trace)
+    elif (
+        rendered_info.is_implemented
+        and rendered_info.data is None
+        and render_error_card
+        and mode != "refresh"
+    ):
+        rendered_content = _render_error_card(
+            "No information rendered from card of type %s" % type
         )
+    elif (
+        not rendered_info.is_implemented
+        and render_error_card
+        and mode == "render_runtime"
+    ):
+        message = (
+            "Card of type %s is a runtime time card with no `render_runtime` implemented. "
+            "Please implement `render_runtime` method to allow rendering this card at runtime."
+        ) % type
+        rendered_content = _render_error_card(message)
 
     # todo : should we save native type for error card or error type ?
     if type is not None and re.match(CARD_ID_PATTERN, type) is not None:
@@ -531,13 +818,31 @@ def create(
         )
         card_id = None
 
-    if rendered_info is not None:
-        card_info = card_datastore.save_card(save_type, rendered_info, card_id=card_id)
-        ctx.obj.echo(
-            "Card created with type: %s and hash: %s"
-            % (card_info.type, card_info.hash[:NUM_SHORT_HASH_CHARS]),
-            fg="green",
-        )
+    if rendered_content is not None:
+        if mode == "refresh":
+            card_datastore.save_data(
+                card_uuid, save_type, rendered_content, card_id=card_id
+            )
+            ctx.obj.echo("Data updated", fg="green")
+        else:
+            card_info = card_datastore.save_card(
+                card_uuid, save_type, rendered_content, card_id=card_id
+            )
+            ctx.obj.echo(
+                "Card created with type: %s and hash: %s"
+                % (card_info.type, card_info.hash[:NUM_SHORT_HASH_CHARS]),
+                fg="green",
+            )
+            if save_metadata:
+                _save_metadata(
+                    ctx.obj.metadata,
+                    task.parent.parent.id,
+                    task.parent.id,
+                    task.id,
+                    task.current_attempt,
+                    card_uuid,
+                    save_metadata,
+                )
 
 
 @card.command()
@@ -655,7 +960,6 @@ def list(
     as_json=False,
     file=None,
 ):
-
     card_id = id
     if pathspec is None:
         list_many_cards(
@@ -687,3 +991,125 @@ def list(
         show_list_as_json=as_json,
         file=file,
     )
+
+
+@card.command(help="Run local card viewer server")
+@click.option(
+    "--run-id",
+    default=None,
+    show_default=True,
+    type=str,
+    help="Run ID of the flow",
+)
+@click.option(
+    "--port",
+    default=8324,
+    show_default=True,
+    type=int,
+    help="Port on which Metaflow card viewer server will run",
+)
+@click.option(
+    "--namespace",
+    "user_namespace",
+    default=None,
+    show_default=True,
+    type=str,
+    help="Namespace of the flow",
+)
+@click.option(
+    "--poll-interval",
+    default=5,
+    show_default=True,
+    type=int,
+    help="Polling interval of the card viewer server.",
+)
+@click.option(
+    "--max-cards",
+    default=30,
+    show_default=True,
+    type=int,
+    help="Maximum number of cards to be shown at any time by the card viewer server",
+)
+@click.pass_context
+def server(ctx, run_id, port, user_namespace, poll_interval, max_cards):
+    from .card_server import create_card_server, CardServerOptions
+
+    user_namespace = resolve_identity() if user_namespace is None else user_namespace
+    run, follow_new_runs, _status_message = _get_run_object(
+        ctx.obj, run_id, user_namespace
+    )
+    if _status_message is not None:
+        ctx.obj.echo(_status_message, fg="red")
+    options = CardServerOptions(
+        flow_name=ctx.obj.flow.name,
+        run_object=run,
+        only_running=False,
+        follow_resumed=False,
+        flow_datastore=ctx.obj.flow_datastore,
+        max_cards=max_cards,
+        follow_new_runs=follow_new_runs,
+        poll_interval=poll_interval,
+    )
+    create_card_server(options, port, ctx.obj)
+
+
+def _get_run_from_cli_set_runid(obj, run_id):
+    # This run-id will be set from the command line args.
+    # So if we hit a MetaflowNotFound exception / Namespace mismatch then
+    # we should raise an exception
+    from metaflow import Run
+
+    flow_name = obj.flow.name
+    if len(run_id.split("/")) > 1:
+        raise CommandException(
+            "run_id should NOT be of the form: `/`. Please provide only run-id"
+        )
+    try:
+        pathspec = "%s/%s" % (flow_name, run_id)
+        # Since we are looking at all namespaces,
+        # we will not
+        namespace(None)
+        return Run(pathspec)
+    except MetaflowNotFound:
+        raise CommandException("No run (%s) found for *%s*." % (run_id, flow_name))
+
+
+def _get_run_object(obj, run_id, user_namespace):
+    from metaflow import Flow
+
+    follow_new_runs = True
+    flow_name = obj.flow.name
+
+    if run_id is not None:
+        follow_new_runs = False
+        run = _get_run_from_cli_set_runid(obj, run_id)
+        obj.echo("Using run-id %s" % run.pathspec, fg="blue", bold=False)
+        return run, follow_new_runs, None
+
+    _msg = "Searching for runs in namespace: %s" % user_namespace
+    obj.echo(_msg, fg="blue", bold=False)
+
+    try:
+        namespace(user_namespace)
+        flow = Flow(pathspec=flow_name)
+        run = flow.latest_run
+    except MetaflowNotFound:
+        # When we have no runs found for the Flow, we need to ensure that
+        # if the `follow_new_runs` is set to True; If `follow_new_runs` is set to True then
+        # we don't raise the Exception and instead we return None and let the
+        # background Thread wait on the Retrieving the run object.
+        _status_msg = "No run found for *%s*." % flow_name
+        return None, follow_new_runs, _status_msg
+
+    except MetaflowNamespaceMismatch:
+        _status_msg = (
+            "No run found for *%s* in namespace *%s*. You can switch the namespace using --namespace"
+            % (
+                flow_name,
+                user_namespace,
+            )
+        )
+        return None, follow_new_runs, _status_msg
+
+    obj.echo("Using run-id %s" % run.pathspec, fg="blue", bold=False)
+    return run, follow_new_runs, None
diff --git a/metaflow/plugins/cards/card_client.py b/metaflow/plugins/cards/card_client.py
index 07800a347f3..65ff1da1124 100644
--- a/metaflow/plugins/cards/card_client.py
+++ b/metaflow/plugins/cards/card_client.py
@@ -2,10 +2,10 @@
 from metaflow.datastore import FlowDataStore
 from metaflow.metaflow_config import CARD_SUFFIX
 from .card_resolver import resolve_paths_from_task, resumed_info
-from .card_datastore import CardDatastore
+from .card_datastore import CardDatastore, CardNameSuffix
 from .exception import (
     UnresolvableDatastoreException,
-    IncorrectArguementException,
+    IncorrectArgumentException,
     IncorrectPathspecException,
 )
 import os
@@ -13,7 +13,7 @@
 import uuid
 
 if TYPE_CHECKING:
-    from metaflow.client.core import Task
+    import metaflow
 
 _TYPE = type
 _ID_FUNC = id
@@ -47,6 +47,7 @@ def __init__(
         self._created_on = created_on
         self._card_ds = card_ds
         self._card_id = id
+        self._data_path = None
 
         # public attributes
         self.hash = hash
@@ -57,6 +58,17 @@ def __init__(
         # Tempfile to open stuff in browser
         self._temp_file = None
 
+    def get_data(self) -> Optional[dict]:
+        # currently an internal method to retrieve a card's data.
+        if self._data_path is None:
+            data_paths = self._card_ds.extract_data_paths(
+                card_type=self.type, card_hash=self.hash, card_id=self._card_id
+            )
+            if len(data_paths) == 0:
+                return None
+            self._data_path = data_paths[0]
+        return self._card_ds.get_card_data(self._data_path)
+
     def get(self) -> str:
         """
         Retrieves the HTML contents of the card from the
@@ -89,6 +101,11 @@ def path(self) -> str:
     def id(self) -> Optional[str]:
         """
         The ID of the card, if specified with `@card(id=ID)`.
+
+        Returns
+        -------
+        Optional[str]
+            ID of the card
         """
         return self._card_id
 
@@ -150,12 +167,12 @@ class CardContainer:
     ```
     """
 
-    def __init__(self, card_paths, card_ds, from_resumed=False, origin_pathspec=None):
+    def __init__(self, card_paths, card_ds, origin_pathspec=None):
         self._card_paths = card_paths
         self._card_ds = card_ds
         self._current = 0
         self._high = len(card_paths)
-        self.from_resumed = from_resumed
+        self.from_resumed = origin_pathspec is not None
         self.origin_pathspec = origin_pathspec
 
     def __len__(self):
@@ -172,7 +189,7 @@ def _get_card(self, index):
         if index >= self._high:
             raise IndexError
         path = self._card_paths[index]
-        card_info = self._card_ds.card_info_from_path(path)
+        card_info = self._card_ds.info_from_path(path, suffix=CardNameSuffix.CARD)
         # todo : find card creation date and put it in client.
         return Card(
             self._card_ds,
@@ -205,7 +222,7 @@ def _repr_html_(self):
 
 
 def get_cards(
-    task: Union[str, "Task"],
+    task: Union[str, "metaflow.Task"],
     id: Optional[str] = None,
     type: Optional[str] = None,
     follow_resumed: bool = True,
@@ -220,14 +237,14 @@ def get_cards(
 
     Parameters
     ----------
-    task : str or `Task`
+    task : Union[str, `Task`]
         A `Task` object or pathspec `{flow_name}/{run_id}/{step_name}/{task_id}` that
         uniquely identifies a task.
-    id : str, optional
+    id : str, optional, default None
         The ID of card to retrieve if multiple cards are present.
-    type : str, optional
+    type : str, optional, default None
         The type of card to retrieve if multiple cards are present.
-    follow_resumed : bool, default: True
+    follow_resumed : bool, default True
         If the task has been resumed, then setting this flag will resolve the card for
         the origin task.
 
@@ -250,8 +267,9 @@ def get_cards(
         task = Task(task_str)
     elif not isinstance(task, Task):
         # Exception that the task argument should be of form `Task` or `str`
-        raise IncorrectArguementException(_TYPE(task))
+        raise IncorrectArgumentException(_TYPE(task))
 
+    origin_taskpathspec = None
     if follow_resumed:
         origin_taskpathspec = resumed_info(task)
         if origin_taskpathspec:
@@ -263,7 +281,6 @@ def get_cards(
     return CardContainer(
         card_paths,
         card_ds,
-        from_resumed=origin_taskpathspec is not None,
         origin_pathspec=origin_taskpathspec,
     )
 
diff --git a/metaflow/plugins/cards/card_creator.py b/metaflow/plugins/cards/card_creator.py
new file mode 100644
index 00000000000..14977563240
--- /dev/null
+++ b/metaflow/plugins/cards/card_creator.py
@@ -0,0 +1,252 @@
+import time
+import subprocess
+import tempfile
+import json
+import sys
+import os
+from metaflow import current
+from typing import Callable, Tuple, Dict
+
+
+ASYNC_TIMEOUT = 30
+
+
+class CardProcessManager:
+    """
+    This class is responsible for managing the card creation processes.
+
+    """
+
+    async_card_processes = {
+        # "carduuid": {
+        #     "proc": subprocess.Popen,
+        #     "started": time.time()
+        # }
+    }
+
+    @classmethod
+    def _register_card_process(cls, carduuid, proc):
+        cls.async_card_processes[carduuid] = {
+            "proc": proc,
+            "started": time.time(),
+        }
+
+    @classmethod
+    def _get_card_process(cls, carduuid):
+        proc_dict = cls.async_card_processes.get(carduuid, None)
+        if proc_dict is not None:
+            return proc_dict["proc"], proc_dict["started"]
+        return None, None
+
+    @classmethod
+    def _remove_card_process(cls, carduuid):
+        if carduuid in cls.async_card_processes:
+            cls.async_card_processes[carduuid]["proc"].kill()
+            del cls.async_card_processes[carduuid]
+
+
+class CardCreator:
+    def __init__(
+        self,
+        top_level_options,
+        should_save_metadata_lambda: Callable[[str], Tuple[bool, Dict]],
+    ):
+        # should_save_metadata_lambda is a lambda that provides a flag to indicate if
+        # card metadata should be written to the metadata store.
+        # It gets called only once when the card is created inside the subprocess.
+        # The intent is that this is a stateful lambda that will ensure that we only end
+        # up writing to the metadata store once.
+        self._top_level_options = top_level_options
+        self._should_save_metadata = should_save_metadata_lambda
+
+    def create(
+        self,
+        card_uuid=None,
+        user_set_card_id=None,
+        runtime_card=False,
+        decorator_attributes=None,
+        card_options=None,
+        logger=None,
+        mode="render",
+        final=False,
+        sync=False,
+    ):
+        # Setting `final` will affect the Reload token set during the card refresh
+        # data creation along with synchronous execution of subprocess.
+        # Setting `sync` will only cause synchronous execution of subprocess.
+        save_metadata = False
+        metadata_dict = {}
+        if mode != "render" and not runtime_card:
+            # silently ignore runtime updates for cards that don't support them
+            return
+        elif mode == "refresh":
+            # don't serialize components, which can be a somewhat expensive operation,
+            # if we are just updating data
+            component_strings = []
+        else:
+            component_strings = current.card._serialize_components(card_uuid)
+            # Since the mode is a render, we can check if we need to write to the metadata store.
+            save_metadata, metadata_dict = self._should_save_metadata(card_uuid)
+        data = current.card._get_latest_data(card_uuid, final=final, mode=mode)
+        runspec = "/".join([current.run_id, current.step_name, current.task_id])
+        self._run_cards_subprocess(
+            card_uuid,
+            user_set_card_id,
+            mode,
+            runspec,
+            decorator_attributes,
+            card_options,
+            component_strings,
+            logger,
+            data,
+            final=final,
+            sync=sync,
+            save_metadata=save_metadata,
+            metadata_dict=metadata_dict,
+        )
+
+    def _run_cards_subprocess(
+        self,
+        card_uuid,
+        user_set_card_id,
+        mode,
+        runspec,
+        decorator_attributes,
+        card_options,
+        component_strings,
+        logger,
+        data=None,
+        final=False,
+        sync=False,
+        save_metadata=False,
+        metadata_dict=None,
+    ):
+        components_file = data_file = None
+        wait = final or sync
+
+        if len(component_strings) > 0:
+            # note that we can't delete temporary files here when calling the subprocess
+            # async due to a race condition. The subprocess must delete them
+            components_file = tempfile.NamedTemporaryFile(
+                "w", suffix=".json", delete=False
+            )
+            json.dump(component_strings, components_file)
+            components_file.seek(0)
+        if data is not None:
+            data_file = tempfile.NamedTemporaryFile("w", suffix=".json", delete=False)
+            json.dump(data, data_file)
+            data_file.seek(0)
+
+        executable = sys.executable
+        cmd = [
+            executable,
+            sys.argv[0],
+        ]
+
+        cmd += self._top_level_options + [
+            "card",
+            "create",
+            runspec,
+            "--delete-input-files",
+            "--card-uuid",
+            card_uuid,
+            "--mode",
+            mode,
+            "--type",
+            decorator_attributes["type"],
+            # Add the options relating to card arguments.
+            # todo : add scope as a CLI arg for the create method.
+        ]
+        if card_options is not None and len(card_options) > 0:
+            cmd += ["--options", json.dumps(card_options)]
+        # set the id argument.
+
+        if decorator_attributes["timeout"] is not None:
+            cmd += ["--timeout", str(decorator_attributes["timeout"])]
+
+        if user_set_card_id is not None:
+            cmd += ["--id", str(user_set_card_id)]
+
+        if decorator_attributes["save_errors"]:
+            cmd += ["--render-error-card"]
+
+        if components_file is not None:
+            cmd += ["--component-file", components_file.name]
+
+        if data_file is not None:
+            cmd += ["--data-file", data_file.name]
+
+        if save_metadata:
+            cmd += ["--save-metadata", json.dumps(metadata_dict)]
+
+        response, fail = self._run_command(
+            cmd,
+            card_uuid,
+            os.environ,
+            timeout=decorator_attributes["timeout"],
+            wait=wait,
+        )
+        if fail:
+            resp = "" if response is None else response.decode("utf-8")
+            logger(
+                "Card render failed with error : \n\n %s" % resp,
+                timestamp=False,
+                bad=True,
+            )
+
+    def _wait_for_async_processes_to_finish(self, card_uuid, async_timeout):
+        _async_proc, _async_started = CardProcessManager._get_card_process(card_uuid)
+        while _async_proc is not None and _async_proc.poll() is None:
+            if time.time() - _async_started > async_timeout:
+                # This means the process has crossed the timeout and we need to kill it.
+                CardProcessManager._remove_card_process(card_uuid)
+                break
+
+    def _run_command(self, cmd, card_uuid, env, wait=True, timeout=None):
+        fail = False
+        timeout_args = {}
+        async_timeout = ASYNC_TIMEOUT
+        if timeout is not None:
+            async_timeout = int(timeout) + 10
+            timeout_args = dict(timeout=int(timeout) + 10)
+
+        if wait:
+            self._wait_for_async_processes_to_finish(card_uuid, async_timeout)
+            try:
+                rep = subprocess.check_output(
+                    cmd, env=env, stderr=subprocess.STDOUT, **timeout_args
+                )
+            except subprocess.CalledProcessError as e:
+                rep = e.output
+                fail = True
+            except subprocess.TimeoutExpired as e:
+                rep = e.output
+                fail = True
+            return rep, fail
+        else:
+            _async_proc, _async_started = CardProcessManager._get_card_process(
+                card_uuid
+            )
+            if _async_proc and _async_proc.poll() is None:
+                if time.time() - _async_started > async_timeout:
+                    CardProcessManager._remove_card_process(card_uuid)
+                    # Since we have removed the card process, we are free to run a new one
+                    # This will also ensure that when a old process is removed a new one is replaced.
+                    return self._run_command(
+                        cmd, card_uuid, env, wait=wait, timeout=timeout
+                    )
+                else:
+                    # silently refuse to run an async process if a previous one is still running
+                    # and timeout hasn't been reached
+                    return "".encode(), False
+            else:
+                CardProcessManager._register_card_process(
+                    card_uuid,
+                    subprocess.Popen(
+                        cmd,
+                        env=env,
+                        stderr=subprocess.DEVNULL,
+                        stdout=subprocess.DEVNULL,
+                    ),
+                )
+                return "".encode(), False
diff --git a/metaflow/plugins/cards/card_datastore.py b/metaflow/plugins/cards/card_datastore.py
index 59931592878..f70f608c372 100644
--- a/metaflow/plugins/cards/card_datastore.py
+++ b/metaflow/plugins/cards/card_datastore.py
@@ -3,9 +3,9 @@
 """
 
 from collections import namedtuple
-from hashlib import sha1
 from io import BytesIO
 import os
+import json
 import shutil
 
 from metaflow.plugins.datastores.local_storage import LocalStorage
@@ -16,7 +16,6 @@
     CARD_SUFFIX,
     CARD_AZUREROOT,
     CARD_GSROOT,
-    SKIP_CARD_DUALWRITE,
 )
 import metaflow.metaflow_config as metaflow_config
 
@@ -28,6 +27,16 @@
 CardInfo = namedtuple("CardInfo", ["type", "hash", "id", "filename"])
 
 
+class CardNameSuffix:
+    DATA = "data.json"
+    CARD = "html"
+
+
+class CardPathSuffix:
+    DATA = "runtime"
+    CARD = "cards"
+
+
 def path_spec_resolver(pathspec):
     splits = pathspec.split("/")
     splits.extend([None] * (4 - len(splits)))
@@ -85,18 +94,22 @@ def __init__(self, flow_datastore, pathspec=None):
         self._run_id = run_id
         self._step_name = step_name
         self._pathspec = pathspec
-        self._temp_card_save_path = self._get_write_path(base_pth=TEMP_DIR_NAME)
+        self._temp_card_save_path = self._get_card_write_path(base_pth=TEMP_DIR_NAME)
 
     @classmethod
-    def get_card_location(cls, base_path, card_name, card_html, card_id=None):
-        chash = sha1(bytes(card_html, "utf-8")).hexdigest()
+    def get_card_location(
+        cls, base_path, card_name, uuid, card_id=None, suffix=CardNameSuffix.CARD
+    ):
+        chash = uuid
         if card_id is None:
-            card_file_name = "%s-%s.html" % (card_name, chash)
+            card_file_name = "%s-%s.%s" % (card_name, chash, suffix)
         else:
-            card_file_name = "%s-%s-%s.html" % (card_name, card_id, chash)
+            card_file_name = "%s-%s-%s.%s" % (card_name, card_id, chash, suffix)
         return os.path.join(base_path, card_file_name)
 
-    def _make_path(self, base_pth, pathspec=None, with_steps=False):
+    def _make_path(
+        self, base_pth, pathspec=None, with_steps=False, suffix=CardPathSuffix.CARD
+    ):
         sysroot = base_pth
         if pathspec is not None:
             # since most cards are at a task level there will always be 4 non-none values returned
@@ -121,7 +134,7 @@ def _make_path(self, base_pth, pathspec=None, with_steps=False):
                 step_name,
                 "tasks",
                 task_id,
-                "cards",
+                suffix,
             ]
         else:
             pth_arr = [
@@ -131,20 +144,49 @@ def _make_path(self, base_pth, pathspec=None, with_steps=False):
                 run_id,
                 "tasks",
                 task_id,
-                "cards",
+                suffix,
             ]
         if sysroot == "" or sysroot is None:
             pth_arr.pop(0)
         return os.path.join(*pth_arr)
 
-    def _get_write_path(self, base_pth=""):
-        return self._make_path(base_pth, pathspec=self._pathspec, with_steps=True)
+    def _get_data_read_path(self, base_pth=""):
+        return self._make_path(
+            base_pth=base_pth,
+            pathspec=self._pathspec,
+            with_steps=True,
+            suffix=CardPathSuffix.DATA,
+        )
+
+    def _get_data_write_path(self, base_pth=""):
+        return self._make_path(
+            base_pth=base_pth,
+            pathspec=self._pathspec,
+            with_steps=True,
+            suffix=CardPathSuffix.DATA,
+        )
 
-    def _get_read_path(self, base_pth="", with_steps=False):
-        return self._make_path(base_pth, pathspec=self._pathspec, with_steps=with_steps)
+    def _get_card_write_path(
+        self,
+        base_pth="",
+    ):
+        return self._make_path(
+            base_pth,
+            pathspec=self._pathspec,
+            with_steps=True,
+            suffix=CardPathSuffix.CARD,
+        )
+
+    def _get_card_read_path(self, base_pth="", with_steps=False):
+        return self._make_path(
+            base_pth,
+            pathspec=self._pathspec,
+            with_steps=with_steps,
+            suffix=CardPathSuffix.CARD,
+        )
 
     @staticmethod
-    def card_info_from_path(path):
+    def info_from_path(path, suffix=CardNameSuffix.CARD):
         """
         Args:
             path (str): The path to the card
@@ -160,8 +202,8 @@ def card_info_from_path(path):
 
         if len(file_split) not in [2, 3]:
             raise Exception(
-                "Invalid card file name %s. Card file names should be of form TYPE-HASH.html or TYPE-ID-HASH.html"
-                % card_file_name
+                "Invalid file name %s. Card/Data file names should be of form TYPE-HASH.%s or TYPE-ID-HASH.%s"
+                % (card_file_name, suffix, suffix)
             )
         card_type, card_hash, card_id = None, None, None
 
@@ -170,60 +212,51 @@ def card_info_from_path(path):
         else:
             card_type, card_id, card_hash = file_split
 
-        card_hash = card_hash.split(".html")[0]
+        card_hash = card_hash.split("." + suffix)[0]
         return CardInfo(card_type, card_hash, card_id, card_file_name)
 
-    def save_card(self, card_type, card_html, card_id=None, overwrite=True):
+    def save_data(self, uuid, card_type, json_data, card_id=None):
         card_file_name = card_type
-        # TEMPORARY_WORKAROUND: FIXME (LATER) : Fix the duplication of below block in a few months.
-        # Check file blame to understand the age of this temporary workaround.
-
-        # This function will end up saving cards at two locations.
-        # Thereby doubling the number of cards. (Which is a temporary fix)
-        # Why do this ? :
-        # When cards were introduced there was an assumption made about task-ids being unique.
-        # This assumption was incorrect.
-        # Only the pathspec needs to be unique but there is no such guarantees about task-ids.
-        # When task-ids are non-unique, card read would result in finding incorrect cards.
-        # This happens because cards were stored based on task-ids.
-        # If we immediately switch from storing based on task-ids to a step-name abstraction folder,
-        # then card reading will crash for many users.
-        # It would especially happen for users who are accessing cards created by a newer
-        # MF client from an older version of MF client.
-        # It will also easily end up breaking the metaflow-ui (which maybe using a client from an older version).
-        # Hence, we are writing cards to both paths so that we can introduce breaking changes later in the future.
-        card_path_with_steps = self.get_card_location(
-            self._get_write_path(), card_file_name, card_html, card_id=card_id
+        loc = self.get_card_location(
+            self._get_data_write_path(),
+            card_file_name,
+            uuid,
+            card_id=card_id,
+            suffix=CardNameSuffix.DATA,
+        )
+        self._backend.save_bytes(
+            [(loc, BytesIO(json.dumps(json_data).encode("utf-8")))], overwrite=True
         )
-        if SKIP_CARD_DUALWRITE:
-            self._backend.save_bytes(
-                [(card_path_with_steps, BytesIO(bytes(card_html, "utf-8")))],
-                overwrite=overwrite,
-            )
-        else:
-            card_path_without_steps = self.get_card_location(
-                self._get_read_path(with_steps=False),
-                card_file_name,
-                card_html,
-                card_id=card_id,
-            )
-            for cp in [card_path_with_steps, card_path_without_steps]:
-                self._backend.save_bytes(
-                    [(cp, BytesIO(bytes(card_html, "utf-8")))], overwrite=overwrite
-                )
 
-        return self.card_info_from_path(card_path_with_steps)
+    def save_card(self, uuid, card_type, card_html, card_id=None, overwrite=True):
+        card_file_name = card_type
+        card_path_with_steps = self.get_card_location(
+            self._get_card_write_path(),
+            card_file_name,
+            uuid,
+            card_id=card_id,
+            suffix=CardNameSuffix.CARD,
+        )
+        self._backend.save_bytes(
+            [(card_path_with_steps, BytesIO(bytes(card_html, "utf-8")))],
+            overwrite=overwrite,
+        )
+        return self.info_from_path(card_path_with_steps, suffix=CardNameSuffix.CARD)
 
     def _list_card_paths(self, card_type=None, card_hash=None, card_id=None):
         # Check for new cards first
         card_paths = []
         card_paths_with_steps = self._backend.list_content(
-            [self._get_read_path(with_steps=True)]
+            [self._get_card_read_path(with_steps=True)]
         )
 
         if len(card_paths_with_steps) == 0:
+            # The listing logic is reading the cards with steps and without steps
+            # because earlier versions of clients (ones that wrote cards before June 2022),
+            # would have written cards without steps. So as a fallback we will try to check for the
+            # cards without steps.
             card_paths_without_steps = self._backend.list_content(
-                [self._get_read_path(with_steps=False)]
+                [self._get_card_read_path(with_steps=False)]
             )
             if len(card_paths_without_steps) == 0:
                 # If there are no files found on the Path then raise an error of
@@ -240,7 +273,7 @@ def _list_card_paths(self, card_type=None, card_hash=None, card_id=None):
         cards_found = []
         for task_card_path in card_paths:
             card_path = task_card_path.path
-            card_info = self.card_info_from_path(card_path)
+            card_info = self.info_from_path(card_path, suffix=CardNameSuffix.CARD)
             if card_type is not None and card_info.type != card_type:
                 continue
             elif card_hash is not None:
@@ -254,11 +287,32 @@ def _list_card_paths(self, card_type=None, card_hash=None, card_id=None):
 
         return cards_found
 
+    def _list_card_data(self, card_type=None, card_hash=None, card_id=None):
+        card_data_paths = self._backend.list_content([self._get_data_read_path()])
+        data_found = []
+
+        for data_path in card_data_paths:
+            _pth = data_path.path
+            card_info = self.info_from_path(_pth, suffix=CardNameSuffix.DATA)
+            if card_type is not None and card_info.type != card_type:
+                continue
+            elif card_hash is not None:
+                if not card_info.hash.startswith(card_hash):
+                    continue
+            elif card_id is not None and card_info.id != card_id:
+                continue
+            if data_path.is_file:
+                data_found.append(_pth)
+
+        return data_found
+
     def create_full_path(self, card_path):
         return os.path.join(self._backend.datastore_root, card_path)
 
     def get_card_names(self, card_paths):
-        return [self.card_info_from_path(path) for path in card_paths]
+        return [
+            self.info_from_path(path, suffix=CardNameSuffix.CARD) for path in card_paths
+        ]
 
     def get_card_html(self, path):
         with self._backend.load_bytes([path]) as get_results:
@@ -267,6 +321,13 @@ def get_card_html(self, path):
                     with open(path, "r") as f:
                         return f.read()
 
+    def get_card_data(self, path):
+        with self._backend.load_bytes([path]) as get_results:
+            for _, path, _ in get_results:
+                if path is not None:
+                    with open(path, "r") as f:
+                        return json.loads(f.read())
+
     def cache_locally(self, path, save_path=None):
         """
         Saves the data present in the `path` the `metaflow_card_cache` directory or to the `save_path`.
@@ -292,6 +353,15 @@ def cache_locally(self, path, save_path=None):
                     shutil.copy(path, main_path)
                     return main_path
 
+    def extract_data_paths(self, card_type=None, card_hash=None, card_id=None):
+        return self._list_card_data(
+            # card_hash is the unique identifier to the card.
+            # Its no longer the actual hash!
+            card_type=card_type,
+            card_hash=card_hash,
+            card_id=card_id,
+        )
+
     def extract_card_paths(self, card_type=None, card_hash=None, card_id=None):
         return self._list_card_paths(
             card_type=card_type, card_hash=card_hash, card_id=card_id
diff --git a/metaflow/plugins/cards/card_decorator.py b/metaflow/plugins/cards/card_decorator.py
index efa13a1ec90..28c0c7f8f10 100644
--- a/metaflow/plugins/cards/card_decorator.py
+++ b/metaflow/plugins/cards/card_decorator.py
@@ -1,22 +1,22 @@
-import subprocess
+import json
 import os
+import re
 import tempfile
-import sys
-import json
+from typing import Tuple, Dict
 
-from typing import Dict, Any
-
-from metaflow.decorators import StepDecorator, flow_decorators
-from metaflow.current import current
+from metaflow.decorators import StepDecorator
+from metaflow.metadata_provider import MetaDatum
+from metaflow.metaflow_current import current
+from metaflow.user_configs.config_options import ConfigInput
+from metaflow.user_configs.config_parameters import dump_config_values
 from metaflow.util import to_unicode
-from .component_serializer import CardComponentCollector, get_card_class
-
-
-# from metaflow import get_metadata
-import re
 
+from .component_serializer import CardComponentCollector, get_card_class
+from .card_creator import CardCreator
 from .exception import CARD_ID_PATTERN, TYPE_CHECK_REGEX
 
+ASYNC_TIMEOUT = 30
+
 
 def warning_message(message, logger=None, ts=False):
     msg = "[@card WARNING] %s" % message
@@ -24,6 +24,24 @@ def warning_message(message, logger=None, ts=False):
         logger(msg, timestamp=ts, bad=True)
 
 
+class MetadataStateManager(object):
+    def __init__(self, info_func):
+        self._info_func = info_func
+        self._metadata_registered = {}
+
+    def register_metadata(self, card_uuid) -> Tuple[bool, Dict]:
+        info = self._info_func()
+        # Check that metadata was not written yet. We only want to write once.
+        if (
+            info is None
+            or info.get(card_uuid) is None
+            or self._metadata_registered.get(card_uuid)
+        ):
+            return False, {}
+        self._metadata_registered[card_uuid] = True
+        return True, info.get(card_uuid)
+
+
 class CardDecorator(StepDecorator):
     """
     Creates a human-readable report, a Metaflow Card, after this step completes.
@@ -32,25 +50,44 @@ class CardDecorator(StepDecorator):
 
     Parameters
     ----------
-    type : str, default: 'default'
+    type : str, default 'default'
         Card type.
-    id : str, optional, default: None
+    id : str, optional, default None
         If multiple cards are present, use this id to identify this card.
-    options : Dict[str, Any], default: {}
+    options : Dict[str, Any], default {}
         Options passed to the card. The contents depend on the card type.
-    timeout : int, default: 45
+    timeout : int, default 45
         Interrupt reporting if it takes more than this many seconds.
+
+    MF Add To Current
+    -----------------
+    card -> metaflow.plugins.cards.component_serializer.CardComponentCollector
+        The `@card` decorator makes the cards available through the `current.card`
+        object. If multiple `@card` decorators are present, you can add an `ID` to
+        distinguish between them using `@card(id=ID)` as the decorator. You will then
+        be able to access that specific card using `current.card[ID].
+
+        Methods available are `append` and `extend`
+
+        @@ Returns
+        -------
+        CardComponentCollector
+            The or one of the cards attached to this step.
     """
 
+    _GLOBAL_CARD_INFO = {}
+
     name = "card"
     defaults = {
         "type": "default",
         "options": {},
         "scope": "task",
+        "rank": None,  # Can be one of "high", "medium", "low". Can help derive ordering on the UI.
         "timeout": 45,
         "id": None,
         "save_errors": True,
         "customize": False,
+        "refresh_interval": 5,
     }
     allow_multiple = True
 
@@ -60,6 +97,14 @@ class CardDecorator(StepDecorator):
 
     _called_once = {}
 
+    card_creator = None
+
+    _config_values = None
+
+    _config_file_name = None
+
+    task_finished_decos = 0
+
     def __init__(self, *args, **kwargs):
         super(CardDecorator, self).__init__(*args, **kwargs)
         self._task_datastore = None
@@ -69,6 +114,11 @@ def __init__(self, *args, **kwargs):
         self._is_editable = False
         self._card_uuid = None
         self._user_set_card_id = None
+        self._metadata_registered = False
+
+    @classmethod
+    def _set_card_creator(cls, card_creator):
+        cls.card_creator = card_creator
 
     def _is_event_registered(self, evt_name):
         return evt_name in self._called_once
@@ -86,21 +136,60 @@ def _set_card_counts_per_step(cls, step_name, total_count):
     def _increment_step_counter(cls):
         cls.step_counter += 1
 
+    @classmethod
+    def _increment_completed_counter(cls):
+        cls.task_finished_decos += 1
+
+    @classmethod
+    def _set_config_values(cls, config_values):
+        cls._config_values = config_values
+
+    @classmethod
+    def _set_config_file_name(cls, flow):
+        # Only create a config file from the very first card decorator.
+        if cls._config_values and not cls._config_file_name:
+            with tempfile.NamedTemporaryFile(
+                mode="w", encoding="utf-8", delete=False
+            ) as config_file:
+                config_value = dump_config_values(flow)
+                json.dump(config_value, config_file)
+                cls._config_file_name = config_file.name
+
+    @classmethod
+    def _register_card_info(cls, **kwargs):
+        if not kwargs.get("card_uuid"):
+            raise ValueError("card_uuid is required")
+        cls._GLOBAL_CARD_INFO[kwargs["card_uuid"]] = kwargs
+
+    @classmethod
+    def all_cards_info(cls):
+        return cls._GLOBAL_CARD_INFO.copy()
+
     def step_init(
         self, flow, graph, step_name, decorators, environment, flow_datastore, logger
     ):
-
         self._flow_datastore = flow_datastore
         self._environment = environment
         self._logger = logger
         self.card_options = None
 
+        # We check for configuration options. We do this here before they are
+        # converted to properties.
+        self._set_config_values(
+            [
+                (config.name, ConfigInput.make_key_name(config.name))
+                for _, config in flow._get_parameters()
+                if config.IS_CONFIG_PARAMETER
+            ]
+        )
+
         self.card_options = self.attributes["options"]
 
         evt_name = "step-init"
         # `'%s-%s'%(evt_name,step_name)` ensures that we capture this once per @card per @step.
         # Since there can be many steps checking if event is registered for `evt_name` will only make it check it once for all steps.
         # Hence, we have `_is_event_registered('%s-%s'%(evt_name,step_name))`
+        self._is_runtime_card = False
         evt = "%s-%s" % (evt_name, step_name)
         if not self._is_event_registered(evt):
             # We set the total count of decorators so that we can use it for
@@ -126,11 +215,31 @@ def task_pre_step(
         ubf_context,
         inputs,
     ):
+        self._task_datastore = task_datastore
+        self._metadata = metadata
+
+        # If we have configs, we need to dump them to a file so we can re-use them
+        # when calling the card creation subprocess.
+        # Since a step can contain multiple card decorators, and all the card creation processes
+        # will reference the same config file (because of how the CardCreator is created (only single class instance)),
+        # we need to ensure that a single config file is being referenced for all card create commands.
+        # This config file will be removed when the last card decorator has finished creating its card.
+        self._set_config_file_name(flow)
+        # The MetadataStateManager is used to track the state of the metadata registration.
+        # It is there to ensure that we only register metadata for the card once. This is so that we
+        # avoid any un-necessary metadata writes because the create command can be called multiple times during the
+        # card creation process.
+        self._metadata_state_manager = MetadataStateManager(self.all_cards_info)
+
         card_type = self.attributes["type"]
         card_class = get_card_class(card_type)
+
+        self._is_runtime_card = False
         if card_class is not None:  # Card type was not found
             if card_class.ALLOW_USER_COMPONENTS:
                 self._is_editable = True
+            self._is_runtime_card = card_class.RUNTIME_UPDATABLE
+
         # We have a step counter to ensure that on calling the final card decorator's `task_pre_step`
         # we call a `finalize` function in the `CardComponentCollector`.
         # This can help ensure the behaviour of the `current.card` object is according to specification.
@@ -155,7 +264,16 @@ def task_pre_step(
         # we need to ensure that `current.card` has `CardComponentCollector` instantiated only once.
         if not self._is_event_registered("pre-step"):
             self._register_event("pre-step")
-            current._update_env({"card": CardComponentCollector(self._logger)})
+            self._set_card_creator(
+                CardCreator(
+                    self._create_top_level_args(flow),
+                    self._metadata_state_manager.register_metadata,
+                )
+            )
+
+            current._update_env(
+                {"card": CardComponentCollector(self._logger, self.card_creator)}
+            )
 
         # this line happens because of decospecs parsing.
         customize = False
@@ -165,28 +283,49 @@ def task_pre_step(
         card_metadata = current.card._add_card(
             self.attributes["type"],
             self._user_set_card_id,
-            self._is_editable,
-            customize,
+            self.attributes,
+            self.card_options,
+            editable=self._is_editable,
+            customize=customize,
+            runtime_card=self._is_runtime_card,
+            refresh_interval=self.attributes["refresh_interval"],
         )
         self._card_uuid = card_metadata["uuid"]
 
+        self._register_card_info(
+            card_uuid=self._card_uuid,
+            rank=self.attributes["rank"],
+            type=self.attributes["type"],
+            options=self.card_options,
+            is_editable=self._is_editable,
+            is_runtime_card=self._is_runtime_card,
+            refresh_interval=self.attributes["refresh_interval"],
+            customize=customize,
+            id=self._user_set_card_id,
+        )
+
         # This means that we are calling `task_pre_step` on the last card decorator.
         # We can now `finalize` method in the CardComponentCollector object.
         # This will set up the `current.card` object for usage inside `@step` code.
         if self.step_counter == self.total_decos_on_step[step_name]:
             current.card._finalize()
 
-        self._task_datastore = task_datastore
-        self._metadata = metadata
-
     def task_finished(
         self, step_name, flow, graph, is_task_ok, retry_count, max_user_code_retries
     ):
-        if not is_task_ok:
-            return
-        component_strings = current.card._serialize_components(self._card_uuid)
-        runspec = "/".join([current.run_id, current.step_name, current.task_id])
-        self._run_cards_subprocess(runspec, component_strings)
+        create_options = dict(
+            card_uuid=self._card_uuid,
+            user_set_card_id=self._user_set_card_id,
+            runtime_card=self._is_runtime_card,
+            decorator_attributes=self.attributes,
+            card_options=self.card_options,
+            logger=self._logger,
+        )
+        if is_task_ok:
+            self.card_creator.create(mode="render", final=True, **create_options)
+            self.card_creator.create(mode="refresh", final=True, **create_options)
+
+        self._cleanup(step_name)
 
     @staticmethod
     def _options(mapping):
@@ -197,10 +336,13 @@ def _options(mapping):
                 for value in v:
                     yield "--%s" % k
                     if not isinstance(value, bool):
-                        yield to_unicode(value)
-
-    def _create_top_level_args(self):
+                        if isinstance(value, tuple):
+                            for val in value:
+                                yield to_unicode(val)
+                        else:
+                            yield to_unicode(value)
 
+    def _create_top_level_args(self, flow):
         top_level_options = {
             "quiet": True,
             "metadata": self._metadata.TYPE,
@@ -213,68 +355,18 @@ def _create_top_level_args(self):
             # We don't provide --with as all execution is taking place in
             # the context of the main process
         }
-        return list(self._options(top_level_options))
-
-    def _run_cards_subprocess(self, runspec, component_strings):
-        temp_file = None
-        if len(component_strings) > 0:
-            temp_file = tempfile.NamedTemporaryFile("w", suffix=".json")
-            json.dump(component_strings, temp_file)
-            temp_file.seek(0)
-        executable = sys.executable
-        cmd = [
-            executable,
-            sys.argv[0],
-        ]
-        cmd += self._create_top_level_args() + [
-            "card",
-            "create",
-            runspec,
-            "--type",
-            self.attributes["type"],
-            # Add the options relating to card arguments.
-            # todo : add scope as a CLI arg for the create method.
-        ]
-        if self.card_options is not None and len(self.card_options) > 0:
-            cmd += ["--options", json.dumps(self.card_options)]
-        # set the id argument.
+        if self._config_values:
+            top_level_options["config-value"] = self._config_values
+            top_level_options["local-config-file"] = self._config_file_name
 
-        if self.attributes["timeout"] is not None:
-            cmd += ["--timeout", str(self.attributes["timeout"])]
-
-        if self._user_set_card_id is not None:
-            cmd += ["--id", str(self._user_set_card_id)]
-
-        if self.attributes["save_errors"]:
-            cmd += ["--render-error-card"]
-
-        if temp_file is not None:
-            cmd += ["--component-file", temp_file.name]
-
-        response, fail = self._run_command(
-            cmd, os.environ, timeout=self.attributes["timeout"]
-        )
-        if fail:
-            resp = "" if response is None else response.decode("utf-8")
-            self._logger(
-                "Card render failed with error : \n\n %s" % resp,
-                timestamp=False,
-                bad=True,
-            )
+        return list(self._options(top_level_options))
 
-    def _run_command(self, cmd, env, timeout=None):
-        fail = False
-        timeout_args = {}
-        if timeout is not None:
-            timeout_args = dict(timeout=int(timeout) + 10)
-        try:
-            rep = subprocess.check_output(
-                cmd, env=env, stderr=subprocess.STDOUT, **timeout_args
-            )
-        except subprocess.CalledProcessError as e:
-            rep = e.output
-            fail = True
-        except subprocess.TimeoutExpired as e:
-            rep = e.output
-            fail = True
-        return rep, fail
+    def _cleanup(self, step_name):
+        self._increment_completed_counter()
+        if self.task_finished_decos == self.total_decos_on_step[step_name]:
+            # Unlink the config file if it exists
+            if self._config_file_name:
+                try:
+                    os.unlink(self._config_file_name)
+                except Exception as e:
+                    pass
diff --git a/metaflow/plugins/cards/card_modules/base.html b/metaflow/plugins/cards/card_modules/base.html
index 38a066179ea..76ff0b87d2d 100644
--- a/metaflow/plugins/cards/card_modules/base.html
+++ b/metaflow/plugins/cards/card_modules/base.html
@@ -18,16 +18,28 @@
 
   
+ {{#RENDER_COMPLETE}} + + {{/RENDER_COMPLETE}} + {{^RENDER_COMPLETE}} + + {{/RENDER_COMPLETE}} diff --git a/metaflow/plugins/cards/card_modules/basic.py b/metaflow/plugins/cards/card_modules/basic.py index b078a8f82d3..256375509f7 100644 --- a/metaflow/plugins/cards/card_modules/basic.py +++ b/metaflow/plugins/cards/card_modules/basic.py @@ -1,9 +1,10 @@ import base64 import json import os -from .card import MetaflowCard, MetaflowCardComponent +from .card import MetaflowCard, MetaflowCardComponent, with_default_component_id from .convert_to_native_type import TaskToDict import uuid +import inspect ABS_DIR_PATH = os.path.dirname(os.path.abspath(__file__)) RENDER_TEMPLATE_PATH = os.path.join(ABS_DIR_PATH, "base.html") @@ -26,9 +27,11 @@ def node_to_type(node_type): graph_dict[stepname] = { "type": node_to_type(step_info[stepname]["type"]), "box_next": step_info[stepname]["type"] not in ("linear", "join"), - "box_ends": None - if "matching_join" not in step_info[stepname] - else step_info[stepname]["matching_join"], + "box_ends": ( + None + if "matching_join" not in step_info[stepname] + else step_info[stepname]["matching_join"] + ), "next": step_info[stepname]["next"], "doc": step_info[stepname]["doc"], } @@ -146,6 +149,8 @@ def render(self): label=self._label, ) datadict.update(img_dict) + if self.component_id is not None: + datadict["id"] = self.component_id return datadict @@ -194,6 +199,8 @@ def render(self): datadict["columns"] = self._headers datadict["data"] = self._data datadict["vertical"] = self._vertical + if self.component_id is not None: + datadict["id"] = self.component_id return datadict @@ -230,9 +237,28 @@ def __init__(self, data=None): super().__init__(title=None, subtitle=None) self._data = data + @with_default_component_id def render(self): datadict = super().render() datadict["data"] = self._data + if self.component_id is not None: + datadict["id"] = self.component_id + return datadict + + +class PythonCodeComponent(DefaultComponent): + + type = "pythonCode" + + def __init__(self, data=None): + super().__init__(title=None, subtitle=None) + self._data = data + + def render(self): + datadict = super().render() + datadict["data"] = self._data + if self.component_id is not None: + datadict["id"] = self.component_id return datadict @@ -295,6 +321,8 @@ def __init__(self, title=None, subtitle=None, data={}): def render(self): datadict = super().render() datadict["data"] = self._data + if self.component_id is not None: + datadict["id"] = self.component_id return datadict @@ -307,7 +335,16 @@ def __init__(self, text=None): def render(self): datadict = super().render() - datadict["source"] = self._text + _text = self._text + # When we have a markdown text that doesn't start with a `#`, + # we need to add a newline to make sure that the markdown + # is rendered correctly. Otherwise `svelte-markdown` will render + # the empty `

` tags during re-renders on card data updates. + if self._text is not None and not self._text.startswith("#"): + _text = "\n" + _text + datadict["source"] = _text + if self.component_id is not None: + datadict["id"] = self.component_id return datadict @@ -319,7 +356,14 @@ class TaskInfoComponent(MetaflowCardComponent): """ def __init__( - self, task, page_title="Task Info", only_repr=True, graph=None, components=[] + self, + task, + page_title="Task Info", + only_repr=True, + graph=None, + components=[], + runtime=False, + flow=None, ): self._task = task self._only_repr = only_repr @@ -328,6 +372,8 @@ def __init__( self._page_title = page_title self.final_component = None self.page_component = None + self.runtime = runtime + self.flow = flow def render(self): """ @@ -340,7 +386,8 @@ def render(self): self._task, graph=self._graph ) # ignore the name as an artifact - del task_data_dict["data"]["name"] + if "name" in task_data_dict["data"]: + del task_data_dict["data"]["name"] _metadata = dict(version=1, template="defaultCardTemplate") # try to parse out metaflow version from tags, but let it go if unset @@ -370,11 +417,12 @@ def render(self): "Task Created On": task_data_dict["created_at"], "Task Finished On": task_data_dict["finished_at"], # Remove Microseconds from timedelta - "Task Duration": str(self._task.finished_at - self._task.created_at).split( - "." - )[0], "Tags": ", ".join(tags), } + if not self.runtime: + task_metadata_dict["Task Duration"] = str( + self._task.finished_at - self._task.created_at + ).split(".")[0] if len(user_info) > 0: task_metadata_dict["User"] = user_info[0].split("user:")[1] @@ -434,8 +482,12 @@ def render(self): p.id for p in self._task.parent.parent["_parameters"].task if p.id != "name" ] if len(param_ids) > 0: + # Extract parameter from the Parameter Task. That is less brittle. + parameter_data = TaskToDict( + only_repr=self._only_repr, runtime=self.runtime + )(self._task.parent.parent["_parameters"].task, graph=self._graph) param_component = ArtifactsComponent( - data=[task_data_dict["data"][pid] for pid in param_ids] + data=[parameter_data["data"][pid] for pid in param_ids] ) else: param_component = TitleComponent(text="No Parameters") @@ -445,6 +497,16 @@ def render(self): contents=[param_component], ).render() + step_func = getattr(self.flow, self._task.parent.id) + code_table = SectionComponent( + title="Task Code", + contents=[ + TableComponent( + data=[[PythonCodeComponent(inspect.getsource(step_func)).render()]] + ) + ], + ).render() + # Don't include parameter ids + "name" in the task artifacts artifactlist = [ task_data_dict["data"][k] @@ -470,6 +532,7 @@ def render(self): page_contents.extend( [ metadata_table, + code_table, parameter_table, artifact_section, ] @@ -514,11 +577,24 @@ class ErrorCard(MetaflowCard): type = "error" - def __init__(self, options={}, components=[], graph=None): + RELOAD_POLICY = MetaflowCard.RELOAD_POLICY_ONCHANGE + + def __init__(self, options={}, components=[], graph=None, **kwargs): self._only_repr = True self._graph = None if graph is None else transform_flow_graph(graph) self._components = components + def reload_content_token(self, task, data): + """ + The reload token will change when the component array has changed in the Metaflow card. + The change in the component array is signified by the change in the component_update_ts. + """ + if task.finished: + return "final" + # `component_update_ts` will never be None. It is set to a default value when the `ComponentStore` is instantiated + # And it is updated when components added / removed / changed from the `ComponentStore`. + return "runtime-%s" % (str(data["component_update_ts"])) + def render(self, task, stack_trace=None): RENDER_TEMPLATE = read_file(RENDER_TEMPLATE_PATH) JS_DATA = read_file(JS_PATH) @@ -559,9 +635,17 @@ class DefaultCardJSON(MetaflowCard): type = "default_json" - def __init__(self, options=dict(only_repr=True), components=[], graph=None): + def __init__( + self, + options=dict(only_repr=True), + components=[], + graph=None, + flow=None, + **kwargs + ): self._only_repr = True self._graph = None if graph is None else transform_flow_graph(graph) + self._flow = flow if "only_repr" in options: self._only_repr = options["only_repr"] self._components = components @@ -572,6 +656,7 @@ def render(self, task): only_repr=self._only_repr, graph=self._graph, components=self._components, + flow=self._flow, ).render() return json.dumps(final_component_dict) @@ -580,16 +665,28 @@ class DefaultCard(MetaflowCard): ALLOW_USER_COMPONENTS = True + RUNTIME_UPDATABLE = True + + RELOAD_POLICY = MetaflowCard.RELOAD_POLICY_ONCHANGE + type = "default" - def __init__(self, options=dict(only_repr=True), components=[], graph=None): + def __init__( + self, + options=dict(only_repr=True), + components=[], + graph=None, + flow=None, + **kwargs + ): self._only_repr = True self._graph = None if graph is None else transform_flow_graph(graph) + self._flow = flow if "only_repr" in options: self._only_repr = options["only_repr"] self._components = components - def render(self, task): + def render(self, task, runtime=False): RENDER_TEMPLATE = read_file(RENDER_TEMPLATE_PATH) JS_DATA = read_file(JS_PATH) CSS_DATA = read_file(CSS_PATH) @@ -598,6 +695,8 @@ def render(self, task): only_repr=self._only_repr, graph=self._graph, components=self._components, + runtime=runtime, + flow=self._flow, ).render() pt = self._get_mustache() data_dict = dict( @@ -608,24 +707,46 @@ def render(self, task): title=task.pathspec, css=CSS_DATA, card_data_id=uuid.uuid4(), + RENDER_COMPLETE=not runtime, ) return pt.render(RENDER_TEMPLATE, data_dict) + def render_runtime(self, task, data): + return self.render(task, runtime=True) + + def refresh(self, task, data): + return data["components"] + + def reload_content_token(self, task, data): + """ + The reload token will change when the component array has changed in the Metaflow card. + The change in the component array is signified by the change in the component_update_ts. + """ + if task.finished: + return "final" + # `component_update_ts` will never be None. It is set to a default value when the `ComponentStore` is instantiated + # And it is updated when components added / removed / changed from the `ComponentStore`. + return "runtime-%s" % (str(data["component_update_ts"])) + class BlankCard(MetaflowCard): ALLOW_USER_COMPONENTS = True + RUNTIME_UPDATABLE = True + + RELOAD_POLICY = MetaflowCard.RELOAD_POLICY_ONCHANGE + type = "blank" - def __init__(self, options=dict(title=""), components=[], graph=None): + def __init__(self, options=dict(title=""), components=[], graph=None, **kwargs): self._graph = None if graph is None else transform_flow_graph(graph) self._title = "" if "title" in options: self._title = options["title"] self._components = components - def render(self, task, components=[]): + def render(self, task, components=[], runtime=False): RENDER_TEMPLATE = read_file(RENDER_TEMPLATE_PATH) JS_DATA = read_file(JS_PATH) CSS_DATA = read_file(CSS_PATH) @@ -650,9 +771,27 @@ def render(self, task, components=[]): title=task.pathspec, css=CSS_DATA, card_data_id=uuid.uuid4(), + RENDER_COMPLETE=not runtime, ) return pt.render(RENDER_TEMPLATE, data_dict) + def render_runtime(self, task, data): + return self.render(task, runtime=True) + + def refresh(self, task, data): + return data["components"] + + def reload_content_token(self, task, data): + """ + The reload token will change when the component array has changed in the Metaflow card. + The change in the component array is signified by the change in the component_update_ts. + """ + if task.finished: + return "final" + # `component_update_ts` will never be None. It is set to a default value when the `ComponentStore` is instantiated + # And it is updated when components added / removed / changed from the `ComponentStore`. + return "runtime-%s" % (str(data["component_update_ts"])) + class TaskSpecCard(MetaflowCard): type = "taskspec_card" diff --git a/metaflow/plugins/cards/card_modules/bundle.css b/metaflow/plugins/cards/card_modules/bundle.css index 8744f2941c6..5b617f253c0 100644 --- a/metaflow/plugins/cards/card_modules/bundle.css +++ b/metaflow/plugins/cards/card_modules/bundle.css @@ -1,170 +1 @@ -.container.svelte-teyund{width:100%;display:flex;flex-direction:column;position:relative}.mf-card * { - box-sizing: border-box; -} - -.mf-card { - background: var(--bg); - color: var(--black); - font-family: "Roboto", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", - "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", - sans-serif; - font-size: 14px; - font-weight: 400; - line-height: 1.5; - text-size-adjust: 100%; - margin: 0; - min-height: 100vh; - overflow-y: visible; - padding: 0; - text-align: left; - -moz-osx-font-smoothing: grayscale; - -webkit-font-smoothing: antialiased; - width: 100%; -} - -.embed .mf-card { - min-height: var(--embed-card-min-height); -} - -.mf-card :is(.mono, code.mono, pre.mono) { - font-family: var(--mono-font); - font-weight: lighter; -} - -.mf-card :is(table, th, td) { - border-spacing: 1px; - text-align: center; - color: var(--black); -} - -.mf-card table { - position: relative; - min-width: 100%; - table-layout: inherit !important; -} - -.mf-card td { - padding: 0.66rem 1.25rem; - background: var(--lt-lt-grey); - border: none; -} - -.mf-card th { - border: none; - color: var(--dk-grey); - font-weight: normal; - padding: 0.5rem; -} - -.mf-card :is(h1, h2, h3, h4, h5) { - font-weight: 700; - margin: 0.5rem 0; -} - -.mf-card ul { - margin: 0; - padding: 0; -} - -.mf-card p { - margin: 0 0 1rem; -} - -.mf-card p:last-of-type { - margin: 0; -} - -.mf-card button { - font-size: 1rem; -} - -.mf-card .textButton { - cursor: pointer; - text-align: left; - background: none; - border: 1px solid transparent; - outline: none; - padding: 0; -} - -.mf-card :is(button.textButton:focus, a:focus, button.textButton:active) { - border: 1px dashed var(--grey); - background: transparent; -} - -.mf-card button.textButton:hover { - color: var(--blue); - text-decoration: none; -} - -.mf-card :is(:not(pre) > code[class*="language-"], pre[class*="language-"]) { - background: transparent !important; - text-shadow: none; - user-select: auto; -} -/* PrismJS 1.25.0 -https://prismjs.com/download.html#themes=prism&languages=clike+python */ -code[class*=language-],pre[class*=language-]{color:#000;background:0 0;text-shadow:0 1px #fff;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}code[class*=language-] ::-moz-selection,code[class*=language-]::-moz-selection,pre[class*=language-] ::-moz-selection,pre[class*=language-]::-moz-selection{text-shadow:none;background:#b3d4fc}code[class*=language-] ::selection,code[class*=language-]::selection,pre[class*=language-] ::selection,pre[class*=language-]::selection{text-shadow:none;background:#b3d4fc}@media print{code[class*=language-],pre[class*=language-]{text-shadow:none}}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#f5f2f0}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#708090}.token.punctuation{color:#999}.token.namespace{opacity:.7}.token.boolean,.token.constant,.token.deleted,.token.number,.token.property,.token.symbol,.token.tag{color:#905}.token.attr-name,.token.builtin,.token.char,.token.inserted,.token.selector,.token.string{color:#690}.language-css .token.string,.style .token.string,.token.entity,.token.operator,.token.url{color:#9a6e3a;background:hsla(0,0%,100%,.5)}.token.atrule,.token.attr-value,.token.keyword{color:#07a}.token.class-name,.token.function{color:#dd4a68}.token.important,.token.regex,.token.variable{color:#e90}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help} -@import url("https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap"); - -:root { - --bg: #ffffff; - --black: #333; - --blue: #0c66de; - --dk-grey: #767676; - --dk-primary: #ef863b; - --dk-secondary: #13172d; - --dk-tertiary: #0f426e; - --error: #cf483e; - --grey: rgba(0, 0, 0, 0.125); - --highlight: #f8d9d8; - --lt-blue: #4fa7ff; - --lt-grey: #f3f3f3; - --lt-lt-grey: #f9f9f9; - --lt-primary: #ffcb8b; - --lt-secondary: #434d81; - --lt-tertiary: #4189c9; - --primary: #faab4a; - --quadrary: #f8d9d8; - --secondary: #2e3454; - --tertiary: #2a679d; - --white: #ffffff; - - --component-spacer: 3rem; - --aside-width: 20rem; - --embed-card-min-height: 12rem; - - --mono-font: ui-monospace, Menlo, Monaco, "Cascadia Mono", "Segoe UI Mono", - "Roboto Mono", "Oxygen Mono", "Ubuntu Monospace", "Source Code Pro", - "Fira Mono", "Droid Sans Mono", "Courier New", monospace; -} - -html, body { - margin: 0; - min-height: 100vh; - overflow-y: visible; - padding: 0; - width: 100%; -} - -.card_app { - width: 100%; - min-height: 100vh; -} - -.embed .card_app { - min-height: var(--embed-card-min-height); -} -aside.svelte-1okdv0e{display:none;line-height:2;text-align:left}@media(min-width: 60rem){aside.svelte-1okdv0e{display:flex;flex-direction:column;height:100vh;justify-content:space-between;padding:2.5rem 0 1.5rem 1.5rem;position:fixed;width:var(--aside-width)}}.embed aside{display:none}aside ul{list-style-type:none}aside a, aside button, aside a:visited{text-decoration:none;cursor:pointer;font-weight:700;color:var(--black)}aside a:hover, aside button:hover{text-decoration:underline}.logoContainer svg{width:100%;max-width:140px;margin-bottom:3.75rem;height:auto}.mainContainer.svelte-13ho8jo{max-width:110rem}main.svelte-13ho8jo{flex:0 1 auto;max-width:100rem;padding:1.5rem}@media(min-width: 60rem){main.svelte-13ho8jo{margin-left:var(--aside-width)}}.embed main{margin:0 auto}.modal.svelte-1hhf5ym{align-items:center;background:rgba(0, 0, 0, 0.5);bottom:0;cursor:pointer;display:flex;height:100%;justify-content:center;left:0;overflow:hidden;position:fixed;right:0;top:0;width:100%;z-index:100}.modalContainer > *{background-color:white;border-radius:5px;cursor:default;flex:0 1 auto;padding:1rem;position:relative}.modal img{max-height:80vh !important}.cancelButton.svelte-1hhf5ym{color:white;cursor:pointer;font-size:2rem;position:absolute;right:1rem;top:1rem}.cancelButton.svelte-1hhf5ym:hover{color:var(--blue)}.nav.svelte-1kdpgko.svelte-1kdpgko{border-radius:0 0 5px 0;display:none;margin:0;top:0}ul.navList.svelte-1kdpgko.svelte-1kdpgko{list-style-type:none}ul.navList.svelte-1kdpgko ul.svelte-1kdpgko{margin:0.5rem 1rem 2rem}.navList.svelte-1kdpgko li.svelte-1kdpgko{display:block;margin:0}.navItem.svelte-1kdpgko li.svelte-1kdpgko:hover{color:var(--blue)}.pageId.svelte-1kdpgko.svelte-1kdpgko{display:block;border-bottom:1px solid var(--grey);padding:0 0.5rem;margin-bottom:1rem}@media(min-width: 60rem){.nav.svelte-1kdpgko.svelte-1kdpgko{display:block}ul.navList.svelte-1kdpgko.svelte-1kdpgko{text-align:left}.navList.svelte-1kdpgko li.svelte-1kdpgko{display:block;margin:0.5rem 0}}.page.svelte-v7ihqd:last-of-type{margin-bottom:var(--component-spacer)}.page:last-of-type section:last-of-type hr{display:none}.container.svelte-ubs992{width:100%;overflow:auto}table.svelte-ubs992{width:100%}.heading.svelte-17n0qr8{margin-bottom:1.5rem}.sectionItems.svelte-17n0qr8{display:block}.sectionItems .imageContainer{max-height:500px}.container.svelte-17n0qr8{scroll-margin:var(--component-spacer)}hr.svelte-17n0qr8{background:var(--grey);border:none;height:1px;margin:var(--component-spacer) 0;padding:0}@media(min-width: 60rem){.sectionItems.svelte-17n0qr8{display:grid;grid-gap:2rem}}header.svelte-1ugmt5d{margin-bottom:var(--component-spacer)}.subtitle.svelte-lu9pnn{font-size:1rem;text-align:left}.log.svelte-1jhmsu{background:var(--lt-grey) !important;font-size:0.9rem;padding:2rem}.title.svelte-117s0ws{text-align:left}figure.svelte-1x96yvr{background:var(--lt-grey);padding:1rem;border-radius:5px;text-align:center;margin:0 auto var(--component-spacer)}@media(min-width: 60rem){figure.svelte-1x96yvr{margin-bottom:0}}img.svelte-1x96yvr{max-width:100%;max-height:500px}.label.svelte-1x96yvr{font-weight:bold;margin:0.5rem 0}.description.svelte-1x96yvr{font-size:0.9rem;font-style:italic;text-align:center;margin:0.5rem 0}.idCell.svelte-pt8vzv{font-weight:bold;text-align:right;background:var(--lt-grey);width:12%}.codeCell.svelte-pt8vzv{text-align:left;user-select:all}td.svelte-gl9h79{text-align:left}td.labelColumn.svelte-gl9h79{text-align:right;background-color:var(--lt-grey);font-weight:700;width:12%;white-space:nowrap}.tableContainer.svelte-q3hq57{overflow:auto}th.svelte-q3hq57{position:sticky;top:-1px;z-index:2;white-space:nowrap;background:var(--white)}.stepwrapper.svelte-18aex7a{display:flex;align-items:center;flex-direction:column;width:100%;position:relative;min-width:var(--dag-step-width)}.childwrapper.svelte-18aex7a{display:flex;width:100%}.gap.svelte-18aex7a{height:var(--dag-gap)}:root { - --dag-border: #282828; - --dag-bg-static: var(--lt-grey); - --dag-bg-success: #a5d46a; - --dag-bg-running: #ffdf80; - --dag-bg-error: #ffa080; - --dag-connector: #cccccc; - --dag-gap: 5rem; - --dag-step-height: 6.25rem; - --dag-step-width: 11.25rem; - --dag-selected: #ffd700; -} -.wrapper.svelte-117ceti.svelte-117ceti{position:relative;z-index:1}.step.svelte-117ceti.svelte-117ceti{font-size:0.75rem;padding:0.5rem;color:var(--dk-grey)}.rectangle.svelte-117ceti.svelte-117ceti{background-color:var(--dag-bg-static);border:1px solid var(--dag-border);box-sizing:border-box;position:relative;height:var(--dag-step-height);width:var(--dag-step-width)}.rectangle.error.svelte-117ceti.svelte-117ceti{background-color:var(--dag-bg-error)}.rectangle.success.svelte-117ceti.svelte-117ceti{background-color:var(--dag-bg-success)}.rectangle.running.svelte-117ceti.svelte-117ceti{background-color:var(--dag-bg-running)}.level.svelte-117ceti.svelte-117ceti{z-index:-1;filter:contrast(0.5);position:absolute}.inner.svelte-117ceti.svelte-117ceti{position:relative;height:100%;width:100%}.name.svelte-117ceti.svelte-117ceti{font-weight:bold;overflow:hidden;text-overflow:ellipsis;display:block}.description.svelte-117ceti.svelte-117ceti{position:absolute;max-height:4rem;bottom:0;left:0;right:0;overflow:hidden;-webkit-line-clamp:4;line-clamp:4;display:-webkit-box;-webkit-box-orient:vertical}.overflown.description.svelte-117ceti.svelte-117ceti{cursor:help}.current.svelte-117ceti .rectangle.svelte-117ceti{box-shadow:0 0 10px var(--dag-selected)}.levelstoshow.svelte-117ceti.svelte-117ceti{position:absolute;bottom:100%;right:0;font-size:0.75rem;font-weight:100;text-align:right}.connectorwrapper.svelte-1hyaq5f{transform-origin:0 0;position:absolute;z-index:0;min-width:var(--strokeWidth)}.flip.svelte-1hyaq5f{transform:scaleX(-1)}.path.svelte-1hyaq5f{--strokeWidth:0.5rem;--strokeColor:var(--dag-connector);--borderRadius:1.25rem;box-sizing:border-box}.straightLine.svelte-1hyaq5f{position:absolute;top:0;bottom:0;left:0;right:0;border-left:var(--strokeWidth) solid var(--strokeColor)}.topLeft.svelte-1hyaq5f{position:absolute;top:0;left:0;right:50%;bottom:calc(var(--dag-gap) / 2 - var(--strokeWidth) / 2);border-radius:0 0 0 var(--borderRadius);border-left:var(--strokeWidth) solid var(--strokeColor);border-bottom:var(--strokeWidth) solid var(--strokeColor)}.bottomRight.svelte-1hyaq5f{position:absolute;top:calc(100% - (var(--dag-gap) / 2 + var(--strokeWidth) / 2));left:50%;right:0;bottom:0;border-radius:0 var(--borderRadius) 0 0;border-top:var(--strokeWidth) solid var(--strokeColor);border-right:var(--strokeWidth) solid var(--strokeColor)} \ No newline at end of file +@import"https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap";code[class*=language-],pre[class*=language-]{color:#000;background:0 0;text-shadow:0 1px #fff;font-family:Consolas,Monaco,Andale Mono,Ubuntu Mono,monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}code[class*=language-] ::-moz-selection,code[class*=language-]::-moz-selection,pre[class*=language-] ::-moz-selection,pre[class*=language-]::-moz-selection{text-shadow:none;background:#b3d4fc}code[class*=language-] ::selection,code[class*=language-]::selection,pre[class*=language-] ::selection,pre[class*=language-]::selection{text-shadow:none;background:#b3d4fc}@media print{code[class*=language-],pre[class*=language-]{text-shadow:none}}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#f5f2f0}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#708090}.token.punctuation{color:#999}.token.namespace{opacity:.7}.token.boolean,.token.constant,.token.deleted,.token.number,.token.property,.token.symbol,.token.tag{color:#905}.token.attr-name,.token.builtin,.token.char,.token.inserted,.token.selector,.token.string{color:#690}.language-css .token.string,.style .token.string,.token.entity,.token.operator,.token.url{color:#9a6e3a;background:#ffffff80}.token.atrule,.token.attr-value,.token.keyword{color:#07a}.token.class-name,.token.function{color:#dd4a68}.token.important,.token.regex,.token.variable{color:#e90}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}:root{--bg: #ffffff;--black: #333;--blue: #0c66de;--dk-grey: #767676;--dk-primary: #ef863b;--dk-secondary: #13172d;--dk-tertiary: #0f426e;--error: #cf483e;--grey: rgba(0, 0, 0, .125);--highlight: #f8d9d8;--lt-blue: #4fa7ff;--lt-grey: #f3f3f3;--lt-lt-grey: #f9f9f9;--lt-primary: #ffcb8b;--lt-secondary: #434d81;--lt-tertiary: #4189c9;--primary: #faab4a;--quadrary: #f8d9d8;--secondary: #2e3454;--tertiary: #2a679d;--white: #ffffff;--component-spacer: 3rem;--aside-width: 20rem;--embed-card-min-height: 12rem;--mono-font: ui-monospace, Menlo, Monaco, "Cascadia Mono", "Segoe UI Mono", "Roboto Mono", "Oxygen Mono", "Ubuntu Monospace", "Source Code Pro", "Fira Mono", "Droid Sans Mono", "Courier New", monospace}html,body{margin:0;min-height:100vh;overflow-y:visible;padding:0;width:100%}.card_app{width:100%;min-height:100vh}.embed .card_app{min-height:var(--embed-card-min-height)}.mf-card *{box-sizing:border-box}.mf-card{background:var(--bg);color:var(--black);font-family:Roboto,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;font-size:14px;font-weight:400;line-height:1.5;text-size-adjust:100%;margin:0;min-height:100vh;overflow-y:visible;padding:0;text-align:left;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;width:100%}.embed .mf-card{min-height:var(--embed-card-min-height)}.mf-card :is(.mono,code.mono,pre.mono){font-family:var(--mono-font);font-weight:lighter}.mf-card :is(table,th,td){border-spacing:1px;text-align:center;color:var(--black)}.mf-card table{position:relative;min-width:100%;table-layout:inherit!important}.mf-card td{padding:.66rem 1.25rem;background:var(--lt-lt-grey);border:none}.mf-card th{border:none;color:var(--dk-grey);font-weight:400;padding:.5rem}.mf-card :is(h1,h2,h3,h4,h5){font-weight:700;margin:.5rem 0}.mf-card ul{margin:0;padding:0}.mf-card p{margin:0 0 1rem}.mf-card p:last-of-type{margin:0}.mf-card button{font-size:1rem}.mf-card .textButton{cursor:pointer;text-align:left;background:none;border:1px solid transparent;outline:none;padding:0}.mf-card :is(button.textButton:focus,a:focus,button.textButton:active){border:1px dashed var(--grey);background:transparent}.mf-card button.textButton:hover{color:var(--blue);text-decoration:none}.mf-card :is(:not(pre)>code[class*=language-],pre[class*=language-]){background:transparent!important;text-shadow:none;-webkit-user-select:auto;user-select:auto}aside.svelte-1okdv0e{display:none;line-height:2;text-align:left}@media (min-width: 60rem){aside.svelte-1okdv0e{display:flex;flex-direction:column;height:100vh;justify-content:space-between;padding:2.5rem 0 1.5rem 1.5rem;position:fixed;width:var(--aside-width)}}.embed aside{display:none}aside ul{list-style-type:none}aside a,aside button,aside a:visited{text-decoration:none;cursor:pointer;font-weight:700;color:var(--black)}aside a:hover,aside button:hover{text-decoration:underline}.logoContainer svg{width:100%;max-width:140px;margin-bottom:3.75rem;height:auto}.idCell.svelte-pt8vzv{font-weight:700;text-align:right;background:var(--lt-grey);width:12%}.codeCell.svelte-pt8vzv{text-align:left;-webkit-user-select:all;user-select:all}.container.svelte-ubs992{width:100%;overflow:auto}table.svelte-ubs992{width:100%}:root{--dag-border: #282828;--dag-bg-static: var(--lt-grey);--dag-bg-success: #a5d46a;--dag-bg-running: #ffdf80;--dag-bg-error: #ffa080;--dag-connector: #cccccc;--dag-gap: 5rem;--dag-step-height: 6.25rem;--dag-step-width: 11.25rem;--dag-selected: #ffd700}.connectorwrapper.svelte-1hyaq5f{transform-origin:0 0;position:absolute;z-index:0;min-width:var(--strokeWidth)}.flip.svelte-1hyaq5f{transform:scaleX(-1)}.path.svelte-1hyaq5f{--strokeWidth:.5rem;--strokeColor:var(--dag-connector);--borderRadius:1.25rem;box-sizing:border-box}.straightLine.svelte-1hyaq5f{position:absolute;top:0;bottom:0;left:0;right:0;border-left:var(--strokeWidth) solid var(--strokeColor)}.topLeft.svelte-1hyaq5f{position:absolute;top:0;left:0;right:50%;bottom:calc(var(--dag-gap) / 2 - var(--strokeWidth) / 2);border-radius:0 0 0 var(--borderRadius);border-left:var(--strokeWidth) solid var(--strokeColor);border-bottom:var(--strokeWidth) solid var(--strokeColor)}.bottomRight.svelte-1hyaq5f{position:absolute;top:calc(100% - (var(--dag-gap) / 2 + var(--strokeWidth) / 2));left:50%;right:0;bottom:0;border-radius:0 var(--borderRadius) 0 0;border-top:var(--strokeWidth) solid var(--strokeColor);border-right:var(--strokeWidth) solid var(--strokeColor)}.wrapper.svelte-117ceti.svelte-117ceti{position:relative;z-index:1}.step.svelte-117ceti.svelte-117ceti{font-size:.75rem;padding:.5rem;color:var(--dk-grey)}.rectangle.svelte-117ceti.svelte-117ceti{background-color:var(--dag-bg-static);border:1px solid var(--dag-border);box-sizing:border-box;position:relative;height:var(--dag-step-height);width:var(--dag-step-width)}.rectangle.error.svelte-117ceti.svelte-117ceti{background-color:var(--dag-bg-error)}.rectangle.success.svelte-117ceti.svelte-117ceti{background-color:var(--dag-bg-success)}.rectangle.running.svelte-117ceti.svelte-117ceti{background-color:var(--dag-bg-running)}.level.svelte-117ceti.svelte-117ceti{z-index:-1;filter:contrast(.5);position:absolute}.inner.svelte-117ceti.svelte-117ceti{position:relative;height:100%;width:100%}.name.svelte-117ceti.svelte-117ceti{font-weight:700;overflow:hidden;text-overflow:ellipsis;display:block}.description.svelte-117ceti.svelte-117ceti{position:absolute;max-height:4rem;bottom:0;left:0;right:0;overflow:hidden;-webkit-line-clamp:4;line-clamp:4;display:-webkit-box;-webkit-box-orient:vertical}.overflown.description.svelte-117ceti.svelte-117ceti{cursor:help}.current.svelte-117ceti .rectangle.svelte-117ceti{box-shadow:0 0 10px var(--dag-selected)}.levelstoshow.svelte-117ceti.svelte-117ceti{position:absolute;bottom:100%;right:0;font-size:.75rem;font-weight:100;text-align:right}.stepwrapper.svelte-18aex7a{display:flex;align-items:center;flex-direction:column;width:100%;position:relative;min-width:var(--dag-step-width)}.childwrapper.svelte-18aex7a{display:flex;width:100%}.gap.svelte-18aex7a{height:var(--dag-gap)}.title.svelte-117s0ws{text-align:left}.subtitle.svelte-lu9pnn{font-size:1rem;text-align:left}header.svelte-1ugmt5d{margin-bottom:var(--component-spacer)}figure.svelte-1x96yvr{background:var(--lt-grey);padding:1rem;border-radius:5px;text-align:center;margin:0 auto var(--component-spacer)}@media (min-width: 60rem){figure.svelte-1x96yvr{margin-bottom:0}}img.svelte-1x96yvr{max-width:100%;max-height:500px}.label.svelte-1x96yvr{font-weight:700;margin:.5rem 0}.description.svelte-1x96yvr{font-size:.9rem;font-style:italic;text-align:center;margin:.5rem 0}.log.svelte-1jhmsu{background:var(--lt-grey)!important;font-size:.9rem;padding:2rem}.page.svelte-v7ihqd:last-of-type{margin-bottom:var(--component-spacer)}.page:last-of-type section:last-of-type hr{display:none}progress.svelte-ljrmzp::-webkit-progress-bar{background-color:#fff!important;min-width:100%}progress.svelte-ljrmzp{background-color:#fff;color:#326cded9!important}progress.svelte-ljrmzp::-moz-progress-bar{background-color:#326cde!important}table .container{background:transparent!important;font-size:10px!important;padding:0!important}table progress{height:4px!important}.container.svelte-ljrmzp{display:flex;align-items:center;justify-content:center;font-size:12px;border-radius:3px;background:#edf5ff;padding:3rem}.inner.svelte-ljrmzp{max-width:410px;width:100%;text-align:center}.info.svelte-ljrmzp{display:flex;justify-content:space-between}table .info{text-align:left;flex-direction:column}label.svelte-ljrmzp{font-weight:700}.labelValue.svelte-ljrmzp{border-left:1px solid rgba(0,0,0,.1);margin-left:.25rem;padding-left:.5rem}.details.svelte-ljrmzp{font-family:var(--mono-font);font-size:8px;color:#333433;line-height:18px;overflow:hidden;white-space:nowrap}progress.svelte-ljrmzp{width:100%;border:none;border-radius:5px;height:8px;background:#fff}.heading.svelte-17n0qr8{margin-bottom:1.5rem}.sectionItems.svelte-17n0qr8{display:block}.sectionItems .imageContainer{max-height:500px}.container.svelte-17n0qr8{scroll-margin:var(--component-spacer)}hr.svelte-17n0qr8{background:var(--grey);border:none;height:1px;margin:var(--component-spacer) 0;padding:0}@media (min-width: 60rem){.sectionItems.svelte-17n0qr8{display:grid;grid-gap:2rem}}td.svelte-gl9h79{text-align:left}td.labelColumn.svelte-gl9h79{text-align:right;background-color:var(--lt-grey);font-weight:700;width:12%;white-space:nowrap}.tableContainer.svelte-q3hq57{overflow:auto}th.svelte-q3hq57{position:sticky;top:-1px;z-index:2;white-space:nowrap;background:var(--white)}.mainContainer.svelte-mqeomk{max-width:110rem}main.svelte-mqeomk{flex:0 1 auto;max-width:100rem;padding:1.5rem}@media (min-width: 60rem){main.svelte-mqeomk{margin-left:var(--aside-width)}}.embed main{margin:0 auto;min-width:80%}.modal.svelte-1hhf5ym{align-items:center;background:#00000080;bottom:0;cursor:pointer;display:flex;height:100%;justify-content:center;left:0;overflow:hidden;position:fixed;right:0;top:0;width:100%;z-index:100}.modalContainer>*{background-color:#fff;border-radius:5px;cursor:default;flex:0 1 auto;padding:1rem;position:relative}.modal img{max-height:80vh!important}.cancelButton.svelte-1hhf5ym{color:#fff;cursor:pointer;font-size:2rem;position:absolute;right:1rem;top:1rem}.cancelButton.svelte-1hhf5ym:hover{color:var(--blue)}.nav.svelte-1kdpgko.svelte-1kdpgko{border-radius:0 0 5px;display:none;margin:0;top:0}ul.navList.svelte-1kdpgko.svelte-1kdpgko{list-style-type:none}ul.navList.svelte-1kdpgko ul.svelte-1kdpgko{margin:.5rem 1rem 2rem}.navList.svelte-1kdpgko li.svelte-1kdpgko{display:block;margin:0}.navItem.svelte-1kdpgko li.svelte-1kdpgko:hover{color:var(--blue)}.pageId.svelte-1kdpgko.svelte-1kdpgko{display:block;border-bottom:1px solid var(--grey);padding:0 .5rem;margin-bottom:1rem}@media (min-width: 60rem){.nav.svelte-1kdpgko.svelte-1kdpgko{display:block}ul.navList.svelte-1kdpgko.svelte-1kdpgko{text-align:left}.navList.svelte-1kdpgko li.svelte-1kdpgko{display:block;margin:.5rem 0}}.container.svelte-teyund{width:100%;display:flex;flex-direction:column;position:relative} diff --git a/metaflow/plugins/cards/card_modules/card.py b/metaflow/plugins/cards/card_modules/card.py index f0d8d7d5706..2f9d257e0c1 100644 --- a/metaflow/plugins/cards/card_modules/card.py +++ b/metaflow/plugins/cards/card_modules/card.py @@ -1,7 +1,8 @@ from typing import TYPE_CHECKING +import uuid if TYPE_CHECKING: - from metaflow.client import Task + import metaflow class MetaflowCard(object): @@ -32,13 +33,41 @@ class MetaflowCard(object): JSON-encodable dictionary containing user-definable options for the class. """ + # RELOAD_POLICY determines whether UIs should + # reload intermediate cards produced by render_runtime + # or whether they can just rely on data updates + + # the UI may keep using the same card + # until the final card is produced + RELOAD_POLICY_NEVER = "never" + + # the UI should reload card every time + # render_runtime() has produced a new card + RELOAD_POLICY_ALWAYS = "always" + + # derive reload token from data and component + # content - force reload only when the content + # changes. The actual policy is card-specific, + # defined by the method reload_content_token() + RELOAD_POLICY_ONCHANGE = "onchange" + + # this token will get replaced in the html with a unique + # string that is used to ensure that data updates and the + # card content matches + RELOAD_POLICY_TOKEN = "[METAFLOW_RELOAD_TOKEN]" + type = None ALLOW_USER_COMPONENTS = False + RUNTIME_UPDATABLE = False + RELOAD_POLICY = RELOAD_POLICY_NEVER scope = "task" # can be task | run - def __init__(self, options={}, components=[], graph=None): + # FIXME document runtime_data + runtime_data = None + + def __init__(self, options={}, components=[], graph=None, flow=None): pass def _get_mustache(self): @@ -49,7 +78,7 @@ def _get_mustache(self): except ImportError: return None - def render(self, task: "Task") -> str: + def render(self, task: "metaflow.Task") -> str: """ Produce custom card contents in HTML. @@ -68,10 +97,61 @@ def render(self, task: "Task") -> str: """ return NotImplementedError() + # FIXME document + def render_runtime(self, task, data): + raise NotImplementedError() + + # FIXME document + def refresh(self, task, data): + raise NotImplementedError() + + # FIXME document + def reload_content_token(self, task, data): + return "content-token" + class MetaflowCardComponent(object): + + # Setting REALTIME_UPDATABLE as True will allow metaflow to update the card + # during Task runtime. + REALTIME_UPDATABLE = False + + _component_id = None + + _logger = None + + @property + def component_id(self): + return self._component_id + + @component_id.setter + def component_id(self, value): + if not isinstance(value, str): + raise TypeError("Component ID must be a string") + self._component_id = value + + def update(self, *args, **kwargs): + """ + #FIXME document + """ + raise NotImplementedError() + def render(self): """ `render` returns a string or dictionary. This class can be called on the client side to dynamically add components to the `MetaflowCard` """ raise NotImplementedError() + + +def create_component_id(component): + uuid_bit = "".join(uuid.uuid4().hex.split("-"))[:6] + return type(component).__name__.lower() + "_" + uuid_bit + + +def with_default_component_id(func): + def ret_func(self, *args, **kwargs): + if self.component_id is None: + self.component_id = create_component_id(self) + return func(self, *args, **kwargs) + + return ret_func diff --git a/metaflow/plugins/cards/card_modules/chevron/renderer.py b/metaflow/plugins/cards/card_modules/chevron/renderer.py index 28368cd7ee1..acb3a179a97 100644 --- a/metaflow/plugins/cards/card_modules/chevron/renderer.py +++ b/metaflow/plugins/cards/card_modules/chevron/renderer.py @@ -61,7 +61,7 @@ def _get_key(key, scopes, warn, keep, def_ldel, def_rdel): # Loop through the scopes for scope in scopes: try: - # For every dot seperated key + # For every dot separated key for child in key.split("."): # Move into the scope try: diff --git a/metaflow/plugins/cards/card_modules/components.py b/metaflow/plugins/cards/card_modules/components.py index 53ec9c417c3..65685cc889a 100644 --- a/metaflow/plugins/cards/card_modules/components.py +++ b/metaflow/plugins/cards/card_modules/components.py @@ -1,4 +1,4 @@ -from typing import Any, List, Optional, Union +from typing import Any, List, Optional, Union, Callable from .basic import ( LogComponent, ErrorComponent, @@ -7,14 +7,45 @@ ImageComponent, SectionComponent, MarkdownComponent, + PythonCodeComponent, ) -from .card import MetaflowCardComponent +from .card import MetaflowCardComponent, with_default_component_id from .convert_to_native_type import TaskToDict, _full_classname from .renderer_tools import render_safely +import uuid +import inspect + + +def _warning_with_component(component, msg): + if component._logger is None: + return None + if component._warned_once: + return None + log_msg = "[@card-component WARNING] %s" % msg + component._logger(log_msg, timestamp=False, bad=True) + component._warned_once = True class UserComponent(MetaflowCardComponent): - pass + + _warned_once = False + + def update(self, *args, **kwargs): + cls_name = self.__class__.__name__ + msg = ( + "MetaflowCardComponent doesn't have an `update` method implemented " + "and is not compatible with realtime updates." + ) % cls_name + _warning_with_component(self, msg) + + +class StubComponent(UserComponent): + def __init__(self, component_id): + self._non_existing_comp_id = component_id + + def update(self, *args, **kwargs): + msg = "Component with id %s doesn't exist. No updates will be made at anytime during runtime." + _warning_with_component(self, msg % self._non_existing_comp_id) class Artifact(UserComponent): @@ -39,6 +70,11 @@ class Artifact(UserComponent): Use a truncated representation. """ + REALTIME_UPDATABLE = True + + def update(self, artifact): + self._artifact = artifact + def __init__( self, artifact: Any, name: Optional[str] = None, compressed: bool = True ): @@ -46,13 +82,16 @@ def __init__( self._name = name self._task_to_dict = TaskToDict(only_repr=compressed) + @with_default_component_id @render_safely def render(self): artifact = self._task_to_dict.infer_object(self._artifact) artifact["name"] = None if self._name is not None: artifact["name"] = str(self._name) - return ArtifactsComponent(data=[artifact]).render() + af_component = ArtifactsComponent(data=[artifact]) + af_component.component_id = self.component_id + return af_component.render() class Table(UserComponent): @@ -96,10 +135,21 @@ class Table(UserComponent): Optional header row for the table. """ + REALTIME_UPDATABLE = True + + def update(self, *args, **kwargs): + msg = ( + "`Table` doesn't have an `update` method implemented. " + "Components within a table can be updated individually " + "but the table itself cannot be updated." + ) + _warning_with_component(self, msg) + def __init__( self, data: Optional[List[List[Union[str, MetaflowCardComponent]]]] = None, headers: Optional[List[str]] = None, + disable_updates: bool = False, ): data = data or [[]] headers = headers or [] @@ -111,8 +161,15 @@ def __init__( if data_bool: self._data = data + if disable_updates: + self.REALTIME_UPDATABLE = False + @classmethod - def from_dataframe(cls, dataframe=None, truncate: bool = True): + def from_dataframe( + cls, + dataframe=None, + truncate: bool = True, + ): """ Create a `Table` based on a Pandas dataframe. @@ -129,14 +186,24 @@ def from_dataframe(cls, dataframe=None, truncate: bool = True): table_data = task_to_dict._parse_pandas_dataframe( dataframe, truncate=truncate ) - return_val = cls(data=table_data["data"], headers=table_data["headers"]) + return_val = cls( + data=table_data["data"], + headers=table_data["headers"], + disable_updates=True, + ) return return_val else: return cls( headers=["Object type %s not supported" % object_type], + disable_updates=True, ) def _render_subcomponents(self): + for row in self._data: + for col in row: + if isinstance(col, VegaChart): + col._chart_inside_table = True + return [ SectionComponent.render_subcomponents( row, @@ -155,11 +222,14 @@ def _render_subcomponents(self): for row in self._data ] + @with_default_component_id @render_safely def render(self): - return TableComponent( + table_component = TableComponent( headers=self._headers, data=self._render_subcomponents() - ).render() + ) + table_component.component_id = self.component_id + return table_component.render() class Image(UserComponent): @@ -213,21 +283,62 @@ class Image(UserComponent): Optional label for the image. """ + REALTIME_UPDATABLE = True + + _PIL_IMAGE_MODULE_PATH = "PIL.Image.Image" + + _MATPLOTLIB_FIGURE_MODULE_PATH = "matplotlib.figure.Figure" + + _PLT_MODULE = None + + _PIL_MODULE = None + + @classmethod + def _get_pil_module(cls): + if cls._PIL_MODULE == "NOT_PRESENT": + return None + if cls._PIL_MODULE is None: + try: + import PIL + except ImportError: + cls._PIL_MODULE = "NOT_PRESENT" + return None + cls._PIL_MODULE = PIL + return cls._PIL_MODULE + + @classmethod + def _get_plt_module(cls): + if cls._PLT_MODULE == "NOT_PRESENT": + return None + if cls._PLT_MODULE is None: + try: + import matplotlib.pyplot as pyplt + except ImportError: + cls._PLT_MODULE = "NOT_PRESENT" + return None + cls._PLT_MODULE = pyplt + return cls._PLT_MODULE + @staticmethod def render_fail_headline(msg): return "[IMAGE_RENDER FAIL]: %s" % msg - def __init__(self, src=None, label=None): - self._error_comp = None + def _set_image_src(self, src, label=None): self._label = label - - if type(src) is not str: + self._src = None + self._error_comp = None + if src is None: + self._error_comp = ErrorComponent( + self.render_fail_headline("`Image` Component `src` cannot be `None`"), + "", + ) + elif type(src) is not str: try: self._src = self._bytes_to_base64(src) except TypeError: self._error_comp = ErrorComponent( self.render_fail_headline( - "first argument should be of type `bytes` or valid image base64 string" + "The `Image` `src` argument should be of type `bytes` or valid image base64 string" ), "Type of %s is invalid" % (str(type(src))), ) @@ -248,11 +359,141 @@ def __init__(self, src=None, label=None): else: self._error_comp = ErrorComponent( self.render_fail_headline( - "first argument should be of type `bytes` or valid image base64 string" + "The `Image` `src` argument should be of type `bytes` or valid image base64 string" ), "String %s is invalid base64 string" % src, ) + def __init__(self, src=None, label=None, disable_updates: bool = True): + if disable_updates: + self.REALTIME_UPDATABLE = False + self._set_image_src(src, label=label) + + def _update_image(self, img_obj, label=None): + task_to_dict = TaskToDict() + parsed_image, err_comp = None, None + + # First set image for bytes/string type + if task_to_dict.object_type(img_obj) in ["bytes", "str"]: + self._set_image_src(img_obj, label=label) + return + + if task_to_dict.object_type(img_obj).startswith("PIL"): + parsed_image, err_comp = self._parse_pil_image(img_obj) + elif _full_classname(img_obj) == self._MATPLOTLIB_FIGURE_MODULE_PATH: + parsed_image, err_comp = self._parse_matplotlib(img_obj) + else: + parsed_image, err_comp = None, ErrorComponent( + self.render_fail_headline( + "Invalid Type. Object %s is not supported. Supported types: %s" + % ( + type(img_obj), + ", ".join( + [ + "str", + "bytes", + self._PIL_IMAGE_MODULE_PATH, + self._MATPLOTLIB_FIGURE_MODULE_PATH, + ] + ), + ) + ), + "", + ) + + if parsed_image is not None: + self._set_image_src(parsed_image, label=label) + else: + self._set_image_src(None, label=label) + self._error_comp = err_comp + + @classmethod + def _pil_parsing_error(cls, error_type): + return None, ErrorComponent( + cls.render_fail_headline( + "first argument for `Image` should be of type %s" + % cls._PIL_IMAGE_MODULE_PATH + ), + "Type of %s is invalid. Type of %s required" + % (error_type, cls._PIL_IMAGE_MODULE_PATH), + ) + + @classmethod + def _parse_pil_image(cls, pilimage): + parsed_value = None + error_component = None + import io + + task_to_dict = TaskToDict() + _img_type = task_to_dict.object_type(pilimage) + + if not _img_type.startswith("PIL"): + return cls._pil_parsing_error(_img_type) + + # Set the module as a part of the class so that + # we don't keep reloading the module everytime + pil_module = cls._get_pil_module() + + if pil_module is None: + return parsed_value, ErrorComponent( + cls.render_fail_headline("PIL cannot be imported"), "" + ) + if not isinstance(pilimage, pil_module.Image.Image): + return cls._pil_parsing_error(_img_type) + + img_byte_arr = io.BytesIO() + try: + pilimage.save(img_byte_arr, format="PNG") + except OSError as e: + return parsed_value, ErrorComponent( + cls.render_fail_headline("PIL Image Not Parsable"), "%s" % repr(e) + ) + img_byte_arr = img_byte_arr.getvalue() + parsed_value = task_to_dict.parse_image(img_byte_arr) + return parsed_value, error_component + + @classmethod + def _parse_matplotlib(cls, plot): + import io + import traceback + + parsed_value = None + error_component = None + pyplt = cls._get_plt_module() + if pyplt is None: + return parsed_value, ErrorComponent( + cls.render_fail_headline("Matplotlib cannot be imported"), + "%s" % traceback.format_exc(), + ) + # First check if it is a valid Matplotlib figure. + figure = None + if _full_classname(plot) == cls._MATPLOTLIB_FIGURE_MODULE_PATH: + figure = plot + + # If it is not valid figure then check if it is matplotlib.axes.Axes or a matplotlib.axes._subplots.AxesSubplot + # These contain the `get_figure` function to get the main figure object. + if figure is None: + if getattr(plot, "get_figure", None) is None: + return parsed_value, ErrorComponent( + cls.render_fail_headline( + "Invalid Type. Object %s is not from `matplotlib`" % type(plot) + ), + "", + ) + else: + figure = plot.get_figure() + + task_to_dict = TaskToDict() + img_bytes_arr = io.BytesIO() + figure.savefig(img_bytes_arr, format="PNG") + parsed_value = task_to_dict.parse_image(img_bytes_arr.getvalue()) + pyplt.close(figure) + if parsed_value is not None: + return parsed_value, error_component + return parsed_value, ErrorComponent( + cls.render_fail_headline("Matplotlib plot's image is not parsable"), "" + ) + @staticmethod def _bytes_to_base64(bytes_arr): task_to_dict = TaskToDict() @@ -264,7 +505,9 @@ def _bytes_to_base64(bytes_arr): return parsed_image @classmethod - def from_pil_image(cls, pilimage, label: Optional[str] = None): + def from_pil_image( + cls, pilimage, label: Optional[str] = None, disable_updates: bool = False + ): """ Create an `Image` from a PIL image. @@ -276,43 +519,29 @@ def from_pil_image(cls, pilimage, label: Optional[str] = None): Optional label for the image. """ try: - import io - - PIL_IMAGE_PATH = "PIL.Image.Image" - task_to_dict = TaskToDict() - if task_to_dict.object_type(pilimage) != PIL_IMAGE_PATH: - return ErrorComponent( - cls.render_fail_headline( - "first argument for `Image` should be of type %s" - % PIL_IMAGE_PATH - ), - "Type of %s is invalid. Type of %s required" - % (task_to_dict.object_type(pilimage), PIL_IMAGE_PATH), - ) - img_byte_arr = io.BytesIO() - try: - pilimage.save(img_byte_arr, format="PNG") - except OSError as e: - return ErrorComponent( - cls.render_fail_headline("PIL Image Not Parsable"), "%s" % repr(e) - ) - img_byte_arr = img_byte_arr.getvalue() - parsed_image = task_to_dict.parse_image(img_byte_arr) + parsed_image, error_comp = cls._parse_pil_image(pilimage) if parsed_image is not None: - return cls(src=parsed_image, label=label) - return ErrorComponent( - cls.render_fail_headline("PIL Image Not Parsable"), "" - ) + img = cls( + src=parsed_image, label=label, disable_updates=disable_updates + ) + else: + img = cls(src=None, label=label, disable_updates=disable_updates) + img._error_comp = error_comp + return img except: import traceback - return ErrorComponent( + img = cls(src=None, label=label, disable_updates=disable_updates) + img._error_comp = ErrorComponent( cls.render_fail_headline("PIL Image Not Parsable"), "%s" % traceback.format_exc(), ) + return img @classmethod - def from_matplotlib(cls, plot, label: Optional[str] = None): + def from_matplotlib( + cls, plot, label: Optional[str] = None, disable_updates: bool = False + ): """ Create an `Image` from a Matplotlib plot. @@ -323,64 +552,62 @@ def from_matplotlib(cls, plot, label: Optional[str] = None): label : str, optional Optional label for the image. """ - import io - try: - try: - import matplotlib.pyplot as pyplt - except ImportError: - return ErrorComponent( - cls.render_fail_headline("Matplotlib cannot be imported"), - "%s" % traceback.format_exc(), - ) - # First check if it is a valid Matplotlib figure. - figure = None - if _full_classname(plot) == "matplotlib.figure.Figure": - figure = plot - - # If it is not valid figure then check if it is matplotlib.axes.Axes or a matplotlib.axes._subplots.AxesSubplot - # These contain the `get_figure` function to get the main figure object. - if figure is None: - if getattr(plot, "get_figure", None) is None: - return ErrorComponent( - cls.render_fail_headline( - "Invalid Type. Object %s is not from `matplotlib`" - % type(plot) - ), - "", - ) - else: - figure = plot.get_figure() - - task_to_dict = TaskToDict() - img_bytes_arr = io.BytesIO() - figure.savefig(img_bytes_arr, format="PNG") - parsed_image = task_to_dict.parse_image(img_bytes_arr.getvalue()) - pyplt.close(figure) + parsed_image, error_comp = cls._parse_matplotlib(plot) if parsed_image is not None: - return cls(src=parsed_image, label=label) - return ErrorComponent( - cls.render_fail_headline("Matplotlib plot's image is not parsable"), "" - ) + img = cls( + src=parsed_image, label=label, disable_updates=disable_updates + ) + else: + img = cls(src=None, label=label, disable_updates=disable_updates) + img._error_comp = error_comp + return img except: import traceback - return ErrorComponent( + img = cls(src=None, label=label, disable_updates=disable_updates) + img._error_comp = ErrorComponent( cls.render_fail_headline("Matplotlib plot's image is not parsable"), "%s" % traceback.format_exc(), ) + return img + @with_default_component_id @render_safely def render(self): if self._error_comp is not None: return self._error_comp.render() if self._src is not None: - return ImageComponent(src=self._src, label=self._label).render() + img_comp = ImageComponent(src=self._src, label=self._label) + img_comp.component_id = self.component_id + return img_comp.render() return ErrorComponent( self.render_fail_headline("`Image` Component `src` argument is `None`"), "" ).render() + def update(self, image, label=None): + """ + Update the image. + + Parameters + ---------- + image : PIL.Image or matplotlib.figure.Figure or matplotlib.axes.Axes or matplotlib.axes._subplots.AxesSubplot or bytes or str + The updated image object + label : str, optional + Optional label for the image. + """ + if not self.REALTIME_UPDATABLE: + msg = ( + "The `Image` component is disabled for realtime updates. " + "Please set `disable_updates` to `False` while creating the `Image` object." + ) + _warning_with_component(self, msg) + return + + _label = label if label is not None else self._label + self._update_image(image, label=_label) + class Error(UserComponent): """ @@ -434,9 +661,213 @@ class Markdown(UserComponent): Text formatted in Markdown. """ + REALTIME_UPDATABLE = True + + def update(self, text=None): + self._text = text + def __init__(self, text=None): self._text = text + @with_default_component_id @render_safely def render(self): - return MarkdownComponent(self._text).render() + comp = MarkdownComponent(self._text) + comp.component_id = self.component_id + return comp.render() + + +class ProgressBar(UserComponent): + """ + A Progress bar for tracking progress of any task. + + Example: + ``` + progress_bar = ProgressBar( + max=100, + label="Progress Bar", + value=0, + unit="%", + metadata="0.1 items/s" + ) + current.card.append( + progress_bar + ) + for i in range(100): + progress_bar.update(i, metadata="%s items/s" % i) + + ``` + + Parameters + ---------- + max : int, default 100 + The maximum value of the progress bar. + label : str, optional, default None + Optional label for the progress bar. + value : int, default 0 + Optional initial value of the progress bar. + unit : str, optional, default None + Optional unit for the progress bar. + metadata : str, optional, default None + Optional additional information to show on the progress bar. + """ + + type = "progressBar" + + REALTIME_UPDATABLE = True + + def __init__( + self, + max: int = 100, + label: Optional[str] = None, + value: int = 0, + unit: Optional[str] = None, + metadata: Optional[str] = None, + ): + self._label = label + self._max = max + self._value = value + self._unit = unit + self._metadata = metadata + + def update(self, new_value: int, metadata: Optional[str] = None): + self._value = new_value + if metadata is not None: + self._metadata = metadata + + @with_default_component_id + @render_safely + def render(self): + data = { + "type": self.type, + "id": self.component_id, + "max": self._max, + "value": self._value, + } + if self._label: + data["label"] = self._label + if self._unit: + data["unit"] = self._unit + if self._metadata: + data["details"] = self._metadata + return data + + +class VegaChart(UserComponent): + type = "vegaChart" + + REALTIME_UPDATABLE = True + + def __init__(self, spec: dict, show_controls: bool = False): + self._spec = spec + self._show_controls = show_controls + self._chart_inside_table = False + + def update(self, spec=None): + """ + Update the chart. + + Parameters + ---------- + spec : dict or altair.Chart + The updated chart spec or an altair Chart Object. + """ + _spec = spec + if self._object_is_altair_chart(spec): + _spec = spec.to_dict() + if _spec is not None: + self._spec = _spec + + @staticmethod + def _object_is_altair_chart(altair_chart): + # This will feel slightly hacky but I am unable to find a natural way of determining the class + # name of the Altair chart. The only way simple way is to extract the full class name and then + # match with heuristics + fulclsname = _full_classname(altair_chart) + if not all([x in fulclsname for x in ["altair", "vegalite", "Chart"]]): + return False + return True + + @classmethod + def from_altair_chart(cls, altair_chart): + if not cls._object_is_altair_chart(altair_chart): + raise ValueError(_full_classname(altair_chart) + " is not an altair chart") + altair_chart_dict = altair_chart.to_dict() + cht = cls(spec=altair_chart_dict) + return cht + + @with_default_component_id + @render_safely + def render(self): + data = { + "type": self.type, + "id": self.component_id, + "spec": self._spec, + } + if not self._show_controls: + data["options"] = {"actions": False} + if "width" not in self._spec and not self._chart_inside_table: + data["spec"]["width"] = "container" + if self._chart_inside_table and "autosize" not in self._spec: + data["spec"]["autosize"] = "fit-x" + return data + + +class PythonCode(UserComponent): + """ + A component to display Python code with syntax highlighting. + + Example: + ```python + @card + @step + def my_step(self): + # Using code_func + def my_function(): + x = 1 + y = 2 + return x + y + current.card.append( + PythonCode(my_function) + ) + + # Using code_string + code = ''' + def another_function(): + return "Hello World" + ''' + current.card.append( + PythonCode(code_string=code) + ) + ``` + + Parameters + ---------- + code_func : Callable[..., Any], optional, default None + The function whose source code should be displayed. + code_string : str, optional, default None + A string containing Python code to display. + Either code_func or code_string must be provided. + """ + + def __init__( + self, + code_func: Optional[Callable[..., Any]] = None, + code_string: Optional[str] = None, + ): + if code_func is not None: + self._code_string = inspect.getsource(code_func) + else: + self._code_string = code_string + + @with_default_component_id + @render_safely + def render(self): + if self._code_string is None: + return ErrorComponent( + "`PythonCode` component requires a `code_func` or `code_string` argument. ", + "None provided for both", + ).render() + _code_component = PythonCodeComponent(self._code_string) + _code_component.component_id = self.component_id + return _code_component.render() diff --git a/metaflow/plugins/cards/card_modules/convert_to_native_type.py b/metaflow/plugins/cards/card_modules/convert_to_native_type.py index 1033cfd759f..43f8a5f824e 100644 --- a/metaflow/plugins/cards/card_modules/convert_to_native_type.py +++ b/metaflow/plugins/cards/card_modules/convert_to_native_type.py @@ -44,7 +44,7 @@ def _full_classname(obj): class TaskToDict: - def __init__(self, only_repr=False): + def __init__(self, only_repr=False, runtime=False): # this dictionary holds all the supported functions import reprlib import pprint @@ -59,6 +59,7 @@ def __init__(self, only_repr=False): r.maxlist = 100 r.maxlevel = 3 self._repr = r + self._runtime = runtime self._only_repr = only_repr self._supported_types = { "tuple": self._parse_tuple, @@ -90,11 +91,16 @@ def __call__(self, task, graph=None): stderr=task.stderr, stdout=task.stdout, created_at=task.created_at.strftime(TIME_FORMAT), - finished_at=task.finished_at.strftime(TIME_FORMAT), + finished_at=None, pathspec=task.pathspec, graph=graph, data={}, ) + if not self._runtime: + if task.finished_at is not None: + task_dict.update( + dict(finished_at=task.finished_at.strftime(TIME_FORMAT)) + ) task_dict["data"], type_infered_objects = self._create_task_data_dict(task) task_dict.update(type_infered_objects) return task_dict @@ -137,7 +143,10 @@ def parse_image(self, data_object): obj_type_name = self._get_object_type(data_object) if obj_type_name == "bytes": # Works for python 3.1+ - import imghdr + # Python 3.13 removes the standard ``imghdr`` module. Metaflow + # vendors a copy so we can keep using ``what`` to detect image + # formats irrespective of the Python version. + from metaflow._vendor import imghdr resp = imghdr.what(None, h=data_object) # Only accept types supported on the web @@ -151,7 +160,7 @@ def _extract_type_infered_object(self, data_object): obj_type_name = self._get_object_type(data_object) if obj_type_name == "bytes": # Works for python 3.1+ - import imghdr + from metaflow._vendor import imghdr resp = imghdr.what(None, h=data_object) # Only accept types supported on the web @@ -307,10 +316,10 @@ def _parse_pandas_column(column_object): # and truncates it to 30 characters. # If there is any form of TypeError or ValueError we set the column value to "Unsupported Type" # We also set columns which are have null values to "null" strings - time_format = "%Y-%m-%dT%H:%M:%SZ" - truncate_long_objects = ( - lambda x: x.astype("string").str.slice(0, 30) + "..." - if x.astype("string").str.len().max() > 30 + time_format = "%Y-%m-%dT%H:%M:%S%Z" + truncate_long_objects = lambda x: ( + x.astype("string").str.slice(0, 30) + "..." + if len(x) > 0 and x.astype("string").str.len().max() > 30 else x.astype("string") ) type_parser = { @@ -348,12 +357,12 @@ def _match_partial_type(): try: col_type = str(column_object.dtype) if col_type in type_parser: - return type_parser[col_type](column_object) + return type_parser[col_type](column_object.fillna("null")) else: parsed_col = _match_partial_type() if parsed_col is not None: - return parsed_col - return truncate_long_objects(column_object) + return parsed_col.fillna("null") + return truncate_long_objects(column_object.fillna("null")) except ValueError as e: return "Unsupported type: {0}".format(col_type) except TypeError as e: @@ -365,13 +374,22 @@ def _parse_pandas_dataframe(self, data_object, truncate=True): if truncate: data = data_object.head() index_column = data.index - time_format = "%Y-%m-%dT%H:%M:%SZ" - if "datetime64" in str(index_column.dtype): - if index_column.__class__.__name__ == "DatetimeIndex": - index_column = index_column.strftime(time_format) - else: - index_column = index_column.dt.strftime(time_format) + # We explicitly cast the `index_column` object to an `Index` or `MultiIndex` having JSON-castable values. + if index_column.__class__.__name__ == "MultiIndex": + from pandas import MultiIndex + + cols = [ + self._parse_pandas_column( + index_column.get_level_values(name).to_series() + ) + for name in index_column.names + ] + index_column = MultiIndex.from_arrays(cols, names=index_column.names) + else: + from pandas import Index + + index_column = Index(self._parse_pandas_column(index_column.to_series())) for col in data.columns: data[col] = self._parse_pandas_column(data[col]) diff --git a/metaflow/plugins/cards/card_modules/main.js b/metaflow/plugins/cards/card_modules/main.js index f27bb1f8cc4..ae60a591648 100644 --- a/metaflow/plugins/cards/card_modules/main.js +++ b/metaflow/plugins/cards/card_modules/main.js @@ -1,21 +1,254 @@ -var app=function(){"use strict";function t(){}function e(t,e){for(const n in e)t[n]=e[n];return t}function n(t){return t()}function i(){return Object.create(null)}function s(t){t.forEach(n)}function o(t){return"function"==typeof t}function r(t,e){return t!=t?e==e:t!==e||t&&"object"==typeof t||"function"==typeof t}let a,l;function c(t,e){return a||(a=document.createElement("a")),a.href=e,t===a.href}function h(e,n,i){e.$$.on_destroy.push(function(e,...n){if(null==e)return t;const i=e.subscribe(...n);return i.unsubscribe?()=>i.unsubscribe():i}(n,i))}function u(t,e,n,i){if(t){const s=d(t,e,n,i);return t[0](s)}}function d(t,n,i,s){return t[1]&&s?e(i.ctx.slice(),t[1](s(n))):i.ctx}function f(t,e,n,i){if(t[2]&&i){const s=t[2](i(n));if(void 0===e.dirty)return s;if("object"==typeof s){const t=[],n=Math.max(e.dirty.length,s.length);for(let i=0;i32){const e=[],n=t.ctx.length/32;for(let t=0;tt.removeEventListener(e,n,i)}function L(t,e,n){null==n?t.removeAttribute(e):t.getAttribute(e)!==n&&t.setAttribute(e,n)}function D(t,e){for(const n in e)L(t,n,e[n])}function P(t,e){e=""+e,t.wholeText!==e&&(t.data=e)}function O(t,e,n,i){null===n?t.style.removeProperty(e):t.style.setProperty(e,n,i?"important":"")}function A(t,e,n){t.classList[n?"add":"remove"](e)}class E{constructor(t=!1){this.is_svg=!1,this.is_svg=t,this.e=this.n=null}c(t){this.h(t)}m(t,e,n=null){this.e||(this.is_svg?this.e=k(e.nodeName):this.e=$(11===e.nodeType?"TEMPLATE":e.nodeName),this.t="TEMPLATE"!==e.tagName?e:e.content,this.c(t)),this.i(n)}h(t){this.e.innerHTML=t,this.n=Array.from("TEMPLATE"===this.e.nodeName?this.e.content.childNodes:this.e.childNodes)}i(t){for(let e=0;e{const s=t.$$.callbacks[e];if(s){const o=function(t,e,{bubbles:n=!1,cancelable:i=!1}={}){const s=document.createEvent("CustomEvent");return s.initCustomEvent(t,n,i,e),s}(e,n,{cancelable:i});return s.slice().forEach((e=>{e.call(t,o)})),!o.defaultPrevented}return!0}}function N(t,e){return z().$$.context.set(t,e),e}function j(t){return z().$$.context.get(t)}const B=[],V=[];let H=[];const W=[],U=Promise.resolve();let q=!1;function Y(t){H.push(t)}function Z(t){W.push(t)}const X=new Set;let G=0;function K(){if(0!==G)return;const t=l;do{try{for(;G{Q.delete(t),i&&(n&&t.d(1),i())})),t.o(e)}else i&&i()}const ot="undefined"!=typeof window?window:"undefined"!=typeof globalThis?globalThis:global;function rt(t,e){const n={},i={},s={$$scope:1};let o=t.length;for(;o--;){const r=t[o],a=e[o];if(a){for(const t in r)t in a||(i[t]=1);for(const t in a)s[t]||(n[t]=a[t],s[t]=1);t[o]=a}else for(const t in r)s[t]=1}for(const t in i)t in n||(n[t]=void 0);return n}function at(t){return"object"==typeof t&&null!==t?t:{}}function lt(t,e,n){const i=t.$$.props[e];void 0!==i&&(t.$$.bound[i]=n,n(t.$$.ctx[i]))}function ct(t){t&&t.c()}function ht(t,e,i,r){const{fragment:a,after_update:l}=t.$$;a&&a.m(e,i),r||Y((()=>{const e=t.$$.on_mount.map(n).filter(o);t.$$.on_destroy?t.$$.on_destroy.push(...e):s(e),t.$$.on_mount=[]})),l.forEach(Y)}function ut(t,e){const n=t.$$;null!==n.fragment&&(!function(t){const e=[],n=[];H.forEach((i=>-1===t.indexOf(i)?e.push(i):n.push(i))),n.forEach((t=>t())),H=e}(n.after_update),s(n.on_destroy),n.fragment&&n.fragment.d(e),n.on_destroy=n.fragment=null,n.ctx=[])}function dt(t,e){-1===t.$$.dirty[0]&&(B.push(t),q||(q=!0,U.then(K)),t.$$.dirty.fill(0)),t.$$.dirty[e/31|0]|=1<{const s=i.length?i[0]:n;return f.ctx&&a(f.ctx[t],f.ctx[t]=s)&&(!f.skip_bound&&f.bound[t]&&f.bound[t](s),p&&dt(e,t)),n})):[],f.update(),p=!0,s(f.before_update),f.fragment=!!r&&r(f.ctx),n.target){if(n.hydrate){const t=function(t){return Array.from(t.childNodes)}(n.target);f.fragment&&f.fragment.l(t),t.forEach(_)}else f.fragment&&f.fragment.c();n.intro&&it(e.$$.fragment),ht(e,n.target,n.anchor,n.customElement),K()}R(d)}class pt{$destroy(){ut(this,1),this.$destroy=t}$on(e,n){if(!o(n))return t;const i=this.$$.callbacks[e]||(this.$$.callbacks[e]=[]);return i.push(n),()=>{const t=i.indexOf(n);-1!==t&&i.splice(t,1)}}$set(t){var e;this.$$set&&(e=t,0!==Object.keys(e).length)&&(this.$$.skip_bound=!0,this.$$set(t),this.$$.skip_bound=!1)}}var gt,mt,bt="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};gt={exports:{}},mt=function(t){var e=/(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i,n=0,i={},s={manual:t.Prism&&t.Prism.manual,disableWorkerMessageHandler:t.Prism&&t.Prism.disableWorkerMessageHandler,util:{encode:function t(e){return e instanceof o?new o(e.type,t(e.content),e.alias):Array.isArray(e)?e.map(t):e.replace(/&/g,"&").replace(/=u.reach);k+=$.value.length,$=$.next){var w=$.value;if(n.length>e.length)return;if(!(w instanceof o)){var C,M=1;if(x){if(!(C=r(v,k,e,b))||C.index>=e.length)break;var S=C.index,L=C.index+C[0].length,D=k;for(D+=$.value.length;D<=S;)D+=($=$.next).value.length;if(k=D-=$.value.length,$.value instanceof o)continue;for(var P=$;P!==n.tail&&(Du.reach&&(u.reach=T);var R=$.prev;if(A&&(R=l(n,R,A),k+=A.length),c(n,R,M),$=l(n,R,new o(d,m?s.tokenize(O,m):O,y,O)),E&&l(n,$,E),1u.reach&&(u.reach=z.reach)}}}}}}(t,h,e,h.head,0),function(t){for(var e=[],n=t.head.next;n!==t.tail;)e.push(n.value),n=n.next;return e}(h)},hooks:{all:{},add:function(t,e){var n=s.hooks.all;n[t]=n[t]||[],n[t].push(e)},run:function(t,e){var n=s.hooks.all[t];if(n&&n.length)for(var i,o=0;i=n[o++];)i(e)}},Token:o};function o(t,e,n,i){this.type=t,this.content=e,this.alias=n,this.length=0|(i||"").length}function r(t,e,n,i){t.lastIndex=e;var s=t.exec(n);if(s&&i&&s[1]){var o=s[1].length;s.index+=o,s[0]=s[0].slice(o)}return s}function a(){var t={value:null,prev:null,next:null},e={value:null,prev:t,next:null};t.next=e,this.head=t,this.tail=e,this.length=0}function l(t,e,n){var i=e.next,s={value:n,prev:e,next:i};return e.next=s,i.prev=s,t.length++,s}function c(t,e,n){for(var i=e.next,s=0;s"+o.content+""},!t.document)return t.addEventListener&&(s.disableWorkerMessageHandler||t.addEventListener("message",(function(e){var n=JSON.parse(e.data),i=n.language,o=n.code,r=n.immediateClose;t.postMessage(s.highlight(o,s.languages[i],i)),r&&t.close()}),!1)),s;var h=s.util.currentScript();function u(){s.manual||s.highlightAll()}if(h&&(s.filename=h.src,h.hasAttribute("data-manual")&&(s.manual=!0)),!s.manual){var d=document.readyState;"loading"===d||"interactive"===d&&h&&h.defer?document.addEventListener("DOMContentLoaded",u):window.requestAnimationFrame?window.requestAnimationFrame(u):window.setTimeout(u,16)}return s}("undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{}),gt.exports&&(gt.exports=mt),void 0!==bt&&(bt.Prism=mt),mt.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0,greedy:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/(\b(?:class|extends|implements|instanceof|interface|new|trait)\s+|\bcatch\s+\()[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:break|catch|continue|do|else|finally|for|function|if|in|instanceof|new|null|return|throw|try|while)\b/,boolean:/\b(?:false|true)\b/,function:/\b\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,punctuation:/[{}[\];(),.:]/},mt.languages.log={string:{pattern:/"(?:[^"\\\r\n]|\\.)*"|'(?![st] | \w)(?:[^'\\\r\n]|\\.)*'/,greedy:!0},exception:{pattern:/(^|[^\w.])[a-z][\w.]*(?:Error|Exception):.*(?:(?:\r\n?|\n)[ \t]*(?:at[ \t].+|\.{3}.*|Caused by:.*))+(?:(?:\r\n?|\n)[ \t]*\.\.\. .*)?/,lookbehind:!0,greedy:!0,alias:["javastacktrace","language-javastacktrace"],inside:mt.languages.javastacktrace||{keyword:/\bat\b/,function:/[a-z_][\w$]*(?=\()/,punctuation:/[.:()]/}},level:[{pattern:/\b(?:ALERT|CRIT|CRITICAL|EMERG|EMERGENCY|ERR|ERROR|FAILURE|FATAL|SEVERE)\b/,alias:["error","important"]},{pattern:/\b(?:WARN|WARNING|WRN)\b/,alias:["warning","important"]},{pattern:/\b(?:DISPLAY|INF|INFO|NOTICE|STATUS)\b/,alias:["info","keyword"]},{pattern:/\b(?:DBG|DEBUG|FINE)\b/,alias:["debug","keyword"]},{pattern:/\b(?:FINER|FINEST|TRACE|TRC|VERBOSE|VRB)\b/,alias:["trace","comment"]}],property:{pattern:/((?:^|[\]|])[ \t]*)[a-z_](?:[\w-]|\b\/\b)*(?:[. ]\(?\w(?:[\w-]|\b\/\b)*\)?)*:(?=\s)/im,lookbehind:!0},separator:{pattern:/(^|[^-+])-{3,}|={3,}|\*{3,}|- - /m,lookbehind:!0,alias:"comment"},url:/\b(?:file|ftp|https?):\/\/[^\s|,;'"]*[^\s|,;'">.]/,email:{pattern:/(^|\s)[-\w+.]+@[a-z][a-z0-9-]*(?:\.[a-z][a-z0-9-]*)+(?=\s)/,lookbehind:!0,alias:"url"},"ip-address":{pattern:/\b(?:\d{1,3}(?:\.\d{1,3}){3})\b/,alias:"constant"},"mac-address":{pattern:/\b[a-f0-9]{2}(?::[a-f0-9]{2}){5}\b/i,alias:"constant"},domain:{pattern:/(^|\s)[a-z][a-z0-9-]*(?:\.[a-z][a-z0-9-]*)*\.[a-z][a-z0-9-]+(?=\s)/,lookbehind:!0,alias:"constant"},uuid:{pattern:/\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/i,alias:"constant"},hash:{pattern:/\b(?:[a-f0-9]{32}){1,2}\b/i,alias:"constant"},"file-path":{pattern:/\b[a-z]:[\\/][^\s|,;:(){}\[\]"']+|(^|[\s:\[\](>|])\.{0,2}\/\w[^\s|,;:(){}\[\]"']*/i,lookbehind:!0,greedy:!0,alias:"string"},date:{pattern:RegExp("\\b\\d{4}[-/]\\d{2}[-/]\\d{2}(?:T(?=\\d{1,2}:)|(?=\\s\\d{1,2}:))|\\b\\d{1,4}[-/ ](?:\\d{1,2}|Apr|Aug|Dec|Feb|Jan|Jul|Jun|Mar|May|Nov|Oct|Sep)[-/ ]\\d{2,4}T?\\b|\\b(?:(?:Fri|Mon|Sat|Sun|Thu|Tue|Wed)(?:\\s{1,2}(?:Apr|Aug|Dec|Feb|Jan|Jul|Jun|Mar|May|Nov|Oct|Sep))?|Apr|Aug|Dec|Feb|Jan|Jul|Jun|Mar|May|Nov|Oct|Sep)\\s{1,2}\\d{1,2}\\b","i"),alias:"number"},time:{pattern:/\b\d{1,2}:\d{1,2}:\d{1,2}(?:[.,:]\d+)?(?:\s?[+-]\d{2}:?\d{2}|Z)?\b/,alias:"number"},boolean:/\b(?:false|null|true)\b/i,number:{pattern:/(^|[^.\w])(?:0x[a-f0-9]+|0o[0-7]+|0b[01]+|v?\d[\da-f]*(?:\.\d+)*(?:e[+-]?\d+)?[a-z]{0,3}\b)\b(?!\.\w)/i,lookbehind:!0},operator:/[;:?<=>~/@!$%&+\-|^(){}*#]/,punctuation:/[\[\].,]/},mt.languages.python={comment:{pattern:/(^|[^\\])#.*/,lookbehind:!0,greedy:!0},"string-interpolation":{pattern:/(?:f|fr|rf)(?:("""|''')[\s\S]*?\1|("|')(?:\\.|(?!\2)[^\\\r\n])*\2)/i,greedy:!0,inside:{interpolation:{pattern:/((?:^|[^{])(?:\{\{)*)\{(?!\{)(?:[^{}]|\{(?!\{)(?:[^{}]|\{(?!\{)(?:[^{}])+\})+\})+\}/,lookbehind:!0,inside:{"format-spec":{pattern:/(:)[^:(){}]+(?=\}$)/,lookbehind:!0},"conversion-option":{pattern:/![sra](?=[:}]$)/,alias:"punctuation"},rest:null}},string:/[\s\S]+/}},"triple-quoted-string":{pattern:/(?:[rub]|br|rb)?("""|''')[\s\S]*?\1/i,greedy:!0,alias:"string"},string:{pattern:/(?:[rub]|br|rb)?("|')(?:\\.|(?!\1)[^\\\r\n])*\1/i,greedy:!0},function:{pattern:/((?:^|\s)def[ \t]+)[a-zA-Z_]\w*(?=\s*\()/g,lookbehind:!0},"class-name":{pattern:/(\bclass\s+)\w+/i,lookbehind:!0},decorator:{pattern:/(^[\t ]*)@\w+(?:\.\w+)*/m,lookbehind:!0,alias:["annotation","punctuation"],inside:{punctuation:/\./}},keyword:/\b(?:_(?=\s*:)|and|as|assert|async|await|break|case|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|match|nonlocal|not|or|pass|print|raise|return|try|while|with|yield)\b/,builtin:/\b(?:__import__|abs|all|any|apply|ascii|basestring|bin|bool|buffer|bytearray|bytes|callable|chr|classmethod|cmp|coerce|compile|complex|delattr|dict|dir|divmod|enumerate|eval|execfile|file|filter|float|format|frozenset|getattr|globals|hasattr|hash|help|hex|id|input|int|intern|isinstance|issubclass|iter|len|list|locals|long|map|max|memoryview|min|next|object|oct|open|ord|pow|property|range|raw_input|reduce|reload|repr|reversed|round|set|setattr|slice|sorted|staticmethod|str|sum|super|tuple|type|unichr|unicode|vars|xrange|zip)\b/,boolean:/\b(?:False|None|True)\b/,number:/\b0(?:b(?:_?[01])+|o(?:_?[0-7])+|x(?:_?[a-f0-9])+)\b|(?:\b\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\B\.\d+(?:_\d+)*)(?:e[+-]?\d+(?:_\d+)*)?j?(?!\w)/i,operator:/[-+%=]=?|!=|:=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]/,punctuation:/[{}[\];(),.:]/},mt.languages.python["string-interpolation"].inside.interpolation.inside.rest=mt.languages.python,mt.languages.py=mt.languages.python;const xt=[];function yt(e,n=t){let i;const s=new Set;function o(t){if(r(e,t)&&(e=t,i)){const t=!xt.length;for(const t of s)t[1](),xt.push(t,e);if(t){for(let t=0;t{s.delete(l),0===s.size&&i&&(i(),i=null)}}}}const _t=yt(void 0),vt=t=>{try{const e=JSON.parse(atob(window.__MF_DATA__[t]));_t.set(e)}catch(t){fetch("/card-example.json").then((t=>t.json())).then((t=>{_t.set(t)})).catch(console.error)}},$t=yt(void 0),kt=t=>{const e={};if(!t)return e;function n(t,i=[]){var s;if("page"===t.type){const i=[];e[t.title]=i,null===(s=null==t?void 0:t.contents)||void 0===s||s.forEach((t=>n(t,i)))}"section"===t.type&&t.title&&i.push(t.title)}return null==t||t.forEach((t=>n(t))),e},wt=(t,e)=>t/(e?parseFloat(getComputedStyle(document.documentElement).fontSize.replace("px","")):16),Ct=(t,e)=>{var n;if(t&&e)return null===(n=(t=>{const e=t.split("/");return{flowname:e[0],runid:e[1],stepname:null==e?void 0:e[2],taskid:null==e?void 0:e[3]}})(t))||void 0===n?void 0:n[e]},Mt=t=>t.scrollHeight>t.clientHeight||t.scrollWidth>t.clientWidth;function St(t){let e,n,i,s,o,r,a,l;return{c(){e=k("svg"),n=k("title"),i=w("metaflow_logo_horizontal"),s=k("g"),o=k("g"),r=k("g"),a=k("path"),l=k("path"),L(a,"d","M223.990273,66.33 C223.515273,61.851 222.686273,57.512 221.505273,53.33 C220.325273,49.148 218.795273,45.122 216.916273,41.271 C212.845273,32.921 207.254273,25.587 200.268273,19.422 C199.270273,18.541 198.243273,17.684 197.189273,16.851 C191.255273,12.166 184.481273,8.355 177.253273,5.55 C174.156273,4.347 170.975273,3.33 167.741273,2.508 C161.273273,0.863 154.593273,0 147.943273,0 C141.755273,0 135.332273,0.576 128.687273,1.722 C127.025273,2.01 125.350273,2.332 123.661273,2.69 C120.283273,3.406 116.851273,4.265 113.365273,5.267 C104.650273,7.769 95.6022727,11.161 86.2442727,15.433 C78.7592727,18.851 71.0762727,22.832 63.2072727,27.373 C47.9762727,36.162 35.7372727,44.969 29.3592727,49.791 C29.0692727,50.01 28.7922727,50.221 28.5262727,50.423 C26.1382727,52.244 24.7522727,53.367 24.5662727,53.519 L0.549272727,73.065 C0.191272727,73.356 0.00727272727,73.773 0,74.194 C-0.00372727273,74.403 0.0362727273,74.614 0.120272727,74.811 C0.205272727,75.008 0.334272727,75.189 0.508272727,75.341 L11.7612727,85.195 C12.1692727,85.552 12.7252727,85.651 13.2162727,85.487 C13.3792727,85.432 13.5362727,85.348 13.6762727,85.234 L35.8492727,67.382 C36.0422727,67.224 37.6152727,65.949 40.3252727,63.903 C44.1192727,61.036 50.1422727,56.656 57.7292727,51.711 C62.0642727,48.884 66.9102727,45.873 72.1412727,42.854 C100.864273,26.278 126.367273,17.874 147.943273,17.874 C148.366273,17.874 148.790273,17.892 149.213273,17.902 C149.655273,17.911 150.096273,17.911 150.538273,17.93 C153.769273,18.068 156.995273,18.463 160.170273,19.097 C164.931273,20.049 169.577273,21.542 173.953273,23.524 C178.328273,25.505 182.433273,27.975 186.112273,30.88 C186.771273,31.4 187.406273,31.94 188.035273,32.485 C188.913273,33.245 189.771273,34.023 190.591273,34.83 C191.998273,36.217 193.317273,37.673 194.548273,39.195 C196.395273,41.479 198.042273,43.912 199.480273,46.485 C199.960273,47.342 200.417273,48.216 200.850273,49.105 C201.112273,49.642 201.343273,50.196 201.587273,50.743 C202.231273,52.185 202.834273,53.649 203.354273,55.158 C203.712273,56.198 204.041273,57.255 204.340273,58.326 C205.836273,63.683 206.590273,69.417 206.590273,75.469 C206.590273,81.221 205.892273,86.677 204.541273,91.804 C203.617273,95.308 202.397273,98.662 200.850273,101.833 C200.417273,102.722 199.960273,103.595 199.480273,104.453 C197.562273,107.884 195.275273,111.066 192.636273,113.976 C190.657273,116.159 188.480273,118.189 186.113273,120.058 C184.553273,121.29 182.909273,122.432 181.208273,123.503 C180.313273,124.067 179.400273,124.609 178.470273,125.126 C177.462273,125.688 176.442273,126.232 175.398273,126.737 C166.961273,130.823 157.423273,133.064 147.943273,133.064 C126.367273,133.064 100.864273,124.659 72.1412727,108.084 C70.5382727,107.159 68.4382727,105.886 66.3072727,104.575 C65.0292727,103.788 63.7402727,102.986 62.5412727,102.237 C59.3442727,100.238 56.7882727,98.61 56.7882727,98.61 C61.8362727,93.901 69.3232727,87.465 78.6472727,81.047 C80.0092727,80.11 81.4192727,79.174 82.8572727,78.243 C84.1052727,77.436 85.3712727,76.63 86.6732727,75.835 C88.2042727,74.9 89.7802727,73.981 91.3822727,73.074 C93.0482727,72.131 94.7512727,71.207 96.4902727,70.307 C111.473273,62.55 129.094273,56.602 147.943273,56.602 C151.750273,56.602 157.745273,57.825 162.114273,61.276 C162.300273,61.422 162.489273,61.578 162.677273,61.74 C163.337273,62.305 164.006273,62.966 164.634273,63.78 C164.957273,64.198 165.269273,64.657 165.564273,65.162 C166.006273,65.92 166.409273,66.782 166.750273,67.775 C166.891273,68.185 167.016273,68.627 167.134273,69.083 C167.586273,70.833 167.863273,72.924 167.863273,75.469 C167.863273,78.552 167.460273,80.974 166.824273,82.92 C166.578273,83.674 166.300273,84.363 165.992273,84.983 C165.855273,85.259 165.711273,85.524 165.564273,85.776 C165.376273,86.099 165.178273,86.396 164.977273,86.682 C164.631273,87.175 164.269273,87.618 163.900273,88.018 C163.730273,88.202 163.559273,88.379 163.387273,88.546 C162.962273,88.96 162.534273,89.331 162.114273,89.662 C157.745273,93.112 151.750273,94.337 147.943273,94.337 C144.485273,94.337 140.682273,93.926 136.589273,93.121 C133.860273,92.584 131.003273,91.871 128.033273,90.987 C123.579273,89.662 118.873273,87.952 113.970273,85.872 C113.768273,85.786 113.552273,85.747 113.336273,85.753 C113.122273,85.76 112.908273,85.813 112.713273,85.912 C106.990273,88.816 101.641273,91.995 96.7462727,95.223 C96.6232727,95.304 96.5182727,95.397 96.4302727,95.5 C95.8122727,96.22 96.0172727,97.397 96.9492727,97.822 L102.445273,100.328 C104.606273,101.314 106.737273,102.238 108.835273,103.102 C110.934273,103.966 113.001273,104.77 115.035273,105.511 C118.086273,106.624 121.064273,107.599 123.965273,108.436 C127.834273,109.551 131.567273,110.421 135.157273,111.043 C139.646273,111.82 143.912273,112.211 147.943273,112.211 C148.367273,112.211 148.923273,112.201 149.591273,112.169 C149.925273,112.153 150.287273,112.131 150.674273,112.102 C155.712273,111.724 165.055273,110.114 173.190273,103.691 C173.547273,103.41 173.869273,103.105 174.210273,102.813 C175.324273,101.86 176.381273,100.866 177.333273,99.8 C177.470273,99.648 177.590273,99.485 177.724273,99.331 C181.300273,95.167 183.699273,90.185 184.875273,84.406 C185.444273,81.609 185.737273,78.631 185.737273,75.469 C185.737273,63.315 181.516273,53.82 173.190273,47.247 C167.050273,42.399 160.228273,40.299 155.083273,39.395 C153.892273,39.186 152.790273,39.037 151.809273,38.938 C150.116273,38.766 148.774273,38.727 147.943273,38.727 C133.456273,38.727 118.519273,41.679 103.545273,47.5 C99.1222727,49.22 94.6912727,51.191 90.2702727,53.403 C88.7972727,54.141 87.3242727,54.905 85.8542727,55.696 C83.5092727,56.957 81.1722727,58.303 78.8382727,59.697 C77.3922727,60.562 75.9492727,61.451 74.5082727,62.366 C72.4422727,63.678 70.3802727,65.023 68.3302727,66.437 C63.8372727,69.535 59.7422727,72.63 56.0902727,75.567 C54.8732727,76.547 53.7052727,77.508 52.5882727,78.446 C48.1222727,82.2 44.4752727,85.581 41.7602727,88.226 C38.3032727,91.593 36.3592727,93.766 36.1632727,93.986 L35.8282727,94.362 L32.0332727,98.61 L30.6432727,100.164 C30.4962727,100.329 30.3932727,100.517 30.3312727,100.715 C30.1482727,101.307 30.3472727,101.981 30.8882727,102.368 L37.2812727,106.938 L37.4862727,107.083 L37.6922727,107.228 C39.8732727,108.766 42.0702727,110.277 44.2792727,111.758 C45.8422727,112.807 47.4102727,113.84 48.9832727,114.858 C51.5302727,116.508 54.0902727,118.103 56.6542727,119.665 C57.8412727,120.388 59.0282727,121.101 60.2162727,121.804 C61.2142727,122.394 62.2102727,122.989 63.2072727,123.565 C76.9772727,131.512 90.1802727,137.744 102.748273,142.242 C104.544273,142.884 106.326273,143.491 108.096273,144.063 C111.635273,145.206 115.121273,146.207 118.553273,147.067 C121.986273,147.925 125.364273,148.642 128.687273,149.215 C135.332273,150.362 141.755273,150.938 147.943273,150.938 C154.593273,150.938 161.273273,150.074 167.741273,148.43 C174.209273,146.786 180.465273,144.361 186.265273,141.238 C190.133273,139.156 193.798273,136.764 197.189273,134.087 C200.352273,131.589 203.264273,128.872 205.911273,125.949 C207.677273,124 209.325273,121.96 210.854273,119.831 C211.618273,118.766 212.353273,117.68 213.057273,116.571 C214.466273,114.356 215.753273,112.053 216.916273,109.667 C220.701273,101.906 223.073273,93.439 224.008273,84.406 C224.310273,81.485 224.465273,78.505 224.465273,75.469 C224.465273,72.364 224.306273,69.316 223.990273,66.33"),L(a,"id","Fill-1"),L(a,"fill","#146EE6"),L(l,"d","M758.389273,75.346 C752.632273,111.56 742.681273,122.23 712.102273,122.23 C710.847273,122.23 709.640273,122.207 708.464273,122.17 C708.321273,122.191 708.191273,122.23 708.028273,122.23 L637.994273,122.23 C636.795273,122.23 636.315273,121.632 636.435273,120.311 L650.585273,31.22 C650.704273,30.016 651.424273,29.417 652.623273,29.417 L667.852273,29.417 C669.050273,29.417 669.530273,30.016 669.410273,31.22 L657.659273,105.802 L714.249273,105.802 L714.249273,105.787 C714.410273,105.794 714.568273,105.802 714.741273,105.802 C718.878273,105.802 722.250273,105.351 725.040273,104.313 C726.434273,103.794 727.684273,103.129 728.810273,102.298 C729.373273,101.884 729.905273,101.426 730.410273,100.927 C734.951273,96.431 737.231273,88.43 739.322273,75.346 C739.328273,75.312 739.331273,75.282 739.337273,75.25 C739.642273,73.311 739.896273,71.474 740.130273,69.679 C740.203273,69.116 740.272273,68.557 740.338273,68.008 C740.412273,67.392 740.461273,66.821 740.525273,66.222 C742.136273,49.927 738.622273,44.525 724.454273,44.525 C723.419273,44.525 722.433273,44.554 721.490273,44.613 C708.297273,45.444 703.831273,52.303 700.461273,71.126 C700.220273,72.472 699.984273,73.877 699.752273,75.346 C699.483273,77.027 699.255273,78.6 699.052273,80.115 C698.993273,80.545 698.948273,80.946 698.895273,81.361 C698.757273,82.465 698.638273,83.528 698.540273,84.544 C698.502273,84.943 698.466273,85.334 698.434273,85.72 C698.344273,86.815 698.281273,87.856 698.246273,88.847 C698.238273,89.049 698.224273,89.269 698.219273,89.469 C698.161273,91.88 698.289273,93.972 698.621273,95.782 C698.649273,95.941 698.686273,96.089 698.717273,96.246 C698.874273,96.992 699.067273,97.689 699.301273,98.337 C699.346273,98.464 699.390273,98.594 699.439273,98.718 C700.039273,100.231 700.864273,101.478 701.963273,102.469 C702.263273,102.738 702.586273,102.987 702.925273,103.22 L679.436273,103.22 C679.393273,102.969 679.343273,102.727 679.305273,102.471 L679.304273,102.471 C679.304273,102.467 679.304273,102.462 679.303273,102.459 C679.259273,102.17 679.236273,101.854 679.198273,101.558 C679.083273,100.634 678.995273,99.671 678.934273,98.674 C678.908273,98.258 678.879273,97.845 678.862273,97.419 C678.816273,96.174 678.804273,94.876 678.832273,93.518 C678.840273,93.114 678.861273,92.69 678.876273,92.276 C678.920273,91.042 678.991273,89.765 679.092273,88.441 C679.117273,88.109 679.134273,87.79 679.162273,87.452 C679.299273,85.836 679.483273,84.137 679.698273,82.382 C679.750273,81.957 679.807273,81.518 679.863273,81.084 C680.104273,79.238 680.369273,77.344 680.687273,75.346 C681.046273,73.067 681.423273,70.889 681.819273,68.808 C687.040273,41.397 695.809273,30.748 717.267273,28.554 C720.250273,28.25 723.472273,28.103 726.971273,28.103 C726.972273,28.103 726.972273,28.103 726.972273,28.103 C747.994273,28.103 757.680273,33.202 759.811273,48.236 C760.779273,55.067 760.187273,63.953 758.389273,75.346 Z M894.023273,31.336 L866.923273,108.56 C863.472273,118.182 861.113273,121.41 854.499273,122.41 C852.379273,122.733 849.831273,122.828 846.659273,122.828 C831.670273,122.828 830.350273,121.267 829.392273,108.56 L825.794273,63.232 L825.794273,63.231 L807.928273,108.56 C804.255273,117.613 802.201273,120.996 795.961273,122.202 C793.442273,122.687 790.260273,122.829 785.985273,122.829 C772.914273,122.829 770.756273,121.267 770.396273,108.56 L767.638273,31.337 C767.638273,29.899 768.238273,29.417 769.557273,29.417 L785.385273,29.417 C786.464273,29.417 786.704273,29.899 786.824273,31.337 L788.895273,100.572 L788.895273,100.571 C789.054273,103.091 789.563273,103.641 791.021273,103.641 C792.939273,103.641 793.419273,103.042 794.618273,100.043 L820.758273,34.576 C821.358273,33.132 822.437273,32.657 823.516273,32.657 L837.665273,32.657 C838.519273,32.657 839.279273,32.977 839.626273,33.817 C839.718273,34.038 839.799273,34.274 839.824273,34.576 L845.220273,100.043 C845.460273,103.042 845.819273,103.641 847.738273,103.641 C849.297273,103.641 849.896273,103.042 850.976273,100.043 L874.838273,31.336 C875.317273,29.898 875.677273,29.417 876.756273,29.417 L892.584273,29.417 C893.903273,29.417 894.383273,29.898 894.023273,31.336 Z M362.708273,31.219 L357.192273,120.311 C357.192273,121.632 356.353273,122.23 355.154273,122.23 L339.926273,122.23 C338.726273,122.23 338.366273,121.632 338.366273,120.311 L342.324273,62.756 L311.986273,117.551 C311.386273,118.749 310.428273,119.348 309.229273,119.348 L297.837273,119.348 C296.758273,119.348 296.038273,118.749 295.560273,117.551 L282.851273,62.767 L282.848273,62.755 L268.339273,120.31 C268.212273,121.009 267.974273,121.492 267.612273,121.807 C267.288273,122.085 266.865273,122.23 266.301273,122.23 L251.073273,122.23 C249.874273,122.23 249.273273,121.632 249.514273,120.31 L272.296273,31.336 C272.537273,30.138 272.897273,29.417 274.095273,29.417 L288.605273,29.417 C291.236273,29.417 292.726273,29.895 293.682273,31.379 C294.120273,32.059 294.457273,32.928 294.720273,34.095 L307.790273,92.489 L339.326273,34.095 C341.485273,30.256 343.043273,29.299 346.880273,29.299 L361.389273,29.299 C362.376273,29.299 362.682273,30.684 362.682273,30.684 C362.682273,30.684 362.708273,31.029 362.708273,31.219 Z M501.706273,31.219 L499.667273,44.049 C499.547273,45.246 498.708273,45.845 497.509273,45.845 L472.448273,45.845 L460.696273,120.31 C460.457273,121.632 459.738273,122.23 458.538273,122.23 L443.309273,122.23 C442.111273,122.23 441.631273,121.632 441.870273,120.31 L453.622273,45.845 L394.820273,45.845 L391.224273,68.507 L391.224273,68.508 L430.555273,68.508 C431.754273,68.508 432.353273,69.106 432.234273,70.31 L430.196273,82.542 C430.076273,83.738 429.236273,84.338 428.038273,84.338 L388.706273,84.338 L385.349273,105.801 L428.397273,105.801 C429.596273,105.801 430.076273,106.4 429.955273,107.597 L427.797273,120.428 C427.676273,121.632 426.958273,122.23 425.759273,122.23 L365.683273,122.23 C364.484273,122.23 364.004273,121.632 364.124273,120.31 L378.273273,31.219 C378.393273,30.015 379.112273,29.417 380.313273,29.417 L500.147273,29.417 C501.346273,29.417 501.826273,30.015 501.706273,31.219 Z M629.471273,70.426 L627.433273,82.659 C627.313273,83.856 626.473273,84.454 625.275273,84.454 L588.223273,84.454 L582.466273,120.311 C582.347273,121.632 581.627273,122.23 580.428273,122.23 L565.200273,122.23 C564.001273,122.23 563.522273,121.632 563.640273,120.311 L577.790273,31.219 C577.910273,30.016 578.629273,29.417 579.828273,29.417 L643.004273,29.417 C644.202273,29.417 644.802273,30.016 644.682273,31.219 L642.644273,44.05 C642.403273,45.247 641.685273,45.846 640.486273,45.846 L594.337273,45.846 L590.741273,68.631 L627.793273,68.631 C628.991273,68.631 629.592273,69.23 629.471273,70.426 Z M388.706273,84.338 L388.712273,84.338 L388.309273,86.876 L388.706273,84.338 Z M510.726273,79.783 L524.396273,48.006 C525.036273,46.466 525.443273,45.589 525.990273,45.096 C526.465273,44.667 527.044273,44.525 527.993273,44.525 C530.391273,44.525 530.391273,45.124 530.751273,48.006 L534.348273,79.783 L510.726273,79.783 Z M542.334273,29.886 C539.756273,28.905 536.043273,28.702 530.511273,28.702 C516.601273,28.702 513.963273,30.016 508.208273,43.087 L474.633273,120.311 C474.154273,121.749 474.513273,122.23 475.832273,122.23 L491.060273,122.23 C492.259273,122.23 492.500273,121.749 493.099273,120.311 L504.011273,95.372 L536.026273,95.372 L539.024273,120.311 C539.144273,121.749 539.144273,122.23 540.344273,122.23 L555.572273,122.23 C556.891273,122.23 557.490273,121.749 557.490273,120.311 L548.617273,43.087 C547.658273,35.042 546.460273,31.458 542.334273,29.886 L542.334273,29.886 Z"),L(l,"id","Fill-2"),L(l,"fill","#333333"),L(r,"id","metaflow_logo_horizontal"),L(r,"transform","translate(92.930727, 93.190000)"),L(o,"id","Metaflow_Logo_Horizontal_TwoColor_Dark_RGB"),L(o,"transform","translate(-92.000000, -93.000000)"),L(s,"id","Page-1"),L(s,"stroke","none"),L(s,"stroke-width","1"),L(s,"fill","none"),L(s,"fill-rule","evenodd"),L(e,"xmlns","http://www.w3.org/2000/svg"),L(e,"xmlns:xlink","http://www.w3.org/1999/xlink"),L(e,"width","896px"),L(e,"height","152px"),L(e,"viewBox","0 0 896 152"),L(e,"version","1.1")},m(t,c){y(t,e,c),x(e,n),x(n,i),x(e,s),x(s,o),x(o,r),x(r,a),x(r,l)},d(t){t&&_(e)}}}function Lt(t){let e,n,i;return{c(){e=k("svg"),n=k("path"),i=k("path"),L(n,"fill-rule","evenodd"),L(n,"clip-rule","evenodd"),L(n,"d","M223.991 66.33C223.516 61.851 222.687 57.512 221.506 53.33C220.326 49.148 218.796 45.122 216.917 41.271C212.846 32.921 207.255 25.587 200.269 19.422C199.271 18.541 198.244 17.684 197.19 16.851C191.256 12.166 184.482 8.355 177.254 5.55C174.157 4.347 170.976 3.33 167.742 2.508C161.274 0.863 154.594 0 147.944 0C141.756 0 135.333 0.576 128.688 1.722C127.026 2.01 125.351 2.332 123.662 2.69C120.284 3.406 116.852 4.265 113.366 5.267C104.651 7.769 95.6025 11.161 86.2445 15.433C78.7595 18.851 71.0765 22.832 63.2075 27.373C47.9765 36.162 35.7375 44.969 29.3595 49.791C29.0695 50.01 28.7925 50.221 28.5265 50.423C26.1385 52.244 24.7525 53.367 24.5665 53.519L0.549511 73.065C0.191511 73.356 0.00751099 73.773 0.000238261 74.194C-0.00348901 74.403 0.036511 74.614 0.120511 74.811C0.205511 75.008 0.334511 75.189 0.508511 75.341L11.7615 85.195C12.1695 85.552 12.7255 85.651 13.2165 85.487C13.3795 85.432 13.5365 85.348 13.6765 85.234L35.8495 67.382C36.0425 67.224 37.6155 65.949 40.3255 63.903C44.1195 61.036 50.1425 56.656 57.7295 51.711C62.0645 48.884 66.9105 45.873 72.1415 42.854C100.865 26.278 126.368 17.874 147.944 17.874C148.367 17.874 148.791 17.892 149.214 17.902C149.656 17.911 150.097 17.911 150.539 17.93C153.77 18.068 156.996 18.463 160.171 19.097C164.932 20.049 169.578 21.542 173.954 23.524C178.329 25.505 182.434 27.975 186.113 30.88C186.772 31.4 187.407 31.94 188.036 32.485C188.914 33.245 189.772 34.023 190.592 34.83C191.999 36.217 193.318 37.673 194.549 39.195C196.396 41.479 198.043 43.912 199.481 46.485C199.961 47.342 200.418 48.216 200.851 49.105C201.113 49.642 201.344 50.196 201.588 50.743C202.232 52.185 202.835 53.649 203.355 55.158C203.713 56.198 204.042 57.255 204.341 58.326C205.837 63.683 206.591 69.417 206.591 75.469C206.591 81.221 205.893 86.677 204.542 91.804C203.618 95.308 202.398 98.662 200.851 101.833C200.418 102.722 199.961 103.595 199.481 104.453C197.563 107.884 195.276 111.066 192.637 113.976C190.658 116.159 188.481 118.189 186.114 120.058C184.554 121.29 182.91 122.432 181.209 123.503C180.314 124.067 179.401 124.609 178.471 125.126C177.463 125.688 176.443 126.232 175.399 126.737C166.962 130.823 157.424 133.064 147.944 133.064C126.368 133.064 100.865 124.659 72.1415 108.084C70.5385 107.159 68.4385 105.886 66.3075 104.575C65.0295 103.788 63.7405 102.986 62.5415 102.237C59.3445 100.238 56.7885 98.61 56.7885 98.61C61.8365 93.901 69.3235 87.465 78.6475 81.047C80.0095 80.11 81.4195 79.174 82.8575 78.243C84.1055 77.436 85.3715 76.63 86.6735 75.835C88.2045 74.9 89.7805 73.981 91.3825 73.074C93.0485 72.131 94.7515 71.207 96.4905 70.307C111.474 62.55 129.095 56.602 147.944 56.602C151.751 56.602 157.746 57.825 162.115 61.276C162.301 61.422 162.49 61.578 162.678 61.74C163.338 62.305 164.007 62.966 164.635 63.78C164.958 64.198 165.27 64.657 165.565 65.162C166.007 65.92 166.41 66.782 166.751 67.775C166.892 68.185 167.017 68.627 167.135 69.083C167.587 70.833 167.864 72.924 167.864 75.469C167.864 78.552 167.461 80.974 166.825 82.92C166.579 83.674 166.301 84.363 165.993 84.983C165.856 85.259 165.712 85.524 165.565 85.776C165.377 86.099 165.179 86.396 164.978 86.682C164.632 87.175 164.27 87.618 163.901 88.018C163.731 88.202 163.56 88.379 163.388 88.546C162.963 88.96 162.535 89.331 162.115 89.662C157.746 93.112 151.751 94.337 147.944 94.337C144.486 94.337 140.683 93.926 136.59 93.121C133.861 92.584 131.004 91.871 128.034 90.987C123.58 89.662 118.874 87.952 113.971 85.872C113.769 85.786 113.553 85.747 113.337 85.753C113.123 85.76 112.909 85.813 112.714 85.912C106.991 88.816 101.642 91.995 96.7465 95.223C96.6235 95.304 96.5185 95.397 96.4305 95.5C95.8125 96.22 96.0175 97.397 96.9495 97.822L102.446 100.328C104.607 101.314 106.738 102.238 108.836 103.102C110.935 103.966 113.002 104.77 115.036 105.511C118.087 106.624 121.065 107.599 123.966 108.436C127.835 109.551 131.568 110.421 135.158 111.043C139.647 111.82 143.913 112.211 147.944 112.211C148.368 112.211 148.924 112.201 149.592 112.169C149.926 112.153 150.288 112.131 150.675 112.102C155.713 111.724 165.056 110.114 173.191 103.691C173.548 103.41 173.87 103.105 174.211 102.813C175.325 101.86 176.382 100.866 177.334 99.8C177.471 99.648 177.591 99.485 177.725 99.331C181.301 95.167 183.7 90.185 184.876 84.406C185.445 81.609 185.738 78.631 185.738 75.469C185.738 63.315 181.517 53.82 173.191 47.247C167.051 42.399 160.229 40.299 155.084 39.395C153.893 39.186 152.791 39.037 151.81 38.938C150.117 38.766 148.775 38.727 147.944 38.727C133.457 38.727 118.52 41.679 103.546 47.5C99.1225 49.22 94.6915 51.191 90.2705 53.403C88.7975 54.141 87.3245 54.905 85.8545 55.696C83.5095 56.957 81.1725 58.303 78.8385 59.697C77.3925 60.562 75.9495 61.451 74.5085 62.366C72.4425 63.678 70.3805 65.023 68.3305 66.437C63.8375 69.535 59.7425 72.63 56.0905 75.567C54.8735 76.547 53.7055 77.508 52.5885 78.446C48.1225 82.2 44.4755 85.581 41.7605 88.226C38.3035 91.593 36.3595 93.766 36.1635 93.986L35.8285 94.362L32.0335 98.61L30.6435 100.164C30.4965 100.329 30.3935 100.517 30.3315 100.715C30.1485 101.307 30.3475 101.981 30.8885 102.368L37.2815 106.938L37.4865 107.083L37.6925 107.228C39.8735 108.766 42.0705 110.277 44.2795 111.758C45.8425 112.807 47.4105 113.84 48.9835 114.858C51.5305 116.508 54.0905 118.103 56.6545 119.665C57.8415 120.388 59.0285 121.101 60.2165 121.804C61.2145 122.394 62.2105 122.989 63.2075 123.565C76.9775 131.512 90.1805 137.744 102.749 142.242C104.545 142.884 106.327 143.491 108.097 144.063C111.636 145.206 115.122 146.207 118.554 147.067C121.987 147.925 125.365 148.642 128.688 149.215C135.333 150.362 141.756 150.938 147.944 150.938C154.594 150.938 161.274 150.074 167.742 148.43C174.21 146.786 180.466 144.361 186.266 141.238C190.134 139.156 193.799 136.764 197.19 134.087C200.353 131.589 203.265 128.872 205.912 125.949C207.678 124 209.326 121.96 210.855 119.831C211.619 118.766 212.354 117.68 213.058 116.571C214.467 114.356 215.754 112.053 216.917 109.667C220.702 101.906 223.074 93.439 224.009 84.406C224.311 81.485 224.466 78.505 224.466 75.469C224.466 72.364 224.307 69.316 223.991 66.33Z"),L(n,"fill","#146EE6"),L(i,"fill-rule","evenodd"),L(i,"clip-rule","evenodd"),L(i,"d","M758.39 75.346C752.633 111.56 742.682 122.23 712.103 122.23C710.848 122.23 709.641 122.207 708.465 122.17C708.322 122.191 708.192 122.23 708.029 122.23H637.995C636.796 122.23 636.316 121.632 636.436 120.311L650.586 31.22C650.705 30.016 651.425 29.417 652.624 29.417H667.853C669.051 29.417 669.531 30.016 669.411 31.22L657.66 105.802H714.25V105.787C714.411 105.794 714.569 105.802 714.742 105.802C718.879 105.802 722.251 105.351 725.041 104.313C726.435 103.794 727.685 103.129 728.811 102.298C729.374 101.884 729.906 101.426 730.411 100.927C734.952 96.431 737.232 88.43 739.323 75.346C739.329 75.312 739.332 75.282 739.338 75.25C739.643 73.311 739.896 71.474 740.13 69.679C740.203 69.116 740.273 68.557 740.339 68.008C740.413 67.392 740.462 66.821 740.526 66.222C742.137 49.927 738.623 44.525 724.455 44.525C723.42 44.525 722.434 44.554 721.491 44.613C708.298 45.444 703.831 52.303 700.461 71.126C700.22 72.472 699.985 73.877 699.753 75.346C699.484 77.027 699.255 78.6 699.052 80.115C698.993 80.545 698.949 80.946 698.896 81.361C698.758 82.465 698.639 83.528 698.541 84.544C698.503 84.943 698.467 85.334 698.435 85.72C698.345 86.815 698.282 87.856 698.247 88.847C698.239 89.049 698.225 89.269 698.22 89.469C698.162 91.88 698.29 93.972 698.622 95.782C698.65 95.941 698.687 96.089 698.718 96.246C698.875 96.992 699.068 97.689 699.302 98.337C699.347 98.464 699.391 98.594 699.44 98.718C700.04 100.231 700.865 101.478 701.964 102.469C702.264 102.738 702.587 102.987 702.926 103.22H679.437C679.394 102.969 679.344 102.727 679.306 102.471H679.305C679.305 102.467 679.305 102.462 679.304 102.459C679.26 102.17 679.237 101.854 679.199 101.558C679.084 100.634 678.996 99.671 678.935 98.674C678.909 98.258 678.879 97.845 678.862 97.419C678.816 96.174 678.805 94.876 678.833 93.518C678.841 93.114 678.862 92.69 678.877 92.276C678.921 91.042 678.992 89.765 679.093 88.441C679.118 88.109 679.135 87.79 679.163 87.452C679.3 85.836 679.484 84.137 679.699 82.382C679.751 81.957 679.808 81.518 679.864 81.084C680.105 79.238 680.37 77.344 680.688 75.346C681.046 73.067 681.424 70.889 681.82 68.808C687.041 41.397 695.81 30.748 717.268 28.554C720.251 28.25 723.472 28.103 726.971 28.103C726.972 28.103 726.973 28.103 726.973 28.103C747.995 28.103 757.681 33.202 759.812 48.236C760.78 55.067 760.188 63.953 758.39 75.346ZM894.023 31.336L866.924 108.56C863.473 118.182 861.114 121.41 854.5 122.41C852.38 122.733 849.832 122.828 846.66 122.828C831.671 122.828 830.351 121.267 829.393 108.56L825.794 63.232V63.231L807.929 108.56C804.256 117.613 802.201 120.996 795.961 122.202C793.442 122.687 790.261 122.829 785.986 122.829C772.915 122.829 770.757 121.267 770.397 108.56L767.638 31.337C767.638 29.899 768.238 29.417 769.557 29.417H785.385C786.464 29.417 786.705 29.899 786.825 31.337L788.896 100.572V100.571C789.055 103.091 789.564 103.641 791.022 103.641C792.94 103.641 793.42 103.042 794.619 100.043L820.759 34.576C821.359 33.132 822.438 32.657 823.517 32.657H837.666C838.52 32.657 839.28 32.977 839.627 33.817C839.719 34.038 839.8 34.274 839.825 34.576L845.221 100.043C845.461 103.042 845.82 103.641 847.739 103.641C849.298 103.641 849.897 103.042 850.977 100.043L874.839 31.336C875.318 29.898 875.678 29.417 876.757 29.417H892.585C893.904 29.417 894.383 29.898 894.023 31.336ZM362.709 31.219L357.193 120.311C357.193 121.632 356.354 122.23 355.155 122.23H339.927C338.727 122.23 338.367 121.632 338.367 120.311L342.325 62.756L311.987 117.551C311.387 118.749 310.429 119.348 309.23 119.348H297.838C296.759 119.348 296.039 118.749 295.561 117.551L282.852 62.767L282.849 62.755L268.34 120.31C268.213 121.009 267.975 121.492 267.613 121.807C267.289 122.085 266.866 122.23 266.302 122.23H251.074C249.875 122.23 249.274 121.632 249.515 120.31L272.297 31.336C272.538 30.138 272.898 29.417 274.096 29.417H288.606C291.237 29.417 292.727 29.895 293.683 31.379C294.121 32.059 294.458 32.928 294.721 34.095L307.791 92.489L339.327 34.095C341.486 30.256 343.044 29.299 346.881 29.299H361.39C362.377 29.299 362.683 30.684 362.683 30.684C362.683 30.684 362.709 31.029 362.709 31.219ZM501.707 31.219L499.668 44.049C499.548 45.246 498.709 45.845 497.51 45.845H472.449L460.697 120.31C460.458 121.632 459.739 122.23 458.539 122.23H443.31C442.112 122.23 441.632 121.632 441.871 120.31L453.623 45.845H394.821L391.225 68.507V68.508H430.556C431.755 68.508 432.354 69.106 432.235 70.31L430.197 82.542C430.077 83.738 429.237 84.338 428.039 84.338H388.707L385.35 105.801H428.398C429.597 105.801 430.077 106.4 429.956 107.597L427.798 120.428C427.677 121.632 426.959 122.23 425.76 122.23H365.684C364.485 122.23 364.005 121.632 364.125 120.31L378.274 31.219C378.394 30.015 379.113 29.417 380.314 29.417H500.148C501.347 29.417 501.827 30.015 501.707 31.219ZM629.471 70.426L627.434 82.659C627.314 83.856 626.474 84.454 625.276 84.454H588.224L582.466 120.311C582.347 121.632 581.628 122.23 580.429 122.23H565.201C564.002 122.23 563.523 121.632 563.641 120.311L577.791 31.219C577.911 30.016 578.629 29.417 579.828 29.417H643.005C644.203 29.417 644.802 30.016 644.682 31.219L642.645 44.05C642.404 45.247 641.686 45.846 640.487 45.846H594.338L590.742 68.631H627.794C628.992 68.631 629.592 69.23 629.471 70.426ZM388.707 84.338H388.713L388.31 86.876L388.707 84.338ZM510.727 79.783L524.397 48.006C525.037 46.466 525.444 45.589 525.991 45.096C526.466 44.667 527.045 44.525 527.994 44.525C530.392 44.525 530.392 45.124 530.752 48.006L534.349 79.783H510.727ZM542.335 29.886C539.757 28.905 536.044 28.702 530.512 28.702C516.602 28.702 513.964 30.016 508.209 43.087L474.634 120.311C474.155 121.749 474.514 122.23 475.833 122.23H491.061C492.26 122.23 492.501 121.749 493.1 120.311L504.012 95.372H536.026L539.025 120.311C539.145 121.749 539.145 122.23 540.345 122.23H555.573C556.892 122.23 557.491 121.749 557.491 120.311L548.617 43.087C547.658 35.042 546.461 31.458 542.335 29.886Z"),L(i,"fill","white"),L(e,"width","895"),L(e,"height","151"),L(e,"viewBox","0 0 895 151"),L(e,"fill","none"),L(e,"xmlns","http://www.w3.org/2000/svg")},m(t,s){y(t,e,s),x(e,n),x(e,i)},d(t){t&&_(e)}}}function Dt(e){let n;function i(t,e){return t[0]?Lt:St}let s=i(e),o=s(e);return{c(){o.c(),n=M()},m(t,e){o.m(t,e),y(t,n,e)},p(t,[e]){s!==(s=i(t))&&(o.d(1),o=s(t),o&&(o.c(),o.m(n.parentNode,n)))},i:t,o:t,d(t){o.d(t),t&&_(n)}}}function Pt(t,e,n){let{light:i=!1}=e;return t.$$set=t=>{"light"in t&&n(0,i=t.light)},[i]}class Ot extends pt{constructor(t){super(),ft(this,t,Pt,Dt,r,{light:0})}}function At(t){let e,n,i,s,o,r;s=new Ot({});const a=t[1].default,l=u(a,t,t[0],null);return{c(){e=$("aside"),n=$("div"),i=$("div"),ct(s.$$.fragment),o=C(),l&&l.c(),L(i,"class","logoContainer"),L(e,"class","svelte-1okdv0e")},m(t,a){y(t,e,a),x(e,n),x(n,i),ht(s,i,null),x(n,o),l&&l.m(n,null),r=!0},p(t,[e]){l&&l.p&&(!r||1&e)&&p(l,a,t,t[0],r?f(a,t[0],e,null):g(t[0]),null)},i(t){r||(it(s.$$.fragment,t),it(l,t),r=!0)},o(t){st(s.$$.fragment,t),st(l,t),r=!1},d(t){t&&_(e),ut(s),l&&l.d(t)}}}function Et(t,e,n){let{$$slots:i={},$$scope:s}=e;return t.$$set=t=>{"$$scope"in t&&n(0,s=t.$$scope)},[s,i]}class Tt extends pt{constructor(t){super(),ft(this,t,Et,At,r,{})}}function Rt(t){let e,n;return{c(){e=$("td"),n=w(t[0]),L(e,"class","idCell svelte-pt8vzv"),L(e,"data-component","artifact-row")},m(t,i){y(t,e,i),x(e,n)},p(t,e){1&e&&P(n,t[0])},d(t){t&&_(e)}}}function zt(e){let n,i,s,o,r,a,l=e[1].data+"",c=null!==e[0]&&Rt(e);return{c(){n=$("tr"),c&&c.c(),i=C(),s=$("td"),o=$("code"),r=w(l),L(o,"class","mono"),L(s,"class","codeCell svelte-pt8vzv"),L(s,"colspan",a=null===e[0]?2:1),L(s,"data-component","artifact-row")},m(t,a){y(t,n,a),c&&c.m(n,null),x(n,i),x(n,s),x(s,o),x(o,r),e[3](o)},p(t,[e]){null!==t[0]?c?c.p(t,e):(c=Rt(t),c.c(),c.m(n,i)):c&&(c.d(1),c=null),2&e&&l!==(l=t[1].data+"")&&P(r,l),1&e&&a!==(a=null===t[0]?2:1)&&L(s,"colspan",a)},i:t,o:t,d(t){t&&_(n),c&&c.d(),e[3](null)}}}function It(t,e,n){let i,{id:s}=e,{artifact:o}=e;return t.$$set=t=>{"id"in t&&n(0,s=t.id),"artifact"in t&&n(1,o=t.artifact)},t.$$.update=()=>{4&t.$$.dirty&&i&&function(){var t;i&&!i.classList.contains("language-python")&&"undefined"!=typeof window&&(null===(t=null===window||void 0===window?void 0:window.Prism)||void 0===t||t.highlightElement(i))}()},[s,o,i,function(t){V[t?"unshift":"push"]((()=>{i=t,n(2,i)}))}]}class Ft extends pt{constructor(t){super(),ft(this,t,It,zt,r,{id:0,artifact:1})}}function Nt(t,e,n){const i=t.slice();return i[3]=e[n],i}function jt(e){let n,i;return n=new Ft({props:{id:e[3].name,artifact:e[3]}}),{c(){ct(n.$$.fragment)},m(t,e){ht(n,t,e),i=!0},p:t,i(t){i||(it(n.$$.fragment,t),i=!0)},o(t){st(n.$$.fragment,t),i=!1},d(t){ut(n,t)}}}function Bt(t){let e,n,i,s=t[0],o=[];for(let e=0;est(o[t],1,1,(()=>{o[t]=null}));return{c(){e=$("div"),n=$("table");for(let t=0;t{if(t.name&&e.name){if(t.name>e.name)return 1;if(t.name{"componentData"in t&&n(1,i=t.componentData)},[o,i]}class Ht extends pt{constructor(t){super(),ft(this,t,Vt,Bt,r,{componentData:1})}} -/*! - * Chart.js v3.9.1 - * https://www.chartjs.org - * (c) 2022 Chart.js Contributors - * Released under the MIT License - */const Wt=function(){let t=0;return function(){return t++}}();function Ut(t){return null==t}function qt(t){if(Array.isArray&&Array.isArray(t))return!0;const e=Object.prototype.toString.call(t);return"[object"===e.slice(0,7)&&"Array]"===e.slice(-6)}function Yt(t){return null!==t&&"[object Object]"===Object.prototype.toString.call(t)}const Zt=t=>("number"==typeof t||t instanceof Number)&&isFinite(+t);function Xt(t,e){return Zt(t)?t:e}function Gt(t,e){return void 0===t?e:t}const Kt=(t,e)=>"string"==typeof t&&t.endsWith("%")?parseFloat(t)/100*e:+t;function Jt(t,e,n){if(t&&"function"==typeof t.call)return t.apply(n,e)}function Qt(t,e,n,i){let s,o,r;if(qt(t))if(o=t.length,i)for(s=o-1;s>=0;s--)e.call(n,t[s],s);else for(s=0;st,x:t=>t.x,y:t=>t.y};function le(t,e){const n=ae[e]||(ae[e]=function(t){const e=function(t){const e=t.split("."),n=[];let i="";for(const t of e)i+=t,i.endsWith("\\")?i=i.slice(0,-1)+".":(n.push(i),i="");return n}(t);return t=>{for(const n of e){if(""===n)break;t=t&&t[n]}return t}}(e));return n(t)}function ce(t){return t.charAt(0).toUpperCase()+t.slice(1)}const he=t=>void 0!==t,ue=t=>"function"==typeof t,de=(t,e)=>{if(t.size!==e.size)return!1;for(const n of t)if(!e.has(n))return!1;return!0};const fe=Math.PI,pe=2*fe,ge=pe+fe,me=Number.POSITIVE_INFINITY,be=fe/180,xe=fe/2,ye=fe/4,_e=2*fe/3,ve=Math.log10,$e=Math.sign;function ke(t){const e=Math.round(t);t=Ce(t,e,t/1e3)?e:t;const n=Math.pow(10,Math.floor(ve(t))),i=t/n;return(i<=1?1:i<=2?2:i<=5?5:10)*n}function we(t){return!isNaN(parseFloat(t))&&isFinite(t)}function Ce(t,e,n){return Math.abs(t-e)l&&c=Math.min(e,n)-i&&t<=Math.max(e,n)+i}function Ie(t,e,n){n=n||(n=>t[n]1;)i=o+s>>1,n(i)?o=i:s=i;return{lo:o,hi:s}}const Fe=(t,e,n,i)=>Ie(t,n,i?i=>t[i][e]<=n:i=>t[i][e]Ie(t,n,(i=>t[i][e]>=n));const je=["push","pop","shift","splice","unshift"];function Be(t,e){const n=t._chartjs;if(!n)return;const i=n.listeners,s=i.indexOf(e);-1!==s&&i.splice(s,1),i.length>0||(je.forEach((e=>{delete t[e]})),delete t._chartjs)}function Ve(t){const e=new Set;let n,i;for(n=0,i=t.length;nArray.prototype.slice.call(t));let s=!1,o=[];return function(...n){o=i(n),s||(s=!0,He.call(window,(()=>{s=!1,t.apply(e,o)})))}}const Ue=t=>"start"===t?"left":"end"===t?"right":"center",qe=(t,e,n)=>"start"===t?e:"end"===t?n:(e+n)/2;function Ye(t,e,n){const i=e.length;let s=0,o=i;if(t._sorted){const{iScale:r,_parsed:a}=t,l=r.axis,{min:c,max:h,minDefined:u,maxDefined:d}=r.getUserBounds();u&&(s=Re(Math.min(Fe(a,r.axis,c).lo,n?i:Fe(e,l,r.getPixelForValue(c)).lo),0,i-1)),o=d?Re(Math.max(Fe(a,r.axis,h,!0).hi+1,n?0:Fe(e,l,r.getPixelForValue(h),!0).hi+1),s,i)-s:i-s}return{start:s,count:o}}function Ze(t){const{xScale:e,yScale:n,_scaleRanges:i}=t,s={xmin:e.min,xmax:e.max,ymin:n.min,ymax:n.max};if(!i)return t._scaleRanges=s,!0;const o=i.xmin!==e.min||i.xmax!==e.max||i.ymin!==n.min||i.ymax!==n.max;return Object.assign(i,s),o}const Xe=t=>0===t||1===t,Ge=(t,e,n)=>-Math.pow(2,10*(t-=1))*Math.sin((t-e)*pe/n),Ke=(t,e,n)=>Math.pow(2,-10*t)*Math.sin((t-e)*pe/n)+1,Je={linear:t=>t,easeInQuad:t=>t*t,easeOutQuad:t=>-t*(t-2),easeInOutQuad:t=>(t/=.5)<1?.5*t*t:-.5*(--t*(t-2)-1),easeInCubic:t=>t*t*t,easeOutCubic:t=>(t-=1)*t*t+1,easeInOutCubic:t=>(t/=.5)<1?.5*t*t*t:.5*((t-=2)*t*t+2),easeInQuart:t=>t*t*t*t,easeOutQuart:t=>-((t-=1)*t*t*t-1),easeInOutQuart:t=>(t/=.5)<1?.5*t*t*t*t:-.5*((t-=2)*t*t*t-2),easeInQuint:t=>t*t*t*t*t,easeOutQuint:t=>(t-=1)*t*t*t*t+1,easeInOutQuint:t=>(t/=.5)<1?.5*t*t*t*t*t:.5*((t-=2)*t*t*t*t+2),easeInSine:t=>1-Math.cos(t*xe),easeOutSine:t=>Math.sin(t*xe),easeInOutSine:t=>-.5*(Math.cos(fe*t)-1),easeInExpo:t=>0===t?0:Math.pow(2,10*(t-1)),easeOutExpo:t=>1===t?1:1-Math.pow(2,-10*t),easeInOutExpo:t=>Xe(t)?t:t<.5?.5*Math.pow(2,10*(2*t-1)):.5*(2-Math.pow(2,-10*(2*t-1))),easeInCirc:t=>t>=1?t:-(Math.sqrt(1-t*t)-1),easeOutCirc:t=>Math.sqrt(1-(t-=1)*t),easeInOutCirc:t=>(t/=.5)<1?-.5*(Math.sqrt(1-t*t)-1):.5*(Math.sqrt(1-(t-=2)*t)+1),easeInElastic:t=>Xe(t)?t:Ge(t,.075,.3),easeOutElastic:t=>Xe(t)?t:Ke(t,.075,.3),easeInOutElastic(t){const e=.1125;return Xe(t)?t:t<.5?.5*Ge(2*t,e,.45):.5+.5*Ke(2*t-1,e,.45)},easeInBack(t){const e=1.70158;return t*t*((e+1)*t-e)},easeOutBack(t){const e=1.70158;return(t-=1)*t*((e+1)*t+e)+1},easeInOutBack(t){let e=1.70158;return(t/=.5)<1?t*t*((1+(e*=1.525))*t-e)*.5:.5*((t-=2)*t*((1+(e*=1.525))*t+e)+2)},easeInBounce:t=>1-Je.easeOutBounce(1-t),easeOutBounce(t){const e=7.5625,n=2.75;return t<1/n?e*t*t:t<2/n?e*(t-=1.5/n)*t+.75:t<2.5/n?e*(t-=2.25/n)*t+.9375:e*(t-=2.625/n)*t+.984375},easeInOutBounce:t=>t<.5?.5*Je.easeInBounce(2*t):.5*Je.easeOutBounce(2*t-1)+.5}; -/*! - * @kurkle/color v0.2.1 - * https://github.com/kurkle/color#readme - * (c) 2022 Jukka Kurkela - * Released under the MIT License - */ -function Qe(t){return t+.5|0}const tn=(t,e,n)=>Math.max(Math.min(t,n),e);function en(t){return tn(Qe(2.55*t),0,255)}function nn(t){return tn(Qe(255*t),0,255)}function sn(t){return tn(Qe(t/2.55)/100,0,1)}function on(t){return tn(Qe(100*t),0,100)}const rn={0:0,1:1,2:2,3:3,4:4,5:5,6:6,7:7,8:8,9:9,A:10,B:11,C:12,D:13,E:14,F:15,a:10,b:11,c:12,d:13,e:14,f:15},an=[..."0123456789ABCDEF"],ln=t=>an[15&t],cn=t=>an[(240&t)>>4]+an[15&t],hn=t=>(240&t)>>4==(15&t),un=t=>hn(t.r)&&hn(t.g)&&hn(t.b)&&hn(t.a);const dn=(t,e)=>t<255?e(t):"";const fn=/^(hsla?|hwb|hsv)\(\s*([-+.e\d]+)(?:deg)?[\s,]+([-+.e\d]+)%[\s,]+([-+.e\d]+)%(?:[\s,]+([-+.e\d]+)(%)?)?\s*\)$/;function pn(t,e,n){const i=e*Math.min(n,1-n),s=(e,s=(e+t/30)%12)=>n-i*Math.max(Math.min(s-3,9-s,1),-1);return[s(0),s(8),s(4)]}function gn(t,e,n){const i=(i,s=(i+t/60)%6)=>n-n*e*Math.max(Math.min(s,4-s,1),0);return[i(5),i(3),i(1)]}function mn(t,e,n){const i=pn(t,1,.5);let s;for(e+n>1&&(s=1/(e+n),e*=s,n*=s),s=0;s<3;s++)i[s]*=1-e-n,i[s]+=e;return i}function bn(t){const e=t.r/255,n=t.g/255,i=t.b/255,s=Math.max(e,n,i),o=Math.min(e,n,i),r=(s+o)/2;let a,l,c;return s!==o&&(c=s-o,l=r>.5?c/(2-s-o):c/(s+o),a=function(t,e,n,i,s){return t===s?(e-n)/i+(e>16&255,o>>8&255,255&o]}return t}(),wn.transparent=[0,0,0,0]);const e=wn[t.toLowerCase()];return e&&{r:e[0],g:e[1],b:e[2],a:4===e.length?e[3]:255}}const Mn=/^rgba?\(\s*([-+.\d]+)(%)?[\s,]+([-+.e\d]+)(%)?[\s,]+([-+.e\d]+)(%)?(?:[\s,/]+([-+.e\d]+)(%)?)?\s*\)$/;const Sn=t=>t<=.0031308?12.92*t:1.055*Math.pow(t,1/2.4)-.055,Ln=t=>t<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4);function Dn(t,e,n){if(t){let i=bn(t);i[e]=Math.max(0,Math.min(i[e]+i[e]*n,0===e?360:1)),i=yn(i),t.r=i[0],t.g=i[1],t.b=i[2]}}function Pn(t,e){return t?Object.assign(e||{},t):t}function On(t){var e={r:0,g:0,b:0,a:255};return Array.isArray(t)?t.length>=3&&(e={r:t[0],g:t[1],b:t[2],a:255},t.length>3&&(e.a=nn(t[3]))):(e=Pn(t,{r:0,g:0,b:0,a:1})).a=nn(e.a),e}function An(t){return"r"===t.charAt(0)?function(t){const e=Mn.exec(t);let n,i,s,o=255;if(e){if(e[7]!==n){const t=+e[7];o=e[8]?en(t):tn(255*t,0,255)}return n=+e[1],i=+e[3],s=+e[5],n=255&(e[2]?en(n):tn(n,0,255)),i=255&(e[4]?en(i):tn(i,0,255)),s=255&(e[6]?en(s):tn(s,0,255)),{r:n,g:i,b:s,a:o}}}(t):vn(t)}class En{constructor(t){if(t instanceof En)return t;const e=typeof t;let n;var i,s,o;"object"===e?n=On(t):"string"===e&&(o=(i=t).length,"#"===i[0]&&(4===o||5===o?s={r:255&17*rn[i[1]],g:255&17*rn[i[2]],b:255&17*rn[i[3]],a:5===o?17*rn[i[4]]:255}:7!==o&&9!==o||(s={r:rn[i[1]]<<4|rn[i[2]],g:rn[i[3]]<<4|rn[i[4]],b:rn[i[5]]<<4|rn[i[6]],a:9===o?rn[i[7]]<<4|rn[i[8]]:255})),n=s||Cn(t)||An(t)),this._rgb=n,this._valid=!!n}get valid(){return this._valid}get rgb(){var t=Pn(this._rgb);return t&&(t.a=sn(t.a)),t}set rgb(t){this._rgb=On(t)}rgbString(){return this._valid?(t=this._rgb)&&(t.a<255?`rgba(${t.r}, ${t.g}, ${t.b}, ${sn(t.a)})`:`rgb(${t.r}, ${t.g}, ${t.b})`):void 0;var t}hexString(){return this._valid?(t=this._rgb,e=un(t)?ln:cn,t?"#"+e(t.r)+e(t.g)+e(t.b)+dn(t.a,e):void 0):void 0;var t,e}hslString(){return this._valid?function(t){if(!t)return;const e=bn(t),n=e[0],i=on(e[1]),s=on(e[2]);return t.a<255?`hsla(${n}, ${i}%, ${s}%, ${sn(t.a)})`:`hsl(${n}, ${i}%, ${s}%)`}(this._rgb):void 0}mix(t,e){if(t){const n=this.rgb,i=t.rgb;let s;const o=e===s?.5:e,r=2*o-1,a=n.a-i.a,l=((r*a==-1?r:(r+a)/(1+r*a))+1)/2;s=1-l,n.r=255&l*n.r+s*i.r+.5,n.g=255&l*n.g+s*i.g+.5,n.b=255&l*n.b+s*i.b+.5,n.a=o*n.a+(1-o)*i.a,this.rgb=n}return this}interpolate(t,e){return t&&(this._rgb=function(t,e,n){const i=Ln(sn(t.r)),s=Ln(sn(t.g)),o=Ln(sn(t.b));return{r:nn(Sn(i+n*(Ln(sn(e.r))-i))),g:nn(Sn(s+n*(Ln(sn(e.g))-s))),b:nn(Sn(o+n*(Ln(sn(e.b))-o))),a:t.a+n*(e.a-t.a)}}(this._rgb,t._rgb,e)),this}clone(){return new En(this.rgb)}alpha(t){return this._rgb.a=nn(t),this}clearer(t){return this._rgb.a*=1-t,this}greyscale(){const t=this._rgb,e=Qe(.3*t.r+.59*t.g+.11*t.b);return t.r=t.g=t.b=e,this}opaquer(t){return this._rgb.a*=1+t,this}negate(){const t=this._rgb;return t.r=255-t.r,t.g=255-t.g,t.b=255-t.b,this}lighten(t){return Dn(this._rgb,2,t),this}darken(t){return Dn(this._rgb,2,-t),this}saturate(t){return Dn(this._rgb,1,t),this}desaturate(t){return Dn(this._rgb,1,-t),this}rotate(t){return function(t,e){var n=bn(t);n[0]=_n(n[0]+e),n=yn(n),t.r=n[0],t.g=n[1],t.b=n[2]}(this._rgb,t),this}}function Tn(t){return new En(t)}function Rn(t){if(t&&"object"==typeof t){const e=t.toString();return"[object CanvasPattern]"===e||"[object CanvasGradient]"===e}return!1}function zn(t){return Rn(t)?t:Tn(t)}function In(t){return Rn(t)?t:Tn(t).saturate(.5).darken(.1).hexString()}const Fn=Object.create(null),Nn=Object.create(null);function jn(t,e){if(!e)return t;const n=e.split(".");for(let e=0,i=n.length;et.chart.platform.getDevicePixelRatio(),this.elements={},this.events=["mousemove","mouseout","click","touchstart","touchmove"],this.font={family:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",size:12,style:"normal",lineHeight:1.2,weight:null},this.hover={},this.hoverBackgroundColor=(t,e)=>In(e.backgroundColor),this.hoverBorderColor=(t,e)=>In(e.borderColor),this.hoverColor=(t,e)=>In(e.color),this.indexAxis="x",this.interaction={mode:"nearest",intersect:!0,includeInvisible:!1},this.maintainAspectRatio=!0,this.onHover=null,this.onClick=null,this.parsing=!0,this.plugins={},this.responsive=!0,this.scale=void 0,this.scales={},this.showLine=!0,this.drawActiveElementsOnTop=!0,this.describe(t)}set(t,e){return Bn(this,t,e)}get(t){return jn(this,t)}describe(t,e){return Bn(Nn,t,e)}override(t,e){return Bn(Fn,t,e)}route(t,e,n,i){const s=jn(this,t),o=jn(this,n),r="_"+e;Object.defineProperties(s,{[r]:{value:s[e],writable:!0},[e]:{enumerable:!0,get(){const t=this[r],e=o[i];return Yt(t)?Object.assign({},e,t):Gt(t,e)},set(t){this[r]=t}}})}}({_scriptable:t=>!t.startsWith("on"),_indexable:t=>"events"!==t,hover:{_fallback:"interaction"},interaction:{_scriptable:!1,_indexable:!1}});function Hn(t,e,n,i,s){let o=e[s];return o||(o=e[s]=t.measureText(s).width,n.push(s)),o>i&&(i=o),i}function Wn(t,e,n,i){let s=(i=i||{}).data=i.data||{},o=i.garbageCollect=i.garbageCollect||[];i.font!==e&&(s=i.data={},o=i.garbageCollect=[],i.font=e),t.save(),t.font=e;let r=0;const a=n.length;let l,c,h,u,d;for(l=0;ln.length){for(l=0;l0&&t.stroke()}(t,e,n,i,null)}function Zn(t,e,n){return n=n||.5,!e||t&&t.x>e.left-n&&t.xe.top-n&&t.y0&&""!==o.strokeColor;let l,c;for(t.save(),t.font=s.string,function(t,e){e.translation&&t.translate(e.translation[0],e.translation[1]);Ut(e.rotation)||t.rotate(e.rotation);e.color&&(t.fillStyle=e.color);e.textAlign&&(t.textAlign=e.textAlign);e.textBaseline&&(t.textBaseline=e.textBaseline)}(t,o),l=0;l+t||0;function ri(t,e){const n={},i=Yt(e),s=i?Object.keys(e):e,o=Yt(t)?i?n=>Gt(t[n],t[e[n]]):e=>t[e]:()=>t;for(const t of s)n[t]=oi(o(t));return n}function ai(t){return ri(t,{top:"y",right:"x",bottom:"y",left:"x"})}function li(t){return ri(t,["topLeft","topRight","bottomLeft","bottomRight"])}function ci(t){const e=ai(t);return e.width=e.left+e.right,e.height=e.top+e.bottom,e}function hi(t,e){t=t||{},e=e||Vn.font;let n=Gt(t.size,e.size);"string"==typeof n&&(n=parseInt(n,10));let i=Gt(t.style,e.style);i&&!(""+i).match(ii)&&(console.warn('Invalid font style specified: "'+i+'"'),i="");const s={family:Gt(t.family,e.family),lineHeight:si(Gt(t.lineHeight,e.lineHeight),n),size:n,style:i,weight:Gt(t.weight,e.weight),string:""};return s.string=function(t){return!t||Ut(t.size)||Ut(t.family)?null:(t.style?t.style+" ":"")+(t.weight?t.weight+" ":"")+t.size+"px "+t.family}(s),s}function ui(t,e,n,i){let s,o,r,a=!0;for(s=0,o=t.length;st[0])){he(i)||(i=wi("_fallback",t));const o={[Symbol.toStringTag]:"Object",_cacheable:!0,_scopes:t,_rootScopes:n,_fallback:i,_getTarget:s,override:s=>fi([s,...t],e,n,i)};return new Proxy(o,{deleteProperty:(e,n)=>(delete e[n],delete e._keys,delete t[0][n],!0),get:(n,i)=>xi(n,i,(()=>function(t,e,n,i){let s;for(const o of e)if(s=wi(mi(o,t),n),he(s))return bi(t,s)?$i(n,i,t,s):s}(i,e,t,n))),getOwnPropertyDescriptor:(t,e)=>Reflect.getOwnPropertyDescriptor(t._scopes[0],e),getPrototypeOf:()=>Reflect.getPrototypeOf(t[0]),has:(t,e)=>Ci(t).includes(e),ownKeys:t=>Ci(t),set(t,e,n){const i=t._storage||(t._storage=s());return t[e]=i[e]=n,delete t._keys,!0}})}function pi(t,e,n,i){const s={_cacheable:!1,_proxy:t,_context:e,_subProxy:n,_stack:new Set,_descriptors:gi(t,i),setContext:e=>pi(t,e,n,i),override:s=>pi(t.override(s),e,n,i)};return new Proxy(s,{deleteProperty:(e,n)=>(delete e[n],delete t[n],!0),get:(t,e,n)=>xi(t,e,(()=>function(t,e,n){const{_proxy:i,_context:s,_subProxy:o,_descriptors:r}=t;let a=i[e];ue(a)&&r.isScriptable(e)&&(a=function(t,e,n,i){const{_proxy:s,_context:o,_subProxy:r,_stack:a}=n;if(a.has(t))throw new Error("Recursion detected: "+Array.from(a).join("->")+"->"+t);a.add(t),e=e(o,r||i),a.delete(t),bi(t,e)&&(e=$i(s._scopes,s,t,e));return e}(e,a,t,n));qt(a)&&a.length&&(a=function(t,e,n,i){const{_proxy:s,_context:o,_subProxy:r,_descriptors:a}=n;if(he(o.index)&&i(t))e=e[o.index%e.length];else if(Yt(e[0])){const n=e,i=s._scopes.filter((t=>t!==n));e=[];for(const l of n){const n=$i(i,s,t,l);e.push(pi(n,o,r&&r[t],a))}}return e}(e,a,t,r.isIndexable));bi(e,a)&&(a=pi(a,s,o&&o[e],r));return a}(t,e,n))),getOwnPropertyDescriptor:(e,n)=>e._descriptors.allKeys?Reflect.has(t,n)?{enumerable:!0,configurable:!0}:void 0:Reflect.getOwnPropertyDescriptor(t,n),getPrototypeOf:()=>Reflect.getPrototypeOf(t),has:(e,n)=>Reflect.has(t,n),ownKeys:()=>Reflect.ownKeys(t),set:(e,n,i)=>(t[n]=i,delete e[n],!0)})}function gi(t,e={scriptable:!0,indexable:!0}){const{_scriptable:n=e.scriptable,_indexable:i=e.indexable,_allKeys:s=e.allKeys}=t;return{allKeys:s,scriptable:n,indexable:i,isScriptable:ue(n)?n:()=>n,isIndexable:ue(i)?i:()=>i}}const mi=(t,e)=>t?t+ce(e):e,bi=(t,e)=>Yt(e)&&"adapters"!==t&&(null===Object.getPrototypeOf(e)||e.constructor===Object);function xi(t,e,n){if(Object.prototype.hasOwnProperty.call(t,e))return t[e];const i=n();return t[e]=i,i}function yi(t,e,n){return ue(t)?t(e,n):t}const _i=(t,e)=>!0===t?e:"string"==typeof t?le(e,t):void 0;function vi(t,e,n,i,s){for(const o of e){const e=_i(n,o);if(e){t.add(e);const o=yi(e._fallback,n,s);if(he(o)&&o!==n&&o!==i)return o}else if(!1===e&&he(i)&&n!==i)return null}return!1}function $i(t,e,n,i){const s=e._rootScopes,o=yi(e._fallback,n,i),r=[...t,...s],a=new Set;a.add(i);let l=ki(a,r,n,o||n,i);return null!==l&&((!he(o)||o===n||(l=ki(a,r,o,l,i),null!==l))&&fi(Array.from(a),[""],s,o,(()=>function(t,e,n){const i=t._getTarget();e in i||(i[e]={});const s=i[e];if(qt(s)&&Yt(n))return n;return s}(e,n,i))))}function ki(t,e,n,i,s){for(;n;)n=vi(t,e,n,i,s);return n}function wi(t,e){for(const n of e){if(!n)continue;const e=n[t];if(he(e))return e}}function Ci(t){let e=t._keys;return e||(e=t._keys=function(t){const e=new Set;for(const n of t)for(const t of Object.keys(n).filter((t=>!t.startsWith("_"))))e.add(t);return Array.from(e)}(t._scopes)),e}function Mi(t,e,n,i){const{iScale:s}=t,{key:o="r"}=this._parsing,r=new Array(i);let a,l,c,h;for(a=0,l=i;ae"x"===t?"y":"x";function Pi(t,e,n,i){const s=t.skip?e:t,o=e,r=n.skip?e:n,a=Oe(o,s),l=Oe(r,o);let c=a/(a+l),h=l/(a+l);c=isNaN(c)?0:c,h=isNaN(h)?0:h;const u=i*c,d=i*h;return{previous:{x:o.x-u*(r.x-s.x),y:o.y-u*(r.y-s.y)},next:{x:o.x+d*(r.x-s.x),y:o.y+d*(r.y-s.y)}}}function Oi(t,e="x"){const n=Di(e),i=t.length,s=Array(i).fill(0),o=Array(i);let r,a,l,c=Li(t,0);for(r=0;r!t.skip))),"monotone"===e.cubicInterpolationMode)Oi(t,s);else{let n=i?t[t.length-1]:t[0];for(o=0,r=t.length;owindow.getComputedStyle(t,null);const Fi=["top","right","bottom","left"];function Ni(t,e,n){const i={};n=n?"-"+n:"";for(let s=0;s<4;s++){const o=Fi[s];i[o]=parseFloat(t[e+"-"+o+n])||0}return i.width=i.left+i.right,i.height=i.top+i.bottom,i}const ji=(t,e,n)=>(t>0||e>0)&&(!n||!n.shadowRoot);function Bi(t,e){if("native"in t)return t;const{canvas:n,currentDevicePixelRatio:i}=e,s=Ii(n),o="border-box"===s.boxSizing,r=Ni(s,"padding"),a=Ni(s,"border","width"),{x:l,y:c,box:h}=function(t,e){const n=t.touches,i=n&&n.length?n[0]:t,{offsetX:s,offsetY:o}=i;let r,a,l=!1;if(ji(s,o,t.target))r=s,a=o;else{const t=e.getBoundingClientRect();r=i.clientX-t.left,a=i.clientY-t.top,l=!0}return{x:r,y:a,box:l}}(t,n),u=r.left+(h&&a.left),d=r.top+(h&&a.top);let{width:f,height:p}=e;return o&&(f-=r.width+a.width,p-=r.height+a.height),{x:Math.round((l-u)/f*n.width/i),y:Math.round((c-d)/p*n.height/i)}}const Vi=t=>Math.round(10*t)/10;function Hi(t,e,n,i){const s=Ii(t),o=Ni(s,"margin"),r=zi(s.maxWidth,t,"clientWidth")||me,a=zi(s.maxHeight,t,"clientHeight")||me,l=function(t,e,n){let i,s;if(void 0===e||void 0===n){const o=Ri(t);if(o){const t=o.getBoundingClientRect(),r=Ii(o),a=Ni(r,"border","width"),l=Ni(r,"padding");e=t.width-l.width-a.width,n=t.height-l.height-a.height,i=zi(r.maxWidth,o,"clientWidth"),s=zi(r.maxHeight,o,"clientHeight")}else e=t.clientWidth,n=t.clientHeight}return{width:e,height:n,maxWidth:i||me,maxHeight:s||me}}(t,e,n);let{width:c,height:h}=l;if("content-box"===s.boxSizing){const t=Ni(s,"border","width"),e=Ni(s,"padding");c-=e.width+t.width,h-=e.height+t.height}return c=Math.max(0,c-o.width),h=Math.max(0,i?Math.floor(c/i):h-o.height),c=Vi(Math.min(c,r,l.maxWidth)),h=Vi(Math.min(h,a,l.maxHeight)),c&&!h&&(h=Vi(c/2)),{width:c,height:h}}function Wi(t,e,n){const i=e||1,s=Math.floor(t.height*i),o=Math.floor(t.width*i);t.height=s/i,t.width=o/i;const r=t.canvas;return r.style&&(n||!r.style.height&&!r.style.width)&&(r.style.height=`${t.height}px`,r.style.width=`${t.width}px`),(t.currentDevicePixelRatio!==i||r.height!==s||r.width!==o)&&(t.currentDevicePixelRatio=i,r.height=s,r.width=o,t.ctx.setTransform(i,0,0,i,0,0),!0)}const Ui=function(){let t=!1;try{const e={get passive(){return t=!0,!1}};window.addEventListener("test",null,e),window.removeEventListener("test",null,e)}catch(t){}return t}();function qi(t,e){const n=function(t,e){return Ii(t).getPropertyValue(e)}(t,e),i=n&&n.match(/^(\d+)(\.\d+)?px$/);return i?+i[1]:void 0}function Yi(t,e,n,i){return{x:t.x+n*(e.x-t.x),y:t.y+n*(e.y-t.y)}}function Zi(t,e,n,i){return{x:t.x+n*(e.x-t.x),y:"middle"===i?n<.5?t.y:e.y:"after"===i?n<1?t.y:e.y:n>0?e.y:t.y}}function Xi(t,e,n,i){const s={x:t.cp2x,y:t.cp2y},o={x:e.cp1x,y:e.cp1y},r=Yi(t,s,n),a=Yi(s,o,n),l=Yi(o,e,n),c=Yi(r,a,n),h=Yi(a,l,n);return Yi(c,h,n)}const Gi=new Map;function Ki(t,e,n){return function(t,e){e=e||{};const n=t+JSON.stringify(e);let i=Gi.get(n);return i||(i=new Intl.NumberFormat(t,e),Gi.set(n,i)),i}(e,n).format(t)}const Ji=function(t,e){return{x:n=>t+t+e-n,setWidth(t){e=t},textAlign:t=>"center"===t?t:"right"===t?"left":"right",xPlus:(t,e)=>t-e,leftForLtr:(t,e)=>t-e}},Qi=function(){return{x:t=>t,setWidth(t){},textAlign:t=>t,xPlus:(t,e)=>t+e,leftForLtr:(t,e)=>t}};function ts(t,e,n){return t?Ji(e,n):Qi()}function es(t){return"angle"===t?{between:Te,compare:Ae,normalize:Ee}:{between:ze,compare:(t,e)=>t-e,normalize:t=>t}}function ns({start:t,end:e,count:n,loop:i,style:s}){return{start:t%n,end:e%n,loop:i&&(e-t+1)%n==0,style:s}}function is(t,e,n){if(!n)return[t];const{property:i,start:s,end:o}=n,r=e.length,{compare:a,between:l,normalize:c}=es(i),{start:h,end:u,loop:d,style:f}=function(t,e,n){const{property:i,start:s,end:o}=n,{between:r,normalize:a}=es(i),l=e.length;let c,h,{start:u,end:d,loop:f}=t;if(f){for(u+=l,d+=l,c=0,h=l;cx||l(s,b,g)&&0!==a(s,b),v=()=>!x||0===a(o,g)||l(o,b,g);for(let t=h,n=h;t<=u;++t)m=e[t%r],m.skip||(g=c(m[i]),g!==b&&(x=l(g,s,o),null===y&&_()&&(y=0===a(g,s)?t:n),null!==y&&v()&&(p.push(ns({start:y,end:t,loop:d,count:r,style:f})),y=null),n=t,b=g));return null!==y&&p.push(ns({start:y,end:u,loop:d,count:r,style:f})),p}function ss(t,e,n,i){return i&&i.setContext&&n?function(t,e,n,i){const s=t._chart.getContext(),o=os(t.options),{_datasetIndex:r,options:{spanGaps:a}}=t,l=n.length,c=[];let h=o,u=e[0].start,d=u;function f(t,e,i,s){const o=a?-1:1;if(t!==e){for(t+=l;n[t%l].skip;)t-=o;for(;n[e%l].skip;)e+=o;t%l!=e%l&&(c.push({start:t%l,end:e%l,loop:i,style:s}),h=s,u=e%l)}}for(const t of e){u=a?u:t.start;let e,o=n[u%l];for(d=u+1;d<=t.end;d++){const a=n[d%l];e=os(i.setContext(di(s,{type:"segment",p0:o,p1:a,p0DataIndex:(d-1)%l,p1DataIndex:d%l,datasetIndex:r}))),rs(e,h)&&f(u,d-1,t.loop,h),o=a,h=e}ui({chart:t,initial:e.initial,numSteps:o,currentStep:Math.min(n-e.start,o)})))}_refresh(){this._request||(this._running=!0,this._request=He.call(window,(()=>{this._update(),this._request=null,this._running&&this._refresh()})))}_update(t=Date.now()){let e=0;this._charts.forEach(((n,i)=>{if(!n.running||!n.items.length)return;const s=n.items;let o,r=s.length-1,a=!1;for(;r>=0;--r)o=s[r],o._active?(o._total>n.duration&&(n.duration=o._total),o.tick(t),a=!0):(s[r]=s[s.length-1],s.pop());a&&(i.draw(),this._notify(i,n,t,"progress")),s.length||(n.running=!1,this._notify(i,n,t,"complete"),n.initial=!1),e+=s.length})),this._lastDate=t,0===e&&(this._running=!1)}_getAnims(t){const e=this._charts;let n=e.get(t);return n||(n={running:!1,initial:!0,items:[],listeners:{complete:[],progress:[]}},e.set(t,n)),n}listen(t,e,n){this._getAnims(t).listeners[e].push(n)}add(t,e){e&&e.length&&this._getAnims(t).items.push(...e)}has(t){return this._getAnims(t).items.length>0}start(t){const e=this._charts.get(t);e&&(e.running=!0,e.start=Date.now(),e.duration=e.items.reduce(((t,e)=>Math.max(t,e._duration)),0),this._refresh())}running(t){if(!this._running)return!1;const e=this._charts.get(t);return!!(e&&e.running&&e.items.length)}stop(t){const e=this._charts.get(t);if(!e||!e.items.length)return;const n=e.items;let i=n.length-1;for(;i>=0;--i)n[i].cancel();e.items=[],this._notify(t,e,Date.now(),"complete")}remove(t){return this._charts.delete(t)}};const ls="transparent",cs={boolean:(t,e,n)=>n>.5?e:t,color(t,e,n){const i=zn(t||ls),s=i.valid&&zn(e||ls);return s&&s.valid?s.mix(i,n).hexString():e},number:(t,e,n)=>t+(e-t)*n};class hs{constructor(t,e,n,i){const s=e[n];i=ui([t.to,i,s,t.from]);const o=ui([t.from,s,i]);this._active=!0,this._fn=t.fn||cs[t.type||typeof o],this._easing=Je[t.easing]||Je.linear,this._start=Math.floor(Date.now()+(t.delay||0)),this._duration=this._total=Math.floor(t.duration),this._loop=!!t.loop,this._target=e,this._prop=n,this._from=o,this._to=i,this._promises=void 0}active(){return this._active}update(t,e,n){if(this._active){this._notify(!1);const i=this._target[this._prop],s=n-this._start,o=this._duration-s;this._start=n,this._duration=Math.floor(Math.max(o,t.duration)),this._total+=s,this._loop=!!t.loop,this._to=ui([t.to,e,i,t.from]),this._from=ui([t.from,i,e])}}cancel(){this._active&&(this.tick(Date.now()),this._active=!1,this._notify(!1))}tick(t){const e=t-this._start,n=this._duration,i=this._prop,s=this._from,o=this._loop,r=this._to;let a;if(this._active=s!==r&&(o||e1?2-a:a,a=this._easing(Math.min(1,Math.max(0,a))),this._target[i]=this._fn(s,r,a))}wait(){const t=this._promises||(this._promises=[]);return new Promise(((e,n)=>{t.push({res:e,rej:n})}))}_notify(t){const e=t?"res":"rej",n=this._promises||[];for(let t=0;t"onProgress"!==t&&"onComplete"!==t&&"fn"!==t}),Vn.set("animations",{colors:{type:"color",properties:["color","borderColor","backgroundColor"]},numbers:{type:"number",properties:["x","y","borderWidth","radius","tension"]}}),Vn.describe("animations",{_fallback:"animation"}),Vn.set("transitions",{active:{animation:{duration:400}},resize:{animation:{duration:0}},show:{animations:{colors:{from:"transparent"},visible:{type:"boolean",duration:0}}},hide:{animations:{colors:{to:"transparent"},visible:{type:"boolean",easing:"linear",fn:t=>0|t}}}});class ds{constructor(t,e){this._chart=t,this._properties=new Map,this.configure(e)}configure(t){if(!Yt(t))return;const e=this._properties;Object.getOwnPropertyNames(t).forEach((n=>{const i=t[n];if(!Yt(i))return;const s={};for(const t of us)s[t]=i[t];(qt(i.properties)&&i.properties||[n]).forEach((t=>{t!==n&&e.has(t)||e.set(t,s)}))}))}_animateOptions(t,e){const n=e.options,i=function(t,e){if(!e)return;let n=t.options;if(!n)return void(t.options=e);n.$shared&&(t.options=n=Object.assign({},n,{$shared:!1,$animations:{}}));return n}(t,n);if(!i)return[];const s=this._createAnimations(i,n);return n.$shared&&function(t,e){const n=[],i=Object.keys(e);for(let e=0;e{t.options=n}),(()=>{})),s}_createAnimations(t,e){const n=this._properties,i=[],s=t.$animations||(t.$animations={}),o=Object.keys(e),r=Date.now();let a;for(a=o.length-1;a>=0;--a){const l=o[a];if("$"===l.charAt(0))continue;if("options"===l){i.push(...this._animateOptions(t,e));continue}const c=e[l];let h=s[l];const u=n.get(l);if(h){if(u&&h.active()){h.update(u,c,r);continue}h.cancel()}u&&u.duration?(s[l]=h=new hs(u,t,l,c),i.push(h)):t[l]=c}return i}update(t,e){if(0===this._properties.size)return void Object.assign(t,e);const n=this._createAnimations(t,e);return n.length?(as.add(this._chart,n),!0):void 0}}function fs(t,e){const n=t&&t.options||{},i=n.reverse,s=void 0===n.min?e:0,o=void 0===n.max?e:0;return{start:i?o:s,end:i?s:o}}function ps(t,e){const n=[],i=t._getSortedDatasetMetas(e);let s,o;for(s=0,o=i.length;s0||!n&&e<0)return s.index}return null}function ys(t,e){const{chart:n,_cachedMeta:i}=t,s=n._stacks||(n._stacks={}),{iScale:o,vScale:r,index:a}=i,l=o.axis,c=r.axis,h=function(t,e,n){return`${t.id}.${e.id}.${n.stack||n.type}`}(o,r,i),u=e.length;let d;for(let t=0;tn[t].axis===e)).shift()}function vs(t,e){const n=t.controller.index,i=t.vScale&&t.vScale.axis;if(i){e=e||t._parsed;for(const t of e){const e=t._stacks;if(!e||void 0===e[i]||void 0===e[i][n])return;delete e[i][n]}}}const $s=t=>"reset"===t||"none"===t,ks=(t,e)=>e?t:Object.assign({},t);class ws{constructor(t,e){this.chart=t,this._ctx=t.ctx,this.index=e,this._cachedDataOpts={},this._cachedMeta=this.getMeta(),this._type=this._cachedMeta.type,this.options=void 0,this._parsing=!1,this._data=void 0,this._objectData=void 0,this._sharedOptions=void 0,this._drawStart=void 0,this._drawCount=void 0,this.enableOptionSharing=!1,this.supportsDecimation=!1,this.$context=void 0,this._syncList=[],this.initialize()}initialize(){const t=this._cachedMeta;this.configure(),this.linkScales(),t._stacked=ms(t.vScale,t),this.addElements()}updateIndex(t){this.index!==t&&vs(this._cachedMeta),this.index=t}linkScales(){const t=this.chart,e=this._cachedMeta,n=this.getDataset(),i=(t,e,n,i)=>"x"===t?e:"r"===t?i:n,s=e.xAxisID=Gt(n.xAxisID,_s(t,"x")),o=e.yAxisID=Gt(n.yAxisID,_s(t,"y")),r=e.rAxisID=Gt(n.rAxisID,_s(t,"r")),a=e.indexAxis,l=e.iAxisID=i(a,s,o,r),c=e.vAxisID=i(a,o,s,r);e.xScale=this.getScaleForId(s),e.yScale=this.getScaleForId(o),e.rScale=this.getScaleForId(r),e.iScale=this.getScaleForId(l),e.vScale=this.getScaleForId(c)}getDataset(){return this.chart.data.datasets[this.index]}getMeta(){return this.chart.getDatasetMeta(this.index)}getScaleForId(t){return this.chart.scales[t]}_getOtherScale(t){const e=this._cachedMeta;return t===e.iScale?e.vScale:e.iScale}reset(){this._update("reset")}_destroy(){const t=this._cachedMeta;this._data&&Be(this._data,this),t._stacked&&vs(t)}_dataCheck(){const t=this.getDataset(),e=t.data||(t.data=[]),n=this._data;if(Yt(e))this._data=function(t){const e=Object.keys(t),n=new Array(e.length);let i,s,o;for(i=0,s=e.length;i{const e="_onData"+ce(t),n=i[t];Object.defineProperty(i,t,{configurable:!0,enumerable:!1,value(...t){const s=n.apply(this,t);return i._chartjs.listeners.forEach((n=>{"function"==typeof n[e]&&n[e](...t)})),s}})})))),this._syncList=[],this._data=e}var i,s}addElements(){const t=this._cachedMeta;this._dataCheck(),this.datasetElementType&&(t.dataset=new this.datasetElementType)}buildOrUpdateElements(t){const e=this._cachedMeta,n=this.getDataset();let i=!1;this._dataCheck();const s=e._stacked;e._stacked=ms(e.vScale,e),e.stack!==n.stack&&(i=!0,vs(e),e.stack=n.stack),this._resyncElements(t),(i||s!==e._stacked)&&ys(this,e._parsed)}configure(){const t=this.chart.config,e=t.datasetScopeKeys(this._type),n=t.getOptionScopes(this.getDataset(),e,!0);this.options=t.createResolver(n,this.getContext()),this._parsing=this.options.parsing,this._cachedDataOpts={}}parse(t,e){const{_cachedMeta:n,_data:i}=this,{iScale:s,_stacked:o}=n,r=s.axis;let a,l,c,h=0===t&&e===i.length||n._sorted,u=t>0&&n._parsed[t-1];if(!1===this._parsing)n._parsed=i,n._sorted=!0,c=i;else{c=qt(i[t])?this.parseArrayData(n,i,t,e):Yt(i[t])?this.parseObjectData(n,i,t,e):this.parsePrimitiveData(n,i,t,e);const s=()=>null===l[r]||u&&l[r]t&&!e.hidden&&e._stacked&&{keys:ps(n,!0),values:null})(e,n,this.chart),l={min:Number.POSITIVE_INFINITY,max:Number.NEGATIVE_INFINITY},{min:c,max:h}=function(t){const{min:e,max:n,minDefined:i,maxDefined:s}=t.getUserBounds();return{min:i?e:Number.NEGATIVE_INFINITY,max:s?n:Number.POSITIVE_INFINITY}}(r);let u,d;function f(){d=i[u];const e=d[r.axis];return!Zt(d[t.axis])||c>e||h=0;--u)if(!f()){this.updateRangeFromParsed(l,t,d,a);break}return l}getAllParsedValues(t){const e=this._cachedMeta._parsed,n=[];let i,s,o;for(i=0,s=e.length;i=0&&tthis.getContext(n,i)),h);return f.$shared&&(f.$shared=a,s[o]=Object.freeze(ks(f,a))),f}_resolveAnimations(t,e,n){const i=this.chart,s=this._cachedDataOpts,o=`animation-${e}`,r=s[o];if(r)return r;let a;if(!1!==i.options.animation){const i=this.chart.config,s=i.datasetAnimationScopeKeys(this._type,e),o=i.getOptionScopes(this.getDataset(),s);a=i.createResolver(o,this.getContext(t,n,e))}const l=new ds(i,a&&a.animations);return a&&a._cacheable&&(s[o]=Object.freeze(l)),l}getSharedOptions(t){if(t.$shared)return this._sharedOptions||(this._sharedOptions=Object.assign({},t))}includeOptions(t,e){return!e||$s(t)||this.chart._animationsDisabled}_getSharedOptions(t,e){const n=this.resolveDataElementOptions(t,e),i=this._sharedOptions,s=this.getSharedOptions(n),o=this.includeOptions(e,s)||s!==i;return this.updateSharedOptions(s,e,n),{sharedOptions:s,includeOptions:o}}updateElement(t,e,n,i){$s(i)?Object.assign(t,n):this._resolveAnimations(e,i).update(t,n)}updateSharedOptions(t,e,n){t&&!$s(e)&&this._resolveAnimations(void 0,e).update(t,n)}_setStyle(t,e,n,i){t.active=i;const s=this.getStyle(e,i);this._resolveAnimations(e,n,i).update(t,{options:!i&&this.getSharedOptions(s)||s})}removeHoverStyle(t,e,n){this._setStyle(t,n,"active",!1)}setHoverStyle(t,e,n){this._setStyle(t,n,"active",!0)}_removeDatasetHoverStyle(){const t=this._cachedMeta.dataset;t&&this._setStyle(t,void 0,"active",!1)}_setDatasetHoverStyle(){const t=this._cachedMeta.dataset;t&&this._setStyle(t,void 0,"active",!0)}_resyncElements(t){const e=this._data,n=this._cachedMeta.data;for(const[t,e,n]of this._syncList)this[t](e,n);this._syncList=[];const i=n.length,s=e.length,o=Math.min(s,i);o&&this.parse(0,o),s>i?this._insertElements(i,s-i,t):s{for(t.length+=e,r=t.length-1;r>=o;r--)t[r]=t[r-e]};for(a(s),r=t;rt-e)))}return t._cache.$bar}(e,t.type);let i,s,o,r,a=e._length;const l=()=>{32767!==o&&-32768!==o&&(he(r)&&(a=Math.min(a,Math.abs(o-r)||a)),r=o)};for(i=0,s=n.length;iMath.abs(a)&&(l=a,c=r),e[n.axis]=c,e._custom={barStart:l,barEnd:c,start:s,end:o,min:r,max:a}}(t,e,n,i):e[n.axis]=n.parse(t,i),e}function Ss(t,e,n,i){const s=t.iScale,o=t.vScale,r=s.getLabels(),a=s===o,l=[];let c,h,u,d;for(c=n,h=n+i;ct.x,n="left",i="right"):(e=t.baset.controller.options.grouped)),s=n.options.stacked,o=[],r=t=>{const n=t.controller.getParsed(e),i=n&&n[t.vScale.axis];if(Ut(i)||isNaN(i))return!0};for(const n of i)if((void 0===e||!r(n))&&((!1===s||-1===o.indexOf(n.stack)||void 0===s&&void 0===n.stack)&&o.push(n.stack),n.index===t))break;return o.length||o.push(void 0),o}_getStackCount(t){return this._getStacks(void 0,t).length}_getStackIndex(t,e,n){const i=this._getStacks(t,n),s=void 0!==e?i.indexOf(e):-1;return-1===s?i.length-1:s}_getRuler(){const t=this.options,e=this._cachedMeta,n=e.iScale,i=[];let s,o;for(s=0,o=e.data.length;s=n?1:-1)}(h,e,o)*s,u===o&&(g-=h/2);const t=e.getPixelForDecimal(0),n=e.getPixelForDecimal(1),i=Math.min(t,n),r=Math.max(t,n);g=Math.max(Math.min(g,r),i),c=g+h}if(g===e.getPixelForValue(o)){const t=$e(h)*e.getLineWidthForValue(o)/2;g+=t,h-=t}return{size:h,base:g,head:c,center:c+h/2}}_calculateBarIndexPixels(t,e){const n=e.scale,i=this.options,s=i.skipNull,o=Gt(i.maxBarThickness,1/0);let r,a;if(e.grouped){const n=s?this._getStackCount(t):e.stackCount,l="flex"===i.barThickness?function(t,e,n,i){const s=e.pixels,o=s[t];let r=t>0?s[t-1]:null,a=t=0;--n)e=Math.max(e,t[n].size(this.resolveDataElementOptions(n))/2);return e>0&&e}getLabelAndValue(t){const e=this._cachedMeta,{xScale:n,yScale:i}=e,s=this.getParsed(t),o=n.getLabelForValue(s.x),r=i.getLabelForValue(s.y),a=s._custom;return{label:e.label,value:"("+o+", "+r+(a?", "+a:"")+")"}}update(t){const e=this._cachedMeta.data;this.updateElements(e,0,e.length,t)}updateElements(t,e,n,i){const s="reset"===i,{iScale:o,vScale:r}=this._cachedMeta,{sharedOptions:a,includeOptions:l}=this._getSharedOptions(e,i),c=o.axis,h=r.axis;for(let u=e;u""}}}};class Rs extends ws{constructor(t,e){super(t,e),this.enableOptionSharing=!0,this.innerRadius=void 0,this.outerRadius=void 0,this.offsetX=void 0,this.offsetY=void 0}linkScales(){}parse(t,e){const n=this.getDataset().data,i=this._cachedMeta;if(!1===this._parsing)i._parsed=n;else{let s,o,r=t=>+n[t];if(Yt(n[t])){const{key:t="value"}=this._parsing;r=e=>+le(n[e],t)}for(s=t,o=t+e;sTe(t,a,l,!0)?1:Math.max(e,e*n,i,i*n),p=(t,e,i)=>Te(t,a,l,!0)?-1:Math.min(e,e*n,i,i*n),g=f(0,c,u),m=f(xe,h,d),b=p(fe,c,u),x=p(fe+xe,h,d);i=(g-b)/2,s=(m-x)/2,o=-(g+b)/2,r=-(m+x)/2}return{ratioX:i,ratioY:s,offsetX:o,offsetY:r}}(d,u,a),b=(n.width-o)/f,x=(n.height-o)/p,y=Math.max(Math.min(b,x)/2,0),_=Kt(this.options.radius,y),v=(_-Math.max(_*a,0))/this._getVisibleDatasetWeightTotal();this.offsetX=g*_,this.offsetY=m*_,i.total=this.calculateTotal(),this.outerRadius=_-v*this._getRingWeightOffset(this.index),this.innerRadius=Math.max(this.outerRadius-v*h,0),this.updateElements(s,0,s.length,t)}_circumference(t,e){const n=this.options,i=this._cachedMeta,s=this._getCircumference();return e&&n.animation.animateRotate||!this.chart.getDataVisibility(t)||null===i._parsed[t]||i.data[t].hidden?0:this.calculateCircumference(i._parsed[t]*s/pe)}updateElements(t,e,n,i){const s="reset"===i,o=this.chart,r=o.chartArea,a=o.options.animation,l=(r.left+r.right)/2,c=(r.top+r.bottom)/2,h=s&&a.animateScale,u=h?0:this.innerRadius,d=h?0:this.outerRadius,{sharedOptions:f,includeOptions:p}=this._getSharedOptions(e,i);let g,m=this._getRotation();for(g=0;g0&&!isNaN(t)?pe*(Math.abs(t)/e):0}getLabelAndValue(t){const e=this._cachedMeta,n=this.chart,i=n.data.labels||[],s=Ki(e._parsed[t],n.options.locale);return{label:i[t]||"",value:s}}getMaxBorderWidth(t){let e=0;const n=this.chart;let i,s,o,r,a;if(!t)for(i=0,s=n.data.datasets.length;i"spacing"!==t,_indexable:t=>"spacing"!==t},Rs.overrides={aspectRatio:1,plugins:{legend:{labels:{generateLabels(t){const e=t.data;if(e.labels.length&&e.datasets.length){const{labels:{pointStyle:n}}=t.legend.options;return e.labels.map(((e,i)=>{const s=t.getDatasetMeta(0).controller.getStyle(i);return{text:e,fillStyle:s.backgroundColor,strokeStyle:s.borderColor,lineWidth:s.borderWidth,pointStyle:n,hidden:!t.getDataVisibility(i),index:i}}))}return[]}},onClick(t,e,n){n.chart.toggleDataVisibility(e.index),n.chart.update()}},tooltip:{callbacks:{title:()=>"",label(t){let e=t.label;const n=": "+t.formattedValue;return qt(e)?(e=e.slice(),e[0]+=n):e+=n,e}}}}};class zs extends ws{initialize(){this.enableOptionSharing=!0,this.supportsDecimation=!0,super.initialize()}update(t){const e=this._cachedMeta,{dataset:n,data:i=[],_dataset:s}=e,o=this.chart._animationsDisabled;let{start:r,count:a}=Ye(e,i,o);this._drawStart=r,this._drawCount=a,Ze(e)&&(r=0,a=i.length),n._chart=this.chart,n._datasetIndex=this.index,n._decimated=!!s._decimated,n.points=i;const l=this.resolveDatasetElementOptions(t);this.options.showLine||(l.borderWidth=0),l.segment=this.options.segment,this.updateElement(n,void 0,{animated:!o,options:l},t),this.updateElements(i,r,a,t)}updateElements(t,e,n,i){const s="reset"===i,{iScale:o,vScale:r,_stacked:a,_dataset:l}=this._cachedMeta,{sharedOptions:c,includeOptions:h}=this._getSharedOptions(e,i),u=o.axis,d=r.axis,{spanGaps:f,segment:p}=this.options,g=we(f)?f:Number.POSITIVE_INFINITY,m=this.chart._animationsDisabled||s||"none"===i;let b=e>0&&this.getParsed(e-1);for(let f=e;f0&&Math.abs(n[u]-b[u])>g,p&&(x.parsed=n,x.raw=l.data[f]),h&&(x.options=c||this.resolveDataElementOptions(f,e.active?"active":i)),m||this.updateElement(e,f,x,i),b=n}}getMaxOverflow(){const t=this._cachedMeta,e=t.dataset,n=e.options&&e.options.borderWidth||0,i=t.data||[];if(!i.length)return n;const s=i[0].size(this.resolveDataElementOptions(0)),o=i[i.length-1].size(this.resolveDataElementOptions(i.length-1));return Math.max(n,s,o)/2}draw(){const t=this._cachedMeta;t.dataset.updateControlPoints(this.chart.chartArea,t.iScale.axis),super.draw()}}zs.id="line",zs.defaults={datasetElementType:"line",dataElementType:"point",showLine:!0,spanGaps:!1},zs.overrides={scales:{_index_:{type:"category"},_value_:{type:"linear"}}};class Is extends ws{constructor(t,e){super(t,e),this.innerRadius=void 0,this.outerRadius=void 0}getLabelAndValue(t){const e=this._cachedMeta,n=this.chart,i=n.data.labels||[],s=Ki(e._parsed[t].r,n.options.locale);return{label:i[t]||"",value:s}}parseObjectData(t,e,n,i){return Mi.bind(this)(t,e,n,i)}update(t){const e=this._cachedMeta.data;this._updateRadius(),this.updateElements(e,0,e.length,t)}getMinMax(){const t=this._cachedMeta,e={min:Number.POSITIVE_INFINITY,max:Number.NEGATIVE_INFINITY};return t.data.forEach(((t,n)=>{const i=this.getParsed(n).r;!isNaN(i)&&this.chart.getDataVisibility(n)&&(ie.max&&(e.max=i))})),e}_updateRadius(){const t=this.chart,e=t.chartArea,n=t.options,i=Math.min(e.right-e.left,e.bottom-e.top),s=Math.max(i/2,0),o=(s-Math.max(n.cutoutPercentage?s/100*n.cutoutPercentage:1,0))/t.getVisibleDatasetCount();this.outerRadius=s-o*this.index,this.innerRadius=this.outerRadius-o}updateElements(t,e,n,i){const s="reset"===i,o=this.chart,r=o.options.animation,a=this._cachedMeta.rScale,l=a.xCenter,c=a.yCenter,h=a.getIndexAngle(0)-.5*fe;let u,d=h;const f=360/this.countVisibleElements();for(u=0;u{!isNaN(this.getParsed(n).r)&&this.chart.getDataVisibility(n)&&e++})),e}_computeAngle(t,e,n){return this.chart.getDataVisibility(t)?Se(this.resolveDataElementOptions(t,e).angle||n):0}}Is.id="polarArea",Is.defaults={dataElementType:"arc",animation:{animateRotate:!0,animateScale:!0},animations:{numbers:{type:"number",properties:["x","y","startAngle","endAngle","innerRadius","outerRadius"]}},indexAxis:"r",startAngle:0},Is.overrides={aspectRatio:1,plugins:{legend:{labels:{generateLabels(t){const e=t.data;if(e.labels.length&&e.datasets.length){const{labels:{pointStyle:n}}=t.legend.options;return e.labels.map(((e,i)=>{const s=t.getDatasetMeta(0).controller.getStyle(i);return{text:e,fillStyle:s.backgroundColor,strokeStyle:s.borderColor,lineWidth:s.borderWidth,pointStyle:n,hidden:!t.getDataVisibility(i),index:i}}))}return[]}},onClick(t,e,n){n.chart.toggleDataVisibility(e.index),n.chart.update()}},tooltip:{callbacks:{title:()=>"",label:t=>t.chart.data.labels[t.dataIndex]+": "+t.formattedValue}}},scales:{r:{type:"radialLinear",angleLines:{display:!1},beginAtZero:!0,grid:{circular:!0},pointLabels:{display:!1},startAngle:0}}};class Fs extends Rs{}Fs.id="pie",Fs.defaults={cutout:0,rotation:0,circumference:360,radius:"100%"};class Ns extends ws{getLabelAndValue(t){const e=this._cachedMeta.vScale,n=this.getParsed(t);return{label:e.getLabels()[t],value:""+e.getLabelForValue(n[e.axis])}}parseObjectData(t,e,n,i){return Mi.bind(this)(t,e,n,i)}update(t){const e=this._cachedMeta,n=e.dataset,i=e.data||[],s=e.iScale.getLabels();if(n.points=i,"resize"!==t){const e=this.resolveDatasetElementOptions(t);this.options.showLine||(e.borderWidth=0);const o={_loop:!0,_fullLoop:s.length===i.length,options:e};this.updateElement(n,void 0,o,t)}this.updateElements(i,0,i.length,t)}updateElements(t,e,n,i){const s=this._cachedMeta.rScale,o="reset"===i;for(let r=e;r{i[t]=n[t]&&n[t].active()?n[t]._to:this[t]})),i}}js.defaults={},js.defaultRoutes=void 0;const Bs={values:t=>qt(t)?t:""+t,numeric(t,e,n){if(0===t)return"0";const i=this.chart.options.locale;let s,o=t;if(n.length>1){const e=Math.max(Math.abs(n[0].value),Math.abs(n[n.length-1].value));(e<1e-4||e>1e15)&&(s="scientific"),o=function(t,e){let n=e.length>3?e[2].value-e[1].value:e[1].value-e[0].value;Math.abs(n)>=1&&t!==Math.floor(t)&&(n=t-Math.floor(t));return n}(t,n)}const r=ve(Math.abs(o)),a=Math.max(Math.min(-1*Math.floor(r),20),0),l={notation:s,minimumFractionDigits:a,maximumFractionDigits:a};return Object.assign(l,this.options.ticks.format),Ki(t,i,l)},logarithmic(t,e,n){if(0===t)return"0";const i=t/Math.pow(10,Math.floor(ve(t)));return 1===i||2===i||5===i?Bs.numeric.call(this,t,e,n):""}};var Vs={formatters:Bs};function Hs(t,e){const n=t.options.ticks,i=n.maxTicksLimit||function(t){const e=t.options.offset,n=t._tickSize(),i=t._length/n+(e?0:1),s=t._maxLength/n;return Math.floor(Math.min(i,s))}(t),s=n.major.enabled?function(t){const e=[];let n,i;for(n=0,i=t.length;ni)return function(t,e,n,i){let s,o=0,r=n[0];for(i=Math.ceil(i),s=0;st-e)).pop(),e}(i);for(let t=0,e=o.length-1;ts)return e}return Math.max(s,1)}(s,e,i);if(o>0){let t,n;const i=o>1?Math.round((a-r)/(o-1)):null;for(Ws(e,l,c,Ut(i)?0:r-i,r),t=0,n=o-1;te.lineWidth,tickColor:(t,e)=>e.color,offset:!1,borderDash:[],borderDashOffset:0,borderWidth:1},title:{display:!1,text:"",padding:{top:4,bottom:4}},ticks:{minRotation:0,maxRotation:50,mirror:!1,textStrokeWidth:0,textStrokeColor:"",padding:3,display:!0,autoSkip:!0,autoSkipPadding:3,labelOffset:0,callback:Vs.formatters.values,minor:{},major:{},align:"center",crossAlign:"near",showLabelBackdrop:!1,backdropColor:"rgba(255, 255, 255, 0.75)",backdropPadding:2}}),Vn.route("scale.ticks","color","","color"),Vn.route("scale.grid","color","","borderColor"),Vn.route("scale.grid","borderColor","","borderColor"),Vn.route("scale.title","color","","color"),Vn.describe("scale",{_fallback:!1,_scriptable:t=>!t.startsWith("before")&&!t.startsWith("after")&&"callback"!==t&&"parser"!==t,_indexable:t=>"borderDash"!==t&&"tickBorderDash"!==t}),Vn.describe("scales",{_fallback:"scale"}),Vn.describe("scale.ticks",{_scriptable:t=>"backdropPadding"!==t&&"callback"!==t,_indexable:t=>"backdropPadding"!==t});const Us=t=>"left"===t?"right":"right"===t?"left":t,qs=(t,e,n)=>"top"===e||"left"===e?t[e]+n:t[e]-n;function Ys(t,e){const n=[],i=t.length/e,s=t.length;let o=0;for(;or+a)))return c}function Xs(t){return t.drawTicks?t.tickLength:0}function Gs(t,e){if(!t.display)return 0;const n=hi(t.font,e),i=ci(t.padding);return(qt(t.text)?t.text.length:1)*n.lineHeight+i.height}function Ks(t,e,n){let i=Ue(t);return(n&&"right"!==e||!n&&"right"===e)&&(i=Us(i)),i}class Js extends js{constructor(t){super(),this.id=t.id,this.type=t.type,this.options=void 0,this.ctx=t.ctx,this.chart=t.chart,this.top=void 0,this.bottom=void 0,this.left=void 0,this.right=void 0,this.width=void 0,this.height=void 0,this._margins={left:0,right:0,top:0,bottom:0},this.maxWidth=void 0,this.maxHeight=void 0,this.paddingTop=void 0,this.paddingBottom=void 0,this.paddingLeft=void 0,this.paddingRight=void 0,this.axis=void 0,this.labelRotation=void 0,this.min=void 0,this.max=void 0,this._range=void 0,this.ticks=[],this._gridLineItems=null,this._labelItems=null,this._labelSizes=null,this._length=0,this._maxLength=0,this._longestTextCache={},this._startPixel=void 0,this._endPixel=void 0,this._reversePixels=!1,this._userMax=void 0,this._userMin=void 0,this._suggestedMax=void 0,this._suggestedMin=void 0,this._ticksLength=0,this._borderValue=0,this._cache={},this._dataLimitsCached=!1,this.$context=void 0}init(t){this.options=t.setContext(this.getContext()),this.axis=t.axis,this._userMin=this.parse(t.min),this._userMax=this.parse(t.max),this._suggestedMin=this.parse(t.suggestedMin),this._suggestedMax=this.parse(t.suggestedMax)}parse(t,e){return t}getUserBounds(){let{_userMin:t,_userMax:e,_suggestedMin:n,_suggestedMax:i}=this;return t=Xt(t,Number.POSITIVE_INFINITY),e=Xt(e,Number.NEGATIVE_INFINITY),n=Xt(n,Number.POSITIVE_INFINITY),i=Xt(i,Number.NEGATIVE_INFINITY),{min:Xt(t,n),max:Xt(e,i),minDefined:Zt(t),maxDefined:Zt(e)}}getMinMax(t){let e,{min:n,max:i,minDefined:s,maxDefined:o}=this.getUserBounds();if(s&&o)return{min:n,max:i};const r=this.getMatchingVisibleMetas();for(let a=0,l=r.length;ai?i:n,i=s&&n>i?n:i,{min:Xt(n,Xt(i,n)),max:Xt(i,Xt(n,i))}}getPadding(){return{left:this.paddingLeft||0,top:this.paddingTop||0,right:this.paddingRight||0,bottom:this.paddingBottom||0}}getTicks(){return this.ticks}getLabels(){const t=this.chart.data;return this.options.labels||(this.isHorizontal()?t.xLabels:t.yLabels)||t.labels||[]}beforeLayout(){this._cache={},this._dataLimitsCached=!1}beforeUpdate(){Jt(this.options.beforeUpdate,[this])}update(t,e,n){const{beginAtZero:i,grace:s,ticks:o}=this.options,r=o.sampleSize;this.beforeUpdate(),this.maxWidth=t,this.maxHeight=e,this._margins=n=Object.assign({left:0,right:0,top:0,bottom:0},n),this.ticks=null,this._labelSizes=null,this._gridLineItems=null,this._labelItems=null,this.beforeSetDimensions(),this.setDimensions(),this.afterSetDimensions(),this._maxLength=this.isHorizontal()?this.width+n.left+n.right:this.height+n.top+n.bottom,this._dataLimitsCached||(this.beforeDataLimits(),this.determineDataLimits(),this.afterDataLimits(),this._range=function(t,e,n){const{min:i,max:s}=t,o=Kt(e,(s-i)/2),r=(t,e)=>n&&0===t?0:t+e;return{min:r(i,-Math.abs(o)),max:r(s,o)}}(this,s,i),this._dataLimitsCached=!0),this.beforeBuildTicks(),this.ticks=this.buildTicks()||[],this.afterBuildTicks();const a=r=s||n<=1||!this.isHorizontal())return void(this.labelRotation=i);const c=this._getLabelSizes(),h=c.widest.width,u=c.highest.height,d=Re(this.chart.width-h,0,this.maxWidth);o=t.offset?this.maxWidth/n:d/(n-1),h+6>o&&(o=d/(n-(t.offset?.5:1)),r=this.maxHeight-Xs(t.grid)-e.padding-Gs(t.title,this.chart.options.font),a=Math.sqrt(h*h+u*u),l=Le(Math.min(Math.asin(Re((c.highest.height+6)/o,-1,1)),Math.asin(Re(r/a,-1,1))-Math.asin(Re(u/a,-1,1)))),l=Math.max(i,Math.min(s,l))),this.labelRotation=l}afterCalculateLabelRotation(){Jt(this.options.afterCalculateLabelRotation,[this])}afterAutoSkip(){}beforeFit(){Jt(this.options.beforeFit,[this])}fit(){const t={width:0,height:0},{chart:e,options:{ticks:n,title:i,grid:s}}=this,o=this._isVisible(),r=this.isHorizontal();if(o){const o=Gs(i,e.options.font);if(r?(t.width=this.maxWidth,t.height=Xs(s)+o):(t.height=this.maxHeight,t.width=Xs(s)+o),n.display&&this.ticks.length){const{first:e,last:i,widest:s,highest:o}=this._getLabelSizes(),a=2*n.padding,l=Se(this.labelRotation),c=Math.cos(l),h=Math.sin(l);if(r){const e=n.mirror?0:h*s.width+c*o.height;t.height=Math.min(this.maxHeight,t.height+e+a)}else{const e=n.mirror?0:c*s.width+h*o.height;t.width=Math.min(this.maxWidth,t.width+e+a)}this._calculatePadding(e,i,h,c)}}this._handleMargins(),r?(this.width=this._length=e.width-this._margins.left-this._margins.right,this.height=t.height):(this.width=t.width,this.height=this._length=e.height-this._margins.top-this._margins.bottom)}_calculatePadding(t,e,n,i){const{ticks:{align:s,padding:o},position:r}=this.options,a=0!==this.labelRotation,l="top"!==r&&"x"===this.axis;if(this.isHorizontal()){const r=this.getPixelForTick(0)-this.left,c=this.right-this.getPixelForTick(this.ticks.length-1);let h=0,u=0;a?l?(h=i*t.width,u=n*e.height):(h=n*t.height,u=i*e.width):"start"===s?u=e.width:"end"===s?h=t.width:"inner"!==s&&(h=t.width/2,u=e.width/2),this.paddingLeft=Math.max((h-r+o)*this.width/(this.width-r),0),this.paddingRight=Math.max((u-c+o)*this.width/(this.width-c),0)}else{let n=e.height/2,i=t.height/2;"start"===s?(n=0,i=t.height):"end"===s&&(n=e.height,i=0),this.paddingTop=n+o,this.paddingBottom=i+o}}_handleMargins(){this._margins&&(this._margins.left=Math.max(this.paddingLeft,this._margins.left),this._margins.top=Math.max(this.paddingTop,this._margins.top),this._margins.right=Math.max(this.paddingRight,this._margins.right),this._margins.bottom=Math.max(this.paddingBottom,this._margins.bottom))}afterFit(){Jt(this.options.afterFit,[this])}isHorizontal(){const{axis:t,position:e}=this.options;return"top"===e||"bottom"===e||"x"===t}isFullSize(){return this.options.fullSize}_convertTicksToLabels(t){let e,n;for(this.beforeTickToLabelConversion(),this.generateTickLabels(t),e=0,n=t.length;e{const n=t.gc,i=n.length/2;let s;if(i>e){for(s=0;s({width:s[t]||0,height:o[t]||0});return{first:v(0),last:v(e-1),widest:v(y),highest:v(_),widths:s,heights:o}}getLabelForValue(t){return t}getPixelForValue(t,e){return NaN}getValueForPixel(t){}getPixelForTick(t){const e=this.ticks;return t<0||t>e.length-1?null:this.getPixelForValue(e[t].value)}getPixelForDecimal(t){this._reversePixels&&(t=1-t);const e=this._startPixel+t*this._length;return Re(this._alignToPixels?Un(this.chart,e,0):e,-32768,32767)}getDecimalForPixel(t){const e=(t-this._startPixel)/this._length;return this._reversePixels?1-e:e}getBasePixel(){return this.getPixelForValue(this.getBaseValue())}getBaseValue(){const{min:t,max:e}=this;return t<0&&e<0?e:t>0&&e>0?t:0}getContext(t){const e=this.ticks||[];if(t>=0&&tr*i?r/n:a/i:a*i0}_computeGridLineItems(t){const e=this.axis,n=this.chart,i=this.options,{grid:s,position:o}=i,r=s.offset,a=this.isHorizontal(),l=this.ticks.length+(r?1:0),c=Xs(s),h=[],u=s.setContext(this.getContext()),d=u.drawBorder?u.borderWidth:0,f=d/2,p=function(t){return Un(n,t,d)};let g,m,b,x,y,_,v,$,k,w,C,M;if("top"===o)g=p(this.bottom),_=this.bottom-c,$=g-f,w=p(t.top)+f,M=t.bottom;else if("bottom"===o)g=p(this.top),w=t.top,M=p(t.bottom)-f,_=g+f,$=this.top+c;else if("left"===o)g=p(this.right),y=this.right-c,v=g-f,k=p(t.left)+f,C=t.right;else if("right"===o)g=p(this.left),k=t.left,C=p(t.right)-f,y=g+f,v=this.left+c;else if("x"===e){if("center"===o)g=p((t.top+t.bottom)/2+.5);else if(Yt(o)){const t=Object.keys(o)[0],e=o[t];g=p(this.chart.scales[t].getPixelForValue(e))}w=t.top,M=t.bottom,_=g+f,$=_+c}else if("y"===e){if("center"===o)g=p((t.left+t.right)/2);else if(Yt(o)){const t=Object.keys(o)[0],e=o[t];g=p(this.chart.scales[t].getPixelForValue(e))}y=g-f,v=y-c,k=t.left,C=t.right}const S=Gt(i.ticks.maxTicksLimit,l),L=Math.max(1,Math.ceil(l/S));for(m=0;me.value===t));if(n>=0){return e.setContext(this.getContext(n)).lineWidth}return 0}drawGrid(t){const e=this.options.grid,n=this.ctx,i=this._gridLineItems||(this._gridLineItems=this._computeGridLineItems(t));let s,o;const r=(t,e,i)=>{i.width&&i.color&&(n.save(),n.lineWidth=i.width,n.strokeStyle=i.color,n.setLineDash(i.borderDash||[]),n.lineDashOffset=i.borderDashOffset,n.beginPath(),n.moveTo(t.x,t.y),n.lineTo(e.x,e.y),n.stroke(),n.restore())};if(e.display)for(s=0,o=i.length;s{this.drawBackground(),this.drawGrid(t),this.drawTitle()}},{z:n+1,draw:()=>{this.drawBorder()}},{z:e,draw:t=>{this.drawLabels(t)}}]:[{z:e,draw:t=>{this.draw(t)}}]}getMatchingVisibleMetas(t){const e=this.chart.getSortedVisibleDatasetMetas(),n=this.axis+"AxisID",i=[];let s,o;for(s=0,o=e.length;s{const i=n.split("."),s=i.pop(),o=[t].concat(i).join("."),r=e[n].split("."),a=r.pop(),l=r.join(".");Vn.route(o,s,l,a)}))}(e,t.defaultRoutes);t.descriptors&&Vn.describe(e,t.descriptors)}(t,o,n),this.override&&Vn.override(t.id,t.overrides)),o}get(t){return this.items[t]}unregister(t){const e=this.items,n=t.id,i=this.scope;n in e&&delete e[n],i&&n in Vn[i]&&(delete Vn[i][n],this.override&&delete Fn[n])}}var to=new class{constructor(){this.controllers=new Qs(ws,"datasets",!0),this.elements=new Qs(js,"elements"),this.plugins=new Qs(Object,"plugins"),this.scales=new Qs(Js,"scales"),this._typedRegistries=[this.controllers,this.scales,this.elements]}add(...t){this._each("register",t)}remove(...t){this._each("unregister",t)}addControllers(...t){this._each("register",t,this.controllers)}addElements(...t){this._each("register",t,this.elements)}addPlugins(...t){this._each("register",t,this.plugins)}addScales(...t){this._each("register",t,this.scales)}getController(t){return this._get(t,this.controllers,"controller")}getElement(t){return this._get(t,this.elements,"element")}getPlugin(t){return this._get(t,this.plugins,"plugin")}getScale(t){return this._get(t,this.scales,"scale")}removeControllers(...t){this._each("unregister",t,this.controllers)}removeElements(...t){this._each("unregister",t,this.elements)}removePlugins(...t){this._each("unregister",t,this.plugins)}removeScales(...t){this._each("unregister",t,this.scales)}_each(t,e,n){[...e].forEach((e=>{const i=n||this._getRegistryForType(e);n||i.isForType(e)||i===this.plugins&&e.id?this._exec(t,i,e):Qt(e,(e=>{const i=n||this._getRegistryForType(e);this._exec(t,i,e)}))}))}_exec(t,e,n){const i=ce(t);Jt(n["before"+i],[],n),e[t](n),Jt(n["after"+i],[],n)}_getRegistryForType(t){for(let e=0;e0&&this.getParsed(e-1);for(let c=e;c0&&Math.abs(n[d]-x[d])>m,g&&(p.parsed=n,p.raw=l.data[c]),u&&(p.options=h||this.resolveDataElementOptions(c,e.active?"active":i)),b||this.updateElement(e,c,p,i),x=n}this.updateSharedOptions(h,i,c)}getMaxOverflow(){const t=this._cachedMeta,e=t.data||[];if(!this.options.showLine){let t=0;for(let n=e.length-1;n>=0;--n)t=Math.max(t,e[n].size(this.resolveDataElementOptions(n))/2);return t>0&&t}const n=t.dataset,i=n.options&&n.options.borderWidth||0;if(!e.length)return i;const s=e[0].size(this.resolveDataElementOptions(0)),o=e[e.length-1].size(this.resolveDataElementOptions(e.length-1));return Math.max(i,s,o)/2}}function no(){throw new Error("This method is not implemented: Check that a complete date adapter is provided.")}eo.id="scatter",eo.defaults={datasetElementType:!1,dataElementType:"point",showLine:!1,fill:!1},eo.overrides={interaction:{mode:"point"},plugins:{tooltip:{callbacks:{title:()=>"",label:t=>"("+t.label+", "+t.formattedValue+")"}}},scales:{x:{type:"linear"},y:{type:"linear"}}};class io{constructor(t){this.options=t||{}}init(t){}formats(){return no()}parse(t,e){return no()}format(t,e){return no()}add(t,e,n){return no()}diff(t,e,n){return no()}startOf(t,e,n){return no()}endOf(t,e){return no()}}io.override=function(t){Object.assign(io.prototype,t)};var so={_date:io};function oo(t,e,n,i){const{controller:s,data:o,_sorted:r}=t,a=s._cachedMeta.iScale;if(a&&e===a.axis&&"r"!==e&&r&&o.length){const t=a._reversePixels?Ne:Fe;if(!i)return t(o,e,n);if(s._sharedOptions){const i=o[0],s="function"==typeof i.getRange&&i.getRange(e);if(s){const i=t(o,e,n-s),r=t(o,e,n+s);return{lo:i.lo,hi:r.hi}}}}return{lo:0,hi:o.length-1}}function ro(t,e,n,i,s){const o=t.getSortedVisibleDatasetMetas(),r=n[e];for(let t=0,n=o.length;t{t[r](e[n],s)&&(o.push({element:t,datasetIndex:i,index:l}),a=a||t.inRange(e.x,e.y,s))})),i&&!a?[]:o}var uo={evaluateInteractionItems:ro,modes:{index(t,e,n,i){const s=Bi(e,t),o=n.axis||"x",r=n.includeInvisible||!1,a=n.intersect?ao(t,s,o,i,r):co(t,s,o,!1,i,r),l=[];return a.length?(t.getSortedVisibleDatasetMetas().forEach((t=>{const e=a[0].index,n=t.data[e];n&&!n.skip&&l.push({element:n,datasetIndex:t.index,index:e})})),l):[]},dataset(t,e,n,i){const s=Bi(e,t),o=n.axis||"xy",r=n.includeInvisible||!1;let a=n.intersect?ao(t,s,o,i,r):co(t,s,o,!1,i,r);if(a.length>0){const e=a[0].datasetIndex,n=t.getDatasetMeta(e).data;a=[];for(let t=0;tao(t,Bi(e,t),n.axis||"xy",i,n.includeInvisible||!1),nearest(t,e,n,i){const s=Bi(e,t),o=n.axis||"xy",r=n.includeInvisible||!1;return co(t,s,o,n.intersect,i,r)},x:(t,e,n,i)=>ho(t,Bi(e,t),"x",n.intersect,i),y:(t,e,n,i)=>ho(t,Bi(e,t),"y",n.intersect,i)}};const fo=["left","top","right","bottom"];function po(t,e){return t.filter((t=>t.pos===e))}function go(t,e){return t.filter((t=>-1===fo.indexOf(t.pos)&&t.box.axis===e))}function mo(t,e){return t.sort(((t,n)=>{const i=e?n:t,s=e?t:n;return i.weight===s.weight?i.index-s.index:i.weight-s.weight}))}function bo(t,e){const n=function(t){const e={};for(const n of t){const{stack:t,pos:i,stackWeight:s}=n;if(!t||!fo.includes(i))continue;const o=e[t]||(e[t]={count:0,placed:0,weight:0,size:0});o.count++,o.weight+=s}return e}(t),{vBoxMaxWidth:i,hBoxMaxHeight:s}=e;let o,r,a;for(o=0,r=t.length;o{i[t]=Math.max(e[t],n[t])})),i}return i(t?["left","right"]:["top","bottom"])}function $o(t,e,n,i){const s=[];let o,r,a,l,c,h;for(o=0,r=t.length,c=0;ot.box.fullSize)),!0),i=mo(po(e,"left"),!0),s=mo(po(e,"right")),o=mo(po(e,"top"),!0),r=mo(po(e,"bottom")),a=go(e,"x"),l=go(e,"y");return{fullSize:n,leftAndTop:i.concat(o),rightAndBottom:s.concat(l).concat(r).concat(a),chartArea:po(e,"chartArea"),vertical:i.concat(s).concat(l),horizontal:o.concat(r).concat(a)}}(t.boxes),l=a.vertical,c=a.horizontal;Qt(t.boxes,(t=>{"function"==typeof t.beforeLayout&&t.beforeLayout()}));const h=l.reduce(((t,e)=>e.box.options&&!1===e.box.options.display?t:t+1),0)||1,u=Object.freeze({outerWidth:e,outerHeight:n,padding:s,availableWidth:o,availableHeight:r,vBoxMaxWidth:o/2/h,hBoxMaxHeight:r/2}),d=Object.assign({},s);yo(d,ci(i));const f=Object.assign({maxPadding:d,w:o,h:r,x:s.left,y:s.top},s),p=bo(l.concat(c),u);$o(a.fullSize,f,u,p),$o(l,f,u,p),$o(c,f,u,p)&&$o(l,f,u,p),function(t){const e=t.maxPadding;function n(n){const i=Math.max(e[n]-t[n],0);return t[n]+=i,i}t.y+=n("top"),t.x+=n("left"),n("right"),n("bottom")}(f),wo(a.leftAndTop,f,u,p),f.x+=f.w,f.y+=f.h,wo(a.rightAndBottom,f,u,p),t.chartArea={left:f.left,top:f.top,right:f.left+f.w,bottom:f.top+f.h,height:f.h,width:f.w},Qt(a.chartArea,(e=>{const n=e.box;Object.assign(n,t.chartArea),n.update(f.w,f.h,{left:0,top:0,right:0,bottom:0})}))}};class Mo{acquireContext(t,e){}releaseContext(t){return!1}addEventListener(t,e,n){}removeEventListener(t,e,n){}getDevicePixelRatio(){return 1}getMaximumSize(t,e,n,i){return e=Math.max(0,e||t.width),n=n||t.height,{width:e,height:Math.max(0,i?Math.floor(e/i):n)}}isAttached(t){return!0}updateConfig(t){}}class So extends Mo{acquireContext(t){return t&&t.getContext&&t.getContext("2d")||null}updateConfig(t){t.options.animation=!1}}const Lo="$chartjs",Do={touchstart:"mousedown",touchmove:"mousemove",touchend:"mouseup",pointerenter:"mouseenter",pointerdown:"mousedown",pointermove:"mousemove",pointerup:"mouseup",pointerleave:"mouseout",pointerout:"mouseout"},Po=t=>null===t||""===t;const Oo=!!Ui&&{passive:!0};function Ao(t,e,n){t.canvas.removeEventListener(e,n,Oo)}function Eo(t,e){for(const n of t)if(n===e||n.contains(e))return!0}function To(t,e,n){const i=t.canvas,s=new MutationObserver((t=>{let e=!1;for(const n of t)e=e||Eo(n.addedNodes,i),e=e&&!Eo(n.removedNodes,i);e&&n()}));return s.observe(document,{childList:!0,subtree:!0}),s}function Ro(t,e,n){const i=t.canvas,s=new MutationObserver((t=>{let e=!1;for(const n of t)e=e||Eo(n.removedNodes,i),e=e&&!Eo(n.addedNodes,i);e&&n()}));return s.observe(document,{childList:!0,subtree:!0}),s}const zo=new Map;let Io=0;function Fo(){const t=window.devicePixelRatio;t!==Io&&(Io=t,zo.forEach(((e,n)=>{n.currentDevicePixelRatio!==t&&e()})))}function No(t,e,n){const i=t.canvas,s=i&&Ri(i);if(!s)return;const o=We(((t,e)=>{const i=s.clientWidth;n(t,e),i{const e=t[0],n=e.contentRect.width,i=e.contentRect.height;0===n&&0===i||o(n,i)}));return r.observe(s),function(t,e){zo.size||window.addEventListener("resize",Fo),zo.set(t,e)}(t,o),r}function jo(t,e,n){n&&n.disconnect(),"resize"===e&&function(t){zo.delete(t),zo.size||window.removeEventListener("resize",Fo)}(t)}function Bo(t,e,n){const i=t.canvas,s=We((e=>{null!==t.ctx&&n(function(t,e){const n=Do[t.type]||t.type,{x:i,y:s}=Bi(t,e);return{type:n,chart:e,native:t,x:void 0!==i?i:null,y:void 0!==s?s:null}}(e,t))}),t,(t=>{const e=t[0];return[e,e.offsetX,e.offsetY]}));return function(t,e,n){t.addEventListener(e,n,Oo)}(i,e,s),s}class Vo extends Mo{acquireContext(t,e){const n=t&&t.getContext&&t.getContext("2d");return n&&n.canvas===t?(function(t,e){const n=t.style,i=t.getAttribute("height"),s=t.getAttribute("width");if(t[Lo]={initial:{height:i,width:s,style:{display:n.display,height:n.height,width:n.width}}},n.display=n.display||"block",n.boxSizing=n.boxSizing||"border-box",Po(s)){const e=qi(t,"width");void 0!==e&&(t.width=e)}if(Po(i))if(""===t.style.height)t.height=t.width/(e||2);else{const e=qi(t,"height");void 0!==e&&(t.height=e)}}(t,e),n):null}releaseContext(t){const e=t.canvas;if(!e[Lo])return!1;const n=e[Lo].initial;["height","width"].forEach((t=>{const i=n[t];Ut(i)?e.removeAttribute(t):e.setAttribute(t,i)}));const i=n.style||{};return Object.keys(i).forEach((t=>{e.style[t]=i[t]})),e.width=e.width,delete e[Lo],!0}addEventListener(t,e,n){this.removeEventListener(t,e);const i=t.$proxies||(t.$proxies={}),s={attach:To,detach:Ro,resize:No}[e]||Bo;i[e]=s(t,e,n)}removeEventListener(t,e){const n=t.$proxies||(t.$proxies={}),i=n[e];if(!i)return;({attach:jo,detach:jo,resize:jo}[e]||Ao)(t,e,i),n[e]=void 0}getDevicePixelRatio(){return window.devicePixelRatio}getMaximumSize(t,e,n,i){return Hi(t,e,n,i)}isAttached(t){const e=Ri(t);return!(!e||!e.isConnected)}}class Ho{constructor(){this._init=[]}notify(t,e,n,i){"beforeInit"===e&&(this._init=this._createDescriptors(t,!0),this._notify(this._init,t,"install"));const s=i?this._descriptors(t).filter(i):this._descriptors(t),o=this._notify(s,t,e,n);return"afterDestroy"===e&&(this._notify(s,t,"stop"),this._notify(this._init,t,"uninstall")),o}_notify(t,e,n,i){i=i||{};for(const s of t){const t=s.plugin;if(!1===Jt(t[n],[e,i,s.options],t)&&i.cancelable)return!1}return!0}invalidate(){Ut(this._cache)||(this._oldCache=this._cache,this._cache=void 0)}_descriptors(t){if(this._cache)return this._cache;const e=this._cache=this._createDescriptors(t);return this._notifyStateChanges(t),e}_createDescriptors(t,e){const n=t&&t.config,i=Gt(n.options&&n.options.plugins,{}),s=function(t){const e={},n=[],i=Object.keys(to.plugins.items);for(let t=0;tt.filter((t=>!e.some((e=>t.plugin.id===e.plugin.id))));this._notify(i(e,n),t,"stop"),this._notify(i(n,e),t,"start")}}function Wo(t,e){return e||!1!==t?!0===t?{}:t:null}function Uo(t,{plugin:e,local:n},i,s){const o=t.pluginScopeKeys(e),r=t.getOptionScopes(i,o);return n&&e.defaults&&r.push(e.defaults),t.createResolver(r,s,[""],{scriptable:!1,indexable:!1,allKeys:!0})}function qo(t,e){const n=Vn.datasets[t]||{};return((e.datasets||{})[t]||{}).indexAxis||e.indexAxis||n.indexAxis||"x"}function Yo(t,e){return"x"===t||"y"===t?t:e.axis||("top"===(n=e.position)||"bottom"===n?"x":"left"===n||"right"===n?"y":void 0)||t.charAt(0).toLowerCase();var n}function Zo(t){const e=t.options||(t.options={});e.plugins=Gt(e.plugins,{}),e.scales=function(t,e){const n=Fn[t.type]||{scales:{}},i=e.scales||{},s=qo(t.type,e),o=Object.create(null),r=Object.create(null);return Object.keys(i).forEach((t=>{const e=i[t];if(!Yt(e))return console.error(`Invalid scale configuration for scale: ${t}`);if(e._proxy)return console.warn(`Ignoring resolver passed as options for scale: ${t}`);const a=Yo(t,e),l=function(t,e){return t===e?"_index_":"_value_"}(a,s),c=n.scales||{};o[a]=o[a]||t,r[t]=oe(Object.create(null),[{axis:a},e,c[a],c[l]])})),t.data.datasets.forEach((n=>{const s=n.type||t.type,a=n.indexAxis||qo(s,e),l=(Fn[s]||{}).scales||{};Object.keys(l).forEach((t=>{const e=function(t,e){let n=t;return"_index_"===t?n=e:"_value_"===t&&(n="x"===e?"y":"x"),n}(t,a),s=n[e+"AxisID"]||o[e]||e;r[s]=r[s]||Object.create(null),oe(r[s],[{axis:e},i[s],l[t]])}))})),Object.keys(r).forEach((t=>{const e=r[t];oe(e,[Vn.scales[e.type],Vn.scale])})),r}(t,e)}function Xo(t){return(t=t||{}).datasets=t.datasets||[],t.labels=t.labels||[],t}const Go=new Map,Ko=new Set;function Jo(t,e){let n=Go.get(t);return n||(n=e(),Go.set(t,n),Ko.add(n)),n}const Qo=(t,e,n)=>{const i=le(e,n);void 0!==i&&t.add(i)};class tr{constructor(t){this._config=function(t){return(t=t||{}).data=Xo(t.data),Zo(t),t}(t),this._scopeCache=new Map,this._resolverCache=new Map}get platform(){return this._config.platform}get type(){return this._config.type}set type(t){this._config.type=t}get data(){return this._config.data}set data(t){this._config.data=Xo(t)}get options(){return this._config.options}set options(t){this._config.options=t}get plugins(){return this._config.plugins}update(){const t=this._config;this.clearCache(),Zo(t)}clearCache(){this._scopeCache.clear(),this._resolverCache.clear()}datasetScopeKeys(t){return Jo(t,(()=>[[`datasets.${t}`,""]]))}datasetAnimationScopeKeys(t,e){return Jo(`${t}.transition.${e}`,(()=>[[`datasets.${t}.transitions.${e}`,`transitions.${e}`],[`datasets.${t}`,""]]))}datasetElementScopeKeys(t,e){return Jo(`${t}-${e}`,(()=>[[`datasets.${t}.elements.${e}`,`datasets.${t}`,`elements.${e}`,""]]))}pluginScopeKeys(t){const e=t.id;return Jo(`${this.type}-plugin-${e}`,(()=>[[`plugins.${e}`,...t.additionalOptionScopes||[]]]))}_cachedScopes(t,e){const n=this._scopeCache;let i=n.get(t);return i&&!e||(i=new Map,n.set(t,i)),i}getOptionScopes(t,e,n){const{options:i,type:s}=this,o=this._cachedScopes(t,n),r=o.get(e);if(r)return r;const a=new Set;e.forEach((e=>{t&&(a.add(t),e.forEach((e=>Qo(a,t,e)))),e.forEach((t=>Qo(a,i,t))),e.forEach((t=>Qo(a,Fn[s]||{},t))),e.forEach((t=>Qo(a,Vn,t))),e.forEach((t=>Qo(a,Nn,t)))}));const l=Array.from(a);return 0===l.length&&l.push(Object.create(null)),Ko.has(e)&&o.set(e,l),l}chartOptionScopes(){const{options:t,type:e}=this;return[t,Fn[e]||{},Vn.datasets[e]||{},{type:e},Vn,Nn]}resolveNamedOptions(t,e,n,i=[""]){const s={$shared:!0},{resolver:o,subPrefixes:r}=er(this._resolverCache,t,i);let a=o;if(function(t,e){const{isScriptable:n,isIndexable:i}=gi(t);for(const s of e){const e=n(s),o=i(s),r=(o||e)&&t[s];if(e&&(ue(r)||nr(r))||o&&qt(r))return!0}return!1}(o,e)){s.$shared=!1;a=pi(o,n=ue(n)?n():n,this.createResolver(t,n,r))}for(const t of e)s[t]=a[t];return s}createResolver(t,e,n=[""],i){const{resolver:s}=er(this._resolverCache,t,n);return Yt(e)?pi(s,e,void 0,i):s}}function er(t,e,n){let i=t.get(e);i||(i=new Map,t.set(e,i));const s=n.join();let o=i.get(s);if(!o){o={resolver:fi(e,n),subPrefixes:n.filter((t=>!t.toLowerCase().includes("hover")))},i.set(s,o)}return o}const nr=t=>Yt(t)&&Object.getOwnPropertyNames(t).reduce(((e,n)=>e||ue(t[n])),!1);const ir=["top","bottom","left","right","chartArea"];function sr(t,e){return"top"===t||"bottom"===t||-1===ir.indexOf(t)&&"x"===e}function or(t,e){return function(n,i){return n[t]===i[t]?n[e]-i[e]:n[t]-i[t]}}function rr(t){const e=t.chart,n=e.options.animation;e.notifyPlugins("afterRender"),Jt(n&&n.onComplete,[t],e)}function ar(t){const e=t.chart,n=e.options.animation;Jt(n&&n.onProgress,[t],e)}function lr(t){return Ti()&&"string"==typeof t?t=document.getElementById(t):t&&t.length&&(t=t[0]),t&&t.canvas&&(t=t.canvas),t}const cr={},hr=t=>{const e=lr(t);return Object.values(cr).filter((t=>t.canvas===e)).pop()};function ur(t,e,n){const i=Object.keys(t);for(const s of i){const i=+s;if(i>=e){const o=t[s];delete t[s],(n>0||i>e)&&(t[i+n]=o)}}}class dr{constructor(t,e){const n=this.config=new tr(e),i=lr(t),s=hr(i);if(s)throw new Error("Canvas is already in use. Chart with ID '"+s.id+"' must be destroyed before the canvas with ID '"+s.canvas.id+"' can be reused.");const o=n.createResolver(n.chartOptionScopes(),this.getContext());this.platform=new(n.platform||function(t){return!Ti()||"undefined"!=typeof OffscreenCanvas&&t instanceof OffscreenCanvas?So:Vo}(i)),this.platform.updateConfig(n);const r=this.platform.acquireContext(i,o.aspectRatio),a=r&&r.canvas,l=a&&a.height,c=a&&a.width;this.id=Wt(),this.ctx=r,this.canvas=a,this.width=c,this.height=l,this._options=o,this._aspectRatio=this.aspectRatio,this._layers=[],this._metasets=[],this._stacks=void 0,this.boxes=[],this.currentDevicePixelRatio=void 0,this.chartArea=void 0,this._active=[],this._lastEvent=void 0,this._listeners={},this._responsiveListeners=void 0,this._sortedMetasets=[],this.scales={},this._plugins=new Ho,this.$proxies={},this._hiddenIndices={},this.attached=!1,this._animationsDisabled=void 0,this.$context=void 0,this._doResize=function(t,e){let n;return function(...i){return e?(clearTimeout(n),n=setTimeout(t,e,i)):t.apply(this,i),e}}((t=>this.update(t)),o.resizeDelay||0),this._dataChanges=[],cr[this.id]=this,r&&a?(as.listen(this,"complete",rr),as.listen(this,"progress",ar),this._initialize(),this.attached&&this.update()):console.error("Failed to create chart: can't acquire context from the given item")}get aspectRatio(){const{options:{aspectRatio:t,maintainAspectRatio:e},width:n,height:i,_aspectRatio:s}=this;return Ut(t)?e&&s?s:i?n/i:null:t}get data(){return this.config.data}set data(t){this.config.data=t}get options(){return this._options}set options(t){this.config.options=t}_initialize(){return this.notifyPlugins("beforeInit"),this.options.responsive?this.resize():Wi(this,this.options.devicePixelRatio),this.bindEvents(),this.notifyPlugins("afterInit"),this}clear(){return qn(this.canvas,this.ctx),this}stop(){return as.stop(this),this}resize(t,e){as.running(this)?this._resizeBeforeDraw={width:t,height:e}:this._resize(t,e)}_resize(t,e){const n=this.options,i=this.canvas,s=n.maintainAspectRatio&&this.aspectRatio,o=this.platform.getMaximumSize(i,t,e,s),r=n.devicePixelRatio||this.platform.getDevicePixelRatio(),a=this.width?"resize":"attach";this.width=o.width,this.height=o.height,this._aspectRatio=this.aspectRatio,Wi(this,r,!0)&&(this.notifyPlugins("resize",{size:o}),Jt(n.onResize,[this,o],this),this.attached&&this._doResize(a)&&this.render())}ensureScalesHaveIDs(){Qt(this.options.scales||{},((t,e)=>{t.id=e}))}buildOrUpdateScales(){const t=this.options,e=t.scales,n=this.scales,i=Object.keys(n).reduce(((t,e)=>(t[e]=!1,t)),{});let s=[];e&&(s=s.concat(Object.keys(e).map((t=>{const n=e[t],i=Yo(t,n),s="r"===i,o="x"===i;return{options:n,dposition:s?"chartArea":o?"bottom":"left",dtype:s?"radialLinear":o?"category":"linear"}})))),Qt(s,(e=>{const s=e.options,o=s.id,r=Yo(o,s),a=Gt(s.type,e.dtype);void 0!==s.position&&sr(s.position,r)===sr(e.dposition)||(s.position=e.dposition),i[o]=!0;let l=null;if(o in n&&n[o].type===a)l=n[o];else{l=new(to.getScale(a))({id:o,type:a,ctx:this.ctx,chart:this}),n[l.id]=l}l.init(s,t)})),Qt(i,((t,e)=>{t||delete n[e]})),Qt(n,(t=>{Co.configure(this,t,t.options),Co.addBox(this,t)}))}_updateMetasets(){const t=this._metasets,e=this.data.datasets.length,n=t.length;if(t.sort(((t,e)=>t.index-e.index)),n>e){for(let t=e;te.length&&delete this._stacks,t.forEach(((t,n)=>{0===e.filter((e=>e===t._dataset)).length&&this._destroyDatasetMeta(n)}))}buildOrUpdateControllers(){const t=[],e=this.data.datasets;let n,i;for(this._removeUnreferencedMetasets(),n=0,i=e.length;n{this.getDatasetMeta(e).controller.reset()}),this)}reset(){this._resetElements(),this.notifyPlugins("reset")}update(t){const e=this.config;e.update();const n=this._options=e.createResolver(e.chartOptionScopes(),this.getContext()),i=this._animationsDisabled=!n.animation;if(this._updateScales(),this._checkEventBindings(),this._updateHiddenIndices(),this._plugins.invalidate(),!1===this.notifyPlugins("beforeUpdate",{mode:t,cancelable:!0}))return;const s=this.buildOrUpdateControllers();this.notifyPlugins("beforeElementsUpdate");let o=0;for(let t=0,e=this.data.datasets.length;t{t.reset()})),this._updateDatasets(t),this.notifyPlugins("afterUpdate",{mode:t}),this._layers.sort(or("z","_idx"));const{_active:r,_lastEvent:a}=this;a?this._eventHandler(a,!0):r.length&&this._updateHoverStyles(r,r,!0),this.render()}_updateScales(){Qt(this.scales,(t=>{Co.removeBox(this,t)})),this.ensureScalesHaveIDs(),this.buildOrUpdateScales()}_checkEventBindings(){const t=this.options,e=new Set(Object.keys(this._listeners)),n=new Set(t.events);de(e,n)&&!!this._responsiveListeners===t.responsive||(this.unbindEvents(),this.bindEvents())}_updateHiddenIndices(){const{_hiddenIndices:t}=this,e=this._getUniformDataChanges()||[];for(const{method:n,start:i,count:s}of e){ur(t,i,"_removeElements"===n?-s:s)}}_getUniformDataChanges(){const t=this._dataChanges;if(!t||!t.length)return;this._dataChanges=[];const e=this.data.datasets.length,n=e=>new Set(t.filter((t=>t[0]===e)).map(((t,e)=>e+","+t.splice(1).join(",")))),i=n(0);for(let t=1;tt.split(","))).map((t=>({method:t[1],start:+t[2],count:+t[3]})))}_updateLayout(t){if(!1===this.notifyPlugins("beforeLayout",{cancelable:!0}))return;Co.update(this,this.width,this.height,t);const e=this.chartArea,n=e.width<=0||e.height<=0;this._layers=[],Qt(this.boxes,(t=>{n&&"chartArea"===t.position||(t.configure&&t.configure(),this._layers.push(...t._layers()))}),this),this._layers.forEach(((t,e)=>{t._idx=e})),this.notifyPlugins("afterLayout")}_updateDatasets(t){if(!1!==this.notifyPlugins("beforeDatasetsUpdate",{mode:t,cancelable:!0})){for(let t=0,e=this.data.datasets.length;t=0;--e)this._drawDataset(t[e]);this.notifyPlugins("afterDatasetsDraw")}_drawDataset(t){const e=this.ctx,n=t._clip,i=!n.disabled,s=this.chartArea,o={meta:t,index:t.index,cancelable:!0};!1!==this.notifyPlugins("beforeDatasetDraw",o)&&(i&&Xn(e,{left:!1===n.left?0:s.left-n.left,right:!1===n.right?this.width:s.right+n.right,top:!1===n.top?0:s.top-n.top,bottom:!1===n.bottom?this.height:s.bottom+n.bottom}),t.controller.draw(),i&&Gn(e),o.cancelable=!1,this.notifyPlugins("afterDatasetDraw",o))}isPointInArea(t){return Zn(t,this.chartArea,this._minPadding)}getElementsAtEventForMode(t,e,n,i){const s=uo.modes[e];return"function"==typeof s?s(this,t,n,i):[]}getDatasetMeta(t){const e=this.data.datasets[t],n=this._metasets;let i=n.filter((t=>t&&t._dataset===e)).pop();return i||(i={type:null,data:[],dataset:null,controller:null,hidden:null,xAxisID:null,yAxisID:null,order:e&&e.order||0,index:t,_dataset:e,_parsed:[],_sorted:!1},n.push(i)),i}getContext(){return this.$context||(this.$context=di(null,{chart:this,type:"chart"}))}getVisibleDatasetCount(){return this.getSortedVisibleDatasetMetas().length}isDatasetVisible(t){const e=this.data.datasets[t];if(!e)return!1;const n=this.getDatasetMeta(t);return"boolean"==typeof n.hidden?!n.hidden:!e.hidden}setDatasetVisibility(t,e){this.getDatasetMeta(t).hidden=!e}toggleDataVisibility(t){this._hiddenIndices[t]=!this._hiddenIndices[t]}getDataVisibility(t){return!this._hiddenIndices[t]}_updateVisibility(t,e,n){const i=n?"show":"hide",s=this.getDatasetMeta(t),o=s.controller._resolveAnimations(void 0,i);he(e)?(s.data[e].hidden=!n,this.update()):(this.setDatasetVisibility(t,n),o.update(s,{visible:n}),this.update((e=>e.datasetIndex===t?i:void 0)))}hide(t,e){this._updateVisibility(t,e,!1)}show(t,e){this._updateVisibility(t,e,!0)}_destroyDatasetMeta(t){const e=this._metasets[t];e&&e.controller&&e.controller._destroy(),delete this._metasets[t]}_stop(){let t,e;for(this.stop(),as.remove(this),t=0,e=this.data.datasets.length;t{e.addEventListener(this,n,i),t[n]=i},i=(t,e,n)=>{t.offsetX=e,t.offsetY=n,this._eventHandler(t)};Qt(this.options.events,(t=>n(t,i)))}bindResponsiveEvents(){this._responsiveListeners||(this._responsiveListeners={});const t=this._responsiveListeners,e=this.platform,n=(n,i)=>{e.addEventListener(this,n,i),t[n]=i},i=(n,i)=>{t[n]&&(e.removeEventListener(this,n,i),delete t[n])},s=(t,e)=>{this.canvas&&this.resize(t,e)};let o;const r=()=>{i("attach",r),this.attached=!0,this.resize(),n("resize",s),n("detach",o)};o=()=>{this.attached=!1,i("resize",s),this._stop(),this._resize(0,0),n("attach",r)},e.isAttached(this.canvas)?r():o()}unbindEvents(){Qt(this._listeners,((t,e)=>{this.platform.removeEventListener(this,e,t)})),this._listeners={},Qt(this._responsiveListeners,((t,e)=>{this.platform.removeEventListener(this,e,t)})),this._responsiveListeners=void 0}updateHoverStyle(t,e,n){const i=n?"set":"remove";let s,o,r,a;for("dataset"===e&&(s=this.getDatasetMeta(t[0].datasetIndex),s.controller["_"+i+"DatasetHoverStyle"]()),r=0,a=t.length;r{const n=this.getDatasetMeta(t);if(!n)throw new Error("No dataset found at index "+t);return{datasetIndex:t,element:n.data[e],index:e}}));!te(n,e)&&(this._active=n,this._lastEvent=null,this._updateHoverStyles(n,e))}notifyPlugins(t,e,n){return this._plugins.notify(this,t,e,n)}_updateHoverStyles(t,e,n){const i=this.options.hover,s=(t,e)=>t.filter((t=>!e.some((e=>t.datasetIndex===e.datasetIndex&&t.index===e.index)))),o=s(e,t),r=n?t:s(t,e);o.length&&this.updateHoverStyle(o,i.mode,!1),r.length&&i.mode&&this.updateHoverStyle(r,i.mode,!0)}_eventHandler(t,e){const n={event:t,replay:e,cancelable:!0,inChartArea:this.isPointInArea(t)},i=e=>(e.options.events||this.options.events).includes(t.native.type);if(!1===this.notifyPlugins("beforeEvent",n,i))return;const s=this._handleEvent(t,e,n.inChartArea);return n.cancelable=!1,this.notifyPlugins("afterEvent",n,i),(s||n.changed)&&this.render(),this}_handleEvent(t,e,n){const{_active:i=[],options:s}=this,o=e,r=this._getActiveElements(t,i,n,o),a=function(t){return"mouseup"===t.type||"click"===t.type||"contextmenu"===t.type}(t),l=function(t,e,n,i){return n&&"mouseout"!==t.type?i?e:t:null}(t,this._lastEvent,n,a);n&&(this._lastEvent=null,Jt(s.onHover,[t,r,this],this),a&&Jt(s.onClick,[t,r,this],this));const c=!te(r,i);return(c||e)&&(this._active=r,this._updateHoverStyles(r,i,e)),this._lastEvent=l,c}_getActiveElements(t,e,n,i){if("mouseout"===t.type)return[];if(!n)return e;const s=this.options.hover;return this.getElementsAtEventForMode(t,s.mode,s,i)}}const fr=()=>Qt(dr.instances,(t=>t._plugins.invalidate())),pr=!0;function gr(t,e,n){const{startAngle:i,pixelMargin:s,x:o,y:r,outerRadius:a,innerRadius:l}=e;let c=s/a;t.beginPath(),t.arc(o,r,a,i-c,n+c),l>s?(c=s/l,t.arc(o,r,l,n+c,i-c,!0)):t.arc(o,r,s,n+xe,i-xe),t.closePath(),t.clip()}function mr(t,e,n,i){const s=ri(t.options.borderRadius,["outerStart","outerEnd","innerStart","innerEnd"]);const o=(n-e)/2,r=Math.min(o,i*e/2),a=t=>{const e=(n-Math.min(o,t))*i/2;return Re(t,0,Math.min(o,e))};return{outerStart:a(s.outerStart),outerEnd:a(s.outerEnd),innerStart:Re(s.innerStart,0,r),innerEnd:Re(s.innerEnd,0,r)}}function br(t,e,n,i){return{x:n+t*Math.cos(e),y:i+t*Math.sin(e)}}function xr(t,e,n,i,s,o){const{x:r,y:a,startAngle:l,pixelMargin:c,innerRadius:h}=e,u=Math.max(e.outerRadius+i+n-c,0),d=h>0?h+i+n+c:0;let f=0;const p=s-l;if(i){const t=((h>0?h-i:0)+(u>0?u-i:0))/2;f=(p-(0!==t?p*t/(t+i):p))/2}const g=(p-Math.max(.001,p*u-n/fe)/u)/2,m=l+g+f,b=s-g-f,{outerStart:x,outerEnd:y,innerStart:_,innerEnd:v}=mr(e,d,u,b-m),$=u-x,k=u-y,w=m+x/$,C=b-y/k,M=d+_,S=d+v,L=m+_/M,D=b-v/S;if(t.beginPath(),o){if(t.arc(r,a,u,w,C),y>0){const e=br(k,C,r,a);t.arc(e.x,e.y,y,C,b+xe)}const e=br(S,b,r,a);if(t.lineTo(e.x,e.y),v>0){const e=br(S,D,r,a);t.arc(e.x,e.y,v,b+xe,D+Math.PI)}if(t.arc(r,a,d,b-v/d,m+_/d,!0),_>0){const e=br(M,L,r,a);t.arc(e.x,e.y,_,L+Math.PI,m-xe)}const n=br($,m,r,a);if(t.lineTo(n.x,n.y),x>0){const e=br($,w,r,a);t.arc(e.x,e.y,x,m-xe,w)}}else{t.moveTo(r,a);const e=Math.cos(w)*u+r,n=Math.sin(w)*u+a;t.lineTo(e,n);const i=Math.cos(C)*u+r,s=Math.sin(C)*u+a;t.lineTo(i,s)}t.closePath()}function yr(t,e,n,i,s,o){const{options:r}=e,{borderWidth:a,borderJoinStyle:l}=r,c="inner"===r.borderAlign;a&&(c?(t.lineWidth=2*a,t.lineJoin=l||"round"):(t.lineWidth=a,t.lineJoin=l||"bevel"),e.fullCircles&&function(t,e,n){const{x:i,y:s,startAngle:o,pixelMargin:r,fullCircles:a}=e,l=Math.max(e.outerRadius-r,0),c=e.innerRadius+r;let h;for(n&&gr(t,e,o+pe),t.beginPath(),t.arc(i,s,c,o+pe,o,!0),h=0;h{to.add(...t),fr()}},unregister:{enumerable:pr,value:(...t)=>{to.remove(...t),fr()}}});class _r extends js{constructor(t){super(),this.options=void 0,this.circumference=void 0,this.startAngle=void 0,this.endAngle=void 0,this.innerRadius=void 0,this.outerRadius=void 0,this.pixelMargin=0,this.fullCircles=0,t&&Object.assign(this,t)}inRange(t,e,n){const i=this.getProps(["x","y"],n),{angle:s,distance:o}=Pe(i,{x:t,y:e}),{startAngle:r,endAngle:a,innerRadius:l,outerRadius:c,circumference:h}=this.getProps(["startAngle","endAngle","innerRadius","outerRadius","circumference"],n),u=this.options.spacing/2,d=Gt(h,a-r)>=pe||Te(s,r,a),f=ze(o,l+u,c+u);return d&&f}getCenterPoint(t){const{x:e,y:n,startAngle:i,endAngle:s,innerRadius:o,outerRadius:r}=this.getProps(["x","y","startAngle","endAngle","innerRadius","outerRadius","circumference"],t),{offset:a,spacing:l}=this.options,c=(i+s)/2,h=(o+r+l+a)/2;return{x:e+Math.cos(c)*h,y:n+Math.sin(c)*h}}tooltipPosition(t){return this.getCenterPoint(t)}draw(t){const{options:e,circumference:n}=this,i=(e.offset||0)/2,s=(e.spacing||0)/2,o=e.circular;if(this.pixelMargin="inner"===e.borderAlign?.33:0,this.fullCircles=n>pe?Math.floor(n/pe):0,0===n||this.innerRadius<0||this.outerRadius<0)return;t.save();let r=0;if(i){r=i/2;const e=(this.startAngle+this.endAngle)/2;t.translate(Math.cos(e)*r,Math.sin(e)*r),this.circumference>=fe&&(r=i)}t.fillStyle=e.backgroundColor,t.strokeStyle=e.borderColor;const a=function(t,e,n,i,s){const{fullCircles:o,startAngle:r,circumference:a}=e;let l=e.endAngle;if(o){xr(t,e,n,i,r+pe,s);for(let e=0;ea&&o>a;return{count:i,start:l,loop:e.loop,ilen:c(r+(c?a-t:t))%o,y=()=>{f!==p&&(t.lineTo(m,p),t.lineTo(m,f),t.lineTo(m,g))};for(l&&(u=s[x(0)],t.moveTo(u.x,u.y)),h=0;h<=a;++h){if(u=s[x(h)],u.skip)continue;const e=u.x,n=u.y,i=0|e;i===d?(np&&(p=n),m=(b*m+e)/++b):(y(),t.lineTo(e,n),d=i,b=0,f=p=n),g=n}y()}function Mr(t){const e=t.options,n=e.borderDash&&e.borderDash.length;return!(t._decimated||t._loop||e.tension||"monotone"===e.cubicInterpolationMode||e.stepped||n)?Cr:wr}_r.id="arc",_r.defaults={borderAlign:"center",borderColor:"#fff",borderJoinStyle:void 0,borderRadius:0,borderWidth:2,offset:0,spacing:0,angle:void 0,circular:!0},_r.defaultRoutes={backgroundColor:"backgroundColor"};const Sr="function"==typeof Path2D;function Lr(t,e,n,i){Sr&&!e.options.segment?function(t,e,n,i){let s=e._path;s||(s=e._path=new Path2D,e.path(s,n,i)&&s.closePath()),vr(t,e.options),t.stroke(s)}(t,e,n,i):function(t,e,n,i){const{segments:s,options:o}=e,r=Mr(e);for(const a of s)vr(t,o,a.style),t.beginPath(),r(t,e,a,{start:n,end:n+i-1})&&t.closePath(),t.stroke()}(t,e,n,i)}class Dr extends js{constructor(t){super(),this.animated=!0,this.options=void 0,this._chart=void 0,this._loop=void 0,this._fullLoop=void 0,this._path=void 0,this._points=void 0,this._segments=void 0,this._decimated=!1,this._pointsUpdated=!1,this._datasetIndex=void 0,t&&Object.assign(this,t)}updateControlPoints(t,e){const n=this.options;if((n.tension||"monotone"===n.cubicInterpolationMode)&&!n.stepped&&!this._pointsUpdated){const i=n.spanGaps?this._loop:this._fullLoop;Ei(this._points,n,t,i,e),this._pointsUpdated=!0}}set points(t){this._points=t,delete this._segments,delete this._path,this._pointsUpdated=!1}get points(){return this._points}get segments(){return this._segments||(this._segments=function(t,e){const n=t.points,i=t.options.spanGaps,s=n.length;if(!s)return[];const o=!!t._loop,{start:r,end:a}=function(t,e,n,i){let s=0,o=e-1;if(n&&!i)for(;ss&&t[o%e].skip;)o--;return o%=e,{start:s,end:o}}(n,s,o,i);return ss(t,!0===i?[{start:r,end:a,loop:o}]:function(t,e,n,i){const s=t.length,o=[];let r,a=e,l=t[e];for(r=e+1;r<=n;++r){const n=t[r%s];n.skip||n.stop?l.skip||(i=!1,o.push({start:e%s,end:(r-1)%s,loop:i}),e=a=n.stop?r:null):(a=r,l.skip&&(e=r)),l=n}return null!==a&&o.push({start:e%s,end:a%s,loop:i}),o}(n,r,a"borderDash"!==t&&"fill"!==t};class Or extends js{constructor(t){super(),this.options=void 0,this.parsed=void 0,this.skip=void 0,this.stop=void 0,t&&Object.assign(this,t)}inRange(t,e,n){const i=this.options,{x:s,y:o}=this.getProps(["x","y"],n);return Math.pow(t-s,2)+Math.pow(e-o,2)-1?t.split("\n"):t}function Vr(t,e){const{element:n,datasetIndex:i,index:s}=e,o=t.getDatasetMeta(i).controller,{label:r,value:a}=o.getLabelAndValue(s);return{chart:t,label:r,parsed:o.getParsed(s),raw:t.data.datasets[i].data[s],formattedValue:a,dataset:o.getDataset(),dataIndex:s,datasetIndex:i,element:n}}function Hr(t,e){const n=t.chart.ctx,{body:i,footer:s,title:o}=t,{boxWidth:r,boxHeight:a}=e,l=hi(e.bodyFont),c=hi(e.titleFont),h=hi(e.footerFont),u=o.length,d=s.length,f=i.length,p=ci(e.padding);let g=p.height,m=0,b=i.reduce(((t,e)=>t+e.before.length+e.lines.length+e.after.length),0);if(b+=t.beforeBody.length+t.afterBody.length,u&&(g+=u*c.lineHeight+(u-1)*e.titleSpacing+e.titleMarginBottom),b){g+=f*(e.displayColors?Math.max(a,l.lineHeight):l.lineHeight)+(b-f)*l.lineHeight+(b-1)*e.bodySpacing}d&&(g+=e.footerMarginTop+d*h.lineHeight+(d-1)*e.footerSpacing);let x=0;const y=function(t){m=Math.max(m,n.measureText(t).width+x)};return n.save(),n.font=c.string,Qt(t.title,y),n.font=l.string,Qt(t.beforeBody.concat(t.afterBody),y),x=e.displayColors?r+2+e.boxPadding:0,Qt(i,(t=>{Qt(t.before,y),Qt(t.lines,y),Qt(t.after,y)})),x=0,n.font=h.string,Qt(t.footer,y),n.restore(),m+=p.width,{width:m,height:g}}function Wr(t,e,n,i){const{x:s,width:o}=n,{width:r,chartArea:{left:a,right:l}}=t;let c="center";return"center"===i?c=s<=(a+l)/2?"left":"right":s<=o/2?c="left":s>=r-o/2&&(c="right"),function(t,e,n,i){const{x:s,width:o}=i,r=n.caretSize+n.caretPadding;return"left"===t&&s+o+r>e.width||"right"===t&&s-o-r<0||void 0}(c,t,e,n)&&(c="center"),c}function Ur(t,e,n){const i=n.yAlign||e.yAlign||function(t,e){const{y:n,height:i}=e;return nt.height-i/2?"bottom":"center"}(t,n);return{xAlign:n.xAlign||e.xAlign||Wr(t,e,n,i),yAlign:i}}function qr(t,e,n,i){const{caretSize:s,caretPadding:o,cornerRadius:r}=t,{xAlign:a,yAlign:l}=n,c=s+o,{topLeft:h,topRight:u,bottomLeft:d,bottomRight:f}=li(r);let p=function(t,e){let{x:n,width:i}=t;return"right"===e?n-=i:"center"===e&&(n-=i/2),n}(e,a);const g=function(t,e,n){let{y:i,height:s}=t;return"top"===e?i+=n:i-="bottom"===e?s+n:s/2,i}(e,l,c);return"center"===l?"left"===a?p+=c:"right"===a&&(p-=c):"left"===a?p-=Math.max(h,d)+s:"right"===a&&(p+=Math.max(u,f)+s),{x:Re(p,0,i.width-e.width),y:Re(g,0,i.height-e.height)}}function Yr(t,e,n){const i=ci(n.padding);return"center"===e?t.x+t.width/2:"right"===e?t.x+t.width-i.right:t.x+i.left}function Zr(t){return jr([],Br(t))}function Xr(t,e){const n=e&&e.dataset&&e.dataset.tooltip&&e.dataset.tooltip.callbacks;return n?t.override(n):t}(class extends js{constructor(t){super(),this.opacity=0,this._active=[],this._eventPosition=void 0,this._size=void 0,this._cachedAnimations=void 0,this._tooltipItems=[],this.$animations=void 0,this.$context=void 0,this.chart=t.chart||t._chart,this._chart=this.chart,this.options=t.options,this.dataPoints=void 0,this.title=void 0,this.beforeBody=void 0,this.body=void 0,this.afterBody=void 0,this.footer=void 0,this.xAlign=void 0,this.yAlign=void 0,this.x=void 0,this.y=void 0,this.height=void 0,this.width=void 0,this.caretX=void 0,this.caretY=void 0,this.labelColors=void 0,this.labelPointStyles=void 0,this.labelTextColors=void 0}initialize(t){this.options=t,this._cachedAnimations=void 0,this.$context=void 0}_resolveAnimations(){const t=this._cachedAnimations;if(t)return t;const e=this.chart,n=this.options.setContext(this.getContext()),i=n.enabled&&e.options.animation&&n.animations,s=new ds(this.chart,i);return i._cacheable&&(this._cachedAnimations=Object.freeze(s)),s}getContext(){return this.$context||(this.$context=(t=this.chart.getContext(),e=this,n=this._tooltipItems,di(t,{tooltip:e,tooltipItems:n,type:"tooltip"})));var t,e,n}getTitle(t,e){const{callbacks:n}=e,i=n.beforeTitle.apply(this,[t]),s=n.title.apply(this,[t]),o=n.afterTitle.apply(this,[t]);let r=[];return r=jr(r,Br(i)),r=jr(r,Br(s)),r=jr(r,Br(o)),r}getBeforeBody(t,e){return Zr(e.callbacks.beforeBody.apply(this,[t]))}getBody(t,e){const{callbacks:n}=e,i=[];return Qt(t,(t=>{const e={before:[],lines:[],after:[]},s=Xr(n,t);jr(e.before,Br(s.beforeLabel.call(this,t))),jr(e.lines,s.label.call(this,t)),jr(e.after,Br(s.afterLabel.call(this,t))),i.push(e)})),i}getAfterBody(t,e){return Zr(e.callbacks.afterBody.apply(this,[t]))}getFooter(t,e){const{callbacks:n}=e,i=n.beforeFooter.apply(this,[t]),s=n.footer.apply(this,[t]),o=n.afterFooter.apply(this,[t]);let r=[];return r=jr(r,Br(i)),r=jr(r,Br(s)),r=jr(r,Br(o)),r}_createItems(t){const e=this._active,n=this.chart.data,i=[],s=[],o=[];let r,a,l=[];for(r=0,a=e.length;rt.filter(e,i,s,n)))),t.itemSort&&(l=l.sort(((e,i)=>t.itemSort(e,i,n)))),Qt(l,(e=>{const n=Xr(t.callbacks,e);i.push(n.labelColor.call(this,e)),s.push(n.labelPointStyle.call(this,e)),o.push(n.labelTextColor.call(this,e))})),this.labelColors=i,this.labelPointStyles=s,this.labelTextColors=o,this.dataPoints=l,l}update(t,e){const n=this.options.setContext(this.getContext()),i=this._active;let s,o=[];if(i.length){const t=Nr[n.position].call(this,i,this._eventPosition);o=this._createItems(n),this.title=this.getTitle(o,n),this.beforeBody=this.getBeforeBody(o,n),this.body=this.getBody(o,n),this.afterBody=this.getAfterBody(o,n),this.footer=this.getFooter(o,n);const e=this._size=Hr(this,n),r=Object.assign({},t,e),a=Ur(this.chart,n,r),l=qr(n,r,a,this.chart);this.xAlign=a.xAlign,this.yAlign=a.yAlign,s={opacity:1,x:l.x,y:l.y,width:e.width,height:e.height,caretX:t.x,caretY:t.y}}else 0!==this.opacity&&(s={opacity:0});this._tooltipItems=o,this.$context=void 0,s&&this._resolveAnimations().update(this,s),t&&n.external&&n.external.call(this,{chart:this.chart,tooltip:this,replay:e})}drawCaret(t,e,n,i){const s=this.getCaretPosition(t,n,i);e.lineTo(s.x1,s.y1),e.lineTo(s.x2,s.y2),e.lineTo(s.x3,s.y3)}getCaretPosition(t,e,n){const{xAlign:i,yAlign:s}=this,{caretSize:o,cornerRadius:r}=n,{topLeft:a,topRight:l,bottomLeft:c,bottomRight:h}=li(r),{x:u,y:d}=t,{width:f,height:p}=e;let g,m,b,x,y,_;return"center"===s?(y=d+p/2,"left"===i?(g=u,m=g-o,x=y+o,_=y-o):(g=u+f,m=g+o,x=y-o,_=y+o),b=g):(m="left"===i?u+Math.max(a,c)+o:"right"===i?u+f-Math.max(l,h)-o:this.caretX,"top"===s?(x=d,y=x-o,g=m-o,b=m+o):(x=d+p,y=x+o,g=m+o,b=m-o),_=x),{x1:g,x2:m,x3:b,y1:x,y2:y,y3:_}}drawTitle(t,e,n){const i=this.title,s=i.length;let o,r,a;if(s){const l=ts(n.rtl,this.x,this.width);for(t.x=Yr(this,n.titleAlign,n),e.textAlign=l.textAlign(n.titleAlign),e.textBaseline="middle",o=hi(n.titleFont),r=n.titleSpacing,e.fillStyle=n.titleColor,e.font=o.string,a=0;a0!==t))?(t.beginPath(),t.fillStyle=s.multiKeyBackground,ei(t,{x:e,y:p,w:l,h:a,radius:r}),t.fill(),t.stroke(),t.fillStyle=o.backgroundColor,t.beginPath(),ei(t,{x:n,y:p+1,w:l-2,h:a-2,radius:r}),t.fill()):(t.fillStyle=s.multiKeyBackground,t.fillRect(e,p,l,a),t.strokeRect(e,p,l,a),t.fillStyle=o.backgroundColor,t.fillRect(n,p+1,l-2,a-2))}t.fillStyle=this.labelTextColors[n]}drawBody(t,e,n){const{body:i}=this,{bodySpacing:s,bodyAlign:o,displayColors:r,boxHeight:a,boxWidth:l,boxPadding:c}=n,h=hi(n.bodyFont);let u=h.lineHeight,d=0;const f=ts(n.rtl,this.x,this.width),p=function(n){e.fillText(n,f.x(t.x+d),t.y+u/2),t.y+=u+s},g=f.textAlign(o);let m,b,x,y,_,v,$;for(e.textAlign=o,e.textBaseline="middle",e.font=h.string,t.x=Yr(this,g,n),e.fillStyle=n.bodyColor,Qt(this.beforeBody,p),d=r&&"right"!==g?"center"===o?l/2+c:l+2+c:0,y=0,v=i.length;y0&&e.stroke()}_updateAnimationTarget(t){const e=this.chart,n=this.$animations,i=n&&n.x,s=n&&n.y;if(i||s){const n=Nr[t.position].call(this,this._active,this._eventPosition);if(!n)return;const o=this._size=Hr(this,t),r=Object.assign({},n,this._size),a=Ur(e,t,r),l=qr(t,r,a,e);i._to===l.x&&s._to===l.y||(this.xAlign=a.xAlign,this.yAlign=a.yAlign,this.width=o.width,this.height=o.height,this.caretX=n.x,this.caretY=n.y,this._resolveAnimations().update(this,l))}}_willRender(){return!!this.opacity}draw(t){const e=this.options.setContext(this.getContext());let n=this.opacity;if(!n)return;this._updateAnimationTarget(e);const i={width:this.width,height:this.height},s={x:this.x,y:this.y};n=Math.abs(n)<.001?0:n;const o=ci(e.padding),r=this.title.length||this.beforeBody.length||this.body.length||this.afterBody.length||this.footer.length;e.enabled&&r&&(t.save(),t.globalAlpha=n,this.drawBackground(s,t,i,e),function(t,e){let n,i;"ltr"!==e&&"rtl"!==e||(n=t.canvas.style,i=[n.getPropertyValue("direction"),n.getPropertyPriority("direction")],n.setProperty("direction",e,"important"),t.prevTextDirection=i)}(t,e.textDirection),s.y+=o.top,this.drawTitle(s,t,e),this.drawBody(s,t,e),this.drawFooter(s,t,e),function(t,e){void 0!==e&&(delete t.prevTextDirection,t.canvas.style.setProperty("direction",e[0],e[1]))}(t,e.textDirection),t.restore())}getActiveElements(){return this._active||[]}setActiveElements(t,e){const n=this._active,i=t.map((({datasetIndex:t,index:e})=>{const n=this.chart.getDatasetMeta(t);if(!n)throw new Error("Cannot find a dataset at index "+t);return{datasetIndex:t,element:n.data[e],index:e}})),s=!te(n,i),o=this._positionChanged(i,e);(s||o)&&(this._active=i,this._eventPosition=e,this._ignoreReplayEvents=!0,this.update(!0))}handleEvent(t,e,n=!0){if(e&&this._ignoreReplayEvents)return!1;this._ignoreReplayEvents=!1;const i=this.options,s=this._active||[],o=this._getActiveElements(t,s,e,n),r=this._positionChanged(o,t),a=e||!te(o,s)||r;return a&&(this._active=o,(i.enabled||i.external)&&(this._eventPosition={x:t.x,y:t.y},this.update(!0,e))),a}_getActiveElements(t,e,n,i){const s=this.options;if("mouseout"===t.type)return[];if(!i)return e;const o=this.chart.getElementsAtEventForMode(t,s.mode,s,n);return s.reverse&&o.reverse(),o}_positionChanged(t,e){const{caretX:n,caretY:i,options:s}=this,o=Nr[s.position].call(this,t,e);return!1!==o&&(n!==o.x||i!==o.y)}}).positioners=Nr;const Gr=(t,e,n,i)=>("string"==typeof e?(n=t.push(e)-1,i.unshift({index:n,label:e})):isNaN(e)&&(n=null),n);class Kr extends Js{constructor(t){super(t),this._startValue=void 0,this._valueRange=0,this._addedLabels=[]}init(t){const e=this._addedLabels;if(e.length){const t=this.getLabels();for(const{index:n,label:i}of e)t[n]===i&&t.splice(n,1);this._addedLabels=[]}super.init(t)}parse(t,e){if(Ut(t))return null;const n=this.getLabels();return e=isFinite(e)&&n[e]===t?e:function(t,e,n,i){const s=t.indexOf(e);return-1===s?Gr(t,e,n,i):s!==t.lastIndexOf(e)?n:s}(n,t,Gt(e,t),this._addedLabels),((t,e)=>null===t?null:Re(Math.round(t),0,e))(e,n.length-1)}determineDataLimits(){const{minDefined:t,maxDefined:e}=this.getUserBounds();let{min:n,max:i}=this.getMinMax(!0);"ticks"===this.options.bounds&&(t||(n=0),e||(i=this.getLabels().length-1)),this.min=n,this.max=i}buildTicks(){const t=this.min,e=this.max,n=this.options.offset,i=[];let s=this.getLabels();s=0===t&&e===s.length-1?s:s.slice(t,e+1),this._valueRange=Math.max(s.length-(n?0:1),1),this._startValue=this.min-(n?.5:0);for(let n=t;n<=e;n++)i.push({value:n});return i}getLabelForValue(t){const e=this.getLabels();return t>=0&&te.length-1?null:this.getPixelForValue(e[t].value)}getValueForPixel(t){return Math.round(this._startValue+this.getDecimalForPixel(t)*this._valueRange)}getBasePixel(){return this.bottom}}function Jr(t,e){const n=[],{bounds:i,step:s,min:o,max:r,precision:a,count:l,maxTicks:c,maxDigits:h,includeBounds:u}=t,d=s||1,f=c-1,{min:p,max:g}=e,m=!Ut(o),b=!Ut(r),x=!Ut(l),y=(g-p)/(h+1);let _,v,$,k,w=ke((g-p)/f/d)*d;if(w<1e-14&&!m&&!b)return[{value:p},{value:g}];k=Math.ceil(g/w)-Math.floor(p/w),k>f&&(w=ke(k*w/f/d)*d),Ut(a)||(_=Math.pow(10,a),w=Math.ceil(w*_)/_),"ticks"===i?(v=Math.floor(p/w)*w,$=Math.ceil(g/w)*w):(v=p,$=g),m&&b&&s&&function(t,e){const n=Math.round(t);return n-e<=t&&n+e>=t}((r-o)/s,w/1e3)?(k=Math.round(Math.min((r-o)/w,c)),w=(r-o)/k,v=o,$=r):x?(v=m?o:v,$=b?r:$,k=l-1,w=($-v)/k):(k=($-v)/w,k=Ce(k,Math.round(k),w/1e3)?Math.round(k):Math.ceil(k));const C=Math.max(De(w),De(v));_=Math.pow(10,Ut(a)?C:a),v=Math.round(v*_)/_,$=Math.round($*_)/_;let M=0;for(m&&(u&&v!==o?(n.push({value:o}),vi=e?i:t,r=t=>s=n?s:t;if(t){const t=$e(i),e=$e(s);t<0&&e<0?r(0):t>0&&e>0&&o(0)}if(i===s){let e=1;(s>=Number.MAX_SAFE_INTEGER||i<=Number.MIN_SAFE_INTEGER)&&(e=Math.abs(.05*s)),r(s+e),t||o(i-e)}this.min=i,this.max=s}getTickLimit(){const t=this.options.ticks;let e,{maxTicksLimit:n,stepSize:i}=t;return i?(e=Math.ceil(this.max/i)-Math.floor(this.min/i)+1,e>1e3&&(console.warn(`scales.${this.id}.ticks.stepSize: ${i} would result generating up to ${e} ticks. Limiting to 1000.`),e=1e3)):(e=this.computeTickLimit(),n=n||11),n&&(e=Math.min(n,e)),e}computeTickLimit(){return Number.POSITIVE_INFINITY}buildTicks(){const t=this.options,e=t.ticks;let n=this.getTickLimit();n=Math.max(2,n);const i=Jr({maxTicks:n,bounds:t.bounds,min:t.min,max:t.max,precision:e.precision,step:e.stepSize,count:e.count,maxDigits:this._maxDigits(),horizontal:this.isHorizontal(),minRotation:e.minRotation||0,includeBounds:!1!==e.includeBounds},this._range||this);return"ticks"===t.bounds&&Me(i,this,"value"),t.reverse?(i.reverse(),this.start=this.max,this.end=this.min):(this.start=this.min,this.end=this.max),i}configure(){const t=this.ticks;let e=this.min,n=this.max;if(super.configure(),this.options.offset&&t.length){const i=(n-e)/Math.max(t.length-1,1)/2;e-=i,n+=i}this._startValue=e,this._endValue=n,this._valueRange=n-e}getLabelForValue(t){return Ki(t,this.chart.options.locale,this.options.ticks.format)}}class ea extends ta{determineDataLimits(){const{min:t,max:e}=this.getMinMax(!0);this.min=Zt(t)?t:0,this.max=Zt(e)?e:1,this.handleTickRangeOptions()}computeTickLimit(){const t=this.isHorizontal(),e=t?this.width:this.height,n=Se(this.options.ticks.minRotation),i=(t?Math.sin(n):Math.cos(n))||.001,s=this._resolveTickFontOptions(0);return Math.ceil(e/Math.min(40,s.lineHeight/i))}getPixelForValue(t){return null===t?NaN:this.getPixelForDecimal((t-this._startValue)/this._valueRange)}getValueForPixel(t){return this._startValue+this.getDecimalForPixel(t)*this._valueRange}}function na(t){return 1===t/Math.pow(10,Math.floor(ve(t)))}ea.id="linear",ea.defaults={ticks:{callback:Vs.formatters.numeric}};class ia extends Js{constructor(t){super(t),this.start=void 0,this.end=void 0,this._startValue=void 0,this._valueRange=0}parse(t,e){const n=ta.prototype.parse.apply(this,[t,e]);if(0!==n)return Zt(n)&&n>0?n:null;this._zero=!0}determineDataLimits(){const{min:t,max:e}=this.getMinMax(!0);this.min=Zt(t)?Math.max(0,t):null,this.max=Zt(e)?Math.max(0,e):null,this.options.beginAtZero&&(this._zero=!0),this.handleTickRangeOptions()}handleTickRangeOptions(){const{minDefined:t,maxDefined:e}=this.getUserBounds();let n=this.min,i=this.max;const s=e=>n=t?n:e,o=t=>i=e?i:t,r=(t,e)=>Math.pow(10,Math.floor(ve(t))+e);n===i&&(n<=0?(s(1),o(10)):(s(r(n,-1)),o(r(i,1)))),n<=0&&s(r(i,-1)),i<=0&&o(r(n,1)),this._zero&&this.min!==this._suggestedMin&&n===r(this.min,0)&&s(r(n,-1)),this.min=n,this.max=i}buildTicks(){const t=this.options,e=function(t,e){const n=Math.floor(ve(e.max)),i=Math.ceil(e.max/Math.pow(10,n)),s=[];let o=Xt(t.min,Math.pow(10,Math.floor(ve(e.min)))),r=Math.floor(ve(o)),a=Math.floor(o/Math.pow(10,r)),l=r<0?Math.pow(10,Math.abs(r)):1;do{s.push({value:o,major:na(o)}),++a,10===a&&(a=1,++r,l=r>=0?1:l),o=Math.round(a*Math.pow(10,r)*l)/l}while(rs?{start:e-n,end:e}:{start:e,end:e+n}}function ra(t){const e={l:t.left+t._padding.left,r:t.right-t._padding.right,t:t.top+t._padding.top,b:t.bottom-t._padding.bottom},n=Object.assign({},e),i=[],s=[],o=t._pointLabels.length,r=t.options.pointLabels,a=r.centerPointLabels?fe/o:0;for(let u=0;ue.r&&(a=(i.end-e.r)/o,t.r=Math.max(t.r,e.r+a)),s.starte.b&&(l=(s.end-e.b)/r,t.b=Math.max(t.b,e.b+l))}function la(t){return 0===t||180===t?"center":t<180?"left":"right"}function ca(t,e,n){return"right"===n?t-=e:"center"===n&&(t-=e/2),t}function ha(t,e,n){return 90===n||270===n?t-=e/2:(n>270||n<90)&&(t-=e),t}function ua(t,e,n,i){const{ctx:s}=t;if(n)s.arc(t.xCenter,t.yCenter,e,0,pe);else{let n=t.getPointPosition(0,e);s.moveTo(n.x,n.y);for(let o=1;o{const n=Jt(this.options.pointLabels.callback,[t,e],this);return n||0===n?n:""})).filter(((t,e)=>this.chart.getDataVisibility(e)))}fit(){const t=this.options;t.display&&t.pointLabels.display?ra(this):this.setCenterPoint(0,0,0,0)}setCenterPoint(t,e,n,i){this.xCenter+=Math.floor((t-e)/2),this.yCenter+=Math.floor((n-i)/2),this.drawingArea-=Math.min(this.drawingArea/2,Math.max(t,e,n,i))}getIndexAngle(t){return Ee(t*(pe/(this._pointLabels.length||1))+Se(this.options.startAngle||0))}getDistanceFromCenterForValue(t){if(Ut(t))return NaN;const e=this.drawingArea/(this.max-this.min);return this.options.reverse?(this.max-t)*e:(t-this.min)*e}getValueForDistanceFromCenter(t){if(Ut(t))return NaN;const e=t/(this.drawingArea/(this.max-this.min));return this.options.reverse?this.max-e:this.min+e}getPointLabelContext(t){const e=this._pointLabels||[];if(t>=0&&t=0;s--){const e=i.setContext(t.getPointLabelContext(s)),o=hi(e.font),{x:r,y:a,textAlign:l,left:c,top:h,right:u,bottom:d}=t._pointLabelItems[s],{backdropColor:f}=e;if(!Ut(f)){const t=li(e.borderRadius),i=ci(e.backdropPadding);n.fillStyle=f;const s=c-i.left,o=h-i.top,r=u-c+i.width,a=d-h+i.height;Object.values(t).some((t=>0!==t))?(n.beginPath(),ei(n,{x:s,y:o,w:r,h:a,radius:t}),n.fill()):n.fillRect(s,o,r,a)}Qn(n,t._pointLabels[s],r,a+o.lineHeight/2,o,{color:e.color,textAlign:l,textBaseline:"middle"})}}(this,s),i.display&&this.ticks.forEach(((t,e)=>{if(0!==e){r=this.getDistanceFromCenterForValue(t.value);!function(t,e,n,i){const s=t.ctx,o=e.circular,{color:r,lineWidth:a}=e;!o&&!i||!r||!a||n<0||(s.save(),s.strokeStyle=r,s.lineWidth=a,s.setLineDash(e.borderDash),s.lineDashOffset=e.borderDashOffset,s.beginPath(),ua(t,n,o,i),s.closePath(),s.stroke(),s.restore())}(this,i.setContext(this.getContext(e-1)),r,s)}})),n.display){for(t.save(),o=s-1;o>=0;o--){const i=n.setContext(this.getPointLabelContext(o)),{color:s,lineWidth:l}=i;l&&s&&(t.lineWidth=l,t.strokeStyle=s,t.setLineDash(i.borderDash),t.lineDashOffset=i.borderDashOffset,r=this.getDistanceFromCenterForValue(e.ticks.reverse?this.min:this.max),a=this.getPointPosition(o,r),t.beginPath(),t.moveTo(this.xCenter,this.yCenter),t.lineTo(a.x,a.y),t.stroke())}t.restore()}}drawBorder(){}drawLabels(){const t=this.ctx,e=this.options,n=e.ticks;if(!n.display)return;const i=this.getIndexAngle(0);let s,o;t.save(),t.translate(this.xCenter,this.yCenter),t.rotate(i),t.textAlign="center",t.textBaseline="middle",this.ticks.forEach(((i,r)=>{if(0===r&&!e.reverse)return;const a=n.setContext(this.getContext(r)),l=hi(a.font);if(s=this.getDistanceFromCenterForValue(this.ticks[r].value),a.showLabelBackdrop){t.font=l.string,o=t.measureText(i.label).width,t.fillStyle=a.backdropColor;const e=ci(a.backdropPadding);t.fillRect(-o/2-e.left,-s-l.size/2-e.top,o+e.width,l.size+e.height)}Qn(t,i.label,0,-s,l,{color:a.color})})),t.restore()}drawTitle(){}}da.id="radialLinear",da.defaults={display:!0,animate:!0,position:"chartArea",angleLines:{display:!0,lineWidth:1,borderDash:[],borderDashOffset:0},grid:{circular:!1},startAngle:0,ticks:{showLabelBackdrop:!0,callback:Vs.formatters.numeric},pointLabels:{backdropColor:void 0,backdropPadding:2,display:!0,font:{size:10},callback:t=>t,padding:5,centerPointLabels:!1}},da.defaultRoutes={"angleLines.color":"borderColor","pointLabels.color":"color","ticks.color":"color"},da.descriptors={angleLines:{_fallback:"grid"}};const fa={millisecond:{common:!0,size:1,steps:1e3},second:{common:!0,size:1e3,steps:60},minute:{common:!0,size:6e4,steps:60},hour:{common:!0,size:36e5,steps:24},day:{common:!0,size:864e5,steps:30},week:{common:!1,size:6048e5,steps:4},month:{common:!0,size:2628e6,steps:12},quarter:{common:!1,size:7884e6,steps:4},year:{common:!0,size:3154e7}},pa=Object.keys(fa);function ga(t,e){return t-e}function ma(t,e){if(Ut(e))return null;const n=t._adapter,{parser:i,round:s,isoWeekday:o}=t._parseOpts;let r=e;return"function"==typeof i&&(r=i(r)),Zt(r)||(r="string"==typeof i?n.parse(r,i):n.parse(r)),null===r?null:(s&&(r="week"!==s||!we(o)&&!0!==o?n.startOf(r,s):n.startOf(r,"isoWeek",o)),+r)}function ba(t,e,n,i){const s=pa.length;for(let o=pa.indexOf(t);o=e?n[i]:n[s]]=!0}}else t[e]=!0}function ya(t,e,n){const i=[],s={},o=e.length;let r,a;for(r=0;r=0&&(e[l].major=!0);return e}(t,i,s,n):i}class _a extends Js{constructor(t){super(t),this._cache={data:[],labels:[],all:[]},this._unit="day",this._majorUnit=void 0,this._offsets={},this._normalized=!1,this._parseOpts=void 0}init(t,e){const n=t.time||(t.time={}),i=this._adapter=new so._date(t.adapters.date);i.init(e),oe(n.displayFormats,i.formats()),this._parseOpts={parser:n.parser,round:n.round,isoWeekday:n.isoWeekday},super.init(t),this._normalized=e.normalized}parse(t,e){return void 0===t?null:ma(this,t)}beforeLayout(){super.beforeLayout(),this._cache={data:[],labels:[],all:[]}}determineDataLimits(){const t=this.options,e=this._adapter,n=t.time.unit||"day";let{min:i,max:s,minDefined:o,maxDefined:r}=this.getUserBounds();function a(t){o||isNaN(t.min)||(i=Math.min(i,t.min)),r||isNaN(t.max)||(s=Math.max(s,t.max))}o&&r||(a(this._getLabelBounds()),"ticks"===t.bounds&&"labels"===t.ticks.source||a(this.getMinMax(!1))),i=Zt(i)&&!isNaN(i)?i:+e.startOf(Date.now(),n),s=Zt(s)&&!isNaN(s)?s:+e.endOf(Date.now(),n)+1,this.min=Math.min(i,s-1),this.max=Math.max(i+1,s)}_getLabelBounds(){const t=this.getLabelTimestamps();let e=Number.POSITIVE_INFINITY,n=Number.NEGATIVE_INFINITY;return t.length&&(e=t[0],n=t[t.length-1]),{min:e,max:n}}buildTicks(){const t=this.options,e=t.time,n=t.ticks,i="labels"===n.source?this.getLabelTimestamps():this._generate();"ticks"===t.bounds&&i.length&&(this.min=this._userMin||i[0],this.max=this._userMax||i[i.length-1]);const s=this.min,o=function(t,e,n){let i=0,s=t.length;for(;ii&&t[s-1]>n;)s--;return i>0||s=pa.indexOf(n);o--){const n=pa[o];if(fa[n].common&&t._adapter.diff(s,i,n)>=e-1)return n}return pa[n?pa.indexOf(n):0]}(this,o.length,e.minUnit,this.min,this.max)),this._majorUnit=n.major.enabled&&"year"!==this._unit?function(t){for(let e=pa.indexOf(t)+1,n=pa.length;e+t.value)))}initOffsets(t){let e,n,i=0,s=0;this.options.offset&&t.length&&(e=this.getDecimalForValue(t[0]),i=1===t.length?1-e:(this.getDecimalForValue(t[1])-e)/2,n=this.getDecimalForValue(t[t.length-1]),s=1===t.length?n:(n-this.getDecimalForValue(t[t.length-2]))/2);const o=t.length<3?.5:.25;i=Re(i,0,o),s=Re(s,0,o),this._offsets={start:i,end:s,factor:1/(i+1+s)}}_generate(){const t=this._adapter,e=this.min,n=this.max,i=this.options,s=i.time,o=s.unit||ba(s.minUnit,e,n,this._getLabelCapacity(e)),r=Gt(s.stepSize,1),a="week"===o&&s.isoWeekday,l=we(a)||!0===a,c={};let h,u,d=e;if(l&&(d=+t.startOf(d,"isoWeek",a)),d=+t.startOf(d,l?"day":o),t.diff(n,e,o)>1e5*r)throw new Error(e+" and "+n+" are too far apart with stepSize of "+r+" "+o);const f="data"===i.ticks.source&&this.getDataTimestamps();for(h=d,u=0;ht-e)).map((t=>+t))}getLabelForValue(t){const e=this._adapter,n=this.options.time;return n.tooltipFormat?e.format(t,n.tooltipFormat):e.format(t,n.displayFormats.datetime)}_tickFormatFunction(t,e,n,i){const s=this.options,o=s.time.displayFormats,r=this._unit,a=this._majorUnit,l=r&&o[r],c=a&&o[a],h=n[e],u=a&&c&&h&&h.major,d=this._adapter.format(t,i||(u?c:l)),f=s.ticks.callback;return f?Jt(f,[d,e,n],this):d}generateTickLabels(t){let e,n,i;for(e=0,n=t.length;e0?r:1}getDataTimestamps(){let t,e,n=this._cache.data||[];if(n.length)return n;const i=this.getMatchingVisibleMetas();if(this._normalized&&i.length)return this._cache.data=i[0].controller.getAllParsedValues(this);for(t=0,e=i.length;t=t[a].pos&&e<=t[l].pos&&({lo:a,hi:l}=Fe(t,"pos",e)),({pos:i,time:o}=t[a]),({pos:s,time:r}=t[l])):(e>=t[a].time&&e<=t[l].time&&({lo:a,hi:l}=Fe(t,"time",e)),({time:i,pos:o}=t[a]),({time:s,pos:r}=t[l]));const c=s-i;return c?o+(r-o)*(e-i)/c:o}_a.id="time",_a.defaults={bounds:"data",adapters:{},time:{parser:!1,unit:!1,round:!1,isoWeekday:!1,minUnit:"millisecond",displayFormats:{}},ticks:{source:"auto",major:{enabled:!1}}};class $a extends _a{constructor(t){super(t),this._table=[],this._minPos=void 0,this._tableRange=void 0}initOffsets(){const t=this._getTimestampsForTable(),e=this._table=this.buildLookupTable(t);this._minPos=va(e,this.min),this._tableRange=va(e,this.max)-this._minPos,super.initOffsets(t)}buildLookupTable(t){const{min:e,max:n}=this,i=[],s=[];let o,r,a,l,c;for(o=0,r=t.length;o=e&&l<=n&&i.push(l);if(i.length<2)return[{time:e,pos:0},{time:n,pos:1}];for(o=0,r=i.length;o{"componentData"in t&&n(1,i=t.componentData)},t.$$.update=()=>{1&t.$$.dirty&&a&&new dr(a,l)},[a,i,function(t){V[t?"unshift":"push"]((()=>{a=t,n(0,a)}))}]}class Ma extends pt{constructor(t){super(),ft(this,t,Ca,wa,r,{componentData:1})}}function Sa(t){let e,n,i;return{c(){e=$("div"),n=C(),i=$("div"),L(e,"class","path topLeft svelte-1hyaq5f"),L(i,"class","path bottomRight svelte-1hyaq5f")},m(t,s){y(t,e,s),y(t,n,s),y(t,i,s)},d(t){t&&_(e),t&&_(n),t&&_(i)}}}function La(t){let e;return{c(){e=$("div"),L(e,"class","path straightLine svelte-1hyaq5f")},m(t,n){y(t,e,n)},d(t){t&&_(e)}}}function Da(e){let n;function i(t,e){return t[5]?La:Sa}let s=i(e),o=s(e);return{c(){n=$("div"),o.c(),L(n,"class","connectorwrapper svelte-1hyaq5f"),O(n,"top",e[1]+"rem"),O(n,"left",e[0]+"rem"),O(n,"width",e[3]+"rem"),O(n,"height",e[4]+"rem"),A(n,"flip",e[2])},m(t,e){y(t,n,e),o.m(n,null)},p(t,[e]){s!==(s=i(t))&&(o.d(1),o=s(t),o&&(o.c(),o.m(n,null))),2&e&&O(n,"top",t[1]+"rem"),1&e&&O(n,"left",t[0]+"rem"),8&e&&O(n,"width",t[3]+"rem"),16&e&&O(n,"height",t[4]+"rem"),4&e&&A(n,"flip",t[2])},i:t,o:t,d(t){t&&_(n),o.d()}}}const Pa=.5;function Oa(t,e,n){let i,s,o,{top:r=0}=e,{left:a=0}=e,{bottom:l=0}=e,{right:c=0}=e,h=!1;return t.$$set=t=>{"top"in t&&n(1,r=t.top),"left"in t&&n(0,a=t.left),"bottom"in t&&n(7,l=t.bottom),"right"in t&&n(6,c=t.right)},t.$$.update=()=>{207&t.$$.dirty&&(n(2,i=c-a<0),n(3,s=Math.abs(c-a)),s<=Pa?(n(3,s=Pa),n(5,h=!0),n(0,a-=Pa/2)):(i?(n(0,a+=Pa/2),n(6,c-=Pa/2)):(n(0,a-=Pa/2),n(6,c+=Pa/2)),n(3,s=Math.abs(c-a))),n(4,o=l-r))},[a,r,i,s,o,h,c,l]}class Aa extends pt{constructor(t){super(),ft(this,t,Oa,Da,r,{top:1,left:0,bottom:7,right:6})}}function Ea(t,e,n){const i=t.slice();return i[4]=e[n],i}function Ta(t){let e,n;return e=new Aa({props:{top:wt(t[4].top),left:wt(t[4].left),bottom:wt(t[4].bottom),right:wt(t[4].right)}}),{c(){ct(e.$$.fragment)},m(t,i){ht(e,t,i),n=!0},p(t,n){const i={};1&n&&(i.top=wt(t[4].top)),1&n&&(i.left=wt(t[4].left)),1&n&&(i.bottom=wt(t[4].bottom)),1&n&&(i.right=wt(t[4].right)),e.$set(i)},i(t){n||(it(e.$$.fragment,t),n=!0)},o(t){st(e.$$.fragment,t),n=!1},d(t){ut(e,t)}}}function Ra(t){let e,n,i=t[0],s=[];for(let e=0;est(s[t],1,1,(()=>{s[t]=null}));return{c(){for(let t=0;t{"steps"in t&&n(1,i=t.steps),"boxes"in t&&n(2,s=t.boxes),"container"in t&&n(3,o=t.container)},t.$$.update=()=>{if(15&t.$$.dirty){n(0,r=[]);const t=o.getBoundingClientRect(),e=t.top,a=t.left;s&&Object.keys(i).forEach((t=>{var o;const l=i[t],c=s[t].getBoundingClientRect();null===(o=l.next)||void 0===o||o.forEach((t=>{const i=s[t].getBoundingClientRect(),o={top:c.bottom-e,left:c.left-a+c.width/2,bottom:i.top-e,right:i.left-a+i.width/2};n(0,r=[...r,o])}))}))}},[r,i,s,o]}class Ia extends pt{constructor(t){super(),ft(this,t,za,Ra,r,{steps:1,boxes:2,container:3})}}const Fa="currentStep";function Na(t,e,n){const i=t.slice();return i[18]=e[n],i[20]=n,i}function ja(t){let e,n,i;return{c(){e=$("div"),n=w("x"),i=w(t[6]),L(e,"class","levelstoshow svelte-117ceti")},m(t,s){y(t,e,s),x(e,n),x(e,i)},p(t,e){64&e&&P(i,t[6])},d(t){t&&_(e)}}}function Ba(t){let e,n;return{c(){e=$("div"),L(e,"class",n="level rectangle "+t[18]+" svelte-117ceti"),O(e,"z-index",-1*(t[20]+1)),O(e,"top",(t[20]+1)*Ha+"px"),O(e,"left",(t[20]+1)*Ha+"px")},m(t,n){y(t,e,n)},p(t,i){128&i&&n!==(n="level rectangle "+t[18]+" svelte-117ceti")&&L(e,"class",n)},d(t){t&&_(e)}}}function Va(e){let n,i,s,o,r,a,l,c,h,u,d,f=e[2].doc+"",p=e[6]&&ja(e),g=e[7],m=[];for(let t=0;t1&&(o=(new Intl.NumberFormat).format(a.num_possible_tasks)),l=a.num_possible_tasks-1,Object.keys(f).forEach((t=>{const e=Number.parseInt(t);a.num_possible_tasks&&a.num_possible_tasks>e&&n(11,l=f[e])}))):l*=Wa,l>0&&(p=new Array(l).fill("")),p=p.map(((t,e)=>{var n,i;if(a.num_possible_tasks){const t=null!==(n=a.num_failed)&&void 0!==n?n:0,s=null!==(i=a.successful_tasks)&&void 0!==i?i:0;return(t-1)/a.num_possible_tasks>=(e+1)/p.length?"error":(t+s)/a.num_possible_tasks>=(e+1)/p.length?"success":"running"}return""}));const g=j(Fa),m=r===g;let b;a.failed||a.num_failed?u=!0:(null!==(i=a.num_possible_tasks)&&void 0!==i?i:0)>(null!==(s=a.successful_tasks)&&void 0!==s?s:0)?d=!0:a.num_possible_tasks&&a.num_possible_tasks===a.successful_tasks&&(h=!0);let x=!1;return I((()=>{n(9,x=Mt(b))})),t.$$set=t=>{"name"in t&&n(1,r=t.name),"step"in t&&n(2,a=t.step),"numLevels"in t&&n(11,l=t.numLevels),"el"in t&&n(0,c=t.el)},[c,r,a,h,u,d,o,p,b,x,m,l,function(t){V[t?"unshift":"push"]((()=>{b=t,n(8,b)}))},function(t){V[t?"unshift":"push"]((()=>{c=t,n(0,c)}))}]}class qa extends pt{constructor(t){super(),ft(this,t,Ua,Va,r,{name:1,step:2,numLevels:11,el:0})}}function Ya(t,e,n){const i=t.slice();return i[11]=e[n],i}function Za(t){let e,n,i,s,o,r;function a(e){t[8](e)}let l={name:t[2],numLevels:t[3],step:t[5]};void 0!==t[4]&&(l.el=t[4]),n=new qa({props:l}),V.push((()=>lt(n,"el",a)));let c=t[7]&&function(t){let e,n,i,s,o=t[5].next,r=[];for(let e=0;est(r[t],1,1,(()=>{r[t]=null}));return{c(){e=$("div"),n=C(),i=$("div");for(let t=0;ti=!1))),n.$set(s),t[7]&&c.p(t,e),t[5].box_ends&&h.p(t,e)},i(t){r||(it(n.$$.fragment,t),it(c),it(h),r=!0)},o(t){st(n.$$.fragment,t),st(c),st(h),r=!1},d(t){t&&_(e),ut(n),c&&c.d(),h&&h.d()}}}function Xa(t){let e,n;return e=new Ja({props:{steps:t[1],stepName:t[11],levels:t[6],boxes:t[0]}}),{c(){ct(e.$$.fragment)},m(t,i){ht(e,t,i),n=!0},p(t,n){const i={};2&n&&(i.steps=t[1]),1&n&&(i.boxes=t[0]),e.$set(i)},i(t){n||(it(e.$$.fragment,t),n=!0)},o(t){st(e.$$.fragment,t),n=!1},d(t){ut(e,t)}}}function Ga(t){let e,n,i=t[5]&&Za(t);return{c(){i&&i.c(),e=M()},m(t,s){i&&i.m(t,s),y(t,e,s),n=!0},p(t,[e]){t[5]&&i.p(t,e)},i(t){n||(it(i),n=!0)},o(t){st(i),n=!1},d(t){i&&i.d(t),t&&_(e)}}}function Ka(t,e,n){var i;let{steps:s}=e,{stepName:o}=e,{levels:r=0}=e,{boxes:a={}}=e,l=null;I((()=>{l&&n(0,a[o]=l,a)}));let c=s[o];c||console.warn("step ",o," not found");const h="foreach"===(null==c?void 0:c.type)?r+1:"join"===(null==c?void 0:c.type)?r-1:r;let u=null===(i=null==c?void 0:c.next)||void 0===i?void 0:i.find((t=>{var e;return"join"!==(null===(e=s[t])||void 0===e?void 0:e.type)}));return t.$$set=t=>{"steps"in t&&n(1,s=t.steps),"stepName"in t&&n(2,o=t.stepName),"levels"in t&&n(3,r=t.levels),"boxes"in t&&n(0,a=t.boxes)},[a,s,o,r,l,c,h,u,function(t){l=t,n(4,l)}]}class Ja extends pt{constructor(t){super(),ft(this,t,Ka,Ga,r,{steps:1,stepName:2,levels:3,boxes:0})}}function Qa(e){let n;return{c(){n=$("p"),n.textContent="No start step"},m(t,e){y(t,n,e)},p:t,i:t,o:t,d(t){t&&_(n)}}}function tl(t){let e,n,i;function s(e){t[6](e)}let o={steps:t[3],stepName:"start"};return void 0!==t[0]&&(o.boxes=t[0]),e=new Ja({props:o}),V.push((()=>lt(e,"boxes",s))),{c(){ct(e.$$.fragment)},m(t,n){ht(e,t,n),i=!0},p(t,i){const s={};!n&&1&i&&(n=!0,s.boxes=t[0],Z((()=>n=!1))),e.$set(s)},i(t){i||(it(e.$$.fragment,t),i=!0)},o(t){st(e.$$.fragment,t),i=!1},d(t){ut(e,t)}}}function el(t){let e,n;return e=new Ia({props:{boxes:t[0],steps:t[3],container:t[1]}}),{c(){ct(e.$$.fragment)},m(t,i){ht(e,t,i),n=!0},p(t,n){const i={};1&n&&(i.boxes=t[0]),2&n&&(i.container=t[1]),e.$set(i)},i(t){n||(it(e.$$.fragment,t),n=!0)},o(t){st(e.$$.fragment,t),n=!1},d(t){ut(e,t)}}}function nl(t){let e,n,i,s,o,r,a,l=!t[2]&&Object.keys(t[0]).length;const c=[tl,Qa],h=[];n=function(t,e){return t[3]?.start?0:1}(t),i=h[n]=c[n](t);let u=l&&el(t);return{c(){e=$("div"),i.c(),s=C(),u&&u.c(),O(e,"position","relative"),O(e,"line-height","1"),L(e,"data-component","dag")},m(i,l){y(i,e,l),h[n].m(e,null),x(e,s),u&&u.m(e,null),t[7](e),o=!0,r||(a=S(window,"resize",t[4]),r=!0)},p(t,[n]){i.p(t,n),5&n&&(l=!t[2]&&Object.keys(t[0]).length),l?u?(u.p(t,n),5&n&&it(u,1)):(u=el(t),u.c(),it(u,1),u.m(e,null)):u&&(et(),st(u,1,1,(()=>{u=null})),nt())},i(t){o||(it(i),it(u),o=!0)},o(t){st(i),st(u),o=!1},d(i){i&&_(e),h[n].d(),u&&u.d(),t[7](null),r=!1,a()}}}const il=100;function sl(t,e,n){let i;var s;h(t,_t,(t=>n(10,i=t)));let{componentData:o}=e;const{data:r}=o;let a,l,c={};N(Fa,Ct(null===(s=null==i?void 0:i.metadata)||void 0===s?void 0:s.pathspec,"stepname"));let u=!1;return t.$$set=t=>{"componentData"in t&&n(5,o=t.componentData)},[c,a,u,r,()=>{n(2,u=!0),clearTimeout(l),l=setTimeout((()=>{n(2,u=!1)}),il)},o,function(t){c=t,n(0,c)},function(t){V[t?"unshift":"push"]((()=>{a=t,n(1,a)}))}]}class ol extends pt{constructor(t){super(),ft(this,t,sl,nl,r,{componentData:5})}}function rl(e){let n;return{c(){n=$("h2"),n.textContent=`${e[0]}`,L(n,"class","title svelte-117s0ws"),L(n,"data-component","title")},m(t,e){y(t,n,e)},p:t,i:t,o:t,d(t){t&&_(n)}}}function al(t,e,n){let{componentData:i}=e;const{text:s}=i;return t.$$set=t=>{"componentData"in t&&n(1,i=t.componentData)},[s,i]}class ll extends pt{constructor(t){super(),ft(this,t,al,rl,r,{componentData:1})}}function cl(e){let n,i,s=e[0].text+"";return{c(){n=$("p"),i=w(s),L(n,"class","subtitle svelte-lu9pnn"),L(n,"data-component","subtitle")},m(t,e){y(t,n,e),x(n,i)},p(t,[e]){1&e&&s!==(s=t[0].text+"")&&P(i,s)},i:t,o:t,d(t){t&&_(n)}}}function hl(t,e,n){let{componentData:i}=e;return t.$$set=t=>{"componentData"in t&&n(0,i=t.componentData)},[i]}class ul extends pt{constructor(t){super(),ft(this,t,hl,cl,r,{componentData:0})}}function dl(e){let n,i,s,o=e[0]&&function(e){let n,i;return n=new ll({props:{componentData:{type:"title",text:e[0]}}}),{c(){ct(n.$$.fragment)},m(t,e){ht(n,t,e),i=!0},p:t,i(t){i||(it(n.$$.fragment,t),i=!0)},o(t){st(n.$$.fragment,t),i=!1},d(t){ut(n,t)}}}(e),r=e[1]&&function(e){let n,i;return n=new ul({props:{componentData:{type:"subtitle",text:e[1]}}}),{c(){ct(n.$$.fragment)},m(t,e){ht(n,t,e),i=!0},p:t,i(t){i||(it(n.$$.fragment,t),i=!0)},o(t){st(n.$$.fragment,t),i=!1},d(t){ut(n,t)}}}(e);return{c(){n=$("header"),o&&o.c(),i=C(),r&&r.c(),L(n,"class","container svelte-1ugmt5d"),L(n,"data-component","heading")},m(t,e){y(t,n,e),o&&o.m(n,null),x(n,i),r&&r.m(n,null),s=!0},p(t,[e]){t[0]&&o.p(t,e),t[1]&&r.p(t,e)},i(t){s||(it(o),it(r),s=!0)},o(t){st(o),st(r),s=!1},d(t){t&&_(n),o&&o.d(),r&&r.d()}}}function fl(t,e,n){let{componentData:i}=e;const{title:s,subtitle:o}=i;return t.$$set=t=>{"componentData"in t&&n(2,i=t.componentData)},[s,o,i]}class pl extends pt{constructor(t){super(),ft(this,t,fl,dl,r,{componentData:2})}}function gl(e){let n,i,s,o,r,a,l,h,u=e[2]&&function(e){let n;return{c(){n=$("div"),n.textContent=`${e[2]}`,L(n,"class","label svelte-1x96yvr")},m(t,e){y(t,n,e)},p:t,d(t){t&&_(n)}}}(e),d=e[3]&&function(e){let n;return{c(){n=$("figcaption"),n.textContent=`${e[3]}`,L(n,"class","description svelte-1x96yvr")},m(t,e){y(t,n,e)},p:t,d(t){t&&_(n)}}}(e);return{c(){n=$("figure"),i=$("div"),s=$("img"),r=C(),u&&u.c(),a=C(),d&&d.c(),c(s.src,o=e[1])||L(s,"src",o),L(s,"alt",e[2]||"image"),L(s,"class","svelte-1x96yvr"),L(i,"class","imageContainer"),L(n,"data-component","image"),L(n,"class","svelte-1x96yvr")},m(t,o){y(t,n,o),x(n,i),x(i,s),x(n,r),u&&u.m(n,null),x(n,a),d&&d.m(n,null),l||(h=S(n,"click",e[4]),l=!0)},p(t,[e]){t[2]&&u.p(t,e),t[3]&&d.p(t,e)},i:t,o:t,d(t){t&&_(n),u&&u.d(),d&&d.d(),l=!1,h()}}}function ml(t,e,n){let{componentData:i}=e;const{src:s,label:o,description:r}=i;return t.$$set=t=>{"componentData"in t&&n(0,i=t.componentData)},[i,s,o,r,()=>$t.set(i)]}class bl extends pt{constructor(t){super(),ft(this,t,ml,gl,r,{componentData:0})}}function xl(e){let n,i;return{c(){n=$("div"),i=$("canvas"),L(n,"data-component","line-chart")},m(t,s){y(t,n,s),x(n,i),e[2](i)},p:t,i:t,o:t,d(t){t&&_(n),e[2](null)}}}function yl(t,e,n){dr.register(Dr,ea,zs,Kr,Or);let{componentData:i}=e;const{config:s,data:o,labels:r}=i;let a;const l=s||{type:"line",data:{labels:r,datasets:[{backgroundColor:ka[2],borderColor:ka[2],data:o||[]}]},options:{plugins:{legend:{display:!1}}}};return t.$$set=t=>{"componentData"in t&&n(1,i=t.componentData)},t.$$.update=()=>{1&t.$$.dirty&&a&&new dr(a,l)},[a,i,function(t){V[t?"unshift":"push"]((()=>{a=t,n(0,a)}))}]}class _l extends pt{constructor(t){super(),ft(this,t,yl,xl,r,{componentData:1})}}function vl(e){let n,i,s,o,r,a,l,c=e[0].data+"";return{c(){n=$("pre"),i=w(" \n "),s=$("code"),o=w("\n "),r=w(c),a=w("\n "),l=w("\n"),L(s,"class","mono language-log"),L(n,"class","log svelte-1jhmsu"),L(n,"data-component","log")},m(t,c){y(t,n,c),x(n,i),x(n,s),x(s,o),x(s,r),x(s,a),e[2](s),x(n,l)},p(t,[e]){1&e&&c!==(c=t[0].data+"")&&P(r,c)},i:t,o:t,d(t){t&&_(n),e[2](null)}}}function $l(t,e,n){let i,{componentData:s}=e;return t.$$set=t=>{"componentData"in t&&n(0,s=t.componentData)},t.$$.update=()=>{2&t.$$.dirty&&i&&function(){var t;i&&(null===(t=null===window||void 0===window?void 0:window.Prism)||void 0===t||t.highlightElement(i))}()},[s,i,function(t){V[t?"unshift":"push"]((()=>{i=t,n(1,i)}))}]}class kl extends pt{constructor(t){super(),ft(this,t,$l,vl,r,{componentData:0})}}function wl(t,e,n){const i=t.slice();return i[18]=e[n],i}function Cl(t,e,n){const i=t.slice();return i[18]=e[n],i}function Ml(t,e,n){const i=t.slice();return i[10]=e[n],i}function Sl(t,e,n){const i=t.slice();return i[13]=e[n],i[15]=n,i}function Ll(t,e,n){const i=t.slice();return i[16]=e[n],i[15]=n,i}function Dl(t,e,n){const i=t.slice();return i[7]=e[n],i}function Pl(t){let e,n,i,s;const o=[Tl,El,Al],r=[];function a(t,e){return"table"===t[0]?0:"list"===t[0]?1:2}return e=a(t),n=r[e]=o[e](t),{c(){n.c(),i=M()},m(t,n){r[e].m(t,n),y(t,i,n),s=!0},p(t,s){let l=e;e=a(t),e===l?r[e].p(t,s):(et(),st(r[l],1,1,(()=>{r[l]=null})),nt(),n=r[e],n?n.p(t,s):(n=r[e]=o[e](t),n.c()),it(n,1),n.m(i.parentNode,i))},i(t){s||(it(n),s=!0)},o(t){st(n),s=!1},d(t){r[e].d(t),t&&_(i)}}}function Ol(t){let e,n,i=t[1],s=[];for(let e=0;est(s[t],1,1,(()=>{s[t]=null}));return{c(){for(let t=0;t{ut(t,1)})),nt()}r?(n=T(r,a(t)),ct(n.$$.fragment),it(n.$$.fragment,1),ht(n,i.parentNode,i)):n=null}else r&&n.$set(s)},i(t){s||(n&&it(n.$$.fragment,t),s=!0)},o(t){n&&st(n.$$.fragment,t),s=!1},d(t){t&&_(i),n&&ut(n,t)}}}function El(t){let e,n,i,s;const o=[Nl,Fl],r=[];function a(t,e){return t[4]?0:1}return e=a(t),n=r[e]=o[e](t),{c(){n.c(),i=M()},m(t,n){r[e].m(t,n),y(t,i,n),s=!0},p(t,s){let l=e;e=a(t),e===l?r[e].p(t,s):(et(),st(r[l],1,1,(()=>{r[l]=null})),nt(),n=r[e],n?n.p(t,s):(n=r[e]=o[e](t),n.c()),it(n,1),n.m(i.parentNode,i))},i(t){s||(it(n),s=!0)},o(t){st(n),s=!1},d(t){r[e].d(t),t&&_(i)}}}function Tl(t){let e,n,i;var s=t[5].table;function o(t){return{props:{$$slots:{default:[ec]},$$scope:{ctx:t}}}}return s&&(e=T(s,o(t))),{c(){e&&ct(e.$$.fragment),n=M()},m(t,s){e&&ht(e,t,s),y(t,n,s),i=!0},p(t,i){const r={};if(8388716&i&&(r.$$scope={dirty:i,ctx:t}),32&i&&s!==(s=t[5].table)){if(e){et();const t=e;st(t.$$.fragment,1,0,(()=>{ut(t,1)})),nt()}s?(e=T(s,o(t)),ct(e.$$.fragment),it(e.$$.fragment,1),ht(e,n.parentNode,n)):e=null}else s&&e.$set(r)},i(t){i||(e&&it(e.$$.fragment,t),i=!0)},o(t){e&&st(e.$$.fragment,t),i=!1},d(t){t&&_(n),e&&ut(e,t)}}}function Rl(e){let n,i=e[6].raw+"";return{c(){n=w(i)},m(t,e){y(t,n,e)},p(t,e){64&e&&i!==(i=t[6].raw+"")&&P(n,i)},i:t,o:t,d(t){t&&_(n)}}}function zl(t){let e,n;return e=new oc({props:{tokens:t[1],renderers:t[5]}}),{c(){ct(e.$$.fragment)},m(t,i){ht(e,t,i),n=!0},p(t,n){const i={};2&n&&(i.tokens=t[1]),32&n&&(i.renderers=t[5]),e.$set(i)},i(t){n||(it(e.$$.fragment,t),n=!0)},o(t){st(e.$$.fragment,t),n=!1},d(t){ut(e,t)}}}function Il(t){let e,n,i,s;const o=[zl,Rl],r=[];function a(t,e){return t[1]?0:1}return e=a(t),n=r[e]=o[e](t),{c(){n.c(),i=M()},m(t,n){r[e].m(t,n),y(t,i,n),s=!0},p(t,s){let l=e;e=a(t),e===l?r[e].p(t,s):(et(),st(r[l],1,1,(()=>{r[l]=null})),nt(),n=r[e],n?n.p(t,s):(n=r[e]=o[e](t),n.c()),it(n,1),n.m(i.parentNode,i))},i(t){s||(it(n),s=!0)},o(t){st(n),s=!1},d(t){r[e].d(t),t&&_(i)}}}function Fl(t){let n,i,s;const o=[{ordered:t[4]},t[6]];var r=t[5].list;function a(t){let n={$$slots:{default:[Vl]},$$scope:{ctx:t}};for(let t=0;t{ut(t,1)})),nt()}r?(n=T(r,a(t)),ct(n.$$.fragment),it(n.$$.fragment,1),ht(n,i.parentNode,i)):n=null}else r&&n.$set(s)},i(t){s||(n&&it(n.$$.fragment,t),s=!0)},o(t){n&&st(n.$$.fragment,t),s=!1},d(t){t&&_(i),n&&ut(n,t)}}}function Nl(t){let n,i,s;const o=[{ordered:t[4]},t[6]];var r=t[5].list;function a(t){let n={$$slots:{default:[Ul]},$$scope:{ctx:t}};for(let t=0;t{ut(t,1)})),nt()}r?(n=T(r,a(t)),ct(n.$$.fragment),it(n.$$.fragment,1),ht(n,i.parentNode,i)):n=null}else r&&n.$set(s)},i(t){s||(n&&it(n.$$.fragment,t),s=!0)},o(t){n&&st(n.$$.fragment,t),s=!1},d(t){t&&_(i),n&&ut(n,t)}}}function jl(t){let e,n,i;return e=new oc({props:{tokens:t[18].tokens,renderers:t[5]}}),{c(){ct(e.$$.fragment),n=C()},m(t,s){ht(e,t,s),y(t,n,s),i=!0},p(t,n){const i={};64&n&&(i.tokens=t[18].tokens),32&n&&(i.renderers=t[5]),e.$set(i)},i(t){i||(it(e.$$.fragment,t),i=!0)},o(t){st(e.$$.fragment,t),i=!1},d(t){ut(e,t),t&&_(n)}}}function Bl(t){let n,i,s;const o=[t[18]];var r=t[5].unorderedlistitem||t[5].listitem;function a(t){let n={$$slots:{default:[jl]},$$scope:{ctx:t}};for(let t=0;t{ut(t,1)})),nt()}r?(n=T(r,a(t)),ct(n.$$.fragment),it(n.$$.fragment,1),ht(n,i.parentNode,i)):n=null}else r&&n.$set(s)},i(t){s||(n&&it(n.$$.fragment,t),s=!0)},o(t){n&&st(n.$$.fragment,t),s=!1},d(t){t&&_(i),n&&ut(n,t)}}}function Vl(t){let e,n,i=t[6].items,s=[];for(let e=0;est(s[t],1,1,(()=>{s[t]=null}));return{c(){for(let t=0;t{ut(t,1)})),nt()}r?(n=T(r,a(t)),ct(n.$$.fragment),it(n.$$.fragment,1),ht(n,i.parentNode,i)):n=null}else r&&n.$set(s)},i(t){s||(n&&it(n.$$.fragment,t),s=!0)},o(t){n&&st(n.$$.fragment,t),s=!1},d(t){t&&_(i),n&&ut(n,t)}}}function Ul(t){let e,n,i=t[6].items,s=[];for(let e=0;est(s[t],1,1,(()=>{s[t]=null}));return{c(){for(let t=0;t{ut(t,1)})),nt()}s?(e=T(s,o(t)),ct(e.$$.fragment),it(e.$$.fragment,1),ht(e,n.parentNode,n)):e=null}else s&&e.$set(r)},i(t){i||(e&&it(e.$$.fragment,t),i=!0)},o(t){e&&st(e.$$.fragment,t),i=!1},d(t){t&&_(n),e&&ut(e,t)}}}function Zl(t){let e,n,i=t[2],s=[];for(let e=0;est(s[t],1,1,(()=>{s[t]=null}));return{c(){for(let t=0;t{ut(t,1)})),nt()}s?(e=T(s,o(t)),ct(e.$$.fragment),it(e.$$.fragment,1),ht(e,n.parentNode,n)):e=null}else s&&e.$set(r)},i(t){i||(e&&it(e.$$.fragment,t),i=!0)},o(t){e&&st(e.$$.fragment,t),i=!1},d(t){t&&_(n),e&&ut(e,t)}}}function Gl(t){let e,n;return e=new oc({props:{tokens:t[13].tokens,renderers:t[5]}}),{c(){ct(e.$$.fragment)},m(t,i){ht(e,t,i),n=!0},p(t,n){const i={};8&n&&(i.tokens=t[13].tokens),32&n&&(i.renderers=t[5]),e.$set(i)},i(t){n||(it(e.$$.fragment,t),n=!0)},o(t){st(e.$$.fragment,t),n=!1},d(t){ut(e,t)}}}function Kl(t){let e,n,i;var s=t[5].tablecell;function o(t){return{props:{header:!1,align:t[6].align[t[15]]||"center",$$slots:{default:[Gl]},$$scope:{ctx:t}}}}return s&&(e=T(s,o(t))),{c(){e&&ct(e.$$.fragment),n=M()},m(t,s){e&&ht(e,t,s),y(t,n,s),i=!0},p(t,i){const r={};if(64&i&&(r.align=t[6].align[t[15]]||"center"),8388648&i&&(r.$$scope={dirty:i,ctx:t}),32&i&&s!==(s=t[5].tablecell)){if(e){et();const t=e;st(t.$$.fragment,1,0,(()=>{ut(t,1)})),nt()}s?(e=T(s,o(t)),ct(e.$$.fragment),it(e.$$.fragment,1),ht(e,n.parentNode,n)):e=null}else s&&e.$set(r)},i(t){i||(e&&it(e.$$.fragment,t),i=!0)},o(t){e&&st(e.$$.fragment,t),i=!1},d(t){t&&_(n),e&&ut(e,t)}}}function Jl(t){let e,n,i=t[10],s=[];for(let e=0;est(s[t],1,1,(()=>{s[t]=null}));return{c(){for(let t=0;t{ut(t,1)})),nt()}s?(e=T(s,o(t)),ct(e.$$.fragment),it(e.$$.fragment,1),ht(e,n.parentNode,n)):e=null}else s&&e.$set(r)},i(t){i||(e&&it(e.$$.fragment,t),i=!0)},o(t){e&&st(e.$$.fragment,t),i=!1},d(t){t&&_(n),e&&ut(e,t)}}}function tc(t){let e,n,i=t[3],s=[];for(let e=0;est(s[t],1,1,(()=>{s[t]=null}));return{c(){for(let t=0;t{ut(t,1)})),nt()}r?(e=T(r,a(t)),ct(e.$$.fragment),it(e.$$.fragment,1),ht(e,n.parentNode,n)):e=null}else r&&e.$set(h);const u={};if(8388712&o&&(u.$$scope={dirty:o,ctx:t}),32&o&&l!==(l=t[5].tablebody)){if(i){et();const t=i;st(t.$$.fragment,1,0,(()=>{ut(t,1)})),nt()}l?(i=T(l,c(t)),ct(i.$$.fragment),it(i.$$.fragment,1),ht(i,s.parentNode,s)):i=null}else l&&i.$set(u)},i(t){o||(e&&it(e.$$.fragment,t),i&&it(i.$$.fragment,t),o=!0)},o(t){e&&st(e.$$.fragment,t),i&&st(i.$$.fragment,t),o=!1},d(t){e&&ut(e,t),t&&_(n),t&&_(s),i&&ut(i,t)}}}function nc(t){let n,i;const s=[t[7],{renderers:t[5]}];let o={};for(let t=0;t{r[l]=null})),nt()),~e?(n=r[e],n?n.p(t,s):(n=r[e]=o[e](t),n.c()),it(n,1),n.m(i.parentNode,i)):n=null)},i(t){s||(it(n),s=!0)},o(t){st(n),s=!1},d(t){~e&&r[e].d(t),t&&_(i)}}}function sc(t,n,i){const s=["type","tokens","header","rows","ordered","renderers"];let o=b(n,s),{type:r}=n,{tokens:a}=n,{header:l}=n,{rows:c}=n,{ordered:h=!1}=n,{renderers:u}=n;return function(){const t=console.warn;console.warn=e=>{e.includes("unknown prop")||e.includes("unexpected slot")||t(e)},I((()=>{console.warn=t}))}(),t.$$set=t=>{n=e(e({},n),m(t)),i(6,o=b(n,s)),"type"in t&&i(0,r=t.type),"tokens"in t&&i(1,a=t.tokens),"header"in t&&i(2,l=t.header),"rows"in t&&i(3,c=t.rows),"ordered"in t&&i(4,h=t.ordered),"renderers"in t&&i(5,u=t.renderers)},[r,a,l,c,h,u,o]}class oc extends pt{constructor(t){super(),ft(this,t,sc,ic,r,{type:0,tokens:1,header:2,rows:3,ordered:4,renderers:5})}}function rc(){return{async:!1,baseUrl:null,breaks:!1,extensions:null,gfm:!0,headerIds:!0,headerPrefix:"",highlight:null,hooks:null,langPrefix:"language-",mangle:!0,pedantic:!1,renderer:null,sanitize:!1,sanitizer:null,silent:!1,smartypants:!1,tokenizer:null,walkTokens:null,xhtml:!1}}let ac={async:!1,baseUrl:null,breaks:!1,extensions:null,gfm:!0,headerIds:!0,headerPrefix:"",highlight:null,hooks:null,langPrefix:"language-",mangle:!0,pedantic:!1,renderer:null,sanitize:!1,sanitizer:null,silent:!1,smartypants:!1,tokenizer:null,walkTokens:null,xhtml:!1};const lc=/[&<>"']/,cc=new RegExp(lc.source,"g"),hc=/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/,uc=new RegExp(hc.source,"g"),dc={"&":"&","<":"<",">":">",'"':""","'":"'"},fc=t=>dc[t];function pc(t,e){if(e){if(lc.test(t))return t.replace(cc,fc)}else if(hc.test(t))return t.replace(uc,fc);return t}const gc=/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/gi;function mc(t){return t.replace(gc,((t,e)=>"colon"===(e=e.toLowerCase())?":":"#"===e.charAt(0)?"x"===e.charAt(1)?String.fromCharCode(parseInt(e.substring(2),16)):String.fromCharCode(+e.substring(1)):""))}const bc=/(^|[^\[])\^/g;function xc(t,e){t="string"==typeof t?t:t.source,e=e||"";const n={replace:(e,i)=>(i=(i=i.source||i).replace(bc,"$1"),t=t.replace(e,i),n),getRegex:()=>new RegExp(t,e)};return n}const yc=/[^\w:]/g,_c=/^$|^[a-z][a-z0-9+.-]*:|^[?#]/i;function vc(t,e,n){if(t){let t;try{t=decodeURIComponent(mc(n)).replace(yc,"").toLowerCase()}catch(t){return null}if(0===t.indexOf("javascript:")||0===t.indexOf("vbscript:")||0===t.indexOf("data:"))return null}e&&!_c.test(n)&&(n=function(t,e){$c[" "+t]||(kc.test(t)?$c[" "+t]=t+"/":$c[" "+t]=Lc(t,"/",!0));t=$c[" "+t];const n=-1===t.indexOf(":");return"//"===e.substring(0,2)?n?e:t.replace(wc,"$1")+e:"/"===e.charAt(0)?n?e:t.replace(Cc,"$1")+e:t+e}(e,n));try{n=encodeURI(n).replace(/%25/g,"%")}catch(t){return null}return n}const $c={},kc=/^[^:]+:\/*[^/]*$/,wc=/^([^:]+:)[\s\S]*$/,Cc=/^([^:]+:\/*[^/]*)[\s\S]*$/;const Mc={exec:function(){}};function Sc(t,e){const n=t.replace(/\|/g,((t,e,n)=>{let i=!1,s=e;for(;--s>=0&&"\\"===n[s];)i=!i;return i?"|":" |"})),i=n.split(/ \|/);let s=0;if(i[0].trim()||i.shift(),i.length>0&&!i[i.length-1].trim()&&i.pop(),i.length>e)i.splice(e);else for(;i.length1;)1&e&&(n+=t),e>>=1,t+=t;return n+t}function Pc(t,e,n,i){const s=e.href,o=e.title?pc(e.title):null,r=t[1].replace(/\\([\[\]])/g,"$1");if("!"!==t[0].charAt(0)){i.state.inLink=!0;const t={type:"link",raw:n,href:s,title:o,text:r,tokens:i.inlineTokens(r)};return i.state.inLink=!1,t}return{type:"image",raw:n,href:s,title:o,text:pc(r)}}class Oc{constructor(t){this.options=t||ac}space(t){const e=this.rules.block.newline.exec(t);if(e&&e[0].length>0)return{type:"space",raw:e[0]}}code(t){const e=this.rules.block.code.exec(t);if(e){const t=e[0].replace(/^ {1,4}/gm,"");return{type:"code",raw:e[0],codeBlockStyle:"indented",text:this.options.pedantic?t:Lc(t,"\n")}}}fences(t){const e=this.rules.block.fences.exec(t);if(e){const t=e[0],n=function(t,e){const n=t.match(/^(\s+)(?:```)/);if(null===n)return e;const i=n[1];return e.split("\n").map((t=>{const e=t.match(/^\s+/);if(null===e)return t;const[n]=e;return n.length>=i.length?t.slice(i.length):t})).join("\n")}(t,e[3]||"");return{type:"code",raw:t,lang:e[2]?e[2].trim().replace(this.rules.inline._escapes,"$1"):e[2],text:n}}}heading(t){const e=this.rules.block.heading.exec(t);if(e){let t=e[2].trim();if(/#$/.test(t)){const e=Lc(t,"#");this.options.pedantic?t=e.trim():e&&!/ $/.test(e)||(t=e.trim())}return{type:"heading",raw:e[0],depth:e[1].length,text:t,tokens:this.lexer.inline(t)}}}hr(t){const e=this.rules.block.hr.exec(t);if(e)return{type:"hr",raw:e[0]}}blockquote(t){const e=this.rules.block.blockquote.exec(t);if(e){const t=e[0].replace(/^ *>[ \t]?/gm,""),n=this.lexer.state.top;this.lexer.state.top=!0;const i=this.lexer.blockTokens(t);return this.lexer.state.top=n,{type:"blockquote",raw:e[0],tokens:i,text:t}}}list(t){let e=this.rules.block.list.exec(t);if(e){let n,i,s,o,r,a,l,c,h,u,d,f,p=e[1].trim();const g=p.length>1,m={type:"list",raw:"",ordered:g,start:g?+p.slice(0,-1):"",loose:!1,items:[]};p=g?`\\d{1,9}\\${p.slice(-1)}`:`\\${p}`,this.options.pedantic&&(p=g?p:"[*+-]");const b=new RegExp(`^( {0,3}${p})((?:[\t ][^\\n]*)?(?:\\n|$))`);for(;t&&(f=!1,e=b.exec(t))&&!this.rules.block.hr.test(t);){if(n=e[0],t=t.substring(n.length),c=e[2].split("\n",1)[0].replace(/^\t+/,(t=>" ".repeat(3*t.length))),h=t.split("\n",1)[0],this.options.pedantic?(o=2,d=c.trimLeft()):(o=e[2].search(/[^ ]/),o=o>4?1:o,d=c.slice(o),o+=e[1].length),a=!1,!c&&/^ *$/.test(h)&&(n+=h+"\n",t=t.substring(h.length+1),f=!0),!f){const e=new RegExp(`^ {0,${Math.min(3,o-1)}}(?:[*+-]|\\d{1,9}[.)])((?:[ \t][^\\n]*)?(?:\\n|$))`),i=new RegExp(`^ {0,${Math.min(3,o-1)}}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)`),s=new RegExp(`^ {0,${Math.min(3,o-1)}}(?:\`\`\`|~~~)`),r=new RegExp(`^ {0,${Math.min(3,o-1)}}#`);for(;t&&(u=t.split("\n",1)[0],h=u,this.options.pedantic&&(h=h.replace(/^ {1,4}(?=( {4})*[^ ])/g," ")),!s.test(h))&&!r.test(h)&&!e.test(h)&&!i.test(t);){if(h.search(/[^ ]/)>=o||!h.trim())d+="\n"+h.slice(o);else{if(a)break;if(c.search(/[^ ]/)>=4)break;if(s.test(c))break;if(r.test(c))break;if(i.test(c))break;d+="\n"+h}a||h.trim()||(a=!0),n+=u+"\n",t=t.substring(u.length+1),c=h.slice(o)}}m.loose||(l?m.loose=!0:/\n *\n *$/.test(n)&&(l=!0)),this.options.gfm&&(i=/^\[[ xX]\] /.exec(d),i&&(s="[ ] "!==i[0],d=d.replace(/^\[[ xX]\] +/,""))),m.items.push({type:"list_item",raw:n,task:!!i,checked:s,loose:!1,text:d}),m.raw+=n}m.items[m.items.length-1].raw=n.trimRight(),m.items[m.items.length-1].text=d.trimRight(),m.raw=m.raw.trimRight();const x=m.items.length;for(r=0;r"space"===t.type)),e=t.length>0&&t.some((t=>/\n.*\n/.test(t.raw)));m.loose=e}if(m.loose)for(r=0;r$/,"$1").replace(this.rules.inline._escapes,"$1"):"",i=e[3]?e[3].substring(1,e[3].length-1).replace(this.rules.inline._escapes,"$1"):e[3];return{type:"def",tag:t,raw:e[0],href:n,title:i}}}table(t){const e=this.rules.block.table.exec(t);if(e){const t={type:"table",header:Sc(e[1]).map((t=>({text:t}))),align:e[2].replace(/^ *|\| *$/g,"").split(/ *\| */),rows:e[3]&&e[3].trim()?e[3].replace(/\n[ \t]*$/,"").split("\n"):[]};if(t.header.length===t.align.length){t.raw=e[0];let n,i,s,o,r=t.align.length;for(n=0;n({text:t})));for(r=t.header.length,i=0;i/i.test(e[0])&&(this.lexer.state.inLink=!1),!this.lexer.state.inRawBlock&&/^<(pre|code|kbd|script)(\s|>)/i.test(e[0])?this.lexer.state.inRawBlock=!0:this.lexer.state.inRawBlock&&/^<\/(pre|code|kbd|script)(\s|>)/i.test(e[0])&&(this.lexer.state.inRawBlock=!1),{type:this.options.sanitize?"text":"html",raw:e[0],inLink:this.lexer.state.inLink,inRawBlock:this.lexer.state.inRawBlock,text:this.options.sanitize?this.options.sanitizer?this.options.sanitizer(e[0]):pc(e[0]):e[0]}}link(t){const e=this.rules.inline.link.exec(t);if(e){const t=e[2].trim();if(!this.options.pedantic&&/^$/.test(t))return;const e=Lc(t.slice(0,-1),"\\");if((t.length-e.length)%2==0)return}else{const t=function(t,e){if(-1===t.indexOf(e[1]))return-1;const n=t.length;let i=0,s=0;for(;s-1){const n=(0===e[0].indexOf("!")?5:4)+e[1].length+t;e[2]=e[2].substring(0,t),e[0]=e[0].substring(0,n).trim(),e[3]=""}}let n=e[2],i="";if(this.options.pedantic){const t=/^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(n);t&&(n=t[1],i=t[3])}else i=e[3]?e[3].slice(1,-1):"";return n=n.trim(),/^$/.test(t)?n.slice(1):n.slice(1,-1)),Pc(e,{href:n?n.replace(this.rules.inline._escapes,"$1"):n,title:i?i.replace(this.rules.inline._escapes,"$1"):i},e[0],this.lexer)}}reflink(t,e){let n;if((n=this.rules.inline.reflink.exec(t))||(n=this.rules.inline.nolink.exec(t))){let t=(n[2]||n[1]).replace(/\s+/g," ");if(t=e[t.toLowerCase()],!t){const t=n[0].charAt(0);return{type:"text",raw:t,text:t}}return Pc(n,t,n[0],this.lexer)}}emStrong(t,e,n=""){let i=this.rules.inline.emStrong.lDelim.exec(t);if(!i)return;if(i[3]&&n.match(/[\p{L}\p{N}]/u))return;const s=i[1]||i[2]||"";if(!s||s&&(""===n||this.rules.inline.punctuation.exec(n))){const n=i[0].length-1;let s,o,r=n,a=0;const l="*"===i[0][0]?this.rules.inline.emStrong.rDelimAst:this.rules.inline.emStrong.rDelimUnd;for(l.lastIndex=0,e=e.slice(-1*t.length+n);null!=(i=l.exec(e));){if(s=i[1]||i[2]||i[3]||i[4]||i[5]||i[6],!s)continue;if(o=s.length,i[3]||i[4]){r+=o;continue}if((i[5]||i[6])&&n%3&&!((n+o)%3)){a+=o;continue}if(r-=o,r>0)continue;o=Math.min(o,o+r+a);const e=t.slice(0,n+i.index+(i[0].length-s.length)+o);if(Math.min(n,o)%2){const t=e.slice(1,-1);return{type:"em",raw:e,text:t,tokens:this.lexer.inlineTokens(t)}}const l=e.slice(2,-2);return{type:"strong",raw:e,text:l,tokens:this.lexer.inlineTokens(l)}}}}codespan(t){const e=this.rules.inline.code.exec(t);if(e){let t=e[2].replace(/\n/g," ");const n=/[^ ]/.test(t),i=/^ /.test(t)&&/ $/.test(t);return n&&i&&(t=t.substring(1,t.length-1)),t=pc(t,!0),{type:"codespan",raw:e[0],text:t}}}br(t){const e=this.rules.inline.br.exec(t);if(e)return{type:"br",raw:e[0]}}del(t){const e=this.rules.inline.del.exec(t);if(e)return{type:"del",raw:e[0],text:e[2],tokens:this.lexer.inlineTokens(e[2])}}autolink(t,e){const n=this.rules.inline.autolink.exec(t);if(n){let t,i;return"@"===n[2]?(t=pc(this.options.mangle?e(n[1]):n[1]),i="mailto:"+t):(t=pc(n[1]),i=t),{type:"link",raw:n[0],text:t,href:i,tokens:[{type:"text",raw:t,text:t}]}}}url(t,e){let n;if(n=this.rules.inline.url.exec(t)){let t,i;if("@"===n[2])t=pc(this.options.mangle?e(n[0]):n[0]),i="mailto:"+t;else{let e;do{e=n[0],n[0]=this.rules.inline._backpedal.exec(n[0])[0]}while(e!==n[0]);t=pc(n[0]),i="www."===n[1]?"http://"+n[0]:n[0]}return{type:"link",raw:n[0],text:t,href:i,tokens:[{type:"text",raw:t,text:t}]}}}inlineText(t,e){const n=this.rules.inline.text.exec(t);if(n){let t;return t=this.lexer.state.inRawBlock?this.options.sanitize?this.options.sanitizer?this.options.sanitizer(n[0]):pc(n[0]):n[0]:pc(this.options.smartypants?e(n[0]):n[0]),{type:"text",raw:n[0],text:t}}}}const Ac={newline:/^(?: *(?:\n|$))+/,code:/^( {4}[^\n]+(?:\n(?: *(?:\n|$))*)?)+/,fences:/^ {0,3}(`{3,}(?=[^`\n]*(?:\n|$))|~{3,})([^\n]*)(?:\n|$)(?:|([\s\S]*?)(?:\n|$))(?: {0,3}\1[~`]* *(?=\n|$)|$)/,hr:/^ {0,3}((?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/,heading:/^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/,blockquote:/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/,list:/^( {0,3}bull)([ \t][^\n]+?)?(?:\n|$)/,html:"^ {0,3}(?:<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?(?:\\?>\\n*|$)|\\n*|$)|\\n*|$)|)[\\s\\S]*?(?:(?:\\n *)+\\n|$)|<(?!script|pre|style|textarea)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n *)+\\n|$)|(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n *)+\\n|$))",def:/^ {0,3}\[(label)\]: *(?:\n *)?([^<\s][^\s]*|<.*?>)(?:(?: +(?:\n *)?| *\n *)(title))? *(?:\n+|$)/,table:Mc,lheading:/^((?:.|\n(?!\n))+?)\n {0,3}(=+|-+) *(?:\n+|$)/,_paragraph:/^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html|table| +\n)[^\n]+)*)/,text:/^[^\n]+/,_label:/(?!\s*\])(?:\\.|[^\[\]\\])+/,_title:/(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/};Ac.def=xc(Ac.def).replace("label",Ac._label).replace("title",Ac._title).getRegex(),Ac.bullet=/(?:[*+-]|\d{1,9}[.)])/,Ac.listItemStart=xc(/^( *)(bull) */).replace("bull",Ac.bullet).getRegex(),Ac.list=xc(Ac.list).replace(/bull/g,Ac.bullet).replace("hr","\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))").replace("def","\\n+(?="+Ac.def.source+")").getRegex(),Ac._tag="address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul",Ac._comment=/|$)/,Ac.html=xc(Ac.html,"i").replace("comment",Ac._comment).replace("tag",Ac._tag).replace("attribute",/ +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(),Ac.paragraph=xc(Ac._paragraph).replace("hr",Ac.hr).replace("heading"," {0,3}#{1,6} ").replace("|lheading","").replace("|table","").replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",Ac._tag).getRegex(),Ac.blockquote=xc(Ac.blockquote).replace("paragraph",Ac.paragraph).getRegex(),Ac.normal={...Ac},Ac.gfm={...Ac.normal,table:"^ *([^\\n ].*\\|.*)\\n {0,3}(?:\\| *)?(:?-+:? *(?:\\| *:?-+:? *)*)(?:\\| *)?(?:\\n((?:(?! *\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)"},Ac.gfm.table=xc(Ac.gfm.table).replace("hr",Ac.hr).replace("heading"," {0,3}#{1,6} ").replace("blockquote"," {0,3}>").replace("code"," {4}[^\\n]").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",Ac._tag).getRegex(),Ac.gfm.paragraph=xc(Ac._paragraph).replace("hr",Ac.hr).replace("heading"," {0,3}#{1,6} ").replace("|lheading","").replace("table",Ac.gfm.table).replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",Ac._tag).getRegex(),Ac.pedantic={...Ac.normal,html:xc("^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+? *(?:\\n{2,}|\\s*$)|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))").replace("comment",Ac._comment).replace(/tag/g,"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(),def:/^ *\[([^\]]+)\]: *]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,heading:/^(#{1,6})(.*)(?:\n+|$)/,fences:Mc,lheading:/^(.+?)\n {0,3}(=+|-+) *(?:\n+|$)/,paragraph:xc(Ac.normal._paragraph).replace("hr",Ac.hr).replace("heading"," *#{1,6} *[^\n]").replace("lheading",Ac.lheading).replace("blockquote"," {0,3}>").replace("|fences","").replace("|list","").replace("|html","").getRegex()};const Ec={escape:/^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,autolink:/^<(scheme:[^\s\x00-\x1f<>]*|email)>/,url:Mc,tag:"^comment|^|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^|^",link:/^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/,reflink:/^!?\[(label)\]\[(ref)\]/,nolink:/^!?\[(ref)\](?:\[\])?/,reflinkSearch:"reflink|nolink(?!\\()",emStrong:{lDelim:/^(?:\*+(?:([punct_])|[^\s*]))|^_+(?:([punct*])|([^\s_]))/,rDelimAst:/^(?:[^_*\\]|\\.)*?\_\_(?:[^_*\\]|\\.)*?\*(?:[^_*\\]|\\.)*?(?=\_\_)|(?:[^*\\]|\\.)+(?=[^*])|[punct_](\*+)(?=[\s]|$)|(?:[^punct*_\s\\]|\\.)(\*+)(?=[punct_\s]|$)|[punct_\s](\*+)(?=[^punct*_\s])|[\s](\*+)(?=[punct_])|[punct_](\*+)(?=[punct_])|(?:[^punct*_\s\\]|\\.)(\*+)(?=[^punct*_\s])/,rDelimUnd:/^(?:[^_*\\]|\\.)*?\*\*(?:[^_*\\]|\\.)*?\_(?:[^_*\\]|\\.)*?(?=\*\*)|(?:[^_\\]|\\.)+(?=[^_])|[punct*](\_+)(?=[\s]|$)|(?:[^punct*_\s\\]|\\.)(\_+)(?=[punct*\s]|$)|[punct*\s](\_+)(?=[^punct*_\s])|[\s](\_+)(?=[punct*])|[punct*](\_+)(?=[punct*])/},code:/^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,br:/^( {2,}|\\)\n(?!\s*$)/,del:Mc,text:/^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\.5&&(n="x"+n.toString(16)),i+="&#"+n+";";return i}Ec._punctuation="!\"#$%&'()+\\-.,/:;<=>?@\\[\\]`^{|}~",Ec.punctuation=xc(Ec.punctuation).replace(/punctuation/g,Ec._punctuation).getRegex(),Ec.blockSkip=/\[[^\]]*?\]\([^\)]*?\)|`[^`]*?`|<[^>]*?>/g,Ec.escapedEmSt=/(?:^|[^\\])(?:\\\\)*\\[*_]/g,Ec._comment=xc(Ac._comment).replace("(?:--\x3e|$)","--\x3e").getRegex(),Ec.emStrong.lDelim=xc(Ec.emStrong.lDelim).replace(/punct/g,Ec._punctuation).getRegex(),Ec.emStrong.rDelimAst=xc(Ec.emStrong.rDelimAst,"g").replace(/punct/g,Ec._punctuation).getRegex(),Ec.emStrong.rDelimUnd=xc(Ec.emStrong.rDelimUnd,"g").replace(/punct/g,Ec._punctuation).getRegex(),Ec._escapes=/\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g,Ec._scheme=/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/,Ec._email=/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/,Ec.autolink=xc(Ec.autolink).replace("scheme",Ec._scheme).replace("email",Ec._email).getRegex(),Ec._attribute=/\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/,Ec.tag=xc(Ec.tag).replace("comment",Ec._comment).replace("attribute",Ec._attribute).getRegex(),Ec._label=/(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/,Ec._href=/<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/,Ec._title=/"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/,Ec.link=xc(Ec.link).replace("label",Ec._label).replace("href",Ec._href).replace("title",Ec._title).getRegex(),Ec.reflink=xc(Ec.reflink).replace("label",Ec._label).replace("ref",Ac._label).getRegex(),Ec.nolink=xc(Ec.nolink).replace("ref",Ac._label).getRegex(),Ec.reflinkSearch=xc(Ec.reflinkSearch,"g").replace("reflink",Ec.reflink).replace("nolink",Ec.nolink).getRegex(),Ec.normal={...Ec},Ec.pedantic={...Ec.normal,strong:{start:/^__|\*\*/,middle:/^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,endAst:/\*\*(?!\*)/g,endUnd:/__(?!_)/g},em:{start:/^_|\*/,middle:/^()\*(?=\S)([\s\S]*?\S)\*(?!\*)|^_(?=\S)([\s\S]*?\S)_(?!_)/,endAst:/\*(?!\*)/g,endUnd:/_(?!_)/g},link:xc(/^!?\[(label)\]\((.*?)\)/).replace("label",Ec._label).getRegex(),reflink:xc(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace("label",Ec._label).getRegex()},Ec.gfm={...Ec.normal,escape:xc(Ec.escape).replace("])","~|])").getRegex(),_extended_email:/[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/,url:/^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,_backpedal:/(?:[^?!.,:;*_'"~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_'"~)]+(?!$))+/,del:/^(~~?)(?=[^\s~])([\s\S]*?[^\s~])\1(?=[^~]|$)/,text:/^([`~]+|[^`~])(?:(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\e+" ".repeat(n.length)));t;)if(!(this.options.extensions&&this.options.extensions.block&&this.options.extensions.block.some((i=>!!(n=i.call({lexer:this},t,e))&&(t=t.substring(n.raw.length),e.push(n),!0)))))if(n=this.tokenizer.space(t))t=t.substring(n.raw.length),1===n.raw.length&&e.length>0?e[e.length-1].raw+="\n":e.push(n);else if(n=this.tokenizer.code(t))t=t.substring(n.raw.length),i=e[e.length-1],!i||"paragraph"!==i.type&&"text"!==i.type?e.push(n):(i.raw+="\n"+n.raw,i.text+="\n"+n.text,this.inlineQueue[this.inlineQueue.length-1].src=i.text);else if(n=this.tokenizer.fences(t))t=t.substring(n.raw.length),e.push(n);else if(n=this.tokenizer.heading(t))t=t.substring(n.raw.length),e.push(n);else if(n=this.tokenizer.hr(t))t=t.substring(n.raw.length),e.push(n);else if(n=this.tokenizer.blockquote(t))t=t.substring(n.raw.length),e.push(n);else if(n=this.tokenizer.list(t))t=t.substring(n.raw.length),e.push(n);else if(n=this.tokenizer.html(t))t=t.substring(n.raw.length),e.push(n);else if(n=this.tokenizer.def(t))t=t.substring(n.raw.length),i=e[e.length-1],!i||"paragraph"!==i.type&&"text"!==i.type?this.tokens.links[n.tag]||(this.tokens.links[n.tag]={href:n.href,title:n.title}):(i.raw+="\n"+n.raw,i.text+="\n"+n.raw,this.inlineQueue[this.inlineQueue.length-1].src=i.text);else if(n=this.tokenizer.table(t))t=t.substring(n.raw.length),e.push(n);else if(n=this.tokenizer.lheading(t))t=t.substring(n.raw.length),e.push(n);else{if(s=t,this.options.extensions&&this.options.extensions.startBlock){let e=1/0;const n=t.slice(1);let i;this.options.extensions.startBlock.forEach((function(t){i=t.call({lexer:this},n),"number"==typeof i&&i>=0&&(e=Math.min(e,i))})),e<1/0&&e>=0&&(s=t.substring(0,e+1))}if(this.state.top&&(n=this.tokenizer.paragraph(s)))i=e[e.length-1],o&&"paragraph"===i.type?(i.raw+="\n"+n.raw,i.text+="\n"+n.text,this.inlineQueue.pop(),this.inlineQueue[this.inlineQueue.length-1].src=i.text):e.push(n),o=s.length!==t.length,t=t.substring(n.raw.length);else if(n=this.tokenizer.text(t))t=t.substring(n.raw.length),i=e[e.length-1],i&&"text"===i.type?(i.raw+="\n"+n.raw,i.text+="\n"+n.text,this.inlineQueue.pop(),this.inlineQueue[this.inlineQueue.length-1].src=i.text):e.push(n);else if(t){const e="Infinite loop on byte: "+t.charCodeAt(0);if(this.options.silent){console.error(e);break}throw new Error(e)}}return this.state.top=!0,e}inline(t,e=[]){return this.inlineQueue.push({src:t,tokens:e}),e}inlineTokens(t,e=[]){let n,i,s,o,r,a,l=t;if(this.tokens.links){const t=Object.keys(this.tokens.links);if(t.length>0)for(;null!=(o=this.tokenizer.rules.inline.reflinkSearch.exec(l));)t.includes(o[0].slice(o[0].lastIndexOf("[")+1,-1))&&(l=l.slice(0,o.index)+"["+Dc("a",o[0].length-2)+"]"+l.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex))}for(;null!=(o=this.tokenizer.rules.inline.blockSkip.exec(l));)l=l.slice(0,o.index)+"["+Dc("a",o[0].length-2)+"]"+l.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);for(;null!=(o=this.tokenizer.rules.inline.escapedEmSt.exec(l));)l=l.slice(0,o.index+o[0].length-2)+"++"+l.slice(this.tokenizer.rules.inline.escapedEmSt.lastIndex),this.tokenizer.rules.inline.escapedEmSt.lastIndex--;for(;t;)if(r||(a=""),r=!1,!(this.options.extensions&&this.options.extensions.inline&&this.options.extensions.inline.some((i=>!!(n=i.call({lexer:this},t,e))&&(t=t.substring(n.raw.length),e.push(n),!0)))))if(n=this.tokenizer.escape(t))t=t.substring(n.raw.length),e.push(n);else if(n=this.tokenizer.tag(t))t=t.substring(n.raw.length),i=e[e.length-1],i&&"text"===n.type&&"text"===i.type?(i.raw+=n.raw,i.text+=n.text):e.push(n);else if(n=this.tokenizer.link(t))t=t.substring(n.raw.length),e.push(n);else if(n=this.tokenizer.reflink(t,this.tokens.links))t=t.substring(n.raw.length),i=e[e.length-1],i&&"text"===n.type&&"text"===i.type?(i.raw+=n.raw,i.text+=n.text):e.push(n);else if(n=this.tokenizer.emStrong(t,l,a))t=t.substring(n.raw.length),e.push(n);else if(n=this.tokenizer.codespan(t))t=t.substring(n.raw.length),e.push(n);else if(n=this.tokenizer.br(t))t=t.substring(n.raw.length),e.push(n);else if(n=this.tokenizer.del(t))t=t.substring(n.raw.length),e.push(n);else if(n=this.tokenizer.autolink(t,Rc))t=t.substring(n.raw.length),e.push(n);else if(this.state.inLink||!(n=this.tokenizer.url(t,Rc))){if(s=t,this.options.extensions&&this.options.extensions.startInline){let e=1/0;const n=t.slice(1);let i;this.options.extensions.startInline.forEach((function(t){i=t.call({lexer:this},n),"number"==typeof i&&i>=0&&(e=Math.min(e,i))})),e<1/0&&e>=0&&(s=t.substring(0,e+1))}if(n=this.tokenizer.inlineText(s,Tc))t=t.substring(n.raw.length),"_"!==n.raw.slice(-1)&&(a=n.raw.slice(-1)),r=!0,i=e[e.length-1],i&&"text"===i.type?(i.raw+=n.raw,i.text+=n.text):e.push(n);else if(t){const e="Infinite loop on byte: "+t.charCodeAt(0);if(this.options.silent){console.error(e);break}throw new Error(e)}}else t=t.substring(n.raw.length),e.push(n);return e}}class Ic{constructor(t){this.options=t||ac}code(t,e,n){const i=(e||"").match(/\S*/)[0];if(this.options.highlight){const e=this.options.highlight(t,i);null!=e&&e!==t&&(n=!0,t=e)}return t=t.replace(/\n$/,"")+"\n",i?'

'+(n?t:pc(t,!0))+"
\n":"
"+(n?t:pc(t,!0))+"
\n"}blockquote(t){return`
\n${t}
\n`}html(t){return t}heading(t,e,n,i){if(this.options.headerIds){return`${t}\n`}return`${t}\n`}hr(){return this.options.xhtml?"
\n":"
\n"}list(t,e,n){const i=e?"ol":"ul";return"<"+i+(e&&1!==n?' start="'+n+'"':"")+">\n"+t+"\n"}listitem(t){return`
  • ${t}
  • \n`}checkbox(t){return" "}paragraph(t){return`

    ${t}

    \n`}table(t,e){return e&&(e=`${e}`),"\n\n"+t+"\n"+e+"
    \n"}tablerow(t){return`\n${t}\n`}tablecell(t,e){const n=e.header?"th":"td";return(e.align?`<${n} align="${e.align}">`:`<${n}>`)+t+`\n`}strong(t){return`${t}`}em(t){return`${t}`}codespan(t){return`${t}`}br(){return this.options.xhtml?"
    ":"
    "}del(t){return`${t}`}link(t,e,n){if(null===(t=vc(this.options.sanitize,this.options.baseUrl,t)))return n;let i='",i}image(t,e,n){if(null===(t=vc(this.options.sanitize,this.options.baseUrl,t)))return n;let i=`${n}":">",i}text(t){return t}}class Fc{strong(t){return t}em(t){return t}codespan(t){return t}del(t){return t}html(t){return t}text(t){return t}link(t,e,n){return""+n}image(t,e,n){return""+n}br(){return""}}class Nc{constructor(){this.seen={}}serialize(t){return t.toLowerCase().trim().replace(/<[!\/a-z].*?>/gi,"").replace(/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g,"").replace(/\s/g,"-")}getNextSafeSlug(t,e){let n=t,i=0;if(this.seen.hasOwnProperty(n)){i=this.seen[t];do{i++,n=t+"-"+i}while(this.seen.hasOwnProperty(n))}return e||(this.seen[t]=i,this.seen[n]=0),n}slug(t,e={}){const n=this.serialize(t);return this.getNextSafeSlug(n,e.dryrun)}}class jc{constructor(t){this.options=t||ac,this.options.renderer=this.options.renderer||new Ic,this.renderer=this.options.renderer,this.renderer.options=this.options,this.textRenderer=new Fc,this.slugger=new Nc}static parse(t,e){return new jc(e).parse(t)}static parseInline(t,e){return new jc(e).parseInline(t)}parse(t,e=!0){let n,i,s,o,r,a,l,c,h,u,d,f,p,g,m,b,x,y,_,v="";const $=t.length;for(n=0;n<$;n++)if(u=t[n],this.options.extensions&&this.options.extensions.renderers&&this.options.extensions.renderers[u.type]&&(_=this.options.extensions.renderers[u.type].call({parser:this},u),!1!==_||!["space","hr","heading","code","table","blockquote","list","html","paragraph","text"].includes(u.type)))v+=_||"";else switch(u.type){case"space":continue;case"hr":v+=this.renderer.hr();continue;case"heading":v+=this.renderer.heading(this.parseInline(u.tokens),u.depth,mc(this.parseInline(u.tokens,this.textRenderer)),this.slugger);continue;case"code":v+=this.renderer.code(u.text,u.lang,u.escaped);continue;case"table":for(c="",l="",o=u.header.length,i=0;i0&&"paragraph"===m.tokens[0].type?(m.tokens[0].text=y+" "+m.tokens[0].text,m.tokens[0].tokens&&m.tokens[0].tokens.length>0&&"text"===m.tokens[0].tokens[0].type&&(m.tokens[0].tokens[0].text=y+" "+m.tokens[0].tokens[0].text)):m.tokens.unshift({type:"text",text:y}):g+=y),g+=this.parse(m.tokens,p),h+=this.renderer.listitem(g,x,b);v+=this.renderer.list(h,d,f);continue;case"html":v+=this.renderer.html(u.text);continue;case"paragraph":v+=this.renderer.paragraph(this.parseInline(u.tokens));continue;case"text":for(h=u.tokens?this.parseInline(u.tokens):u.text;n+1<$&&"text"===t[n+1].type;)u=t[++n],h+="\n"+(u.tokens?this.parseInline(u.tokens):u.text);v+=e?this.renderer.paragraph(h):h;continue;default:{const t='Token with "'+u.type+'" type was not found.';if(this.options.silent)return void console.error(t);throw new Error(t)}}return v}parseInline(t,e){e=e||this.renderer;let n,i,s,o="";const r=t.length;for(n=0;n{"function"==typeof i&&(s=i,i=null);const o={...i},r=function(t,e,n){return i=>{if(i.message+="\nPlease report this to https://github.com/markedjs/marked.",t){const t="

    An error occurred:

    "+pc(i.message+"",!0)+"
    ";return e?Promise.resolve(t):n?void n(null,t):t}if(e)return Promise.reject(i);if(!n)throw i;n(i)}}((i={...Hc.defaults,...o}).silent,i.async,s);if(null==n)return r(new Error("marked(): input parameter is undefined or null"));if("string"!=typeof n)return r(new Error("marked(): input parameter is of type "+Object.prototype.toString.call(n)+", string expected"));if(function(t){t&&t.sanitize&&!t.silent&&console.warn("marked(): sanitize and sanitizer parameters are deprecated since version 0.7.0, should not be used and will be removed in the future. Read more here: https://marked.js.org/#/USING_ADVANCED.md#options")}(i),i.hooks&&(i.hooks.options=i),s){const o=i.highlight;let a;try{i.hooks&&(n=i.hooks.preprocess(n)),a=t(n,i)}catch(t){return r(t)}const l=function(t){let n;if(!t)try{i.walkTokens&&Hc.walkTokens(a,i.walkTokens),n=e(a,i),i.hooks&&(n=i.hooks.postprocess(n))}catch(e){t=e}return i.highlight=o,t?r(t):s(null,n)};if(!o||o.length<3)return l();if(delete i.highlight,!a.length)return l();let c=0;return Hc.walkTokens(a,(function(t){"code"===t.type&&(c++,setTimeout((()=>{o(t.text,t.lang,(function(e,n){if(e)return l(e);null!=n&&n!==t.text&&(t.text=n,t.escaped=!0),c--,0===c&&l()}))}),0))})),void(0===c&&l())}if(i.async)return Promise.resolve(i.hooks?i.hooks.preprocess(n):n).then((e=>t(e,i))).then((t=>i.walkTokens?Promise.all(Hc.walkTokens(t,i.walkTokens)).then((()=>t)):t)).then((t=>e(t,i))).then((t=>i.hooks?i.hooks.postprocess(t):t)).catch(r);try{i.hooks&&(n=i.hooks.preprocess(n));const s=t(n,i);i.walkTokens&&Hc.walkTokens(s,i.walkTokens);let o=e(s,i);return i.hooks&&(o=i.hooks.postprocess(o)),o}catch(t){return r(t)}}}function Hc(t,e,n){return Vc(zc.lex,jc.parse)(t,e,n)}Hc.options=Hc.setOptions=function(t){var e;return Hc.defaults={...Hc.defaults,...t},e=Hc.defaults,ac=e,Hc},Hc.getDefaults=rc,Hc.defaults=ac,Hc.use=function(...t){const e=Hc.defaults.extensions||{renderers:{},childTokens:{}};t.forEach((t=>{const n={...t};if(n.async=Hc.defaults.async||n.async||!1,t.extensions&&(t.extensions.forEach((t=>{if(!t.name)throw new Error("extension name required");if(t.renderer){const n=e.renderers[t.name];e.renderers[t.name]=n?function(...e){let i=t.renderer.apply(this,e);return!1===i&&(i=n.apply(this,e)),i}:t.renderer}if(t.tokenizer){if(!t.level||"block"!==t.level&&"inline"!==t.level)throw new Error("extension level must be 'block' or 'inline'");e[t.level]?e[t.level].unshift(t.tokenizer):e[t.level]=[t.tokenizer],t.start&&("block"===t.level?e.startBlock?e.startBlock.push(t.start):e.startBlock=[t.start]:"inline"===t.level&&(e.startInline?e.startInline.push(t.start):e.startInline=[t.start]))}t.childTokens&&(e.childTokens[t.name]=t.childTokens)})),n.extensions=e),t.renderer){const e=Hc.defaults.renderer||new Ic;for(const n in t.renderer){const i=e[n];e[n]=(...s)=>{let o=t.renderer[n].apply(e,s);return!1===o&&(o=i.apply(e,s)),o}}n.renderer=e}if(t.tokenizer){const e=Hc.defaults.tokenizer||new Oc;for(const n in t.tokenizer){const i=e[n];e[n]=(...s)=>{let o=t.tokenizer[n].apply(e,s);return!1===o&&(o=i.apply(e,s)),o}}n.tokenizer=e}if(t.hooks){const e=Hc.defaults.hooks||new Bc;for(const n in t.hooks){const i=e[n];Bc.passThroughHooks.has(n)?e[n]=s=>{if(Hc.defaults.async)return Promise.resolve(t.hooks[n].call(e,s)).then((t=>i.call(e,t)));const o=t.hooks[n].call(e,s);return i.call(e,o)}:e[n]=(...s)=>{let o=t.hooks[n].apply(e,s);return!1===o&&(o=i.apply(e,s)),o}}n.hooks=e}if(t.walkTokens){const e=Hc.defaults.walkTokens;n.walkTokens=function(n){let i=[];return i.push(t.walkTokens.call(this,n)),e&&(i=i.concat(e.call(this,n))),i}}Hc.setOptions(n)}))},Hc.walkTokens=function(t,e){let n=[];for(const i of t)switch(n=n.concat(e.call(Hc,i)),i.type){case"table":for(const t of i.header)n=n.concat(Hc.walkTokens(t.tokens,e));for(const t of i.rows)for(const i of t)n=n.concat(Hc.walkTokens(i.tokens,e));break;case"list":n=n.concat(Hc.walkTokens(i.items,e));break;default:Hc.defaults.extensions&&Hc.defaults.extensions.childTokens&&Hc.defaults.extensions.childTokens[i.type]?Hc.defaults.extensions.childTokens[i.type].forEach((function(t){n=n.concat(Hc.walkTokens(i[t],e))})):i.tokens&&(n=n.concat(Hc.walkTokens(i.tokens,e)))}return n},Hc.parseInline=Vc(zc.lexInline,jc.parseInline),Hc.Parser=jc,Hc.parser=jc.parse,Hc.Renderer=Ic,Hc.TextRenderer=Fc,Hc.Lexer=zc,Hc.lexer=zc.lex,Hc.Tokenizer=Oc,Hc.Slugger=Nc,Hc.Hooks=Bc,Hc.parse=Hc,Hc.options,Hc.setOptions,Hc.use,Hc.walkTokens,Hc.parseInline;const Wc={};function Uc(e){let n;return{c(){n=w(e[1])},m(t,e){y(t,n,e)},p(t,e){2&e&&P(n,t[1])},i:t,o:t,d(t){t&&_(n)}}}function qc(t){let e,n;const i=t[5].default,s=u(i,t,t[4],null);return{c(){e=$("h6"),s&&s.c(),L(e,"id",t[2])},m(t,i){y(t,e,i),s&&s.m(e,null),n=!0},p(t,o){s&&s.p&&(!n||16&o)&&p(s,i,t,t[4],n?f(i,t[4],o,null):g(t[4]),null),(!n||4&o)&&L(e,"id",t[2])},i(t){n||(it(s,t),n=!0)},o(t){st(s,t),n=!1},d(t){t&&_(e),s&&s.d(t)}}}function Yc(t){let e,n;const i=t[5].default,s=u(i,t,t[4],null);return{c(){e=$("h5"),s&&s.c(),L(e,"id",t[2])},m(t,i){y(t,e,i),s&&s.m(e,null),n=!0},p(t,o){s&&s.p&&(!n||16&o)&&p(s,i,t,t[4],n?f(i,t[4],o,null):g(t[4]),null),(!n||4&o)&&L(e,"id",t[2])},i(t){n||(it(s,t),n=!0)},o(t){st(s,t),n=!1},d(t){t&&_(e),s&&s.d(t)}}}function Zc(t){let e,n;const i=t[5].default,s=u(i,t,t[4],null);return{c(){e=$("h4"),s&&s.c(),L(e,"id",t[2])},m(t,i){y(t,e,i),s&&s.m(e,null),n=!0},p(t,o){s&&s.p&&(!n||16&o)&&p(s,i,t,t[4],n?f(i,t[4],o,null):g(t[4]),null),(!n||4&o)&&L(e,"id",t[2])},i(t){n||(it(s,t),n=!0)},o(t){st(s,t),n=!1},d(t){t&&_(e),s&&s.d(t)}}}function Xc(t){let e,n;const i=t[5].default,s=u(i,t,t[4],null);return{c(){e=$("h3"),s&&s.c(),L(e,"id",t[2])},m(t,i){y(t,e,i),s&&s.m(e,null),n=!0},p(t,o){s&&s.p&&(!n||16&o)&&p(s,i,t,t[4],n?f(i,t[4],o,null):g(t[4]),null),(!n||4&o)&&L(e,"id",t[2])},i(t){n||(it(s,t),n=!0)},o(t){st(s,t),n=!1},d(t){t&&_(e),s&&s.d(t)}}}function Gc(t){let e,n;const i=t[5].default,s=u(i,t,t[4],null);return{c(){e=$("h2"),s&&s.c(),L(e,"id",t[2])},m(t,i){y(t,e,i),s&&s.m(e,null),n=!0},p(t,o){s&&s.p&&(!n||16&o)&&p(s,i,t,t[4],n?f(i,t[4],o,null):g(t[4]),null),(!n||4&o)&&L(e,"id",t[2])},i(t){n||(it(s,t),n=!0)},o(t){st(s,t),n=!1},d(t){t&&_(e),s&&s.d(t)}}}function Kc(t){let e,n;const i=t[5].default,s=u(i,t,t[4],null);return{c(){e=$("h1"),s&&s.c(),L(e,"id",t[2])},m(t,i){y(t,e,i),s&&s.m(e,null),n=!0},p(t,o){s&&s.p&&(!n||16&o)&&p(s,i,t,t[4],n?f(i,t[4],o,null):g(t[4]),null),(!n||4&o)&&L(e,"id",t[2])},i(t){n||(it(s,t),n=!0)},o(t){st(s,t),n=!1},d(t){t&&_(e),s&&s.d(t)}}}function Jc(t){let e,n,i,s;const o=[Kc,Gc,Xc,Zc,Yc,qc,Uc],r=[];function a(t,e){return 1===t[0]?0:2===t[0]?1:3===t[0]?2:4===t[0]?3:5===t[0]?4:6===t[0]?5:6}return e=a(t),n=r[e]=o[e](t),{c(){n.c(),i=M()},m(t,n){r[e].m(t,n),y(t,i,n),s=!0},p(t,[s]){let l=e;e=a(t),e===l?r[e].p(t,s):(et(),st(r[l],1,1,(()=>{r[l]=null})),nt(),n=r[e],n?n.p(t,s):(n=r[e]=o[e](t),n.c()),it(n,1),n.m(i.parentNode,i))},i(t){s||(it(n),s=!0)},o(t){st(n),s=!1},d(t){r[e].d(t),t&&_(i)}}}function Qc(t,e,n){let i,{$$slots:s={},$$scope:o}=e,{depth:r}=e,{raw:a}=e,{text:l}=e;const{slug:c,getOptions:h}=j(Wc),u=h();return t.$$set=t=>{"depth"in t&&n(0,r=t.depth),"raw"in t&&n(1,a=t.raw),"text"in t&&n(3,l=t.text),"$$scope"in t&&n(4,o=t.$$scope)},t.$$.update=()=>{8&t.$$.dirty&&n(2,i=u.headerIds?u.headerPrefix+c(l):void 0)},[r,a,i,l,o,s]}function th(t){let e,n;const i=t[1].default,s=u(i,t,t[0],null);return{c(){e=$("p"),s&&s.c()},m(t,i){y(t,e,i),s&&s.m(e,null),n=!0},p(t,[e]){s&&s.p&&(!n||1&e)&&p(s,i,t,t[0],n?f(i,t[0],e,null):g(t[0]),null)},i(t){n||(it(s,t),n=!0)},o(t){st(s,t),n=!1},d(t){t&&_(e),s&&s.d(t)}}}function eh(t,e,n){let{$$slots:i={},$$scope:s}=e;return t.$$set=t=>{"$$scope"in t&&n(0,s=t.$$scope)},[s,i]}function nh(t){let e;const n=t[3].default,i=u(n,t,t[2],null);return{c(){i&&i.c()},m(t,n){i&&i.m(t,n),e=!0},p(t,[s]){i&&i.p&&(!e||4&s)&&p(i,n,t,t[2],e?f(n,t[2],s,null):g(t[2]),null)},i(t){e||(it(i,t),e=!0)},o(t){st(i,t),e=!1},d(t){i&&i.d(t)}}}function ih(t,e,n){let{$$slots:i={},$$scope:s}=e,{text:o}=e,{raw:r}=e;return t.$$set=t=>{"text"in t&&n(0,o=t.text),"raw"in t&&n(1,r=t.raw),"$$scope"in t&&n(2,s=t.$$scope)},[o,r,s,i]}function sh(e){let n,i;return{c(){n=$("img"),c(n.src,i=e[0])||L(n,"src",i),L(n,"title",e[1]),L(n,"alt",e[2])},m(t,e){y(t,n,e)},p(t,[e]){1&e&&!c(n.src,i=t[0])&&L(n,"src",i),2&e&&L(n,"title",t[1]),4&e&&L(n,"alt",t[2])},i:t,o:t,d(t){t&&_(n)}}}function oh(t,e,n){let{href:i=""}=e,{title:s}=e,{text:o=""}=e;return t.$$set=t=>{"href"in t&&n(0,i=t.href),"title"in t&&n(1,s=t.title),"text"in t&&n(2,o=t.text)},[i,s,o]}function rh(t){let e,n;const i=t[3].default,s=u(i,t,t[2],null);return{c(){e=$("a"),s&&s.c(),L(e,"href",t[0]),L(e,"title",t[1])},m(t,i){y(t,e,i),s&&s.m(e,null),n=!0},p(t,[o]){s&&s.p&&(!n||4&o)&&p(s,i,t,t[2],n?f(i,t[2],o,null):g(t[2]),null),(!n||1&o)&&L(e,"href",t[0]),(!n||2&o)&&L(e,"title",t[1])},i(t){n||(it(s,t),n=!0)},o(t){st(s,t),n=!1},d(t){t&&_(e),s&&s.d(t)}}}function ah(t,e,n){let{$$slots:i={},$$scope:s}=e,{href:o=""}=e,{title:r}=e;return t.$$set=t=>{"href"in t&&n(0,o=t.href),"title"in t&&n(1,r=t.title),"$$scope"in t&&n(2,s=t.$$scope)},[o,r,s,i]}function lh(t){let e,n;const i=t[1].default,s=u(i,t,t[0],null);return{c(){e=$("em"),s&&s.c()},m(t,i){y(t,e,i),s&&s.m(e,null),n=!0},p(t,[e]){s&&s.p&&(!n||1&e)&&p(s,i,t,t[0],n?f(i,t[0],e,null):g(t[0]),null)},i(t){n||(it(s,t),n=!0)},o(t){st(s,t),n=!1},d(t){t&&_(e),s&&s.d(t)}}}function ch(t,e,n){let{$$slots:i={},$$scope:s}=e;return t.$$set=t=>{"$$scope"in t&&n(0,s=t.$$scope)},[s,i]}function hh(t){let e,n;const i=t[1].default,s=u(i,t,t[0],null);return{c(){e=$("del"),s&&s.c()},m(t,i){y(t,e,i),s&&s.m(e,null),n=!0},p(t,[e]){s&&s.p&&(!n||1&e)&&p(s,i,t,t[0],n?f(i,t[0],e,null):g(t[0]),null)},i(t){n||(it(s,t),n=!0)},o(t){st(s,t),n=!1},d(t){t&&_(e),s&&s.d(t)}}}function uh(t,e,n){let{$$slots:i={},$$scope:s}=e;return t.$$set=t=>{"$$scope"in t&&n(0,s=t.$$scope)},[s,i]}function dh(e){let n,i,s=e[0].replace(/`/g,"")+"";return{c(){n=$("code"),i=w(s)},m(t,e){y(t,n,e),x(n,i)},p(t,[e]){1&e&&s!==(s=t[0].replace(/`/g,"")+"")&&P(i,s)},i:t,o:t,d(t){t&&_(n)}}}function fh(t,e,n){let{raw:i}=e;return t.$$set=t=>{"raw"in t&&n(0,i=t.raw)},[i]}function ph(t){let e,n;const i=t[1].default,s=u(i,t,t[0],null);return{c(){e=$("strong"),s&&s.c()},m(t,i){y(t,e,i),s&&s.m(e,null),n=!0},p(t,[e]){s&&s.p&&(!n||1&e)&&p(s,i,t,t[0],n?f(i,t[0],e,null):g(t[0]),null)},i(t){n||(it(s,t),n=!0)},o(t){st(s,t),n=!1},d(t){t&&_(e),s&&s.d(t)}}}function gh(t,e,n){let{$$slots:i={},$$scope:s}=e;return t.$$set=t=>{"$$scope"in t&&n(0,s=t.$$scope)},[s,i]}function mh(t){let e,n;const i=t[1].default,s=u(i,t,t[0],null);return{c(){e=$("table"),s&&s.c()},m(t,i){y(t,e,i),s&&s.m(e,null),n=!0},p(t,[e]){s&&s.p&&(!n||1&e)&&p(s,i,t,t[0],n?f(i,t[0],e,null):g(t[0]),null)},i(t){n||(it(s,t),n=!0)},o(t){st(s,t),n=!1},d(t){t&&_(e),s&&s.d(t)}}}function bh(t,e,n){let{$$slots:i={},$$scope:s}=e;return t.$$set=t=>{"$$scope"in t&&n(0,s=t.$$scope)},[s,i]}function xh(t){let e,n;const i=t[1].default,s=u(i,t,t[0],null);return{c(){e=$("thead"),s&&s.c()},m(t,i){y(t,e,i),s&&s.m(e,null),n=!0},p(t,[e]){s&&s.p&&(!n||1&e)&&p(s,i,t,t[0],n?f(i,t[0],e,null):g(t[0]),null)},i(t){n||(it(s,t),n=!0)},o(t){st(s,t),n=!1},d(t){t&&_(e),s&&s.d(t)}}}function yh(t,e,n){let{$$slots:i={},$$scope:s}=e;return t.$$set=t=>{"$$scope"in t&&n(0,s=t.$$scope)},[s,i]}function _h(t){let e,n;const i=t[1].default,s=u(i,t,t[0],null);return{c(){e=$("tbody"),s&&s.c()},m(t,i){y(t,e,i),s&&s.m(e,null),n=!0},p(t,[e]){s&&s.p&&(!n||1&e)&&p(s,i,t,t[0],n?f(i,t[0],e,null):g(t[0]),null)},i(t){n||(it(s,t),n=!0)},o(t){st(s,t),n=!1},d(t){t&&_(e),s&&s.d(t)}}}function vh(t,e,n){let{$$slots:i={},$$scope:s}=e;return t.$$set=t=>{"$$scope"in t&&n(0,s=t.$$scope)},[s,i]}function $h(t){let e,n;const i=t[1].default,s=u(i,t,t[0],null);return{c(){e=$("tr"),s&&s.c()},m(t,i){y(t,e,i),s&&s.m(e,null),n=!0},p(t,[e]){s&&s.p&&(!n||1&e)&&p(s,i,t,t[0],n?f(i,t[0],e,null):g(t[0]),null)},i(t){n||(it(s,t),n=!0)},o(t){st(s,t),n=!1},d(t){t&&_(e),s&&s.d(t)}}}function kh(t,e,n){let{$$slots:i={},$$scope:s}=e;return t.$$set=t=>{"$$scope"in t&&n(0,s=t.$$scope)},[s,i]}function wh(t){let e,n;const i=t[3].default,s=u(i,t,t[2],null);return{c(){e=$("td"),s&&s.c(),L(e,"align",t[1])},m(t,i){y(t,e,i),s&&s.m(e,null),n=!0},p(t,o){s&&s.p&&(!n||4&o)&&p(s,i,t,t[2],n?f(i,t[2],o,null):g(t[2]),null),(!n||2&o)&&L(e,"align",t[1])},i(t){n||(it(s,t),n=!0)},o(t){st(s,t),n=!1},d(t){t&&_(e),s&&s.d(t)}}}function Ch(t){let e,n;const i=t[3].default,s=u(i,t,t[2],null);return{c(){e=$("th"),s&&s.c(),L(e,"align",t[1])},m(t,i){y(t,e,i),s&&s.m(e,null),n=!0},p(t,o){s&&s.p&&(!n||4&o)&&p(s,i,t,t[2],n?f(i,t[2],o,null):g(t[2]),null),(!n||2&o)&&L(e,"align",t[1])},i(t){n||(it(s,t),n=!0)},o(t){st(s,t),n=!1},d(t){t&&_(e),s&&s.d(t)}}}function Mh(t){let e,n,i,s;const o=[Ch,wh],r=[];function a(t,e){return t[0]?0:1}return e=a(t),n=r[e]=o[e](t),{c(){n.c(),i=M()},m(t,n){r[e].m(t,n),y(t,i,n),s=!0},p(t,[s]){let l=e;e=a(t),e===l?r[e].p(t,s):(et(),st(r[l],1,1,(()=>{r[l]=null})),nt(),n=r[e],n?n.p(t,s):(n=r[e]=o[e](t),n.c()),it(n,1),n.m(i.parentNode,i))},i(t){s||(it(n),s=!0)},o(t){st(n),s=!1},d(t){r[e].d(t),t&&_(i)}}}function Sh(t,e,n){let{$$slots:i={},$$scope:s}=e,{header:o}=e,{align:r}=e;return t.$$set=t=>{"header"in t&&n(0,o=t.header),"align"in t&&n(1,r=t.align),"$$scope"in t&&n(2,s=t.$$scope)},[o,r,s,i]}function Lh(t){let e,n;const i=t[3].default,s=u(i,t,t[2],null);return{c(){e=$("ul"),s&&s.c()},m(t,i){y(t,e,i),s&&s.m(e,null),n=!0},p(t,e){s&&s.p&&(!n||4&e)&&p(s,i,t,t[2],n?f(i,t[2],e,null):g(t[2]),null)},i(t){n||(it(s,t),n=!0)},o(t){st(s,t),n=!1},d(t){t&&_(e),s&&s.d(t)}}}function Dh(t){let e,n;const i=t[3].default,s=u(i,t,t[2],null);return{c(){e=$("ol"),s&&s.c(),L(e,"start",t[1])},m(t,i){y(t,e,i),s&&s.m(e,null),n=!0},p(t,o){s&&s.p&&(!n||4&o)&&p(s,i,t,t[2],n?f(i,t[2],o,null):g(t[2]),null),(!n||2&o)&&L(e,"start",t[1])},i(t){n||(it(s,t),n=!0)},o(t){st(s,t),n=!1},d(t){t&&_(e),s&&s.d(t)}}}function Ph(t){let e,n,i,s;const o=[Dh,Lh],r=[];function a(t,e){return t[0]?0:1}return e=a(t),n=r[e]=o[e](t),{c(){n.c(),i=M()},m(t,n){r[e].m(t,n),y(t,i,n),s=!0},p(t,[s]){let l=e;e=a(t),e===l?r[e].p(t,s):(et(),st(r[l],1,1,(()=>{r[l]=null})),nt(),n=r[e],n?n.p(t,s):(n=r[e]=o[e](t),n.c()),it(n,1),n.m(i.parentNode,i))},i(t){s||(it(n),s=!0)},o(t){st(n),s=!1},d(t){r[e].d(t),t&&_(i)}}}function Oh(t,e,n){let{$$slots:i={},$$scope:s}=e,{ordered:o}=e,{start:r}=e;return t.$$set=t=>{"ordered"in t&&n(0,o=t.ordered),"start"in t&&n(1,r=t.start),"$$scope"in t&&n(2,s=t.$$scope)},[o,r,s,i]}function Ah(t){let e,n;const i=t[1].default,s=u(i,t,t[0],null);return{c(){e=$("li"),s&&s.c()},m(t,i){y(t,e,i),s&&s.m(e,null),n=!0},p(t,[e]){s&&s.p&&(!n||1&e)&&p(s,i,t,t[0],n?f(i,t[0],e,null):g(t[0]),null)},i(t){n||(it(s,t),n=!0)},o(t){st(s,t),n=!1},d(t){t&&_(e),s&&s.d(t)}}}function Eh(t,e,n){let{$$slots:i={},$$scope:s}=e;return t.$$set=t=>{"$$scope"in t&&n(0,s=t.$$scope)},[s,i]}function Th(e){let n;return{c(){n=$("hr")},m(t,e){y(t,n,e)},p:t,i:t,o:t,d(t){t&&_(n)}}}function Rh(e){let n,i;return{c(){n=new E(!1),i=M(),n.a=i},m(t,s){n.m(e[0],t,s),y(t,i,s)},p(t,[e]){1&e&&n.p(t[0])},i:t,o:t,d(t){t&&_(i),t&&n.d()}}}function zh(t,e,n){let{text:i}=e;return t.$$set=t=>{"text"in t&&n(0,i=t.text)},[i]}function Ih(t){let e,n;const i=t[1].default,s=u(i,t,t[0],null);return{c(){e=$("blockquote"),s&&s.c()},m(t,i){y(t,e,i),s&&s.m(e,null),n=!0},p(t,[e]){s&&s.p&&(!n||1&e)&&p(s,i,t,t[0],n?f(i,t[0],e,null):g(t[0]),null)},i(t){n||(it(s,t),n=!0)},o(t){st(s,t),n=!1},d(t){t&&_(e),s&&s.d(t)}}}function Fh(t,e,n){let{$$slots:i={},$$scope:s}=e;return t.$$set=t=>{"$$scope"in t&&n(0,s=t.$$scope)},[s,i]}function Nh(e){let n,i,s;return{c(){n=$("pre"),i=$("code"),s=w(e[1]),L(n,"class",e[0])},m(t,e){y(t,n,e),x(n,i),x(i,s)},p(t,[e]){2&e&&P(s,t[1]),1&e&&L(n,"class",t[0])},i:t,o:t,d(t){t&&_(n)}}}function jh(t,e,n){let{lang:i}=e,{text:s}=e;return t.$$set=t=>{"lang"in t&&n(0,i=t.lang),"text"in t&&n(1,s=t.text)},[i,s]}function Bh(t){let e,n;const i=t[1].default,s=u(i,t,t[0],null);return{c(){e=$("br"),s&&s.c()},m(t,i){y(t,e,i),s&&s.m(t,i),n=!0},p(t,[e]){s&&s.p&&(!n||1&e)&&p(s,i,t,t[0],n?f(i,t[0],e,null):g(t[0]),null)},i(t){n||(it(s,t),n=!0)},o(t){st(s,t),n=!1},d(t){t&&_(e),s&&s.d(t)}}}function Vh(t,e,n){let{$$slots:i={},$$scope:s}=e;return t.$$set=t=>{"$$scope"in t&&n(0,s=t.$$scope)},[s,i]}const Hh={heading:class extends pt{constructor(t){super(),ft(this,t,Qc,Jc,r,{depth:0,raw:1,text:3})}},paragraph:class extends pt{constructor(t){super(),ft(this,t,eh,th,r,{})}},text:class extends pt{constructor(t){super(),ft(this,t,ih,nh,r,{text:0,raw:1})}},image:class extends pt{constructor(t){super(),ft(this,t,oh,sh,r,{href:0,title:1,text:2})}},link:class extends pt{constructor(t){super(),ft(this,t,ah,rh,r,{href:0,title:1})}},em:class extends pt{constructor(t){super(),ft(this,t,ch,lh,r,{})}},strong:class extends pt{constructor(t){super(),ft(this,t,gh,ph,r,{})}},codespan:class extends pt{constructor(t){super(),ft(this,t,fh,dh,r,{raw:0})}},del:class extends pt{constructor(t){super(),ft(this,t,uh,hh,r,{})}},table:class extends pt{constructor(t){super(),ft(this,t,bh,mh,r,{})}},tablehead:class extends pt{constructor(t){super(),ft(this,t,yh,xh,r,{})}},tablebody:class extends pt{constructor(t){super(),ft(this,t,vh,_h,r,{})}},tablerow:class extends pt{constructor(t){super(),ft(this,t,kh,$h,r,{})}},tablecell:class extends pt{constructor(t){super(),ft(this,t,Sh,Mh,r,{header:0,align:1})}},list:class extends pt{constructor(t){super(),ft(this,t,Oh,Ph,r,{ordered:0,start:1})}},orderedlistitem:null,unorderedlistitem:null,listitem:class extends pt{constructor(t){super(),ft(this,t,Eh,Ah,r,{})}},hr:class extends pt{constructor(t){super(),ft(this,t,null,Th,r,{})}},html:class extends pt{constructor(t){super(),ft(this,t,zh,Rh,r,{text:0})}},blockquote:class extends pt{constructor(t){super(),ft(this,t,Fh,Ih,r,{})}},code:class extends pt{constructor(t){super(),ft(this,t,jh,Nh,r,{lang:0,text:1})}},br:class extends pt{constructor(t){super(),ft(this,t,Vh,Bh,r,{})}}},Wh={baseUrl:null,breaks:!1,gfm:!0,headerIds:!0,headerPrefix:"",highlight:null,langPrefix:"language-",mangle:!0,pedantic:!1,renderer:null,sanitize:!1,sanitizer:null,silent:!1,smartLists:!1,smartypants:!1,tokenizer:null,xhtml:!1};function Uh(t){let e,n;return e=new oc({props:{tokens:t[0],renderers:t[1]}}),{c(){ct(e.$$.fragment)},m(t,i){ht(e,t,i),n=!0},p(t,[n]){const i={};1&n&&(i.tokens=t[0]),2&n&&(i.renderers=t[1]),e.$set(i)},i(t){n||(it(e.$$.fragment,t),n=!0)},o(t){st(e.$$.fragment,t),n=!1},d(t){ut(e,t)}}}function qh(t,e,n){let i,s,o,r,{source:a=[]}=e,{renderers:l={}}=e,{options:c={}}=e,{isInline:h=!1}=e;const u=F();let d,f,p;return N(Wc,{slug:t=>s?s.slug(t):"",getOptions:()=>o}),I((()=>{n(7,p=!0)})),t.$$set=t=>{"source"in t&&n(2,a=t.source),"renderers"in t&&n(3,l=t.renderers),"options"in t&&n(4,c=t.options),"isInline"in t&&n(5,h=t.isInline)},t.$$.update=()=>{4&t.$$.dirty&&n(8,i=Array.isArray(a)),4&t.$$.dirty&&(s=a?new Nc:void 0),16&t.$$.dirty&&n(9,o={...Wh,...c}),869&t.$$.dirty&&(i?n(0,d=a):(n(6,f=new zc(o)),n(0,d=h?f.inlineTokens(a):f.lex(a)),u("parsed",{tokens:d}))),8&t.$$.dirty&&n(1,r={...Hh,...l}),385&t.$$.dirty&&p&&!i&&u("parsed",{tokens:d})},[d,r,a,l,c,h,f,p,i,o]}class Yh extends pt{constructor(t){super(),ft(this,t,qh,Uh,r,{source:2,renderers:3,options:4,isInline:5})}}function Zh(t){let e,n;return e=new Yh({props:{source:t[0].source}}),{c(){ct(e.$$.fragment)},m(t,i){ht(e,t,i),n=!0},p(t,[n]){const i={};1&n&&(i.source=t[0].source),e.$set(i)},i(t){n||(it(e.$$.fragment,t),n=!0)},o(t){st(e.$$.fragment,t),n=!1},d(t){ut(e,t)}}}function Xh(t,e,n){let{componentData:i}=e;return t.$$set=t=>{"componentData"in t&&n(0,i=t.componentData)},[i]}class Gh extends pt{constructor(t){super(),ft(this,t,Xh,Zh,r,{componentData:0})}}function Kh(t){let e,n;const i=t[3].default,s=u(i,t,t[2],null);return{c(){e=$("div"),s&&s.c(),L(e,"id",`page-${t[0]||"No Title"}`),L(e,"class","page svelte-v7ihqd"),L(e,"data-component","page")},m(t,i){y(t,e,i),s&&s.m(e,null),n=!0},p(t,[e]){s&&s.p&&(!n||4&e)&&p(s,i,t,t[2],n?f(i,t[2],e,null):g(t[2]),null)},i(t){n||(it(s,t),n=!0)},o(t){st(s,t),n=!1},d(t){t&&_(e),s&&s.d(t)}}}function Jh(t,e,n){let{$$slots:i={},$$scope:s}=e,{componentData:o}=e;const{title:r}=o;return t.$$set=t=>{"componentData"in t&&n(1,o=t.componentData),"$$scope"in t&&n(2,s=t.$$scope)},[r,o,s,i]}class Qh extends pt{constructor(t){super(),ft(this,t,Jh,Kh,r,{componentData:1})}}function tu(e){let n,i,s,o,r,a,l,c,h=e[1]&&function(e){let n;return{c(){n=$("h3"),n.textContent=`${e[1]}`},m(t,e){y(t,n,e)},p:t,d(t){t&&_(n)}}}(e),d=e[2]&&function(e){let n;return{c(){n=$("p"),n.textContent=`${e[2]}`,L(n,"class","description")},m(t,e){y(t,n,e)},p:t,d(t){t&&_(n)}}}(e);const m=e[6].default,b=u(m,e,e[5],null);return{c(){n=$("section"),i=$("div"),h&&h.c(),s=C(),d&&d.c(),o=C(),r=$("div"),b&&b.c(),a=C(),l=$("hr"),L(i,"class","heading svelte-17n0qr8"),L(r,"class","sectionItems svelte-17n0qr8"),L(r,"style",e[0]),L(l,"class","svelte-17n0qr8"),L(n,"class","container svelte-17n0qr8"),L(n,"data-component","section"),L(n,"data-section-id",e[1]),A(n,"columns",e[3])},m(t,e){y(t,n,e),x(n,i),h&&h.m(i,null),x(i,s),d&&d.m(i,null),x(n,o),x(n,r),b&&b.m(r,null),x(n,a),x(n,l),c=!0},p(t,[e]){t[1]&&h.p(t,e),t[2]&&d.p(t,e),b&&b.p&&(!c||32&e)&&p(b,m,t,t[5],c?f(m,t[5],e,null):g(t[5]),null),(!c||1&e)&&L(r,"style",t[0])},i(t){c||(it(b,t),c=!0)},o(t){st(b,t),c=!1},d(t){t&&_(n),h&&h.d(),d&&d.d(),b&&b.d(t)}}}function eu(t,e,n){let{$$slots:i={},$$scope:s}=e,{componentData:o}=e;const{title:r,subtitle:a,columns:l}=o;let c;return l&&(c=`grid-template-columns: repeat(${l||1}, 1fr);`),t.$$set=t=>{"componentData"in t&&n(4,o=t.componentData),"$$scope"in t&&n(5,s=t.$$scope)},[c,r,a,l,o,s,i]}class nu extends pt{constructor(t){super(),ft(this,t,eu,tu,r,{componentData:4})}}function iu(e){let n;return{c(){n=$("p"),n.textContent=`${e[0]}`,L(n,"data-component","text")},m(t,e){y(t,n,e)},p:t,i:t,o:t,d(t){t&&_(n)}}}function su(t,e,n){let{componentData:i}=e;const{text:s}=i;return t.$$set=t=>{"componentData"in t&&n(1,i=t.componentData)},[s,i]}class ou extends pt{constructor(t){super(),ft(this,t,su,iu,r,{componentData:1})}}function ru(e){let n;return{c(){n=w(e[0])},m(t,e){y(t,n,e)},p(t,e){1&e&&P(n,t[0])},i:t,o:t,d(t){t&&_(n)}}}function au(t){let e,n,i;var s=t[1];function o(t){return{props:{componentData:t[0]}}}return s&&(e=T(s,o(t))),{c(){e&&ct(e.$$.fragment),n=M()},m(t,s){e&&ht(e,t,s),y(t,n,s),i=!0},p(t,i){const r={};if(1&i&&(r.componentData=t[0]),2&i&&s!==(s=t[1])){if(e){et();const t=e;st(t.$$.fragment,1,0,(()=>{ut(t,1)})),nt()}s?(e=T(s,o(t)),ct(e.$$.fragment),it(e.$$.fragment,1),ht(e,n.parentNode,n)):e=null}else s&&e.$set(r)},i(t){i||(e&&it(e.$$.fragment,t),i=!0)},o(t){e&&st(e.$$.fragment,t),i=!1},d(t){t&&_(n),e&&ut(e,t)}}}function lu(t){let e,n,i,s;const o=[au,ru],r=[];function a(t,e){return t[1]?0:1}return e=a(t),n=r[e]=o[e](t),{c(){n.c(),i=M()},m(t,n){r[e].m(t,n),y(t,i,n),s=!0},p(t,[s]){let l=e;e=a(t),e===l?r[e].p(t,s):(et(),st(r[l],1,1,(()=>{r[l]=null})),nt(),n=r[e],n?n.p(t,s):(n=r[e]=o[e](t),n.c()),it(n,1),n.m(i.parentNode,i))},i(t){s||(it(n),s=!0)},o(t){st(n),s=!1},d(t){r[e].d(t),t&&_(i)}}}function cu(t,e,n){let i,{componentData:s}=e;const o={artifacts:Ht,barChart:Ma,dag:ol,heading:pl,image:bl,lineChart:_l,log:kl,markdown:Gh,text:ou},r=null==s?void 0:s.type;return r&&(i=null==o?void 0:o[r],i||console.error("Unknown component type: ",r)),t.$$set=t=>{"componentData"in t&&n(0,s=t.componentData)},[s,i]}class hu extends pt{constructor(t){super(),ft(this,t,cu,lu,r,{componentData:0})}}function uu(t,e,n){const i=t.slice();return i[3]=e[n],i[5]=n,i}function du(t,e,n){const i=t.slice();return i[6]=e[n],i}function fu(e){let n,i,s;return i=new hu({props:{componentData:e[6][e[5]]}}),{c(){n=$("td"),ct(i.$$.fragment),L(n,"class","svelte-gl9h79")},m(t,e){y(t,n,e),ht(i,n,null),s=!0},p:t,i(t){s||(it(i.$$.fragment,t),s=!0)},o(t){st(i.$$.fragment,t),s=!1},d(t){t&&_(n),ut(i)}}}function pu(t){let e,n,i,s,o,r,a=t[3]+"",l=t[1],c=[];for(let e=0;est(c[t],1,1,(()=>{c[t]=null}));return{c(){e=$("tr"),n=$("td"),i=w(a),s=C();for(let t=0;tst(r[t],1,1,(()=>{r[t]=null}));return{c(){e=$("div"),n=$("table"),i=$("tbody");for(let t=0;t{"componentData"in t&&n(2,i=t.componentData)},[s,o,i]}class bu extends pt{constructor(t){super(),ft(this,t,mu,gu,r,{componentData:2})}}function xu(t,e,n){const i=t.slice();return i[3]=e[n],i}function yu(t,e,n){const i=t.slice();return i[6]=e[n],i}function _u(t,e,n){const i=t.slice();return i[9]=e[n],i}function vu(e){let n,i,s=e[9]+"";return{c(){n=$("th"),i=w(s),L(n,"class","svelte-q3hq57")},m(t,e){y(t,n,e),x(n,i)},p:t,d(t){t&&_(n)}}}function $u(e){let n,i,s;return i=new hu({props:{componentData:e[6]}}),{c(){n=$("td"),ct(i.$$.fragment)},m(t,e){y(t,n,e),ht(i,n,null),s=!0},p:t,i(t){s||(it(i.$$.fragment,t),s=!0)},o(t){st(i.$$.fragment,t),s=!1},d(t){t&&_(n),ut(i)}}}function ku(t){let e,n,i,s=t[3],o=[];for(let e=0;est(o[t],1,1,(()=>{o[t]=null}));return{c(){e=$("tr");for(let t=0;tst(u[t],1,1,(()=>{u[t]=null}));return{c(){e=$("div"),n=$("table"),i=$("thead"),s=$("tr");for(let t=0;t{"componentData"in t&&n(2,i=t.componentData)},[s,o,i]}class Mu extends pt{constructor(t){super(),ft(this,t,Cu,wu,r,{componentData:2})}}function Su(t){let e,n,i=t[1]&&t[2]&&function(t){let e,n,i;var s=t[3];function o(t){return{props:{componentData:t[0]}}}return s&&(e=T(s,o(t))),{c(){e&&ct(e.$$.fragment),n=M()},m(t,s){e&&ht(e,t,s),y(t,n,s),i=!0},p(t,i){const r={};if(1&i&&(r.componentData=t[0]),s!==(s=t[3])){if(e){et();const t=e;st(t.$$.fragment,1,0,(()=>{ut(t,1)})),nt()}s?(e=T(s,o(t)),ct(e.$$.fragment),it(e.$$.fragment,1),ht(e,n.parentNode,n)):e=null}else s&&e.$set(r)},i(t){i||(e&&it(e.$$.fragment,t),i=!0)},o(t){e&&st(e.$$.fragment,t),i=!1},d(t){t&&_(n),e&&ut(e,t)}}}(t);return{c(){i&&i.c(),e=M()},m(t,s){i&&i.m(t,s),y(t,e,s),n=!0},p(t,[e]){t[1]&&t[2]&&i.p(t,e)},i(t){n||(it(i),n=!0)},o(t){st(i),n=!1},d(t){i&&i.d(t),t&&_(e)}}}function Lu(t,e,n){let{componentData:i}=e;const{columns:s,data:o,vertical:r}=i,a=r?bu:Mu;return t.$$set=t=>{"componentData"in t&&n(0,i=t.componentData)},[i,s,o,a]}class Du extends pt{constructor(t){super(),ft(this,t,Lu,Su,r,{componentData:0})}}function Pu(t,e,n){const i=t.slice();return i[3]=e[n],i}function Ou(t){let e,n,i;var s=t[1];function o(t){return{props:{componentData:t[0]}}}return s&&(e=T(s,o(t))),{c(){e&&ct(e.$$.fragment),n=M()},m(t,s){e&&ht(e,t,s),y(t,n,s),i=!0},p(t,i){const r={};if(1&i&&(r.componentData=t[0]),s!==(s=t[1])){if(e){et();const t=e;st(t.$$.fragment,1,0,(()=>{ut(t,1)})),nt()}s?(e=T(s,o(t)),ct(e.$$.fragment),it(e.$$.fragment,1),ht(e,n.parentNode,n)):e=null}else s&&e.$set(r)},i(t){i||(e&&it(e.$$.fragment,t),i=!0)},o(t){e&&st(e.$$.fragment,t),i=!1},d(t){t&&_(n),e&&ut(e,t)}}}function Au(t){let e,n,i;var s=t[1];function o(t){return{props:{componentData:t[0],$$slots:{default:[Tu]},$$scope:{ctx:t}}}}return s&&(e=T(s,o(t))),{c(){e&&ct(e.$$.fragment),n=M()},m(t,s){e&&ht(e,t,s),y(t,n,s),i=!0},p(t,i){const r={};if(1&i&&(r.componentData=t[0]),65&i&&(r.$$scope={dirty:i,ctx:t}),s!==(s=t[1])){if(e){et();const t=e;st(t.$$.fragment,1,0,(()=>{ut(t,1)})),nt()}s?(e=T(s,o(t)),ct(e.$$.fragment),it(e.$$.fragment,1),ht(e,n.parentNode,n)):e=null}else s&&e.$set(r)},i(t){i||(e&&it(e.$$.fragment,t),i=!0)},o(t){e&&st(e.$$.fragment,t),i=!1},d(t){t&&_(n),e&&ut(e,t)}}}function Eu(t){let e,n;return e=new Iu({props:{componentData:t[3]}}),{c(){ct(e.$$.fragment)},m(t,i){ht(e,t,i),n=!0},p(t,n){const i={};1&n&&(i.componentData=t[3]),e.$set(i)},i(t){n||(it(e.$$.fragment,t),n=!0)},o(t){st(e.$$.fragment,t),n=!1},d(t){ut(e,t)}}}function Tu(t){let e,n,i=t[0].contents,s=[];for(let e=0;est(s[t],1,1,(()=>{s[t]=null}));return{c(){for(let t=0;t{r[l]=null})),nt(),n=r[e],n?n.p(t,s):(n=r[e]=o[e](t),n.c()),it(n,1),n.m(i.parentNode,i))},i(t){s||(it(n),s=!0)},o(t){st(n),s=!1},d(t){r[e].d(t),t&&_(i)}}}(t);return{c(){i&&i.c(),e=M()},m(t,s){i&&i.m(t,s),y(t,e,s),n=!0},p(t,[e]){t[1]&&i.p(t,e)},i(t){n||(it(i),n=!0)},o(t){st(i),n=!1},d(t){i&&i.d(t),t&&_(e)}}}function zu(t,e,n){let{componentData:i}=e;const s={artifacts:Ht,barChart:Ma,dag:ol,heading:pl,image:bl,lineChart:_l,log:kl,markdown:Gh,page:Qh,section:nu,subtitle:ul,table:Du,text:ou,title:ll};let o=null==s?void 0:s[i.type];return o||console.error("Unknown component type: ",i.type),t.$$set=t=>{"componentData"in t&&n(0,i=t.componentData)},[i,o]}class Iu extends pt{constructor(t){super(),ft(this,t,zu,Ru,r,{componentData:0})}}function Fu(t){let e,n,i;const s=t[1].default,o=u(s,t,t[0],null);return{c(){e=$("main"),n=$("div"),o&&o.c(),L(n,"class","mainContainer svelte-13ho8jo"),L(e,"class","svelte-13ho8jo")},m(t,s){y(t,e,s),x(e,n),o&&o.m(n,null),i=!0},p(t,[e]){o&&o.p&&(!i||1&e)&&p(o,s,t,t[0],i?f(s,t[0],e,null):g(t[0]),null)},i(t){i||(it(o,t),i=!0)},o(t){st(o,t),i=!1},d(t){t&&_(e),o&&o.d(t)}}}function Nu(t,e,n){let{$$slots:i={},$$scope:s}=e;return t.$$set=t=>{"$$scope"in t&&n(0,s=t.$$scope)},[s,i]}class ju extends pt{constructor(t){super(),ft(this,t,Nu,Fu,r,{})}}const Bu=/^[a-z0-9]+(-[a-z0-9]+)*$/,Vu=Object.freeze({left:0,top:0,width:16,height:16,rotate:0,vFlip:!1,hFlip:!1});function Hu(t){return{...Vu,...t}}const Wu=(t,e,n,i="")=>{const s=t.split(":");if("@"===t.slice(0,1)){if(s.length<2||s.length>3)return null;i=s.shift().slice(1)}if(s.length>3||!s.length)return null;if(s.length>1){const t=s.pop(),n=s.pop(),o={provider:s.length>0?s[0]:i,prefix:n,name:t};return e&&!Uu(o)?null:o}const o=s[0],r=o.split("-");if(r.length>1){const t={provider:i,prefix:r.shift(),name:r.join("-")};return e&&!Uu(t)?null:t}if(n&&""===i){const t={provider:i,prefix:"",name:o};return e&&!Uu(t,n)?null:t}return null},Uu=(t,e)=>!!t&&!(""!==t.provider&&!t.provider.match(Bu)||!(e&&""===t.prefix||t.prefix.match(Bu))||!t.name.match(Bu));function qu(t,e,n=!1){const i=function e(n,i){if(void 0!==t.icons[n])return Object.assign({},t.icons[n]);if(i>5)return null;const s=t.aliases;if(s&&void 0!==s[n]){const t=s[n],o=e(t.parent,i+1);return o?function(t,e){const n={...t};for(const t in Vu){const i=t;if(void 0!==e[i]){const t=e[i];if(void 0===n[i]){n[i]=t;continue}switch(i){case"rotate":n[i]=(n[i]+t)%4;break;case"hFlip":case"vFlip":n[i]=t!==n[i];break;default:n[i]=t}}}return n}(o,t):o}const o=t.chars;return!i&&o&&void 0!==o[n]?e(o[n],i+1):null}(e,0);if(i)for(const e in Vu)void 0===i[e]&&void 0!==t[e]&&(i[e]=t[e]);return i&&n?Hu(i):i}function Yu(t,e,n){n=n||{};const i=[];if("object"!=typeof t||"object"!=typeof t.icons)return i;t.not_found instanceof Array&&t.not_found.forEach((t=>{e(t,null),i.push(t)}));const s=t.icons;Object.keys(s).forEach((n=>{const s=qu(t,n,!0);s&&(e(n,s),i.push(n))}));const o=n.aliases||"all";if("none"!==o&&"object"==typeof t.aliases){const n=t.aliases;Object.keys(n).forEach((s=>{if("variations"===o&&function(t){for(const e in Vu)if(void 0!==t[e])return!0;return!1}(n[s]))return;const r=qu(t,s,!0);r&&(e(s,r),i.push(s))}))}return i}const Zu={provider:"string",aliases:"object",not_found:"object"};for(const t in Vu)Zu[t]=typeof Vu[t];function Xu(t){if("object"!=typeof t||null===t)return null;const e=t;if("string"!=typeof e.prefix||!t.icons||"object"!=typeof t.icons)return null;for(const e in Zu)if(void 0!==t[e]&&typeof t[e]!==Zu[e])return null;const n=e.icons;for(const t in n){const e=n[t];if(!t.match(Bu)||"string"!=typeof e.body)return null;for(const t in Vu)if(void 0!==e[t]&&typeof e[t]!=typeof Vu[t])return null}const i=e.aliases;if(i)for(const t in i){const e=i[t],s=e.parent;if(!t.match(Bu)||"string"!=typeof s||!n[s]&&!i[s])return null;for(const t in Vu)if(void 0!==e[t]&&typeof e[t]!=typeof Vu[t])return null}return e}let Gu=Object.create(null);try{const t=window||self;t&&1===t._iconifyStorage.version&&(Gu=t._iconifyStorage.storage)}catch(t){}function Ku(t,e){void 0===Gu[t]&&(Gu[t]=Object.create(null));const n=Gu[t];return void 0===n[e]&&(n[e]=function(t,e){return{provider:t,prefix:e,icons:Object.create(null),missing:Object.create(null)}}(t,e)),n[e]}function Ju(t,e){if(!Xu(e))return[];const n=Date.now();return Yu(e,((e,i)=>{i?t.icons[e]=i:t.missing[e]=n}))}let Qu=!1;function td(t){return"boolean"==typeof t&&(Qu=t),Qu}function ed(t,e){const n=Wu(t,!0,Qu);if(!n)return!1;return function(t,e,n){try{if("string"==typeof n.body)return t.icons[e]=Object.freeze(Hu(n)),!0}catch(t){}return!1}(Ku(n.provider,n.prefix),n.name,e)}const nd=Object.freeze({inline:!1,width:null,height:null,hAlign:"center",vAlign:"middle",slice:!1,hFlip:!1,vFlip:!1,rotate:0});const id=/(-?[0-9.]*[0-9]+[0-9.]*)/g,sd=/^-?[0-9.]*[0-9]+[0-9.]*$/g;function od(t,e,n){if(1===e)return t;if(n=void 0===n?100:n,"number"==typeof t)return Math.ceil(t*e*n)/n;if("string"!=typeof t)return t;const i=t.split(id);if(null===i||!i.length)return t;const s=[];let o=i.shift(),r=sd.test(o);for(;;){if(r){const t=parseFloat(o);isNaN(t)?s.push(o):s.push(Math.ceil(t*e*n)/n)}else s.push(o);if(o=i.shift(),void 0===o)return s.join("");r=!r}}function rd(t){let e="";switch(t.hAlign){case"left":e+="xMin";break;case"right":e+="xMax";break;default:e+="xMid"}switch(t.vAlign){case"top":e+="YMin";break;case"bottom":e+="YMax";break;default:e+="YMid"}return e+=t.slice?" slice":" meet",e}const ad=/\sid="(\S+)"/g,ld="IconifyId"+Date.now().toString(16)+(16777216*Math.random()|0).toString(16);let cd=0;function hd(t,e=ld){const n=[];let i;for(;i=ad.exec(t);)n.push(i[1]);return n.length?(n.forEach((n=>{const i="function"==typeof e?e(n):e+(cd++).toString(),s=n.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");t=t.replace(new RegExp('([#;"])('+s+')([")]|\\.[a-z])',"g"),"$1"+i+"$3")})),t):t}const ud=Object.create(null);function dd(t){return ud[t]||ud[""]}function fd(t){let e;if("string"==typeof t.resources)e=[t.resources];else if(e=t.resources,!(e instanceof Array&&e.length))return null;return{resources:e,path:void 0===t.path?"/":t.path,maxURL:t.maxURL?t.maxURL:500,rotate:t.rotate?t.rotate:750,timeout:t.timeout?t.timeout:5e3,random:!0===t.random,index:t.index?t.index:0,dataAfterTimeout:!1!==t.dataAfterTimeout}}const pd=Object.create(null),gd=["https://api.simplesvg.com","https://api.unisvg.com"],md=[];for(;gd.length>0;)1===gd.length||Math.random()>.5?md.push(gd.shift()):md.push(gd.pop());function bd(t,e){const n=fd(e);return null!==n&&(pd[t]=n,!0)}function xd(t){return pd[t]}pd[""]=fd({resources:["https://api.iconify.design"].concat(md)});const yd=(t,e)=>{let n=t,i=-1!==n.indexOf("?");return Object.keys(e).forEach((t=>{let s;try{s=function(t){switch(typeof t){case"boolean":return t?"true":"false";case"number":case"string":return encodeURIComponent(t);default:throw new Error("Invalid parameter")}}(e[t])}catch(t){return}n+=(i?"&":"?")+encodeURIComponent(t)+"="+s,i=!0})),n},_d={},vd={};let $d=(()=>{let t;try{if(t=fetch,"function"==typeof t)return t}catch(t){}return null})();const kd={prepare:(t,e,n)=>{const i=[];let s=_d[e];void 0===s&&(s=function(t,e){const n=xd(t);if(!n)return 0;let i;if(n.maxURL){let t=0;n.resources.forEach((e=>{const n=e;t=Math.max(t,n.length)}));const s=yd(e+".json",{icons:""});i=n.maxURL-t-n.path.length-s.length}else i=0;const s=t+":"+e;return vd[t]=n.path,_d[s]=i,i}(t,e));const o="icons";let r={type:o,provider:t,prefix:e,icons:[]},a=0;return n.forEach(((n,l)=>{a+=n.length+1,a>=s&&l>0&&(i.push(r),r={type:o,provider:t,prefix:e,icons:[]},a=n.length),r.icons.push(n)})),i.push(r),i},send:(t,e,n)=>{if(!$d)return void n("abort",424);let i=function(t){if("string"==typeof t){if(void 0===vd[t]){const e=xd(t);if(!e)return"/";vd[t]=e.path}return vd[t]}return"/"}(e.provider);switch(e.type){case"icons":{const t=e.prefix,n=e.icons.join(",");i+=yd(t+".json",{icons:n});break}case"custom":{const t=e.uri;i+="/"===t.slice(0,1)?t.slice(1):t;break}default:return void n("abort",400)}let s=503;$d(t+i).then((t=>{const e=t.status;if(200===e)return s=501,t.json();setTimeout((()=>{n(function(t){return 404===t}(e)?"abort":"next",e)}))})).then((t=>{"object"==typeof t&&null!==t?setTimeout((()=>{n("success",t)})):setTimeout((()=>{n("next",s)}))})).catch((()=>{n("next",s)}))}};const wd=Object.create(null),Cd=Object.create(null);function Md(t,e){t.forEach((t=>{const n=t.provider;if(void 0===wd[n])return;const i=wd[n],s=t.prefix,o=i[s];o&&(i[s]=o.filter((t=>t.id!==e)))}))}let Sd=0;var Ld={resources:[],index:0,timeout:2e3,rotate:750,random:!1,dataAfterTimeout:!1};function Dd(t,e,n,i){const s=t.resources.length,o=t.random?Math.floor(Math.random()*s):t.index;let r;if(t.random){let e=t.resources.slice(0);for(r=[];e.length>1;){const t=Math.floor(Math.random()*e.length);r.push(e[t]),e=e.slice(0,t).concat(e.slice(t+1))}r=r.concat(e)}else r=t.resources.slice(o).concat(t.resources.slice(0,o));const a=Date.now();let l,c="pending",h=0,u=null,d=[],f=[];function p(){u&&(clearTimeout(u),u=null)}function g(){"pending"===c&&(c="aborted"),p(),d.forEach((t=>{"pending"===t.status&&(t.status="aborted")})),d=[]}function m(t,e){e&&(f=[]),"function"==typeof t&&f.push(t)}function b(){c="failed",f.forEach((t=>{t(void 0,l)}))}function x(){d.forEach((t=>{"pending"===t.status&&(t.status="aborted")})),d=[]}function y(){if("pending"!==c)return;p();const i=r.shift();if(void 0===i)return d.length?void(u=setTimeout((()=>{p(),"pending"===c&&(x(),b())}),t.timeout)):void b();const s={status:"pending",resource:i,callback:(e,n)=>{!function(e,n,i){const s="success"!==n;switch(d=d.filter((t=>t!==e)),c){case"pending":break;case"failed":if(s||!t.dataAfterTimeout)return;break;default:return}if("abort"===n)return l=i,void b();if(s)return l=i,void(d.length||(r.length?y():b()));if(p(),x(),!t.random){const n=t.resources.indexOf(e.resource);-1!==n&&n!==t.index&&(t.index=n)}c="completed",f.forEach((t=>{t(i)}))}(s,e,n)}};d.push(s),h++,u=setTimeout(y,t.rotate),n(i,e,s.callback)}return"function"==typeof i&&f.push(i),setTimeout(y),function(){return{startTime:a,payload:e,status:c,queriesSent:h,queriesPending:d.length,subscribe:m,abort:g}}}function Pd(t){const e=function(t){if(!("object"==typeof t&&"object"==typeof t.resources&&t.resources instanceof Array&&t.resources.length))throw new Error("Invalid Reduncancy configuration");const e=Object.create(null);let n;for(n in Ld)void 0!==t[n]?e[n]=t[n]:e[n]=Ld[n];return e}(t);let n=[];function i(){n=n.filter((t=>"pending"===t().status))}const s={query:function(t,s,o){const r=Dd(e,t,s,((t,e)=>{i(),o&&o(t,e)}));return n.push(r),r},find:function(t){const e=n.find((e=>t(e)));return void 0!==e?e:null},setIndex:t=>{e.index=t},getIndex:()=>e.index,cleanup:i};return s}function Od(){}const Ad=Object.create(null);function Ed(t,e,n){let i,s;if("string"==typeof t){const e=dd(t);if(!e)return n(void 0,424),Od;s=e.send;const o=function(t){if(void 0===Ad[t]){const e=xd(t);if(!e)return;const n={config:e,redundancy:Pd(e)};Ad[t]=n}return Ad[t]}(t);o&&(i=o.redundancy)}else{const e=fd(t);if(e){i=Pd(e);const n=dd(t.resources?t.resources[0]:"");n&&(s=n.send)}}return i&&s?i.query(e,s,n)().abort:(n(void 0,424),Od)}const Td={};function Rd(){}const zd=Object.create(null),Id=Object.create(null),Fd=Object.create(null),Nd=Object.create(null);function jd(t,e){void 0===Fd[t]&&(Fd[t]=Object.create(null));const n=Fd[t];n[e]||(n[e]=!0,setTimeout((()=>{n[e]=!1,function(t,e){void 0===Cd[t]&&(Cd[t]=Object.create(null));const n=Cd[t];n[e]||(n[e]=!0,setTimeout((()=>{if(n[e]=!1,void 0===wd[t]||void 0===wd[t][e])return;const i=wd[t][e].slice(0);if(!i.length)return;const s=Ku(t,e);let o=!1;i.forEach((n=>{const i=n.icons,r=i.pending.length;i.pending=i.pending.filter((n=>{if(n.prefix!==e)return!0;const r=n.name;if(void 0!==s.icons[r])i.loaded.push({provider:t,prefix:e,name:r});else{if(void 0===s.missing[r])return o=!0,!0;i.missing.push({provider:t,prefix:e,name:r})}return!1})),i.pending.length!==r&&(o||Md([{provider:t,prefix:e}],n.id),n.callback(i.loaded.slice(0),i.missing.slice(0),i.pending.slice(0),n.abort))}))})))}(t,e)})))}const Bd=Object.create(null);function Vd(t,e,n){void 0===Id[t]&&(Id[t]=Object.create(null));const i=Id[t];void 0===Nd[t]&&(Nd[t]=Object.create(null));const s=Nd[t];void 0===zd[t]&&(zd[t]=Object.create(null));const o=zd[t];void 0===i[e]?i[e]=n:i[e]=i[e].concat(n).sort(),s[e]||(s[e]=!0,setTimeout((()=>{s[e]=!1;const n=i[e];delete i[e];const r=dd(t);if(!r)return void function(){const n=(""===t?"":"@"+t+":")+e,i=Math.floor(Date.now()/6e4);Bd[n]{Ed(t,n,((i,s)=>{const r=Ku(t,e);if("object"!=typeof i){if(404!==s)return;const t=Date.now();n.icons.forEach((e=>{r.missing[e]=t}))}else try{const n=Ju(r,i);if(!n.length)return;const s=o[e];n.forEach((t=>{delete s[t]})),Td.store&&Td.store(t,i)}catch(t){console.error(t)}jd(t,e)}))}))})))}const Hd=(t,e)=>{const n=function(t,e=!0,n=!1){const i=[];return t.forEach((t=>{const s="string"==typeof t?Wu(t,!1,n):t;e&&!Uu(s,n)||i.push({provider:s.provider,prefix:s.prefix,name:s.name})})),i}(t,!0,td()),i=function(t){const e={loaded:[],missing:[],pending:[]},n=Object.create(null);t.sort(((t,e)=>t.provider!==e.provider?t.provider.localeCompare(e.provider):t.prefix!==e.prefix?t.prefix.localeCompare(e.prefix):t.name.localeCompare(e.name)));let i={provider:"",prefix:"",name:""};return t.forEach((t=>{if(i.name===t.name&&i.prefix===t.prefix&&i.provider===t.provider)return;i=t;const s=t.provider,o=t.prefix,r=t.name;void 0===n[s]&&(n[s]=Object.create(null));const a=n[s];void 0===a[o]&&(a[o]=Ku(s,o));const l=a[o];let c;c=void 0!==l.icons[r]?e.loaded:""===o||void 0!==l.missing[r]?e.missing:e.pending;const h={provider:s,prefix:o,name:r};c.push(h)})),e}(n);if(!i.pending.length){let t=!0;return e&&setTimeout((()=>{t&&e(i.loaded,i.missing,i.pending,Rd)})),()=>{t=!1}}const s=Object.create(null),o=[];let r,a;i.pending.forEach((t=>{const e=t.provider,n=t.prefix;if(n===a&&e===r)return;r=e,a=n,o.push({provider:e,prefix:n}),void 0===zd[e]&&(zd[e]=Object.create(null));const i=zd[e];void 0===i[n]&&(i[n]=Object.create(null)),void 0===s[e]&&(s[e]=Object.create(null));const l=s[e];void 0===l[n]&&(l[n]=[])}));const l=Date.now();return i.pending.forEach((t=>{const e=t.provider,n=t.prefix,i=t.name,o=zd[e][n];void 0===o[i]&&(o[i]=l,s[e][n].push(i))})),o.forEach((t=>{const e=t.provider,n=t.prefix;s[e][n].length&&Vd(e,n,s[e][n])})),e?function(t,e,n){const i=Sd++,s=Md.bind(null,n,i);if(!e.pending.length)return s;const o={id:i,icons:e,callback:t,abort:s};return n.forEach((t=>{const e=t.provider,n=t.prefix;void 0===wd[e]&&(wd[e]=Object.create(null));const i=wd[e];void 0===i[n]&&(i[n]=[]),i[n].push(o)})),s}(e,i,o):Rd},Wd="iconify2",Ud="iconify",qd=Ud+"-count",Yd=Ud+"-version",Zd=36e5,Xd={local:!0,session:!0};let Gd=!1;const Kd={local:0,session:0},Jd={local:[],session:[]};let Qd="undefined"==typeof window?{}:window;function tf(t){const e=t+"Storage";try{if(Qd&&Qd[e]&&"number"==typeof Qd[e].length)return Qd[e]}catch(t){}return Xd[t]=!1,null}function ef(t,e,n){try{return t.setItem(qd,n.toString()),Kd[e]=n,!0}catch(t){return!1}}function nf(t){const e=t.getItem(qd);if(e){const t=parseInt(e);return t||0}return 0}const sf=()=>{if(Gd)return;Gd=!0;const t=Math.floor(Date.now()/Zd)-168;function e(e){const n=tf(e);if(!n)return;const i=e=>{const i=Ud+e.toString(),s=n.getItem(i);if("string"!=typeof s)return!1;let o=!0;try{const e=JSON.parse(s);if("object"!=typeof e||"number"!=typeof e.cached||e.cached0}}catch(t){o=!1}return o||n.removeItem(i),o};try{const t=n.getItem(Yd);if(t!==Wd)return t&&function(t){try{const e=nf(t);for(let n=0;n=0;t--)i(t)||(t===s-1?s--:Jd[e].push(t));ef(n,e,s)}catch(t){}}for(const t in Xd)e(t)},of=(t,e)=>{function n(n){if(!Xd[n])return!1;const i=tf(n);if(!i)return!1;let s=Jd[n].shift();if(void 0===s&&(s=Kd[n],!ef(i,n,s+1)))return!1;try{const n={cached:Math.floor(Date.now()/Zd),provider:t,data:e};i.setItem(Ud+s.toString(),JSON.stringify(n))}catch(t){return!1}return!0}Gd||sf(),Object.keys(e.icons).length&&(e.not_found&&delete(e=Object.assign({},e)).not_found,n("local")||n("session"))},rf=/[\s,]+/;function af(t,e){e.split(rf).forEach((e=>{switch(e.trim()){case"horizontal":t.hFlip=!0;break;case"vertical":t.vFlip=!0}}))}function lf(t,e){e.split(rf).forEach((e=>{const n=e.trim();switch(n){case"left":case"center":case"right":t.hAlign=n;break;case"top":case"middle":case"bottom":t.vAlign=n;break;case"slice":case"crop":t.slice=!0;break;case"meet":t.slice=!1}}))}function cf(t,e=0){const n=t.replace(/^-?[0-9.]*/,"");function i(t){for(;t<0;)t+=4;return t%4}if(""===n){const e=parseInt(t);return isNaN(e)?0:i(e)}if(n!==t){let e=0;switch(n){case"%":e=25;break;case"deg":e=90}if(e){let s=parseFloat(t.slice(0,t.length-n.length));return isNaN(s)?0:(s/=e,s%1==0?i(s):0)}}return e}const hf={xmlns:"http://www.w3.org/2000/svg","xmlns:xlink":"http://www.w3.org/1999/xlink","aria-hidden":!0,role:"img"};function uf(t,e){const n=function(t,e){const n={};for(const i in t){const s=i;if(n[s]=t[s],void 0===e[s])continue;const o=e[s];switch(s){case"inline":case"slice":"boolean"==typeof o&&(n[s]=o);break;case"hFlip":case"vFlip":!0===o&&(n[s]=!n[s]);break;case"hAlign":case"vAlign":"string"==typeof o&&""!==o&&(n[s]=o);break;case"width":case"height":("string"==typeof o&&""!==o||"number"==typeof o&&o||null===o)&&(n[s]=o);break;case"rotate":"number"==typeof o&&(n[s]+=o)}}return n}(nd,e),i={...hf};let s="string"==typeof e.style?e.style:"";for(let t in e){const o=e[t];if(void 0!==o)switch(t){case"icon":case"style":case"onLoad":break;case"inline":case"hFlip":case"vFlip":n[t]=!0===o||"true"===o||1===o;break;case"flip":"string"==typeof o&&af(n,o);break;case"align":"string"==typeof o&&lf(n,o);break;case"color":s=s+(s.length>0&&";"!==s.trim().slice(-1)?";":"")+"color: "+o+"; ";break;case"rotate":"string"==typeof o?n[t]=cf(o):"number"==typeof o&&(n[t]=o);break;case"ariaHidden":case"aria-hidden":!0!==o&&"true"!==o&&delete i["aria-hidden"];break;default:if("on:"===t.slice(0,3))break;void 0===nd[t]&&(i[t]=o)}}const o=function(t,e){const n={left:t.left,top:t.top,width:t.width,height:t.height};let i,s,o=t.body;[t,e].forEach((t=>{const e=[],i=t.hFlip,s=t.vFlip;let r,a=t.rotate;switch(i?s?a+=2:(e.push("translate("+(n.width+n.left).toString()+" "+(0-n.top).toString()+")"),e.push("scale(-1 1)"),n.top=n.left=0):s&&(e.push("translate("+(0-n.left).toString()+" "+(n.height+n.top).toString()+")"),e.push("scale(1 -1)"),n.top=n.left=0),a<0&&(a-=4*Math.floor(a/4)),a%=4,a){case 1:r=n.height/2+n.top,e.unshift("rotate(90 "+r.toString()+" "+r.toString()+")");break;case 2:e.unshift("rotate(180 "+(n.width/2+n.left).toString()+" "+(n.height/2+n.top).toString()+")");break;case 3:r=n.width/2+n.left,e.unshift("rotate(-90 "+r.toString()+" "+r.toString()+")")}a%2==1&&(0===n.left&&0===n.top||(r=n.left,n.left=n.top,n.top=r),n.width!==n.height&&(r=n.width,n.width=n.height,n.height=r)),e.length&&(o=''+o+"")})),null===e.width&&null===e.height?(s="1em",i=od(s,n.width/n.height)):null!==e.width&&null!==e.height?(i=e.width,s=e.height):null!==e.height?(s=e.height,i=od(s,n.width/n.height)):(i=e.width,s=od(i,n.height/n.width)),"auto"===i&&(i=n.width),"auto"===s&&(s=n.height),i="string"==typeof i?i:i.toString()+"",s="string"==typeof s?s:s.toString()+"";const r={attributes:{width:i,height:s,preserveAspectRatio:rd(e),viewBox:n.left.toString()+" "+n.top.toString()+" "+n.width.toString()+" "+n.height.toString()},body:o};return e.inline&&(r.inline=!0),r}(t,n);for(let t in o.attributes)i[t]=o.attributes[t];o.inline&&(s="vertical-align: -0.125em; "+s),""!==s&&(i.style=s);let r=0,a=e.id;return"string"==typeof a&&(a=a.replace(/-/g,"_")),{attributes:i,body:hd(o.body,a?()=>a+"ID"+r++:"iconifySvelte")}}var df;if(td(!0),df=kd,ud[""]=df,"undefined"!=typeof document&&"undefined"!=typeof window){Td.store=of,sf();const t=window;if(void 0!==t.IconifyPreload){const e=t.IconifyPreload,n="Invalid IconifyPreload syntax.";"object"==typeof e&&null!==e&&(e instanceof Array?e:[e]).forEach((t=>{try{("object"!=typeof t||null===t||t instanceof Array||"object"!=typeof t.icons||"string"!=typeof t.prefix||!function(t,e){if("object"!=typeof t)return!1;if("string"!=typeof e&&(e="string"==typeof t.provider?t.provider:""),Qu&&""===e&&("string"!=typeof t.prefix||""===t.prefix)){let e=!1;return Xu(t)&&(t.prefix="",Yu(t,((t,n)=>{n&&ed(t,n)&&(e=!0)}))),e}return!("string"!=typeof t.prefix||!Uu({provider:e,prefix:t.prefix,name:"a"}))&&!!Ju(Ku(e,t.prefix),t)}(t))&&console.error(n)}catch(t){console.error(n)}}))}if(void 0!==t.IconifyProviders){const e=t.IconifyProviders;if("object"==typeof e&&null!==e)for(let t in e){const n="IconifyProviders["+t+"] is invalid.";try{const i=e[t];if("object"!=typeof i||!i||void 0===i.resources)continue;bd(t,i)||console.error(n)}catch(t){console.error(n)}}}}function ff(t,e,n,i,s){function o(){e.loading&&(e.loading.abort(),e.loading=null)}if("object"==typeof t&&null!==t&&"string"==typeof t.body)return e.name="",o(),{data:Hu(t)};let r;if("string"!=typeof t||null===(r=Wu(t,!1,!0)))return o(),null;const a=function(t){const e="string"==typeof t?Wu(t,!0,Qu):t;return e?function(t,e){const n=t.icons[e];return void 0===n?null:n}(Ku(e.provider,e.prefix),e.name):null}(r);if(null===a)return!n||e.loading&&e.loading.name===t||(o(),e.name="",e.loading={name:t,abort:Hd([r],i)}),null;o(),e.name!==t&&(e.name=t,s&&!e.destroyed&&s(t));const l=["iconify"];return""!==r.prefix&&l.push("iconify--"+r.prefix),""!==r.provider&&l.push("iconify--"+r.provider),{data:a,classes:l}}function pf(t){let n,i=t[0].body+"",s=[t[0].attributes],o={};for(let t=0;t{"function"==typeof n.onLoad&&n.onLoad(t);F()("load",{icon:t})};function c(){i(3,a++,a)}var h;return I((()=>{i(2,r=!0)})),h=()=>{i(1,s.destroyed=!0,s),s.loading&&(s.loading.abort(),i(1,s.loading=null,s))},z().$$.on_destroy.push(h),t.$$set=t=>{i(6,n=e(e({},n),m(t)))},t.$$.update=()=>{{const a=ff(n.icon,s,r,c,l);i(0,o=a?(t=a.data,e=n,t?uf(t,e):null):null),o&&a.classes&&i(0,o.attributes.class=("string"==typeof n.class?n.class+" ":"")+a.classes.join(" "),o)}var t,e},n=m(n),[o,s,r,a]}class bf extends pt{constructor(t){super(),ft(this,t,mf,gf,r,{})}}function xf(t){let e,n,i,o,r,a,l,c,h;return i=new bf({props:{icon:"mdi:close"}}),a=new Iu({props:{componentData:t[1]}}),{c(){e=$("div"),n=$("span"),ct(i.$$.fragment),o=C(),r=$("div"),ct(a.$$.fragment),L(n,"class","cancelButton svelte-1hhf5ym"),L(r,"class","modalContainer"),L(e,"class","modal svelte-1hhf5ym"),L(e,"data-component","modal")},m(s,u){y(s,e,u),x(e,n),ht(i,n,null),x(e,o),x(e,r),ht(a,r,null),l=!0,c||(h=[S(r,"click",_f),S(e,"click",t[3])],c=!0)},p(t,e){const n={};2&e&&(n.componentData=t[1]),a.$set(n)},i(t){l||(it(i.$$.fragment,t),it(a.$$.fragment,t),l=!0)},o(t){st(i.$$.fragment,t),st(a.$$.fragment,t),l=!1},d(t){t&&_(e),ut(i),ut(a),c=!1,s(h)}}}function yf(t){let e,n,i,s,o=t[0]&&t[1]&&xf(t);return{c(){o&&o.c(),e=M()},m(r,a){o&&o.m(r,a),y(r,e,a),n=!0,i||(s=S(window,"keyup",t[2]),i=!0)},p(t,[n]){t[0]&&t[1]?o?(o.p(t,n),3&n&&it(o,1)):(o=xf(t),o.c(),it(o,1),o.m(e.parentNode,e)):o&&(et(),st(o,1,1,(()=>{o=null})),nt())},i(t){n||(it(o),n=!0)},o(t){st(o),n=!1},d(t){o&&o.d(t),t&&_(e),i=!1,s()}}}const _f=t=>{t?.stopImmediatePropagation()};function vf(t,e,n){let i;h(t,$t,(t=>n(1,i=t)));let{componentData:s}=e;return t.$$set=t=>{"componentData"in t&&n(0,s=t.componentData)},[s,i,function(t){"Escape"===t.code&&$t.set(void 0)},function(t){t.stopImmediatePropagation(),$t.set(void 0)}]}class $f extends pt{constructor(t){super(),ft(this,t,vf,yf,r,{componentData:0})}}function kf(t,e,n){const i=t.slice();return i[2]=e[n][0],i[3]=e[n][1],i}function wf(t,e,n){const i=t.slice();return i[6]=e[n],i}function Cf(t){let e,n,i=t[2]+"";return{c(){e=$("span"),n=w(i),L(e,"class","pageId svelte-1kdpgko")},m(t,i){y(t,e,i),x(e,n)},p(t,e){1&e&&i!==(i=t[2]+"")&&P(n,i)},d(t){t&&_(e)}}}function Mf(t){let e,n,i,s,o,r,a=t[6]+"";function l(){return t[1](t[6])}return{c(){e=$("li"),n=$("button"),i=w(a),s=C(),L(n,"class","textButton"),L(e,"class","sectionLink svelte-1kdpgko")},m(t,a){y(t,e,a),x(e,n),x(n,i),x(e,s),o||(r=S(n,"click",l),o=!0)},p(e,n){t=e,1&n&&a!==(a=t[6]+"")&&P(i,a)},d(t){t&&_(e),o=!1,r()}}}function Sf(t){let e,n,i,s,o=t[2]&&Cf(t),r=t[3]||[],a=[];for(let e=0;e{"pageHierarchy"in t&&n(0,i=t.pageHierarchy)},[i,t=>function(t){const e=document.querySelector(`[data-section-id="${t}"]`);null==e||e.scrollIntoView({behavior:"smooth"})}(t)]}class Pf extends pt{constructor(t){super(),ft(this,t,Df,Lf,r,{pageHierarchy:0})}}const{Boolean:Of}=ot;function Af(t,e,n){const i=t.slice();return i[5]=e[n],i}function Ef(t){let e,n;return e=new Pf({props:{pageHierarchy:kt(t[0]?.components)}}),{c(){ct(e.$$.fragment)},m(t,i){ht(e,t,i),n=!0},p(t,n){const i={};1&n&&(i.pageHierarchy=kt(t[0]?.components)),e.$set(i)},i(t){n||(it(e.$$.fragment,t),n=!0)},o(t){st(e.$$.fragment,t),n=!1},d(t){ut(e,t)}}}function Tf(t){let e,n;return e=new Iu({props:{componentData:t[5]}}),{c(){ct(e.$$.fragment)},m(t,i){ht(e,t,i),n=!0},p(t,n){const i={};1&n&&(i.componentData=t[5]),e.$set(i)},i(t){n||(it(e.$$.fragment,t),n=!0)},o(t){st(e.$$.fragment,t),n=!1},d(t){ut(e,t)}}}function Rf(t){let e,n,i=t[0]?.components||[],s=[];for(let e=0;est(s[t],1,1,(()=>{s[t]=null}));return{c(){for(let t=0;t{l=null})),nt())},i(t){a||(it(n.$$.fragment,t),it(s.$$.fragment,t),it(l),a=!0)},o(t){st(n.$$.fragment,t),st(s.$$.fragment,t),st(l),a=!1},d(t){t&&_(e),ut(n),ut(s),t&&_(o),l&&l.d(t),t&&_(r)}}}function Ff(t,e,n){let i,s;h(t,_t,(t=>n(0,i=t))),h(t,$t,(t=>n(1,s=t)));let{cardDataId:o}=e;vt(o);const r=new URLSearchParams(null===window||void 0===window?void 0:window.location.search);let a=Boolean(r.get("embed"));return t.$$set=t=>{"cardDataId"in t&&n(3,o=t.cardDataId)},[i,s,a,o]}class Nf extends pt{constructor(t){super(),ft(this,t,Ff,If,r,{cardDataId:3})}}var jf;let Bf;try{const t=window.mfCardDataId,e=window.mfContainerId,n=null===(jf=document.querySelector(`[data-container="${e}"]`))||void 0===jf?void 0:jf.querySelector(".card_app");Bf=new Nf({target:null!=n?n:document.querySelector(".card_app"),props:{cardDataId:t}})}catch(t){throw new Error(t)}return Bf}(); -//# sourceMappingURL=main.js.map +(function(ie,Pe){typeof exports=="object"&&typeof module<"u"?module.exports=Pe():typeof define=="function"&&define.amd?define(Pe):(ie=typeof globalThis<"u"?globalThis:ie||self,ie["Outerbounds Cards"]=Pe())})(this,function(){"use strict";var s7e=Object.defineProperty;var Ez=ie=>{throw TypeError(ie)};var o7e=(ie,Pe,xi)=>Pe in ie?s7e(ie,Pe,{enumerable:!0,configurable:!0,writable:!0,value:xi}):ie[Pe]=xi;var Lt=(ie,Pe,xi)=>o7e(ie,typeof Pe!="symbol"?Pe+"":Pe,xi),a7e=(ie,Pe,xi)=>Pe.has(ie)||Ez("Cannot "+xi);var Cz=(ie,Pe,xi)=>Pe.has(ie)?Ez("Cannot add the same private member more than once"):Pe instanceof WeakSet?Pe.add(ie):Pe.set(ie,xi);var s2=(ie,Pe,xi)=>(a7e(ie,Pe,"access private method"),xi);var Yu,g5,kz,_z,wz;function ie(){}function Pe(e,t){for(const n in t)e[n]=t[n];return e}function xi(e){return e()}function m5(){return Object.create(null)}function Xu(e){e.forEach(xi)}function y5(e){return typeof e=="function"}function ye(e,t){return e!=e?t==t:e!==t||e&&typeof e=="object"||typeof e=="function"}let Lh;function Ih(e,t){return e===t?!0:(Lh||(Lh=document.createElement("a")),Lh.href=t,e===Lh.href)}function Az(e){return Object.keys(e).length===0}function $z(e,...t){if(e==null){for(const i of t)i(void 0);return ie}const n=e.subscribe(...t);return n.unsubscribe?()=>n.unsubscribe():n}function Ph(e,t,n){e.$$.on_destroy.push($z(t,n))}function mt(e,t,n,i){if(e){const r=b5(e,t,n,i);return e[0](r)}}function b5(e,t,n,i){return e[1]&&i?Pe(n.ctx.slice(),e[1](i(t))):n.ctx}function yt(e,t,n,i){return e[2],t.dirty}function bt(e,t,n,i,r,s){if(r){const o=b5(t,n,i,s);e.p(o,r)}}function vt(e){if(e.ctx.length>32){const t=[],n=e.ctx.length/32;for(let i=0;ie.removeEventListener(t,n,i)}function M(e,t,n){n==null?e.removeAttribute(t):e.getAttribute(t)!==n&&e.setAttribute(t,n)}const Fz=["width","height"];function x5(e,t){const n=Object.getOwnPropertyDescriptors(e.__proto__);for(const i in t)t[i]==null?e.removeAttribute(i):i==="style"?e.style.cssText=t[i]:i==="__value"?e.value=e[i]=t[i]:n[i]&&n[i].set&&Fz.indexOf(i)===-1?e[i]=t[i]:M(e,i,t[i])}function _5(e,t){for(const n in t)M(e,n,t[n])}function Dz(e){return Array.from(e.childNodes)}function ft(e,t){t=""+t,e.data!==t&&(e.data=t)}function _i(e,t,n,i){n==null?e.style.removeProperty(t):e.style.setProperty(t,n,"")}function ni(e,t,n){e.classList.toggle(t,!!n)}function Tz(e,t,{bubbles:n=!1,cancelable:i=!1}={}){return new CustomEvent(e,{detail:t,bubbles:n,cancelable:i})}class Mz{constructor(t=!1){Lt(this,"is_svg",!1);Lt(this,"e");Lt(this,"n");Lt(this,"t");Lt(this,"a");this.is_svg=t,this.e=this.n=null}c(t){this.h(t)}m(t,n,i=null){this.e||(this.is_svg?this.e=Pi(n.nodeName):this.e=G(n.nodeType===11?"TEMPLATE":n.nodeName),this.t=n.tagName!=="TEMPLATE"?n:n.content,this.c(t)),this.i(i)}h(t){this.e.innerHTML=t,this.n=Array.from(this.e.nodeName==="TEMPLATE"?this.e.content.childNodes:this.e.childNodes)}i(t){for(let n=0;n{const r=e.$$.callbacks[t];if(r){const s=Tz(t,n,{cancelable:i});return r.slice().forEach(o=>{o.call(e,s)}),!s.defaultPrevented}return!0}}function E5(e,t){return qc().$$.context.set(e,t),t}function C5(e){return qc().$$.context.get(e)}function k5(e,t){const n=e.$$.callbacks[t.type];n&&n.slice().forEach(i=>i.call(this,t))}const Zu=[],zi=[];let Ju=[];const u2=[],Nz=Promise.resolve();let l2=!1;function Rz(){l2||(l2=!0,Nz.then(A5))}function c2(e){Ju.push(e)}function f2(e){u2.push(e)}const d2=new Set;let Qu=0;function A5(){if(Qu!==0)return;const e=Uc;do{try{for(;Que.indexOf(i)===-1?t.push(i):n.push(i)),n.forEach(i=>i()),Ju=t}const zh=new Set;let _a;function Se(){_a={r:0,c:[],p:_a}}function Fe(){_a.r||Xu(_a.c),_a=_a.p}function D(e,t){e&&e.i&&(zh.delete(e),e.i(t))}function N(e,t,n,i){if(e&&e.o){if(zh.has(e))return;zh.add(e),_a.c.push(()=>{zh.delete(e),i&&(n&&e.d(1),i())}),e.o(t)}else i&&i()}function Ue(e){return(e==null?void 0:e.length)!==void 0?e:Array.from(e)}function wi(e,t){const n={},i={},r={$$scope:1};let s=e.length;for(;s--;){const o=e[s],a=t[s];if(a){for(const u in o)u in a||(i[u]=1);for(const u in a)r[u]||(n[u]=a[u],r[u]=1);e[s]=a}else for(const u in o)r[u]=1}for(const o in i)o in n||(n[o]=void 0);return n}function ar(e){return typeof e=="object"&&e!==null?e:{}}function h2(e,t,n){const i=e.$$.props[t];i!==void 0&&(e.$$.bound[i]=n,n(e.$$.ctx[i]))}function he(e){e&&e.c()}function ce(e,t,n){const{fragment:i,after_update:r}=e.$$;i&&i.m(t,n),c2(()=>{const s=e.$$.on_mount.map(xi).filter(y5);e.$$.on_destroy?e.$$.on_destroy.push(...s):Xu(s),e.$$.on_mount=[]}),r.forEach(c2)}function fe(e,t){const n=e.$$;n.fragment!==null&&(Lz(n.after_update),Xu(n.on_destroy),n.fragment&&n.fragment.d(t),n.on_destroy=n.fragment=null,n.ctx=[])}function Iz(e,t){e.$$.dirty[0]===-1&&(Zu.push(e),Rz(),e.$$.dirty.fill(0)),e.$$.dirty[t/31|0]|=1<{const p=h.length?h[0]:d;return l.ctx&&r(l.ctx[f],l.ctx[f]=p)&&(!l.skip_bound&&l.bound[f]&&l.bound[f](p),c&&Iz(e,f)),d}):[],l.update(),c=!0,Xu(l.before_update),l.fragment=i?i(l.ctx):!1,t.target){if(t.hydrate){const f=Dz(t.target);l.fragment&&l.fragment.l(f),f.forEach(L)}else l.fragment&&l.fragment.c();t.intro&&D(e.$$.fragment),ce(e,t.target,t.anchor),A5()}jc(u)}class xe{constructor(){Lt(this,"$$");Lt(this,"$$set")}$destroy(){fe(this,1),this.$destroy=ie}$on(t,n){if(!y5(n))return ie;const i=this.$$.callbacks[t]||(this.$$.callbacks[t]=[]);return i.push(n),()=>{const r=i.indexOf(n);r!==-1&&i.splice(r,1)}}$set(t){this.$$set&&!Az(t)&&(this.$$.skip_bound=!0,this.$$set(t),this.$$.skip_bound=!1)}}const Pz="4";typeof window<"u"&&(window.__svelte||(window.__svelte={v:new Set})).v.add(Pz);var zz=typeof window<"u"?window:typeof WorkerGlobalScope<"u"&&self instanceof WorkerGlobalScope?self:{},Br=function(e){var t=/(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i,n=0,i={},r={manual:e.Prism&&e.Prism.manual,disableWorkerMessageHandler:e.Prism&&e.Prism.disableWorkerMessageHandler,util:{encode:function h(p){return p instanceof s?new s(p.type,h(p.content),p.alias):Array.isArray(p)?p.map(h):p.replace(/&/g,"&").replace(/"u")return null;if("currentScript"in document)return document.currentScript;try{throw new Error}catch(m){var h=(/at [^(\r\n]*\((.*):[^:]+:[^:]+\)$/i.exec(m.stack)||[])[1];if(h){var p=document.getElementsByTagName("script");for(var g in p)if(p[g].src==h)return p[g]}return null}},isActive:function(h,p,g){for(var m="no-"+p;h;){var y=h.classList;if(y.contains(p))return!0;if(y.contains(m))return!1;h=h.parentElement}return!!g}},languages:{plain:i,plaintext:i,text:i,txt:i,extend:function(h,p){var g=r.util.clone(r.languages[h]);for(var m in p)g[m]=p[m];return g},insertBefore:function(h,p,g,m){var y=(m=m||r.languages)[h],b={};for(var v in y)if(y.hasOwnProperty(v)){if(v==p)for(var x in g)g.hasOwnProperty(x)&&(b[x]=g[x]);g.hasOwnProperty(v)||(b[v]=y[v])}var _=m[h];return m[h]=b,r.languages.DFS(r.languages,function(E,w){w===_&&E!=h&&(this[E]=b)}),b},DFS:function h(p,g,m,y){y=y||{};var b=r.util.objId;for(var v in p)if(p.hasOwnProperty(v)){g.call(p,v,p[v],m||v);var x=p[v],_=r.util.type(x);_!=="Object"||y[b(x)]?_!=="Array"||y[b(x)]||(y[b(x)]=!0,h(x,g,v,y)):(y[b(x)]=!0,h(x,g,null,y))}}},plugins:{},highlightAll:function(h,p){r.highlightAllUnder(document,h,p)},highlightAllUnder:function(h,p,g){var m={callback:g,container:h,selector:'code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code'};r.hooks.run("before-highlightall",m),m.elements=Array.prototype.slice.apply(m.container.querySelectorAll(m.selector)),r.hooks.run("before-all-elements-highlight",m);for(var y,b=0;y=m.elements[b++];)r.highlightElement(y,p===!0,m.callback)},highlightElement:function(h,p,g){var m=r.util.getLanguage(h),y=r.languages[m];r.util.setLanguage(h,m);var b=h.parentElement;b&&b.nodeName.toLowerCase()==="pre"&&r.util.setLanguage(b,m);var v={element:h,language:m,grammar:y,code:h.textContent};function x(E){v.highlightedCode=E,r.hooks.run("before-insert",v),v.element.innerHTML=v.highlightedCode,r.hooks.run("after-highlight",v),r.hooks.run("complete",v),g&&g.call(v.element)}if(r.hooks.run("before-sanity-check",v),(b=v.element.parentElement)&&b.nodeName.toLowerCase()==="pre"&&!b.hasAttribute("tabindex")&&b.setAttribute("tabindex","0"),!v.code)return r.hooks.run("complete",v),void(g&&g.call(v.element));if(r.hooks.run("before-highlight",v),v.grammar)if(p&&e.Worker){var _=new Worker(r.filename);_.onmessage=function(E){x(E.data)},_.postMessage(JSON.stringify({language:v.language,code:v.code,immediateClose:!0}))}else x(r.highlight(v.code,v.grammar,v.language));else x(r.util.encode(v.code))},highlight:function(h,p,g){var m={code:h,grammar:p,language:g};return r.hooks.run("before-tokenize",m),m.tokens=r.tokenize(m.code,m.grammar),r.hooks.run("after-tokenize",m),s.stringify(r.util.encode(m.tokens),m.language)},tokenize:function(h,p){var g=p.rest;if(g){for(var m in g)p[m]=g[m];delete p.rest}var y=new a;return u(y,y.head,h),function b(v,x,_,E,w,C){for(var k in _)if(_.hasOwnProperty(k)&&_[k]){var S=_[k];S=Array.isArray(S)?S:[S];for(var $=0;$=C.reach);ne+=z.value.length,z=z.next){var be=z.value;if(x.length>v.length)return;if(!(be instanceof s)){var de,Te=1;if(A){if(!(de=o(H,ne,v,F))||de.index>=v.length)break;var or=de.index,ct=de.index+de[0].length,$e=ne;for($e+=z.value.length;$e<=or;)z=z.next,$e+=z.value.length;if($e-=z.value.length,ne=$e,z.value instanceof s)continue;for(var Ot=z;Ot!==x.tail&&($eC.reach&&(C.reach=Le);var K=z.prev;le&&(K=u(x,K,le),ne+=le.length),l(x,K,Te);var Kt=new s(k,R?r.tokenize(Ii,R):Ii,T,Ii);if(z=u(x,K,Kt),Re&&u(x,z,Re),1C.reach&&(C.reach=Xe.reach)}}}}}}(h,y,p,y.head,0),function(b){for(var v=[],x=b.head.next;x!==b.tail;)v.push(x.value),x=x.next;return v}(y)},hooks:{all:{},add:function(h,p){var g=r.hooks.all;g[h]=g[h]||[],g[h].push(p)},run:function(h,p){var g=r.hooks.all[h];if(g&&g.length)for(var m,y=0;m=g[y++];)m(p)}},Token:s};function s(h,p,g,m){this.type=h,this.content=p,this.alias=g,this.length=0|(m||"").length}function o(h,p,g,m){h.lastIndex=p;var y=h.exec(g);if(y&&m&&y[1]){var b=y[1].length;y.index+=b,y[0]=y[0].slice(b)}return y}function a(){var h={value:null,prev:null,next:null},p={value:null,prev:h,next:null};h.next=p,this.head=h,this.tail=p,this.length=0}function u(h,p,g){var m=p.next,y={value:g,prev:p,next:m};return p.next=y,m.prev=y,h.length++,y}function l(h,p,g){for(var m=p.next,y=0;y"+y.content+""},!e.document)return e.addEventListener&&(r.disableWorkerMessageHandler||e.addEventListener("message",function(h){var p=JSON.parse(h.data),g=p.language,m=p.code,y=p.immediateClose;e.postMessage(r.highlight(m,r.languages[g],g)),y&&e.close()},!1)),r;var c=r.util.currentScript();function f(){r.manual||r.highlightAll()}if(c&&(r.filename=c.src,c.hasAttribute("data-manual")&&(r.manual=!0)),!r.manual){var d=document.readyState;d==="loading"||d==="interactive"&&c&&c.defer?document.addEventListener("DOMContentLoaded",f):window.requestAnimationFrame?window.requestAnimationFrame(f):window.setTimeout(f,16)}return r}(zz);typeof module<"u"&&module.exports&&(module.exports=Br),typeof global<"u"&&(global.Prism=Br),Br.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0,greedy:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/(\b(?:class|extends|implements|instanceof|interface|new|trait)\s+|\bcatch\s+\()[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:break|catch|continue|do|else|finally|for|function|if|in|instanceof|new|null|return|throw|try|while)\b/,boolean:/\b(?:false|true)\b/,function:/\b\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,punctuation:/[{}[\];(),.:]/},Br.languages.log={string:{pattern:/"(?:[^"\\\r\n]|\\.)*"|'(?![st] | \w)(?:[^'\\\r\n]|\\.)*'/,greedy:!0},exception:{pattern:/(^|[^\w.])[a-z][\w.]*(?:Error|Exception):.*(?:(?:\r\n?|\n)[ \t]*(?:at[ \t].+|\.{3}.*|Caused by:.*))+(?:(?:\r\n?|\n)[ \t]*\.\.\. .*)?/,lookbehind:!0,greedy:!0,alias:["javastacktrace","language-javastacktrace"],inside:Br.languages.javastacktrace||{keyword:/\bat\b/,function:/[a-z_][\w$]*(?=\()/,punctuation:/[.:()]/}},level:[{pattern:/\b(?:ALERT|CRIT|CRITICAL|EMERG|EMERGENCY|ERR|ERROR|FAILURE|FATAL|SEVERE)\b/,alias:["error","important"]},{pattern:/\b(?:WARN|WARNING|WRN)\b/,alias:["warning","important"]},{pattern:/\b(?:DISPLAY|INF|INFO|NOTICE|STATUS)\b/,alias:["info","keyword"]},{pattern:/\b(?:DBG|DEBUG|FINE)\b/,alias:["debug","keyword"]},{pattern:/\b(?:FINER|FINEST|TRACE|TRC|VERBOSE|VRB)\b/,alias:["trace","comment"]}],property:{pattern:/((?:^|[\]|])[ \t]*)[a-z_](?:[\w-]|\b\/\b)*(?:[. ]\(?\w(?:[\w-]|\b\/\b)*\)?)*:(?=\s)/im,lookbehind:!0},separator:{pattern:/(^|[^-+])-{3,}|={3,}|\*{3,}|- - /m,lookbehind:!0,alias:"comment"},url:/\b(?:file|ftp|https?):\/\/[^\s|,;'"]*[^\s|,;'">.]/,email:{pattern:/(^|\s)[-\w+.]+@[a-z][a-z0-9-]*(?:\.[a-z][a-z0-9-]*)+(?=\s)/,lookbehind:!0,alias:"url"},"ip-address":{pattern:/\b(?:\d{1,3}(?:\.\d{1,3}){3})\b/,alias:"constant"},"mac-address":{pattern:/\b[a-f0-9]{2}(?::[a-f0-9]{2}){5}\b/i,alias:"constant"},domain:{pattern:/(^|\s)[a-z][a-z0-9-]*(?:\.[a-z][a-z0-9-]*)*\.[a-z][a-z0-9-]+(?=\s)/,lookbehind:!0,alias:"constant"},uuid:{pattern:/\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/i,alias:"constant"},hash:{pattern:/\b(?:[a-f0-9]{32}){1,2}\b/i,alias:"constant"},"file-path":{pattern:/\b[a-z]:[\\/][^\s|,;:(){}\[\]"']+|(^|[\s:\[\](>|])\.{0,2}\/\w[^\s|,;:(){}\[\]"']*/i,lookbehind:!0,greedy:!0,alias:"string"},date:{pattern:RegExp("\\b\\d{4}[-/]\\d{2}[-/]\\d{2}(?:T(?=\\d{1,2}:)|(?=\\s\\d{1,2}:))|\\b\\d{1,4}[-/ ](?:\\d{1,2}|Apr|Aug|Dec|Feb|Jan|Jul|Jun|Mar|May|Nov|Oct|Sep)[-/ ]\\d{2,4}T?\\b|\\b(?:(?:Fri|Mon|Sat|Sun|Thu|Tue|Wed)(?:\\s{1,2}(?:Apr|Aug|Dec|Feb|Jan|Jul|Jun|Mar|May|Nov|Oct|Sep))?|Apr|Aug|Dec|Feb|Jan|Jul|Jun|Mar|May|Nov|Oct|Sep)\\s{1,2}\\d{1,2}\\b","i"),alias:"number"},time:{pattern:/\b\d{1,2}:\d{1,2}:\d{1,2}(?:[.,:]\d+)?(?:\s?[+-]\d{2}:?\d{2}|Z)?\b/,alias:"number"},boolean:/\b(?:false|null|true)\b/i,number:{pattern:/(^|[^.\w])(?:0x[a-f0-9]+|0o[0-7]+|0b[01]+|v?\d[\da-f]*(?:\.\d+)*(?:e[+-]?\d+)?[a-z]{0,3}\b)\b(?!\.\w)/i,lookbehind:!0},operator:/[;:?<=>~/@!$%&+\-|^(){}*#]/,punctuation:/[\[\].,]/},Br.languages.python={comment:{pattern:/(^|[^\\])#.*/,lookbehind:!0,greedy:!0},"string-interpolation":{pattern:/(?:f|fr|rf)(?:("""|''')[\s\S]*?\1|("|')(?:\\.|(?!\2)[^\\\r\n])*\2)/i,greedy:!0,inside:{interpolation:{pattern:/((?:^|[^{])(?:\{\{)*)\{(?!\{)(?:[^{}]|\{(?!\{)(?:[^{}]|\{(?!\{)(?:[^{}])+\})+\})+\}/,lookbehind:!0,inside:{"format-spec":{pattern:/(:)[^:(){}]+(?=\}$)/,lookbehind:!0},"conversion-option":{pattern:/![sra](?=[:}]$)/,alias:"punctuation"},rest:null}},string:/[\s\S]+/}},"triple-quoted-string":{pattern:/(?:[rub]|br|rb)?("""|''')[\s\S]*?\1/i,greedy:!0,alias:"string"},string:{pattern:/(?:[rub]|br|rb)?("|')(?:\\.|(?!\1)[^\\\r\n])*\1/i,greedy:!0},function:{pattern:/((?:^|\s)def[ \t]+)[a-zA-Z_]\w*(?=\s*\()/g,lookbehind:!0},"class-name":{pattern:/(\bclass\s+)\w+/i,lookbehind:!0},decorator:{pattern:/(^[\t ]*)@\w+(?:\.\w+)*/m,lookbehind:!0,alias:["annotation","punctuation"],inside:{punctuation:/\./}},keyword:/\b(?:_(?=\s*:)|and|as|assert|async|await|break|case|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|match|nonlocal|not|or|pass|print|raise|return|try|while|with|yield)\b/,builtin:/\b(?:__import__|abs|all|any|apply|ascii|basestring|bin|bool|buffer|bytearray|bytes|callable|chr|classmethod|cmp|coerce|compile|complex|delattr|dict|dir|divmod|enumerate|eval|execfile|file|filter|float|format|frozenset|getattr|globals|hasattr|hash|help|hex|id|input|int|intern|isinstance|issubclass|iter|len|list|locals|long|map|max|memoryview|min|next|object|oct|open|ord|pow|property|range|raw_input|reduce|reload|repr|reversed|round|set|setattr|slice|sorted|staticmethod|str|sum|super|tuple|type|unichr|unicode|vars|xrange|zip)\b/,boolean:/\b(?:False|None|True)\b/,number:/\b0(?:b(?:_?[01])+|o(?:_?[0-7])+|x(?:_?[a-f0-9])+)\b|(?:\b\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\B\.\d+(?:_\d+)*)(?:e[+-]?\d+(?:_\d+)*)?j?(?!\w)/i,operator:/[-+%=]=?|!=|:=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]/,punctuation:/[{}[\];(),.:]/},Br.languages.python["string-interpolation"].inside.interpolation.inside.rest=Br.languages.python,Br.languages.py=Br.languages.python;const el=[];function $5(e,t=ie){let n;const i=new Set;function r(a){if(ye(e,a)&&(e=a,n)){const u=!el.length;for(const l of i)l[1](),el.push(l,e);if(u){for(let l=0;l{i.delete(l),i.size===0&&n&&(n(),n=null)}}return{set:r,update:s,subscribe:o}}const wa=$5(void 0);window.metaflow_card_update=e=>(wa==null||wa.update(t=>{const n={...t};return Object.values(e).forEach(i=>(n==null?void 0:n.components)&&S5(n.components,i)),n}),!0);const Bz=(e,t)=>{e.data&&(e.data=JSON.parse(JSON.stringify(t.data))),JSON.stringify(t.spec)===JSON.stringify(e.spec)||(e.spec=JSON.parse(JSON.stringify(t.spec)))},S5=(e,t)=>{const n=e.findIndex(i=>t.id===(i==null?void 0:i.id));n>-1?e[n].type=="vegaChart"?Bz(e[n],t):Object.assign(e[n],t):e.forEach(i=>{var r;(i.type==="section"||i.type==="page")&&((r=i==null?void 0:i.contents)!=null&&r.length)&&S5(i.contents,t)})},Uz=e=>{try{const t=JSON.parse(atob(window.__MF_DATA__[e]));wa.set(t)}catch{fetch("/card-example.json").then(n=>n.json()).then(n=>{wa.set(n)}).catch(console.error)}},Hc=$5(void 0),F5=e=>{const t={};if(!e)return t;function n(i,r=[]){var s;if(i.type==="page"){const o=[];t[i.title]=o,(s=i==null?void 0:i.contents)==null||s.forEach(a=>n(a,o))}i.type==="section"&&i.title&&r.push(i.title)}return e==null||e.forEach(i=>n(i)),t},mo=(e,t)=>e/16,jz=e=>{const t=e.split("/");return{flowname:t[0],runid:t[1],stepname:t==null?void 0:t[2],taskid:t==null?void 0:t[3]}},qz=(e,t)=>{var n;if(e)return(n=jz(e))==null?void 0:n[t]},Wz=e=>e.scrollHeight>e.clientHeight||e.scrollWidth>e.clientWidth;function Hz(e){let t,n,i,r,s,o,a,u;return{c(){t=Pi("svg"),n=Pi("title"),i=Be("metaflow_logo_horizontal"),r=Pi("g"),s=Pi("g"),o=Pi("g"),a=Pi("path"),u=Pi("path"),M(a,"d","M223.990273,66.33 C223.515273,61.851 222.686273,57.512 221.505273,53.33 C220.325273,49.148 218.795273,45.122 216.916273,41.271 C212.845273,32.921 207.254273,25.587 200.268273,19.422 C199.270273,18.541 198.243273,17.684 197.189273,16.851 C191.255273,12.166 184.481273,8.355 177.253273,5.55 C174.156273,4.347 170.975273,3.33 167.741273,2.508 C161.273273,0.863 154.593273,0 147.943273,0 C141.755273,0 135.332273,0.576 128.687273,1.722 C127.025273,2.01 125.350273,2.332 123.661273,2.69 C120.283273,3.406 116.851273,4.265 113.365273,5.267 C104.650273,7.769 95.6022727,11.161 86.2442727,15.433 C78.7592727,18.851 71.0762727,22.832 63.2072727,27.373 C47.9762727,36.162 35.7372727,44.969 29.3592727,49.791 C29.0692727,50.01 28.7922727,50.221 28.5262727,50.423 C26.1382727,52.244 24.7522727,53.367 24.5662727,53.519 L0.549272727,73.065 C0.191272727,73.356 0.00727272727,73.773 0,74.194 C-0.00372727273,74.403 0.0362727273,74.614 0.120272727,74.811 C0.205272727,75.008 0.334272727,75.189 0.508272727,75.341 L11.7612727,85.195 C12.1692727,85.552 12.7252727,85.651 13.2162727,85.487 C13.3792727,85.432 13.5362727,85.348 13.6762727,85.234 L35.8492727,67.382 C36.0422727,67.224 37.6152727,65.949 40.3252727,63.903 C44.1192727,61.036 50.1422727,56.656 57.7292727,51.711 C62.0642727,48.884 66.9102727,45.873 72.1412727,42.854 C100.864273,26.278 126.367273,17.874 147.943273,17.874 C148.366273,17.874 148.790273,17.892 149.213273,17.902 C149.655273,17.911 150.096273,17.911 150.538273,17.93 C153.769273,18.068 156.995273,18.463 160.170273,19.097 C164.931273,20.049 169.577273,21.542 173.953273,23.524 C178.328273,25.505 182.433273,27.975 186.112273,30.88 C186.771273,31.4 187.406273,31.94 188.035273,32.485 C188.913273,33.245 189.771273,34.023 190.591273,34.83 C191.998273,36.217 193.317273,37.673 194.548273,39.195 C196.395273,41.479 198.042273,43.912 199.480273,46.485 C199.960273,47.342 200.417273,48.216 200.850273,49.105 C201.112273,49.642 201.343273,50.196 201.587273,50.743 C202.231273,52.185 202.834273,53.649 203.354273,55.158 C203.712273,56.198 204.041273,57.255 204.340273,58.326 C205.836273,63.683 206.590273,69.417 206.590273,75.469 C206.590273,81.221 205.892273,86.677 204.541273,91.804 C203.617273,95.308 202.397273,98.662 200.850273,101.833 C200.417273,102.722 199.960273,103.595 199.480273,104.453 C197.562273,107.884 195.275273,111.066 192.636273,113.976 C190.657273,116.159 188.480273,118.189 186.113273,120.058 C184.553273,121.29 182.909273,122.432 181.208273,123.503 C180.313273,124.067 179.400273,124.609 178.470273,125.126 C177.462273,125.688 176.442273,126.232 175.398273,126.737 C166.961273,130.823 157.423273,133.064 147.943273,133.064 C126.367273,133.064 100.864273,124.659 72.1412727,108.084 C70.5382727,107.159 68.4382727,105.886 66.3072727,104.575 C65.0292727,103.788 63.7402727,102.986 62.5412727,102.237 C59.3442727,100.238 56.7882727,98.61 56.7882727,98.61 C61.8362727,93.901 69.3232727,87.465 78.6472727,81.047 C80.0092727,80.11 81.4192727,79.174 82.8572727,78.243 C84.1052727,77.436 85.3712727,76.63 86.6732727,75.835 C88.2042727,74.9 89.7802727,73.981 91.3822727,73.074 C93.0482727,72.131 94.7512727,71.207 96.4902727,70.307 C111.473273,62.55 129.094273,56.602 147.943273,56.602 C151.750273,56.602 157.745273,57.825 162.114273,61.276 C162.300273,61.422 162.489273,61.578 162.677273,61.74 C163.337273,62.305 164.006273,62.966 164.634273,63.78 C164.957273,64.198 165.269273,64.657 165.564273,65.162 C166.006273,65.92 166.409273,66.782 166.750273,67.775 C166.891273,68.185 167.016273,68.627 167.134273,69.083 C167.586273,70.833 167.863273,72.924 167.863273,75.469 C167.863273,78.552 167.460273,80.974 166.824273,82.92 C166.578273,83.674 166.300273,84.363 165.992273,84.983 C165.855273,85.259 165.711273,85.524 165.564273,85.776 C165.376273,86.099 165.178273,86.396 164.977273,86.682 C164.631273,87.175 164.269273,87.618 163.900273,88.018 C163.730273,88.202 163.559273,88.379 163.387273,88.546 C162.962273,88.96 162.534273,89.331 162.114273,89.662 C157.745273,93.112 151.750273,94.337 147.943273,94.337 C144.485273,94.337 140.682273,93.926 136.589273,93.121 C133.860273,92.584 131.003273,91.871 128.033273,90.987 C123.579273,89.662 118.873273,87.952 113.970273,85.872 C113.768273,85.786 113.552273,85.747 113.336273,85.753 C113.122273,85.76 112.908273,85.813 112.713273,85.912 C106.990273,88.816 101.641273,91.995 96.7462727,95.223 C96.6232727,95.304 96.5182727,95.397 96.4302727,95.5 C95.8122727,96.22 96.0172727,97.397 96.9492727,97.822 L102.445273,100.328 C104.606273,101.314 106.737273,102.238 108.835273,103.102 C110.934273,103.966 113.001273,104.77 115.035273,105.511 C118.086273,106.624 121.064273,107.599 123.965273,108.436 C127.834273,109.551 131.567273,110.421 135.157273,111.043 C139.646273,111.82 143.912273,112.211 147.943273,112.211 C148.367273,112.211 148.923273,112.201 149.591273,112.169 C149.925273,112.153 150.287273,112.131 150.674273,112.102 C155.712273,111.724 165.055273,110.114 173.190273,103.691 C173.547273,103.41 173.869273,103.105 174.210273,102.813 C175.324273,101.86 176.381273,100.866 177.333273,99.8 C177.470273,99.648 177.590273,99.485 177.724273,99.331 C181.300273,95.167 183.699273,90.185 184.875273,84.406 C185.444273,81.609 185.737273,78.631 185.737273,75.469 C185.737273,63.315 181.516273,53.82 173.190273,47.247 C167.050273,42.399 160.228273,40.299 155.083273,39.395 C153.892273,39.186 152.790273,39.037 151.809273,38.938 C150.116273,38.766 148.774273,38.727 147.943273,38.727 C133.456273,38.727 118.519273,41.679 103.545273,47.5 C99.1222727,49.22 94.6912727,51.191 90.2702727,53.403 C88.7972727,54.141 87.3242727,54.905 85.8542727,55.696 C83.5092727,56.957 81.1722727,58.303 78.8382727,59.697 C77.3922727,60.562 75.9492727,61.451 74.5082727,62.366 C72.4422727,63.678 70.3802727,65.023 68.3302727,66.437 C63.8372727,69.535 59.7422727,72.63 56.0902727,75.567 C54.8732727,76.547 53.7052727,77.508 52.5882727,78.446 C48.1222727,82.2 44.4752727,85.581 41.7602727,88.226 C38.3032727,91.593 36.3592727,93.766 36.1632727,93.986 L35.8282727,94.362 L32.0332727,98.61 L30.6432727,100.164 C30.4962727,100.329 30.3932727,100.517 30.3312727,100.715 C30.1482727,101.307 30.3472727,101.981 30.8882727,102.368 L37.2812727,106.938 L37.4862727,107.083 L37.6922727,107.228 C39.8732727,108.766 42.0702727,110.277 44.2792727,111.758 C45.8422727,112.807 47.4102727,113.84 48.9832727,114.858 C51.5302727,116.508 54.0902727,118.103 56.6542727,119.665 C57.8412727,120.388 59.0282727,121.101 60.2162727,121.804 C61.2142727,122.394 62.2102727,122.989 63.2072727,123.565 C76.9772727,131.512 90.1802727,137.744 102.748273,142.242 C104.544273,142.884 106.326273,143.491 108.096273,144.063 C111.635273,145.206 115.121273,146.207 118.553273,147.067 C121.986273,147.925 125.364273,148.642 128.687273,149.215 C135.332273,150.362 141.755273,150.938 147.943273,150.938 C154.593273,150.938 161.273273,150.074 167.741273,148.43 C174.209273,146.786 180.465273,144.361 186.265273,141.238 C190.133273,139.156 193.798273,136.764 197.189273,134.087 C200.352273,131.589 203.264273,128.872 205.911273,125.949 C207.677273,124 209.325273,121.96 210.854273,119.831 C211.618273,118.766 212.353273,117.68 213.057273,116.571 C214.466273,114.356 215.753273,112.053 216.916273,109.667 C220.701273,101.906 223.073273,93.439 224.008273,84.406 C224.310273,81.485 224.465273,78.505 224.465273,75.469 C224.465273,72.364 224.306273,69.316 223.990273,66.33"),M(a,"id","Fill-1"),M(a,"fill","#146EE6"),M(u,"d","M758.389273,75.346 C752.632273,111.56 742.681273,122.23 712.102273,122.23 C710.847273,122.23 709.640273,122.207 708.464273,122.17 C708.321273,122.191 708.191273,122.23 708.028273,122.23 L637.994273,122.23 C636.795273,122.23 636.315273,121.632 636.435273,120.311 L650.585273,31.22 C650.704273,30.016 651.424273,29.417 652.623273,29.417 L667.852273,29.417 C669.050273,29.417 669.530273,30.016 669.410273,31.22 L657.659273,105.802 L714.249273,105.802 L714.249273,105.787 C714.410273,105.794 714.568273,105.802 714.741273,105.802 C718.878273,105.802 722.250273,105.351 725.040273,104.313 C726.434273,103.794 727.684273,103.129 728.810273,102.298 C729.373273,101.884 729.905273,101.426 730.410273,100.927 C734.951273,96.431 737.231273,88.43 739.322273,75.346 C739.328273,75.312 739.331273,75.282 739.337273,75.25 C739.642273,73.311 739.896273,71.474 740.130273,69.679 C740.203273,69.116 740.272273,68.557 740.338273,68.008 C740.412273,67.392 740.461273,66.821 740.525273,66.222 C742.136273,49.927 738.622273,44.525 724.454273,44.525 C723.419273,44.525 722.433273,44.554 721.490273,44.613 C708.297273,45.444 703.831273,52.303 700.461273,71.126 C700.220273,72.472 699.984273,73.877 699.752273,75.346 C699.483273,77.027 699.255273,78.6 699.052273,80.115 C698.993273,80.545 698.948273,80.946 698.895273,81.361 C698.757273,82.465 698.638273,83.528 698.540273,84.544 C698.502273,84.943 698.466273,85.334 698.434273,85.72 C698.344273,86.815 698.281273,87.856 698.246273,88.847 C698.238273,89.049 698.224273,89.269 698.219273,89.469 C698.161273,91.88 698.289273,93.972 698.621273,95.782 C698.649273,95.941 698.686273,96.089 698.717273,96.246 C698.874273,96.992 699.067273,97.689 699.301273,98.337 C699.346273,98.464 699.390273,98.594 699.439273,98.718 C700.039273,100.231 700.864273,101.478 701.963273,102.469 C702.263273,102.738 702.586273,102.987 702.925273,103.22 L679.436273,103.22 C679.393273,102.969 679.343273,102.727 679.305273,102.471 L679.304273,102.471 C679.304273,102.467 679.304273,102.462 679.303273,102.459 C679.259273,102.17 679.236273,101.854 679.198273,101.558 C679.083273,100.634 678.995273,99.671 678.934273,98.674 C678.908273,98.258 678.879273,97.845 678.862273,97.419 C678.816273,96.174 678.804273,94.876 678.832273,93.518 C678.840273,93.114 678.861273,92.69 678.876273,92.276 C678.920273,91.042 678.991273,89.765 679.092273,88.441 C679.117273,88.109 679.134273,87.79 679.162273,87.452 C679.299273,85.836 679.483273,84.137 679.698273,82.382 C679.750273,81.957 679.807273,81.518 679.863273,81.084 C680.104273,79.238 680.369273,77.344 680.687273,75.346 C681.046273,73.067 681.423273,70.889 681.819273,68.808 C687.040273,41.397 695.809273,30.748 717.267273,28.554 C720.250273,28.25 723.472273,28.103 726.971273,28.103 C726.972273,28.103 726.972273,28.103 726.972273,28.103 C747.994273,28.103 757.680273,33.202 759.811273,48.236 C760.779273,55.067 760.187273,63.953 758.389273,75.346 Z M894.023273,31.336 L866.923273,108.56 C863.472273,118.182 861.113273,121.41 854.499273,122.41 C852.379273,122.733 849.831273,122.828 846.659273,122.828 C831.670273,122.828 830.350273,121.267 829.392273,108.56 L825.794273,63.232 L825.794273,63.231 L807.928273,108.56 C804.255273,117.613 802.201273,120.996 795.961273,122.202 C793.442273,122.687 790.260273,122.829 785.985273,122.829 C772.914273,122.829 770.756273,121.267 770.396273,108.56 L767.638273,31.337 C767.638273,29.899 768.238273,29.417 769.557273,29.417 L785.385273,29.417 C786.464273,29.417 786.704273,29.899 786.824273,31.337 L788.895273,100.572 L788.895273,100.571 C789.054273,103.091 789.563273,103.641 791.021273,103.641 C792.939273,103.641 793.419273,103.042 794.618273,100.043 L820.758273,34.576 C821.358273,33.132 822.437273,32.657 823.516273,32.657 L837.665273,32.657 C838.519273,32.657 839.279273,32.977 839.626273,33.817 C839.718273,34.038 839.799273,34.274 839.824273,34.576 L845.220273,100.043 C845.460273,103.042 845.819273,103.641 847.738273,103.641 C849.297273,103.641 849.896273,103.042 850.976273,100.043 L874.838273,31.336 C875.317273,29.898 875.677273,29.417 876.756273,29.417 L892.584273,29.417 C893.903273,29.417 894.383273,29.898 894.023273,31.336 Z M362.708273,31.219 L357.192273,120.311 C357.192273,121.632 356.353273,122.23 355.154273,122.23 L339.926273,122.23 C338.726273,122.23 338.366273,121.632 338.366273,120.311 L342.324273,62.756 L311.986273,117.551 C311.386273,118.749 310.428273,119.348 309.229273,119.348 L297.837273,119.348 C296.758273,119.348 296.038273,118.749 295.560273,117.551 L282.851273,62.767 L282.848273,62.755 L268.339273,120.31 C268.212273,121.009 267.974273,121.492 267.612273,121.807 C267.288273,122.085 266.865273,122.23 266.301273,122.23 L251.073273,122.23 C249.874273,122.23 249.273273,121.632 249.514273,120.31 L272.296273,31.336 C272.537273,30.138 272.897273,29.417 274.095273,29.417 L288.605273,29.417 C291.236273,29.417 292.726273,29.895 293.682273,31.379 C294.120273,32.059 294.457273,32.928 294.720273,34.095 L307.790273,92.489 L339.326273,34.095 C341.485273,30.256 343.043273,29.299 346.880273,29.299 L361.389273,29.299 C362.376273,29.299 362.682273,30.684 362.682273,30.684 C362.682273,30.684 362.708273,31.029 362.708273,31.219 Z M501.706273,31.219 L499.667273,44.049 C499.547273,45.246 498.708273,45.845 497.509273,45.845 L472.448273,45.845 L460.696273,120.31 C460.457273,121.632 459.738273,122.23 458.538273,122.23 L443.309273,122.23 C442.111273,122.23 441.631273,121.632 441.870273,120.31 L453.622273,45.845 L394.820273,45.845 L391.224273,68.507 L391.224273,68.508 L430.555273,68.508 C431.754273,68.508 432.353273,69.106 432.234273,70.31 L430.196273,82.542 C430.076273,83.738 429.236273,84.338 428.038273,84.338 L388.706273,84.338 L385.349273,105.801 L428.397273,105.801 C429.596273,105.801 430.076273,106.4 429.955273,107.597 L427.797273,120.428 C427.676273,121.632 426.958273,122.23 425.759273,122.23 L365.683273,122.23 C364.484273,122.23 364.004273,121.632 364.124273,120.31 L378.273273,31.219 C378.393273,30.015 379.112273,29.417 380.313273,29.417 L500.147273,29.417 C501.346273,29.417 501.826273,30.015 501.706273,31.219 Z M629.471273,70.426 L627.433273,82.659 C627.313273,83.856 626.473273,84.454 625.275273,84.454 L588.223273,84.454 L582.466273,120.311 C582.347273,121.632 581.627273,122.23 580.428273,122.23 L565.200273,122.23 C564.001273,122.23 563.522273,121.632 563.640273,120.311 L577.790273,31.219 C577.910273,30.016 578.629273,29.417 579.828273,29.417 L643.004273,29.417 C644.202273,29.417 644.802273,30.016 644.682273,31.219 L642.644273,44.05 C642.403273,45.247 641.685273,45.846 640.486273,45.846 L594.337273,45.846 L590.741273,68.631 L627.793273,68.631 C628.991273,68.631 629.592273,69.23 629.471273,70.426 Z M388.706273,84.338 L388.712273,84.338 L388.309273,86.876 L388.706273,84.338 Z M510.726273,79.783 L524.396273,48.006 C525.036273,46.466 525.443273,45.589 525.990273,45.096 C526.465273,44.667 527.044273,44.525 527.993273,44.525 C530.391273,44.525 530.391273,45.124 530.751273,48.006 L534.348273,79.783 L510.726273,79.783 Z M542.334273,29.886 C539.756273,28.905 536.043273,28.702 530.511273,28.702 C516.601273,28.702 513.963273,30.016 508.208273,43.087 L474.633273,120.311 C474.154273,121.749 474.513273,122.23 475.832273,122.23 L491.060273,122.23 C492.259273,122.23 492.500273,121.749 493.099273,120.311 L504.011273,95.372 L536.026273,95.372 L539.024273,120.311 C539.144273,121.749 539.144273,122.23 540.344273,122.23 L555.572273,122.23 C556.891273,122.23 557.490273,121.749 557.490273,120.311 L548.617273,43.087 C547.658273,35.042 546.460273,31.458 542.334273,29.886 L542.334273,29.886 Z"),M(u,"id","Fill-2"),M(u,"fill","#333333"),M(o,"id","metaflow_logo_horizontal"),M(o,"transform","translate(92.930727, 93.190000)"),M(s,"id","Metaflow_Logo_Horizontal_TwoColor_Dark_RGB"),M(s,"transform","translate(-92.000000, -93.000000)"),M(r,"id","Page-1"),M(r,"stroke","none"),M(r,"stroke-width","1"),M(r,"fill","none"),M(r,"fill-rule","evenodd"),M(t,"xmlns","http://www.w3.org/2000/svg"),M(t,"xmlns:xlink","http://www.w3.org/1999/xlink"),M(t,"width","896px"),M(t,"height","152px"),M(t,"viewBox","0 0 896 152"),M(t,"version","1.1")},m(l,c){I(l,t,c),V(t,n),V(n,i),V(t,r),V(r,s),V(s,o),V(o,a),V(o,u)},d(l){l&&L(t)}}}function Gz(e){let t,n,i;return{c(){t=Pi("svg"),n=Pi("path"),i=Pi("path"),M(n,"fill-rule","evenodd"),M(n,"clip-rule","evenodd"),M(n,"d","M223.991 66.33C223.516 61.851 222.687 57.512 221.506 53.33C220.326 49.148 218.796 45.122 216.917 41.271C212.846 32.921 207.255 25.587 200.269 19.422C199.271 18.541 198.244 17.684 197.19 16.851C191.256 12.166 184.482 8.355 177.254 5.55C174.157 4.347 170.976 3.33 167.742 2.508C161.274 0.863 154.594 0 147.944 0C141.756 0 135.333 0.576 128.688 1.722C127.026 2.01 125.351 2.332 123.662 2.69C120.284 3.406 116.852 4.265 113.366 5.267C104.651 7.769 95.6025 11.161 86.2445 15.433C78.7595 18.851 71.0765 22.832 63.2075 27.373C47.9765 36.162 35.7375 44.969 29.3595 49.791C29.0695 50.01 28.7925 50.221 28.5265 50.423C26.1385 52.244 24.7525 53.367 24.5665 53.519L0.549511 73.065C0.191511 73.356 0.00751099 73.773 0.000238261 74.194C-0.00348901 74.403 0.036511 74.614 0.120511 74.811C0.205511 75.008 0.334511 75.189 0.508511 75.341L11.7615 85.195C12.1695 85.552 12.7255 85.651 13.2165 85.487C13.3795 85.432 13.5365 85.348 13.6765 85.234L35.8495 67.382C36.0425 67.224 37.6155 65.949 40.3255 63.903C44.1195 61.036 50.1425 56.656 57.7295 51.711C62.0645 48.884 66.9105 45.873 72.1415 42.854C100.865 26.278 126.368 17.874 147.944 17.874C148.367 17.874 148.791 17.892 149.214 17.902C149.656 17.911 150.097 17.911 150.539 17.93C153.77 18.068 156.996 18.463 160.171 19.097C164.932 20.049 169.578 21.542 173.954 23.524C178.329 25.505 182.434 27.975 186.113 30.88C186.772 31.4 187.407 31.94 188.036 32.485C188.914 33.245 189.772 34.023 190.592 34.83C191.999 36.217 193.318 37.673 194.549 39.195C196.396 41.479 198.043 43.912 199.481 46.485C199.961 47.342 200.418 48.216 200.851 49.105C201.113 49.642 201.344 50.196 201.588 50.743C202.232 52.185 202.835 53.649 203.355 55.158C203.713 56.198 204.042 57.255 204.341 58.326C205.837 63.683 206.591 69.417 206.591 75.469C206.591 81.221 205.893 86.677 204.542 91.804C203.618 95.308 202.398 98.662 200.851 101.833C200.418 102.722 199.961 103.595 199.481 104.453C197.563 107.884 195.276 111.066 192.637 113.976C190.658 116.159 188.481 118.189 186.114 120.058C184.554 121.29 182.91 122.432 181.209 123.503C180.314 124.067 179.401 124.609 178.471 125.126C177.463 125.688 176.443 126.232 175.399 126.737C166.962 130.823 157.424 133.064 147.944 133.064C126.368 133.064 100.865 124.659 72.1415 108.084C70.5385 107.159 68.4385 105.886 66.3075 104.575C65.0295 103.788 63.7405 102.986 62.5415 102.237C59.3445 100.238 56.7885 98.61 56.7885 98.61C61.8365 93.901 69.3235 87.465 78.6475 81.047C80.0095 80.11 81.4195 79.174 82.8575 78.243C84.1055 77.436 85.3715 76.63 86.6735 75.835C88.2045 74.9 89.7805 73.981 91.3825 73.074C93.0485 72.131 94.7515 71.207 96.4905 70.307C111.474 62.55 129.095 56.602 147.944 56.602C151.751 56.602 157.746 57.825 162.115 61.276C162.301 61.422 162.49 61.578 162.678 61.74C163.338 62.305 164.007 62.966 164.635 63.78C164.958 64.198 165.27 64.657 165.565 65.162C166.007 65.92 166.41 66.782 166.751 67.775C166.892 68.185 167.017 68.627 167.135 69.083C167.587 70.833 167.864 72.924 167.864 75.469C167.864 78.552 167.461 80.974 166.825 82.92C166.579 83.674 166.301 84.363 165.993 84.983C165.856 85.259 165.712 85.524 165.565 85.776C165.377 86.099 165.179 86.396 164.978 86.682C164.632 87.175 164.27 87.618 163.901 88.018C163.731 88.202 163.56 88.379 163.388 88.546C162.963 88.96 162.535 89.331 162.115 89.662C157.746 93.112 151.751 94.337 147.944 94.337C144.486 94.337 140.683 93.926 136.59 93.121C133.861 92.584 131.004 91.871 128.034 90.987C123.58 89.662 118.874 87.952 113.971 85.872C113.769 85.786 113.553 85.747 113.337 85.753C113.123 85.76 112.909 85.813 112.714 85.912C106.991 88.816 101.642 91.995 96.7465 95.223C96.6235 95.304 96.5185 95.397 96.4305 95.5C95.8125 96.22 96.0175 97.397 96.9495 97.822L102.446 100.328C104.607 101.314 106.738 102.238 108.836 103.102C110.935 103.966 113.002 104.77 115.036 105.511C118.087 106.624 121.065 107.599 123.966 108.436C127.835 109.551 131.568 110.421 135.158 111.043C139.647 111.82 143.913 112.211 147.944 112.211C148.368 112.211 148.924 112.201 149.592 112.169C149.926 112.153 150.288 112.131 150.675 112.102C155.713 111.724 165.056 110.114 173.191 103.691C173.548 103.41 173.87 103.105 174.211 102.813C175.325 101.86 176.382 100.866 177.334 99.8C177.471 99.648 177.591 99.485 177.725 99.331C181.301 95.167 183.7 90.185 184.876 84.406C185.445 81.609 185.738 78.631 185.738 75.469C185.738 63.315 181.517 53.82 173.191 47.247C167.051 42.399 160.229 40.299 155.084 39.395C153.893 39.186 152.791 39.037 151.81 38.938C150.117 38.766 148.775 38.727 147.944 38.727C133.457 38.727 118.52 41.679 103.546 47.5C99.1225 49.22 94.6915 51.191 90.2705 53.403C88.7975 54.141 87.3245 54.905 85.8545 55.696C83.5095 56.957 81.1725 58.303 78.8385 59.697C77.3925 60.562 75.9495 61.451 74.5085 62.366C72.4425 63.678 70.3805 65.023 68.3305 66.437C63.8375 69.535 59.7425 72.63 56.0905 75.567C54.8735 76.547 53.7055 77.508 52.5885 78.446C48.1225 82.2 44.4755 85.581 41.7605 88.226C38.3035 91.593 36.3595 93.766 36.1635 93.986L35.8285 94.362L32.0335 98.61L30.6435 100.164C30.4965 100.329 30.3935 100.517 30.3315 100.715C30.1485 101.307 30.3475 101.981 30.8885 102.368L37.2815 106.938L37.4865 107.083L37.6925 107.228C39.8735 108.766 42.0705 110.277 44.2795 111.758C45.8425 112.807 47.4105 113.84 48.9835 114.858C51.5305 116.508 54.0905 118.103 56.6545 119.665C57.8415 120.388 59.0285 121.101 60.2165 121.804C61.2145 122.394 62.2105 122.989 63.2075 123.565C76.9775 131.512 90.1805 137.744 102.749 142.242C104.545 142.884 106.327 143.491 108.097 144.063C111.636 145.206 115.122 146.207 118.554 147.067C121.987 147.925 125.365 148.642 128.688 149.215C135.333 150.362 141.756 150.938 147.944 150.938C154.594 150.938 161.274 150.074 167.742 148.43C174.21 146.786 180.466 144.361 186.266 141.238C190.134 139.156 193.799 136.764 197.19 134.087C200.353 131.589 203.265 128.872 205.912 125.949C207.678 124 209.326 121.96 210.855 119.831C211.619 118.766 212.354 117.68 213.058 116.571C214.467 114.356 215.754 112.053 216.917 109.667C220.702 101.906 223.074 93.439 224.009 84.406C224.311 81.485 224.466 78.505 224.466 75.469C224.466 72.364 224.307 69.316 223.991 66.33Z"),M(n,"fill","#146EE6"),M(i,"fill-rule","evenodd"),M(i,"clip-rule","evenodd"),M(i,"d","M758.39 75.346C752.633 111.56 742.682 122.23 712.103 122.23C710.848 122.23 709.641 122.207 708.465 122.17C708.322 122.191 708.192 122.23 708.029 122.23H637.995C636.796 122.23 636.316 121.632 636.436 120.311L650.586 31.22C650.705 30.016 651.425 29.417 652.624 29.417H667.853C669.051 29.417 669.531 30.016 669.411 31.22L657.66 105.802H714.25V105.787C714.411 105.794 714.569 105.802 714.742 105.802C718.879 105.802 722.251 105.351 725.041 104.313C726.435 103.794 727.685 103.129 728.811 102.298C729.374 101.884 729.906 101.426 730.411 100.927C734.952 96.431 737.232 88.43 739.323 75.346C739.329 75.312 739.332 75.282 739.338 75.25C739.643 73.311 739.896 71.474 740.13 69.679C740.203 69.116 740.273 68.557 740.339 68.008C740.413 67.392 740.462 66.821 740.526 66.222C742.137 49.927 738.623 44.525 724.455 44.525C723.42 44.525 722.434 44.554 721.491 44.613C708.298 45.444 703.831 52.303 700.461 71.126C700.22 72.472 699.985 73.877 699.753 75.346C699.484 77.027 699.255 78.6 699.052 80.115C698.993 80.545 698.949 80.946 698.896 81.361C698.758 82.465 698.639 83.528 698.541 84.544C698.503 84.943 698.467 85.334 698.435 85.72C698.345 86.815 698.282 87.856 698.247 88.847C698.239 89.049 698.225 89.269 698.22 89.469C698.162 91.88 698.29 93.972 698.622 95.782C698.65 95.941 698.687 96.089 698.718 96.246C698.875 96.992 699.068 97.689 699.302 98.337C699.347 98.464 699.391 98.594 699.44 98.718C700.04 100.231 700.865 101.478 701.964 102.469C702.264 102.738 702.587 102.987 702.926 103.22H679.437C679.394 102.969 679.344 102.727 679.306 102.471H679.305C679.305 102.467 679.305 102.462 679.304 102.459C679.26 102.17 679.237 101.854 679.199 101.558C679.084 100.634 678.996 99.671 678.935 98.674C678.909 98.258 678.879 97.845 678.862 97.419C678.816 96.174 678.805 94.876 678.833 93.518C678.841 93.114 678.862 92.69 678.877 92.276C678.921 91.042 678.992 89.765 679.093 88.441C679.118 88.109 679.135 87.79 679.163 87.452C679.3 85.836 679.484 84.137 679.699 82.382C679.751 81.957 679.808 81.518 679.864 81.084C680.105 79.238 680.37 77.344 680.688 75.346C681.046 73.067 681.424 70.889 681.82 68.808C687.041 41.397 695.81 30.748 717.268 28.554C720.251 28.25 723.472 28.103 726.971 28.103C726.972 28.103 726.973 28.103 726.973 28.103C747.995 28.103 757.681 33.202 759.812 48.236C760.78 55.067 760.188 63.953 758.39 75.346ZM894.023 31.336L866.924 108.56C863.473 118.182 861.114 121.41 854.5 122.41C852.38 122.733 849.832 122.828 846.66 122.828C831.671 122.828 830.351 121.267 829.393 108.56L825.794 63.232V63.231L807.929 108.56C804.256 117.613 802.201 120.996 795.961 122.202C793.442 122.687 790.261 122.829 785.986 122.829C772.915 122.829 770.757 121.267 770.397 108.56L767.638 31.337C767.638 29.899 768.238 29.417 769.557 29.417H785.385C786.464 29.417 786.705 29.899 786.825 31.337L788.896 100.572V100.571C789.055 103.091 789.564 103.641 791.022 103.641C792.94 103.641 793.42 103.042 794.619 100.043L820.759 34.576C821.359 33.132 822.438 32.657 823.517 32.657H837.666C838.52 32.657 839.28 32.977 839.627 33.817C839.719 34.038 839.8 34.274 839.825 34.576L845.221 100.043C845.461 103.042 845.82 103.641 847.739 103.641C849.298 103.641 849.897 103.042 850.977 100.043L874.839 31.336C875.318 29.898 875.678 29.417 876.757 29.417H892.585C893.904 29.417 894.383 29.898 894.023 31.336ZM362.709 31.219L357.193 120.311C357.193 121.632 356.354 122.23 355.155 122.23H339.927C338.727 122.23 338.367 121.632 338.367 120.311L342.325 62.756L311.987 117.551C311.387 118.749 310.429 119.348 309.23 119.348H297.838C296.759 119.348 296.039 118.749 295.561 117.551L282.852 62.767L282.849 62.755L268.34 120.31C268.213 121.009 267.975 121.492 267.613 121.807C267.289 122.085 266.866 122.23 266.302 122.23H251.074C249.875 122.23 249.274 121.632 249.515 120.31L272.297 31.336C272.538 30.138 272.898 29.417 274.096 29.417H288.606C291.237 29.417 292.727 29.895 293.683 31.379C294.121 32.059 294.458 32.928 294.721 34.095L307.791 92.489L339.327 34.095C341.486 30.256 343.044 29.299 346.881 29.299H361.39C362.377 29.299 362.683 30.684 362.683 30.684C362.683 30.684 362.709 31.029 362.709 31.219ZM501.707 31.219L499.668 44.049C499.548 45.246 498.709 45.845 497.51 45.845H472.449L460.697 120.31C460.458 121.632 459.739 122.23 458.539 122.23H443.31C442.112 122.23 441.632 121.632 441.871 120.31L453.623 45.845H394.821L391.225 68.507V68.508H430.556C431.755 68.508 432.354 69.106 432.235 70.31L430.197 82.542C430.077 83.738 429.237 84.338 428.039 84.338H388.707L385.35 105.801H428.398C429.597 105.801 430.077 106.4 429.956 107.597L427.798 120.428C427.677 121.632 426.959 122.23 425.76 122.23H365.684C364.485 122.23 364.005 121.632 364.125 120.31L378.274 31.219C378.394 30.015 379.113 29.417 380.314 29.417H500.148C501.347 29.417 501.827 30.015 501.707 31.219ZM629.471 70.426L627.434 82.659C627.314 83.856 626.474 84.454 625.276 84.454H588.224L582.466 120.311C582.347 121.632 581.628 122.23 580.429 122.23H565.201C564.002 122.23 563.523 121.632 563.641 120.311L577.791 31.219C577.911 30.016 578.629 29.417 579.828 29.417H643.005C644.203 29.417 644.802 30.016 644.682 31.219L642.645 44.05C642.404 45.247 641.686 45.846 640.487 45.846H594.338L590.742 68.631H627.794C628.992 68.631 629.592 69.23 629.471 70.426ZM388.707 84.338H388.713L388.31 86.876L388.707 84.338ZM510.727 79.783L524.397 48.006C525.037 46.466 525.444 45.589 525.991 45.096C526.466 44.667 527.045 44.525 527.994 44.525C530.392 44.525 530.392 45.124 530.752 48.006L534.349 79.783H510.727ZM542.335 29.886C539.757 28.905 536.044 28.702 530.512 28.702C516.602 28.702 513.964 30.016 508.209 43.087L474.634 120.311C474.155 121.749 474.514 122.23 475.833 122.23H491.061C492.26 122.23 492.501 121.749 493.1 120.311L504.012 95.372H536.026L539.025 120.311C539.145 121.749 539.145 122.23 540.345 122.23H555.573C556.892 122.23 557.491 121.749 557.491 120.311L548.617 43.087C547.658 35.042 546.461 31.458 542.335 29.886Z"),M(i,"fill","white"),M(t,"width","895"),M(t,"height","151"),M(t,"viewBox","0 0 895 151"),M(t,"fill","none"),M(t,"xmlns","http://www.w3.org/2000/svg")},m(r,s){I(r,t,s),V(t,n),V(t,i)},d(r){r&&L(t)}}}function Vz(e){let t;function n(s,o){return s[0]?Gz:Hz}let i=n(e),r=i(e);return{c(){r.c(),t=Ne()},m(s,o){r.m(s,o),I(s,t,o)},p(s,[o]){i!==(i=n(s))&&(r.d(1),r=i(s),r&&(r.c(),r.m(t.parentNode,t)))},i:ie,o:ie,d(s){s&&L(t),r.d(s)}}}function Yz(e,t,n){let{light:i=!1}=t;return e.$$set=r=>{"light"in r&&n(0,i=r.light)},[i]}class Xz extends xe{constructor(t){super(),ve(this,t,Yz,Vz,ye,{light:0})}}function Kz(e){let t,n,i,r,s,o;r=new Xz({});const a=e[1].default,u=mt(a,e,e[0],null);return{c(){t=G("aside"),n=G("div"),i=G("div"),he(r.$$.fragment),s=He(),u&&u.c(),M(i,"class","logoContainer"),M(t,"class","svelte-1okdv0e")},m(l,c){I(l,t,c),V(t,n),V(n,i),ce(r,i,null),V(n,s),u&&u.m(n,null),o=!0},p(l,[c]){u&&u.p&&(!o||c&1)&&bt(u,a,l,l[0],o?yt(a,l[0],c,null):vt(l[0]),null)},i(l){o||(D(r.$$.fragment,l),D(u,l),o=!0)},o(l){N(r.$$.fragment,l),N(u,l),o=!1},d(l){l&&L(t),fe(r),u&&u.d(l)}}}function Zz(e,t,n){let{$$slots:i={},$$scope:r}=t;return e.$$set=s=>{"$$scope"in s&&n(0,r=s.$$scope)},[r,i]}class Jz extends xe{constructor(t){super(),ve(this,t,Zz,Kz,ye,{})}}function D5(e){let t,n;return{c(){t=G("td"),n=Be(e[0]),M(t,"class","idCell svelte-pt8vzv"),M(t,"data-component","artifact-row")},m(i,r){I(i,t,r),V(t,n)},p(i,r){r&1&&ft(n,i[0])},d(i){i&&L(t)}}}function Qz(e){let t,n,i,r,s=e[1].data+"",o,a,u=e[0]!==null&&D5(e);return{c(){t=G("tr"),u&&u.c(),n=He(),i=G("td"),r=G("code"),o=Be(s),M(r,"class","mono"),M(i,"class","codeCell svelte-pt8vzv"),M(i,"colspan",a=e[0]===null?2:1),M(i,"data-component","artifact-row")},m(l,c){I(l,t,c),u&&u.m(t,null),V(t,n),V(t,i),V(i,r),V(r,o),e[3](r)},p(l,[c]){l[0]!==null?u?u.p(l,c):(u=D5(l),u.c(),u.m(t,n)):u&&(u.d(1),u=null),c&2&&s!==(s=l[1].data+"")&&ft(o,s),c&1&&a!==(a=l[0]===null?2:1)&&M(i,"colspan",a)},i:ie,o:ie,d(l){l&&L(t),u&&u.d(),e[3](null)}}}function eB(e,t,n){let{id:i}=t,{artifact:r}=t,s;function o(){var u;s&&!s.classList.contains("language-python")&&typeof window<"u"&&((u=window==null?void 0:window.Prism)==null||u.highlightElement(s))}function a(u){zi[u?"unshift":"push"](()=>{s=u,n(2,s)})}return e.$$set=u=>{"id"in u&&n(0,i=u.id),"artifact"in u&&n(1,r=u.artifact)},e.$$.update=()=>{e.$$.dirty&4&&s&&o()},[i,r,s,a]}class tB extends xe{constructor(t){super(),ve(this,t,eB,Qz,ye,{id:0,artifact:1})}}function T5(e,t,n){const i=e.slice();return i[2]=t[n],i}function M5(e){let t,n;return t=new tB({props:{id:e[2].name,artifact:e[2]}}),{c(){he(t.$$.fragment)},m(i,r){ce(t,i,r),n=!0},p:ie,i(i){n||(D(t.$$.fragment,i),n=!0)},o(i){N(t.$$.fragment,i),n=!1},d(i){fe(t,i)}}}function nB(e){let t,n,i,r=Ue(e[0]),s=[];for(let a=0;aN(s[a],1,1,()=>{s[a]=null});return{c(){t=G("div"),n=G("table");for(let a=0;a{if(s.name&&o.name){if(s.name>o.name)return 1;if(s.name{"componentData"in s&&n(1,i=s.componentData)},[r,i]}class N5 extends xe{constructor(t){super(),ve(this,t,iB,nB,ye,{componentData:1})}}function rB(e){let t,n,i;return{c(){t=G("div"),n=He(),i=G("div"),M(t,"class","path topLeft svelte-1hyaq5f"),M(i,"class","path bottomRight svelte-1hyaq5f")},m(r,s){I(r,t,s),I(r,n,s),I(r,i,s)},d(r){r&&(L(t),L(n),L(i))}}}function sB(e){let t;return{c(){t=G("div"),M(t,"class","path straightLine svelte-1hyaq5f")},m(n,i){I(n,t,i)},d(n){n&&L(t)}}}function oB(e){let t;function n(s,o){return s[5]?sB:rB}let i=n(e),r=i(e);return{c(){t=G("div"),r.c(),M(t,"class","connectorwrapper svelte-1hyaq5f"),_i(t,"top",e[1]+"rem"),_i(t,"left",e[0]+"rem"),_i(t,"width",e[3]+"rem"),_i(t,"height",e[4]+"rem"),ni(t,"flip",e[2])},m(s,o){I(s,t,o),r.m(t,null)},p(s,[o]){i!==(i=n(s))&&(r.d(1),r=i(s),r&&(r.c(),r.m(t,null))),o&2&&_i(t,"top",s[1]+"rem"),o&1&&_i(t,"left",s[0]+"rem"),o&8&&_i(t,"width",s[3]+"rem"),o&16&&_i(t,"height",s[4]+"rem"),o&4&&ni(t,"flip",s[2])},i:ie,o:ie,d(s){s&&L(t),r.d()}}}const Ea=.5;function aB(e,t,n){let{top:i=0}=t,{left:r=0}=t,{bottom:s=0}=t,{right:o=0}=t,a,u,l,c=!1;return e.$$set=f=>{"top"in f&&n(1,i=f.top),"left"in f&&n(0,r=f.left),"bottom"in f&&n(7,s=f.bottom),"right"in f&&n(6,o=f.right)},e.$$.update=()=>{e.$$.dirty&207&&(n(2,a=o-r<0),n(3,u=Math.abs(o-r)),u<=Ea?(n(3,u=Ea),n(5,c=!0),n(0,r-=Ea/2)):(a?(n(0,r+=Ea/2),n(6,o-=Ea/2)):(n(0,r-=Ea/2),n(6,o+=Ea/2)),n(3,u=Math.abs(o-r))),n(4,l=s-i))},[r,i,a,u,l,c,o,s]}class uB extends xe{constructor(t){super(),ve(this,t,aB,oB,ye,{top:1,left:0,bottom:7,right:6})}}function R5(e,t,n){const i=e.slice();return i[4]=t[n],i}function O5(e){let t,n;return t=new uB({props:{top:mo(e[4].top),left:mo(e[4].left),bottom:mo(e[4].bottom),right:mo(e[4].right)}}),{c(){he(t.$$.fragment)},m(i,r){ce(t,i,r),n=!0},p(i,r){const s={};r&1&&(s.top=mo(i[4].top)),r&1&&(s.left=mo(i[4].left)),r&1&&(s.bottom=mo(i[4].bottom)),r&1&&(s.right=mo(i[4].right)),t.$set(s)},i(i){n||(D(t.$$.fragment,i),n=!0)},o(i){N(t.$$.fragment,i),n=!1},d(i){fe(t,i)}}}function lB(e){let t,n,i=Ue(e[0]),r=[];for(let o=0;oN(r[o],1,1,()=>{r[o]=null});return{c(){for(let o=0;o{"steps"in a&&n(1,i=a.steps),"boxes"in a&&n(2,r=a.boxes),"container"in a&&n(3,s=a.container)},e.$$.update=()=>{if(e.$$.dirty&15){n(0,o=[]);const a=s.getBoundingClientRect(),u=a.top,l=a.left;r&&Object.keys(i).forEach(c=>{var h;const f=i[c],d=r[c].getBoundingClientRect();(h=f.next)==null||h.forEach(p=>{const g=r[p].getBoundingClientRect(),m={top:d.bottom-u,left:d.left-l+d.width/2,bottom:g.top-u,right:g.left-l+g.width/2};n(0,o=[...o,m])})})}},[o,i,r,s]}class fB extends xe{constructor(t){super(),ve(this,t,cB,lB,ye,{steps:1,boxes:2,container:3})}}const L5="currentStep";function I5(e,t,n){const i=e.slice();return i[16]=t[n],i[18]=n,i}function P5(e){let t,n,i;return{c(){t=G("div"),n=Be("x"),i=Be(e[6]),M(t,"class","levelstoshow svelte-117ceti")},m(r,s){I(r,t,s),V(t,n),V(t,i)},p(r,s){s&64&&ft(i,r[6])},d(r){r&&L(t)}}}function z5(e){let t,n;return{c(){t=G("div"),M(t,"class",n="level rectangle "+e[16]+" svelte-117ceti"),_i(t,"z-index",(e[18]+1)*-1),_i(t,"top",(e[18]+1)*B5+"px"),_i(t,"left",(e[18]+1)*B5+"px")},m(i,r){I(i,t,r)},p(i,r){r&128&&n!==(n="level rectangle "+i[16]+" svelte-117ceti")&&M(t,"class",n)},d(i){i&&L(t)}}}function dB(e){let t,n,i,r,s,o,a,u,l=e[2].doc+"",c,f,d,h=e[6]&&P5(e),p=Ue(e[7]),g=[];for(let m=0;m1&&(c=new Intl.NumberFormat().format(r.num_possible_tasks)),s=r.num_possible_tasks-1,Object.keys(f).forEach(v=>{const x=Number.parseInt(v);r.num_possible_tasks&&r.num_possible_tasks>x&&n(11,s=f[x])})):s*=hB,s>0&&(d=new Array(s).fill("")),d=d.map((v,x)=>{if(r.num_possible_tasks){const _=r.num_failed??0,E=r.successful_tasks??0;return(_-1)/r.num_possible_tasks>=(x+1)/d.length?"error":(_+E)/r.num_possible_tasks>=(x+1)/d.length?"success":"running"}return""});const h=C5(L5),p=i===h;r.failed||r.num_failed?u=!0:(r.num_possible_tasks??0)>(r.successful_tasks??0)?l=!0:r.num_possible_tasks&&r.num_possible_tasks===r.successful_tasks&&(a=!0);let g,m=!1;Wc(()=>{n(9,m=Wz(g))});function y(v){zi[v?"unshift":"push"](()=>{g=v,n(8,g)})}function b(v){zi[v?"unshift":"push"](()=>{o=v,n(0,o)})}return e.$$set=v=>{"name"in v&&n(1,i=v.name),"step"in v&&n(2,r=v.step),"numLevels"in v&&n(11,s=v.numLevels),"el"in v&&n(0,o=v.el)},[o,i,r,a,u,l,c,d,g,m,p,s,y,b]}let gB=class extends xe{constructor(t){super(),ve(this,t,pB,dB,ye,{name:1,step:2,numLevels:11,el:0})}};function U5(e,t,n){const i=e.slice();return i[10]=t[n],i}function mB(e){let t,n,i,r,s,o;function a(f){e[8](f)}let u={name:e[2],numLevels:e[3],step:e[5]};e[4]!==void 0&&(u.el=e[4]),n=new gB({props:u}),zi.push(()=>h2(n,"el",a));let l=e[7]&&yB(e),c=e[5].box_ends&&bB(e);return{c(){t=G("div"),he(n.$$.fragment),r=He(),l&&l.c(),s=He(),c&&c.c(),M(t,"class","stepwrapper svelte-18aex7a")},m(f,d){I(f,t,d),ce(n,t,null),V(t,r),l&&l.m(t,null),V(t,s),c&&c.m(t,null),o=!0},p(f,d){const h={};d&4&&(h.name=f[2]),d&8&&(h.numLevels=f[3]),!i&&d&16&&(i=!0,h.el=f[4],f2(()=>i=!1)),n.$set(h),f[7]&&l.p(f,d),f[5].box_ends&&c.p(f,d)},i(f){o||(D(n.$$.fragment,f),D(l),D(c),o=!0)},o(f){N(n.$$.fragment,f),N(l),N(c),o=!1},d(f){f&&L(t),fe(n),l&&l.d(),c&&c.d()}}}function yB(e){let t,n,i,r,s=Ue(e[5].next),o=[];for(let u=0;uN(o[u],1,1,()=>{o[u]=null});return{c(){t=G("div"),n=He(),i=G("div");for(let u=0;u{a&&n(0,o[r]=a,o)});let l=i[r];l||console.warn("step ",r," not found");const c=(l==null?void 0:l.type)==="foreach"?s+1:(l==null?void 0:l.type)==="join"?s-1:s;let f=(h=l==null?void 0:l.next)==null?void 0:h.find(p=>{var g;return((g=i[p])==null?void 0:g.type)!=="join"});function d(p){a=p,n(4,a)}return e.$$set=p=>{"steps"in p&&n(1,i=p.steps),"stepName"in p&&n(2,r=p.stepName),"levels"in p&&n(3,s=p.levels),"boxes"in p&&n(0,o=p.boxes)},[o,i,r,s,a,l,c,f,d]}class p2 extends xe{constructor(t){super(),ve(this,t,xB,vB,ye,{steps:1,stepName:2,levels:3,boxes:0})}}function _B(e){let t;return{c(){t=G("p"),t.textContent="No start step"},m(n,i){I(n,t,i)},p:ie,i:ie,o:ie,d(n){n&&L(t)}}}function wB(e){let t,n,i;function r(o){e[6](o)}let s={steps:e[3],stepName:"start"};return e[0]!==void 0&&(s.boxes=e[0]),t=new p2({props:s}),zi.push(()=>h2(t,"boxes",r)),{c(){he(t.$$.fragment)},m(o,a){ce(t,o,a),i=!0},p(o,a){const u={};!n&&a&1&&(n=!0,u.boxes=o[0],f2(()=>n=!1)),t.$set(u)},i(o){i||(D(t.$$.fragment,o),i=!0)},o(o){N(t.$$.fragment,o),i=!1},d(o){fe(t,o)}}}function q5(e){let t,n;return t=new fB({props:{boxes:e[0],steps:e[3],container:e[1]}}),{c(){he(t.$$.fragment)},m(i,r){ce(t,i,r),n=!0},p(i,r){const s={};r&1&&(s.boxes=i[0]),r&2&&(s.container=i[1]),t.$set(s)},i(i){n||(D(t.$$.fragment,i),n=!0)},o(i){N(t.$$.fragment,i),n=!1},d(i){fe(t,i)}}}function EB(e){let t,n,i,r,s=!e[2]&&Object.keys(e[0]).length,o,a,u;const l=[wB,_B],c=[];function f(h,p){var g;return(g=h[3])!=null&&g.start?0:1}n=f(e),i=c[n]=l[n](e);let d=s&&q5(e);return{c(){t=G("div"),i.c(),r=He(),d&&d.c(),_i(t,"position","relative"),_i(t,"line-height","1"),M(t,"data-component","dag")},m(h,p){I(h,t,p),c[n].m(t,null),V(t,r),d&&d.m(t,null),e[7](t),o=!0,a||(u=Ku(window,"resize",e[4]),a=!0)},p(h,[p]){i.p(h,p),p&5&&(s=!h[2]&&Object.keys(h[0]).length),s?d?(d.p(h,p),p&5&&D(d,1)):(d=q5(h),d.c(),D(d,1),d.m(t,null)):d&&(Se(),N(d,1,1,()=>{d=null}),Fe())},i(h){o||(D(i),D(d),o=!0)},o(h){N(i),N(d),o=!1},d(h){h&&L(t),c[n].d(),d&&d.d(),e[7](null),a=!1,u()}}}const CB=100;function kB(e,t,n){var h;let i;Ph(e,wa,p=>n(9,i=p));let{componentData:r}=t;const{data:s}=r;let o={},a;E5(L5,qz((h=i==null?void 0:i.metadata)==null?void 0:h.pathspec,"stepname"));let u,l=!1;const c=()=>{n(2,l=!0),clearTimeout(u),u=setTimeout(()=>{n(2,l=!1)},CB)};function f(p){o=p,n(0,o)}function d(p){zi[p?"unshift":"push"](()=>{a=p,n(1,a)})}return e.$$set=p=>{"componentData"in p&&n(5,r=p.componentData)},[o,a,l,s,c,r,f,d]}class W5 extends xe{constructor(t){super(),ve(this,t,kB,EB,ye,{componentData:5})}}function AB(e){var r;let t,n=(((r=e[0])==null?void 0:r.text)||"")+"",i;return{c(){t=G("h2"),i=Be(n),M(t,"class","title svelte-117s0ws"),M(t,"data-component","title")},m(s,o){I(s,t,o),V(t,i)},p(s,[o]){var a;o&1&&n!==(n=(((a=s[0])==null?void 0:a.text)||"")+"")&&ft(i,n)},i:ie,o:ie,d(s){s&&L(t)}}}function $B(e,t,n){let{componentData:i}=t;return e.$$set=r=>{"componentData"in r&&n(0,i=r.componentData)},[i]}class H5 extends xe{constructor(t){super(),ve(this,t,$B,AB,ye,{componentData:0})}}function SB(e){var r;let t,n=(((r=e[0])==null?void 0:r.text)||"")+"",i;return{c(){t=G("p"),i=Be(n),M(t,"class","subtitle svelte-lu9pnn"),M(t,"data-component","subtitle")},m(s,o){I(s,t,o),V(t,i)},p(s,[o]){var a;o&1&&n!==(n=(((a=s[0])==null?void 0:a.text)||"")+"")&&ft(i,n)},i:ie,o:ie,d(s){s&&L(t)}}}function FB(e,t,n){let{componentData:i}=t;return e.$$set=r=>{"componentData"in r&&n(0,i=r.componentData)},[i]}class G5 extends xe{constructor(t){super(),ve(this,t,FB,SB,ye,{componentData:0})}}function V5(e){let t,n;return t=new H5({props:{componentData:{type:"title",text:e[1]}}}),{c(){he(t.$$.fragment)},m(i,r){ce(t,i,r),n=!0},p(i,r){const s={};r&2&&(s.componentData={type:"title",text:i[1]}),t.$set(s)},i(i){n||(D(t.$$.fragment,i),n=!0)},o(i){N(t.$$.fragment,i),n=!1},d(i){fe(t,i)}}}function Y5(e){let t,n;return t=new G5({props:{componentData:{type:"subtitle",text:e[0]}}}),{c(){he(t.$$.fragment)},m(i,r){ce(t,i,r),n=!0},p(i,r){const s={};r&1&&(s.componentData={type:"subtitle",text:i[0]}),t.$set(s)},i(i){n||(D(t.$$.fragment,i),n=!0)},o(i){N(t.$$.fragment,i),n=!1},d(i){fe(t,i)}}}function DB(e){let t,n,i,r=e[1]&&V5(e),s=e[0]&&Y5(e);return{c(){t=G("header"),r&&r.c(),n=He(),s&&s.c(),M(t,"class","container svelte-1ugmt5d"),M(t,"data-component","heading")},m(o,a){I(o,t,a),r&&r.m(t,null),V(t,n),s&&s.m(t,null),i=!0},p(o,[a]){o[1]?r?(r.p(o,a),a&2&&D(r,1)):(r=V5(o),r.c(),D(r,1),r.m(t,n)):r&&(Se(),N(r,1,1,()=>{r=null}),Fe()),o[0]?s?(s.p(o,a),a&1&&D(s,1)):(s=Y5(o),s.c(),D(s,1),s.m(t,null)):s&&(Se(),N(s,1,1,()=>{s=null}),Fe())},i(o){i||(D(r),D(s),i=!0)},o(o){N(r),N(s),i=!1},d(o){o&&L(t),r&&r.d(),s&&s.d()}}}function TB(e,t,n){let i,r,{componentData:s}=t;return e.$$set=o=>{"componentData"in o&&n(2,s=o.componentData)},e.$$.update=()=>{e.$$.dirty&4&&n(1,{title:i,subtitle:r}=s,i,(n(0,r),n(2,s)))},[r,i,s]}let X5=class extends xe{constructor(t){super(),ve(this,t,TB,DB,ye,{componentData:2})}};function K5(e){let t,n;return{c(){t=G("div"),n=Be(e[2]),M(t,"class","label svelte-1x96yvr")},m(i,r){I(i,t,r),V(t,n)},p(i,r){r&4&&ft(n,i[2])},d(i){i&&L(t)}}}function Z5(e){let t,n;return{c(){t=G("figcaption"),n=Be(e[1]),M(t,"class","description svelte-1x96yvr")},m(i,r){I(i,t,r),V(t,n)},p(i,r){r&2&&ft(n,i[1])},d(i){i&&L(t)}}}function MB(e){let t,n,i,r,s,o,a,u,l,c=e[2]&&K5(e),f=e[1]&&Z5(e);return{c(){t=G("figure"),n=G("div"),i=G("img"),o=He(),c&&c.c(),a=He(),f&&f.c(),Ih(i.src,r=e[3])||M(i,"src",r),M(i,"alt",s=e[2]||"image"),M(i,"class","svelte-1x96yvr"),M(n,"class","imageContainer"),M(t,"data-component","image"),M(t,"class","svelte-1x96yvr")},m(d,h){I(d,t,h),V(t,n),V(n,i),V(t,o),c&&c.m(t,null),V(t,a),f&&f.m(t,null),u||(l=Ku(t,"click",e[4]),u=!0)},p(d,[h]){h&8&&!Ih(i.src,r=d[3])&&M(i,"src",r),h&4&&s!==(s=d[2]||"image")&&M(i,"alt",s),d[2]?c?c.p(d,h):(c=K5(d),c.c(),c.m(t,a)):c&&(c.d(1),c=null),d[1]?f?f.p(d,h):(f=Z5(d),f.c(),f.m(t,null)):f&&(f.d(1),f=null)},i:ie,o:ie,d(d){d&&L(t),c&&c.d(),f&&f.d(),u=!1,l()}}}function NB(e,t,n){let i,r,s,{componentData:o}=t;const a=()=>Hc.set(o);return e.$$set=u=>{"componentData"in u&&n(0,o=u.componentData)},e.$$.update=()=>{e.$$.dirty&1&&n(3,{src:i,label:r,description:s}=o,i,(n(2,r),n(0,o)),(n(1,s),n(0,o)))},[o,s,r,i,a]}let J5=class extends xe{constructor(t){super(),ve(this,t,NB,MB,ye,{componentData:0})}};function RB(e){let t,n,i,r,s=e[0].data+"",o,a,u;return{c(){t=G("pre"),n=Be(` + `),i=G("code"),r=Be(` + `),o=Be(s),a=Be(` + `),u=Be(` +`),M(i,"class","mono language-log"),M(t,"class","log svelte-1jhmsu"),M(t,"data-component","log")},m(l,c){I(l,t,c),V(t,n),V(t,i),V(i,r),V(i,o),V(i,a),e[2](i),V(t,u)},p(l,[c]){c&1&&s!==(s=l[0].data+"")&&ft(o,s)},i:ie,o:ie,d(l){l&&L(t),e[2](null)}}}function OB(e,t,n){let{componentData:i}=t,r;function s(){var a;r&&((a=window==null?void 0:window.Prism)==null||a.highlightElement(r))}function o(a){zi[a?"unshift":"push"](()=>{r=a,n(1,r)})}return e.$$set=a=>{"componentData"in a&&n(0,i=a.componentData)},e.$$.update=()=>{e.$$.dirty&2&&r&&s()},[i,r,o]}let Q5=class extends xe{constructor(t){super(),ve(this,t,OB,RB,ye,{componentData:0})}};function LB(){const e=console.warn;console.warn=t=>{t.includes("unknown prop")||t.includes("unexpected slot")||e(t)},Wc(()=>{console.warn=e})}function e4(e,t,n){const i=e.slice();return i[18]=t[n],i}function t4(e,t,n){const i=e.slice();return i[18]=t[n],i}function n4(e,t,n){const i=e.slice();return i[10]=t[n],i}function i4(e,t,n){const i=e.slice();return i[13]=t[n],i[15]=n,i}function r4(e,t,n){const i=e.slice();return i[16]=t[n],i[15]=n,i}function s4(e,t,n){const i=e.slice();return i[7]=t[n],i}function IB(e){let t,n,i,r;const s=[UB,BB,zB],o=[];function a(u,l){return u[0]==="table"?0:u[0]==="list"?1:2}return t=a(e),n=o[t]=s[t](e),{c(){n.c(),i=Ne()},m(u,l){o[t].m(u,l),I(u,i,l),r=!0},p(u,l){let c=t;t=a(u),t===c?o[t].p(u,l):(Se(),N(o[c],1,1,()=>{o[c]=null}),Fe(),n=o[t],n?n.p(u,l):(n=o[t]=s[t](u),n.c()),D(n,1),n.m(i.parentNode,i))},i(u){r||(D(n),r=!0)},o(u){N(n),r=!1},d(u){u&&L(i),o[t].d(u)}}}function PB(e){let t,n,i=Ue(e[1]),r=[];for(let o=0;oN(r[o],1,1,()=>{r[o]=null});return{c(){for(let o=0;o{fe(l,1)}),Fe()}s?(t=Ke(s,o(a,u)),he(t.$$.fragment),D(t.$$.fragment,1),ce(t,n.parentNode,n)):t=null}else if(s){const l=u&64?wi(r,[ar(a[6])]):{};u&8388706&&(l.$$scope={dirty:u,ctx:a}),t.$set(l)}},i(a){i||(t&&D(t.$$.fragment,a),i=!0)},o(a){t&&N(t.$$.fragment,a),i=!1},d(a){a&&L(n),t&&fe(t,a)}}}function BB(e){let t,n,i,r;const s=[GB,HB],o=[];function a(u,l){return u[4]?0:1}return t=a(e),n=o[t]=s[t](e),{c(){n.c(),i=Ne()},m(u,l){o[t].m(u,l),I(u,i,l),r=!0},p(u,l){let c=t;t=a(u),t===c?o[t].p(u,l):(Se(),N(o[c],1,1,()=>{o[c]=null}),Fe(),n=o[t],n?n.p(u,l):(n=o[t]=s[t](u),n.c()),D(n,1),n.m(i.parentNode,i))},i(u){r||(D(n),r=!0)},o(u){N(n),r=!1},d(u){u&&L(i),o[t].d(u)}}}function UB(e){let t,n,i;var r=e[5].table;function s(o,a){return{props:{$$slots:{default:[iU]},$$scope:{ctx:o}}}}return r&&(t=Ke(r,s(e))),{c(){t&&he(t.$$.fragment),n=Ne()},m(o,a){t&&ce(t,o,a),I(o,n,a),i=!0},p(o,a){if(a&32&&r!==(r=o[5].table)){if(t){Se();const u=t;N(u.$$.fragment,1,0,()=>{fe(u,1)}),Fe()}r?(t=Ke(r,s(o)),he(t.$$.fragment),D(t.$$.fragment,1),ce(t,n.parentNode,n)):t=null}else if(r){const u={};a&8388716&&(u.$$scope={dirty:a,ctx:o}),t.$set(u)}},i(o){i||(t&&D(t.$$.fragment,o),i=!0)},o(o){t&&N(t.$$.fragment,o),i=!1},d(o){o&&L(n),t&&fe(t,o)}}}function jB(e){let t=e[6].raw+"",n;return{c(){n=Be(t)},m(i,r){I(i,n,r)},p(i,r){r&64&&t!==(t=i[6].raw+"")&&ft(n,t)},i:ie,o:ie,d(i){i&&L(n)}}}function qB(e){let t,n;return t=new Ca({props:{tokens:e[1],renderers:e[5]}}),{c(){he(t.$$.fragment)},m(i,r){ce(t,i,r),n=!0},p(i,r){const s={};r&2&&(s.tokens=i[1]),r&32&&(s.renderers=i[5]),t.$set(s)},i(i){n||(D(t.$$.fragment,i),n=!0)},o(i){N(t.$$.fragment,i),n=!1},d(i){fe(t,i)}}}function WB(e){let t,n,i,r;const s=[qB,jB],o=[];function a(u,l){return u[1]?0:1}return t=a(e),n=o[t]=s[t](e),{c(){n.c(),i=Ne()},m(u,l){o[t].m(u,l),I(u,i,l),r=!0},p(u,l){let c=t;t=a(u),t===c?o[t].p(u,l):(Se(),N(o[c],1,1,()=>{o[c]=null}),Fe(),n=o[t],n?n.p(u,l):(n=o[t]=s[t](u),n.c()),D(n,1),n.m(i.parentNode,i))},i(u){r||(D(n),r=!0)},o(u){N(n),r=!1},d(u){u&&L(i),o[t].d(u)}}}function HB(e){let t,n,i;const r=[{ordered:e[4]},e[6]];var s=e[5].list;function o(a,u){let l={$$slots:{default:[YB]},$$scope:{ctx:a}};for(let c=0;c{fe(l,1)}),Fe()}s?(t=Ke(s,o(a,u)),he(t.$$.fragment),D(t.$$.fragment,1),ce(t,n.parentNode,n)):t=null}else if(s){const l=u&80?wi(r,[u&16&&{ordered:a[4]},u&64&&ar(a[6])]):{};u&8388704&&(l.$$scope={dirty:u,ctx:a}),t.$set(l)}},i(a){i||(t&&D(t.$$.fragment,a),i=!0)},o(a){t&&N(t.$$.fragment,a),i=!1},d(a){a&&L(n),t&&fe(t,a)}}}function GB(e){let t,n,i;const r=[{ordered:e[4]},e[6]];var s=e[5].list;function o(a,u){let l={$$slots:{default:[KB]},$$scope:{ctx:a}};for(let c=0;c{fe(l,1)}),Fe()}s?(t=Ke(s,o(a,u)),he(t.$$.fragment),D(t.$$.fragment,1),ce(t,n.parentNode,n)):t=null}else if(s){const l=u&80?wi(r,[u&16&&{ordered:a[4]},u&64&&ar(a[6])]):{};u&8388704&&(l.$$scope={dirty:u,ctx:a}),t.$set(l)}},i(a){i||(t&&D(t.$$.fragment,a),i=!0)},o(a){t&&N(t.$$.fragment,a),i=!1},d(a){a&&L(n),t&&fe(t,a)}}}function VB(e){let t,n,i;return t=new Ca({props:{tokens:e[18].tokens,renderers:e[5]}}),{c(){he(t.$$.fragment),n=He()},m(r,s){ce(t,r,s),I(r,n,s),i=!0},p(r,s){const o={};s&64&&(o.tokens=r[18].tokens),s&32&&(o.renderers=r[5]),t.$set(o)},i(r){i||(D(t.$$.fragment,r),i=!0)},o(r){N(t.$$.fragment,r),i=!1},d(r){r&&L(n),fe(t,r)}}}function o4(e){let t,n,i;const r=[e[18]];var s=e[5].unorderedlistitem||e[5].listitem;function o(a,u){let l={$$slots:{default:[VB]},$$scope:{ctx:a}};for(let c=0;c{fe(l,1)}),Fe()}s?(t=Ke(s,o(a,u)),he(t.$$.fragment),D(t.$$.fragment,1),ce(t,n.parentNode,n)):t=null}else if(s){const l=u&64?wi(r,[ar(a[18])]):{};u&8388704&&(l.$$scope={dirty:u,ctx:a}),t.$set(l)}},i(a){i||(t&&D(t.$$.fragment,a),i=!0)},o(a){t&&N(t.$$.fragment,a),i=!1},d(a){a&&L(n),t&&fe(t,a)}}}function YB(e){let t,n,i=Ue(e[6].items),r=[];for(let o=0;oN(r[o],1,1,()=>{r[o]=null});return{c(){for(let o=0;o{fe(l,1)}),Fe()}s?(t=Ke(s,o(a,u)),he(t.$$.fragment),D(t.$$.fragment,1),ce(t,n.parentNode,n)):t=null}else if(s){const l=u&64?wi(r,[ar(a[18])]):{};u&8388704&&(l.$$scope={dirty:u,ctx:a}),t.$set(l)}},i(a){i||(t&&D(t.$$.fragment,a),i=!0)},o(a){t&&N(t.$$.fragment,a),i=!1},d(a){a&&L(n),t&&fe(t,a)}}}function KB(e){let t,n,i=Ue(e[6].items),r=[];for(let o=0;oN(r[o],1,1,()=>{r[o]=null});return{c(){for(let o=0;o{fe(u,1)}),Fe()}r?(t=Ke(r,s(o)),he(t.$$.fragment),D(t.$$.fragment,1),ce(t,n.parentNode,n)):t=null}else if(r){const u={};a&64&&(u.align=o[6].align[o[15]]||"center"),a&8388644&&(u.$$scope={dirty:a,ctx:o}),t.$set(u)}},i(o){i||(t&&D(t.$$.fragment,o),i=!0)},o(o){t&&N(t.$$.fragment,o),i=!1},d(o){o&&L(n),t&&fe(t,o)}}}function JB(e){let t,n,i=Ue(e[2]),r=[];for(let o=0;oN(r[o],1,1,()=>{r[o]=null});return{c(){for(let o=0;o{fe(u,1)}),Fe()}r?(t=Ke(r,s(o)),he(t.$$.fragment),D(t.$$.fragment,1),ce(t,n.parentNode,n)):t=null}else if(r){const u={};a&8388708&&(u.$$scope={dirty:a,ctx:o}),t.$set(u)}},i(o){i||(t&&D(t.$$.fragment,o),i=!0)},o(o){t&&N(t.$$.fragment,o),i=!1},d(o){o&&L(n),t&&fe(t,o)}}}function eU(e){let t,n;return t=new Ca({props:{tokens:e[13].tokens,renderers:e[5]}}),{c(){he(t.$$.fragment)},m(i,r){ce(t,i,r),n=!0},p(i,r){const s={};r&8&&(s.tokens=i[13].tokens),r&32&&(s.renderers=i[5]),t.$set(s)},i(i){n||(D(t.$$.fragment,i),n=!0)},o(i){N(t.$$.fragment,i),n=!1},d(i){fe(t,i)}}}function l4(e){let t,n,i;var r=e[5].tablecell;function s(o,a){return{props:{header:!1,align:o[6].align[o[15]]||"center",$$slots:{default:[eU]},$$scope:{ctx:o}}}}return r&&(t=Ke(r,s(e))),{c(){t&&he(t.$$.fragment),n=Ne()},m(o,a){t&&ce(t,o,a),I(o,n,a),i=!0},p(o,a){if(a&32&&r!==(r=o[5].tablecell)){if(t){Se();const u=t;N(u.$$.fragment,1,0,()=>{fe(u,1)}),Fe()}r?(t=Ke(r,s(o)),he(t.$$.fragment),D(t.$$.fragment,1),ce(t,n.parentNode,n)):t=null}else if(r){const u={};a&64&&(u.align=o[6].align[o[15]]||"center"),a&8388648&&(u.$$scope={dirty:a,ctx:o}),t.$set(u)}},i(o){i||(t&&D(t.$$.fragment,o),i=!0)},o(o){t&&N(t.$$.fragment,o),i=!1},d(o){o&&L(n),t&&fe(t,o)}}}function tU(e){let t,n,i=Ue(e[10]),r=[];for(let o=0;oN(r[o],1,1,()=>{r[o]=null});return{c(){for(let o=0;o{fe(u,1)}),Fe()}r?(t=Ke(r,s(o)),he(t.$$.fragment),D(t.$$.fragment,1),ce(t,n.parentNode,n)):t=null}else if(r){const u={};a&8388712&&(u.$$scope={dirty:a,ctx:o}),t.$set(u)}},i(o){i||(t&&D(t.$$.fragment,o),i=!0)},o(o){t&&N(t.$$.fragment,o),i=!1},d(o){o&&L(n),t&&fe(t,o)}}}function nU(e){let t,n,i=Ue(e[3]),r=[];for(let o=0;oN(r[o],1,1,()=>{r[o]=null});return{c(){for(let o=0;o{fe(d,1)}),Fe()}o?(t=Ke(o,a(c)),he(t.$$.fragment),D(t.$$.fragment,1),ce(t,n.parentNode,n)):t=null}else if(o){const d={};f&8388708&&(d.$$scope={dirty:f,ctx:c}),t.$set(d)}if(f&32&&u!==(u=c[5].tablebody)){if(i){Se();const d=i;N(d.$$.fragment,1,0,()=>{fe(d,1)}),Fe()}u?(i=Ke(u,l(c)),he(i.$$.fragment),D(i.$$.fragment,1),ce(i,r.parentNode,r)):i=null}else if(u){const d={};f&8388712&&(d.$$scope={dirty:f,ctx:c}),i.$set(d)}},i(c){s||(t&&D(t.$$.fragment,c),i&&D(i.$$.fragment,c),s=!0)},o(c){t&&N(t.$$.fragment,c),i&&N(i.$$.fragment,c),s=!1},d(c){c&&(L(n),L(r)),t&&fe(t,c),i&&fe(i,c)}}}function f4(e){let t,n;const i=[e[7],{renderers:e[5]}];let r={};for(let s=0;s{o[c]=null}),Fe()),~t?(n=o[t],n?n.p(u,l):(n=o[t]=s[t](u),n.c()),D(n,1),n.m(i.parentNode,i)):n=null)},i(u){r||(D(n),r=!0)},o(u){N(n),r=!1},d(u){u&&L(i),~t&&o[t].d(u)}}}function sU(e,t,n){const i=["type","tokens","header","rows","ordered","renderers"];let r=v5(t,i),{type:s=void 0}=t,{tokens:o=void 0}=t,{header:a=void 0}=t,{rows:u=void 0}=t,{ordered:l=!1}=t,{renderers:c}=t;return LB(),e.$$set=f=>{t=Pe(Pe({},t),o2(f)),n(6,r=v5(t,i)),"type"in f&&n(0,s=f.type),"tokens"in f&&n(1,o=f.tokens),"header"in f&&n(2,a=f.header),"rows"in f&&n(3,u=f.rows),"ordered"in f&&n(4,l=f.ordered),"renderers"in f&&n(5,c=f.renderers)},[s,o,a,u,l,c,r]}let Ca=class extends xe{constructor(t){super(),ve(this,t,sU,rU,ye,{type:0,tokens:1,header:2,rows:3,ordered:4,renderers:5})}};function g2(){return{async:!1,baseUrl:null,breaks:!1,extensions:null,gfm:!0,headerIds:!0,headerPrefix:"",highlight:null,hooks:null,langPrefix:"language-",mangle:!0,pedantic:!1,renderer:null,sanitize:!1,sanitizer:null,silent:!1,smartypants:!1,tokenizer:null,walkTokens:null,xhtml:!1}}let yo=g2();function d4(e){yo=e}const h4=/[&<>"']/,oU=new RegExp(h4.source,"g"),p4=/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/,aU=new RegExp(p4.source,"g"),uU={"&":"&","<":"<",">":">",'"':""","'":"'"},g4=e=>uU[e];function _n(e,t){if(t){if(h4.test(e))return e.replace(oU,g4)}else if(p4.test(e))return e.replace(aU,g4);return e}const lU=/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig;function m4(e){return e.replace(lU,(t,n)=>(n=n.toLowerCase(),n==="colon"?":":n.charAt(0)==="#"?n.charAt(1)==="x"?String.fromCharCode(parseInt(n.substring(2),16)):String.fromCharCode(+n.substring(1)):""))}const cU=/(^|[^\[])\^/g;function it(e,t){e=typeof e=="string"?e:e.source,t=t||"";const n={replace:(i,r)=>(r=r.source||r,r=r.replace(cU,"$1"),e=e.replace(i,r),n),getRegex:()=>new RegExp(e,t)};return n}const fU=/[^\w:]/g,dU=/^$|^[a-z][a-z0-9+.-]*:|^[?#]/i;function y4(e,t,n){if(e){let i;try{i=decodeURIComponent(m4(n)).replace(fU,"").toLowerCase()}catch{return null}if(i.indexOf("javascript:")===0||i.indexOf("vbscript:")===0||i.indexOf("data:")===0)return null}t&&!dU.test(n)&&(n=mU(t,n));try{n=encodeURI(n).replace(/%25/g,"%")}catch{return null}return n}const Bh={},hU=/^[^:]+:\/*[^/]*$/,pU=/^([^:]+:)[\s\S]*$/,gU=/^([^:]+:\/*[^/]*)[\s\S]*$/;function mU(e,t){Bh[" "+e]||(hU.test(e)?Bh[" "+e]=e+"/":Bh[" "+e]=jh(e,"/",!0)),e=Bh[" "+e];const n=e.indexOf(":")===-1;return t.substring(0,2)==="//"?n?t:e.replace(pU,"$1")+t:t.charAt(0)==="/"?n?t:e.replace(gU,"$1")+t:e+t}const Uh={exec:function(){}};function b4(e,t){const n=e.replace(/\|/g,(s,o,a)=>{let u=!1,l=o;for(;--l>=0&&a[l]==="\\";)u=!u;return u?"|":" |"}),i=n.split(/ \|/);let r=0;if(i[0].trim()||i.shift(),i.length>0&&!i[i.length-1].trim()&&i.pop(),i.length>t)i.splice(t);else for(;i.length{const s=r.match(/^\s+/);if(s===null)return r;const[o]=s;return o.length>=i.length?r.slice(i.length):r}).join(` +`)}class qh{constructor(t){this.options=t||yo}space(t){const n=this.rules.block.newline.exec(t);if(n&&n[0].length>0)return{type:"space",raw:n[0]}}code(t){const n=this.rules.block.code.exec(t);if(n){const i=n[0].replace(/^ {1,4}/gm,"");return{type:"code",raw:n[0],codeBlockStyle:"indented",text:this.options.pedantic?i:jh(i,` +`)}}}fences(t){const n=this.rules.block.fences.exec(t);if(n){const i=n[0],r=vU(i,n[3]||"");return{type:"code",raw:i,lang:n[2]?n[2].trim().replace(this.rules.inline._escapes,"$1"):n[2],text:r}}}heading(t){const n=this.rules.block.heading.exec(t);if(n){let i=n[2].trim();if(/#$/.test(i)){const r=jh(i,"#");(this.options.pedantic||!r||/ $/.test(r))&&(i=r.trim())}return{type:"heading",raw:n[0],depth:n[1].length,text:i,tokens:this.lexer.inline(i)}}}hr(t){const n=this.rules.block.hr.exec(t);if(n)return{type:"hr",raw:n[0]}}blockquote(t){const n=this.rules.block.blockquote.exec(t);if(n){const i=n[0].replace(/^ *>[ \t]?/gm,""),r=this.lexer.state.top;this.lexer.state.top=!0;const s=this.lexer.blockTokens(i);return this.lexer.state.top=r,{type:"blockquote",raw:n[0],tokens:s,text:i}}}list(t){let n=this.rules.block.list.exec(t);if(n){let i,r,s,o,a,u,l,c,f,d,h,p,g=n[1].trim();const m=g.length>1,y={type:"list",raw:"",ordered:m,start:m?+g.slice(0,-1):"",loose:!1,items:[]};g=m?`\\d{1,9}\\${g.slice(-1)}`:`\\${g}`,this.options.pedantic&&(g=m?g:"[*+-]");const b=new RegExp(`^( {0,3}${g})((?:[ ][^\\n]*)?(?:\\n|$))`);for(;t&&(p=!1,!(!(n=b.exec(t))||this.rules.block.hr.test(t)));){if(i=n[0],t=t.substring(i.length),c=n[2].split(` +`,1)[0].replace(/^\t+/,x=>" ".repeat(3*x.length)),f=t.split(` +`,1)[0],this.options.pedantic?(o=2,h=c.trimLeft()):(o=n[2].search(/[^ ]/),o=o>4?1:o,h=c.slice(o),o+=n[1].length),u=!1,!c&&/^ *$/.test(f)&&(i+=f+` +`,t=t.substring(f.length+1),p=!0),!p){const x=new RegExp(`^ {0,${Math.min(3,o-1)}}(?:[*+-]|\\d{1,9}[.)])((?:[ ][^\\n]*)?(?:\\n|$))`),_=new RegExp(`^ {0,${Math.min(3,o-1)}}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)`),E=new RegExp(`^ {0,${Math.min(3,o-1)}}(?:\`\`\`|~~~)`),w=new RegExp(`^ {0,${Math.min(3,o-1)}}#`);for(;t&&(d=t.split(` +`,1)[0],f=d,this.options.pedantic&&(f=f.replace(/^ {1,4}(?=( {4})*[^ ])/g," ")),!(E.test(f)||w.test(f)||x.test(f)||_.test(t)));){if(f.search(/[^ ]/)>=o||!f.trim())h+=` +`+f.slice(o);else{if(u||c.search(/[^ ]/)>=4||E.test(c)||w.test(c)||_.test(c))break;h+=` +`+f}!u&&!f.trim()&&(u=!0),i+=d+` +`,t=t.substring(d.length+1),c=f.slice(o)}}y.loose||(l?y.loose=!0:/\n *\n *$/.test(i)&&(l=!0)),this.options.gfm&&(r=/^\[[ xX]\] /.exec(h),r&&(s=r[0]!=="[ ] ",h=h.replace(/^\[[ xX]\] +/,""))),y.items.push({type:"list_item",raw:i,task:!!r,checked:s,loose:!1,text:h}),y.raw+=i}y.items[y.items.length-1].raw=i.trimRight(),y.items[y.items.length-1].text=h.trimRight(),y.raw=y.raw.trimRight();const v=y.items.length;for(a=0;aE.type==="space"),_=x.length>0&&x.some(E=>/\n.*\n/.test(E.raw));y.loose=_}if(y.loose)for(a=0;a$/,"$1").replace(this.rules.inline._escapes,"$1"):"",s=n[3]?n[3].substring(1,n[3].length-1).replace(this.rules.inline._escapes,"$1"):n[3];return{type:"def",tag:i,raw:n[0],href:r,title:s}}}table(t){const n=this.rules.block.table.exec(t);if(n){const i={type:"table",header:b4(n[1]).map(r=>({text:r})),align:n[2].replace(/^ *|\| *$/g,"").split(/ *\| */),rows:n[3]&&n[3].trim()?n[3].replace(/\n[ \t]*$/,"").split(` +`):[]};if(i.header.length===i.align.length){i.raw=n[0];let r=i.align.length,s,o,a,u;for(s=0;s({text:l}));for(r=i.header.length,o=0;o/i.test(n[0])&&(this.lexer.state.inLink=!1),!this.lexer.state.inRawBlock&&/^<(pre|code|kbd|script)(\s|>)/i.test(n[0])?this.lexer.state.inRawBlock=!0:this.lexer.state.inRawBlock&&/^<\/(pre|code|kbd|script)(\s|>)/i.test(n[0])&&(this.lexer.state.inRawBlock=!1),{type:this.options.sanitize?"text":"html",raw:n[0],inLink:this.lexer.state.inLink,inRawBlock:this.lexer.state.inRawBlock,block:!1,text:this.options.sanitize?this.options.sanitizer?this.options.sanitizer(n[0]):_n(n[0]):n[0]}}link(t){const n=this.rules.inline.link.exec(t);if(n){const i=n[2].trim();if(!this.options.pedantic&&/^$/.test(i))return;const o=jh(i.slice(0,-1),"\\");if((i.length-o.length)%2===0)return}else{const o=yU(n[2],"()");if(o>-1){const u=(n[0].indexOf("!")===0?5:4)+n[1].length+o;n[2]=n[2].substring(0,o),n[0]=n[0].substring(0,u).trim(),n[3]=""}}let r=n[2],s="";if(this.options.pedantic){const o=/^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(r);o&&(r=o[1],s=o[3])}else s=n[3]?n[3].slice(1,-1):"";return r=r.trim(),/^$/.test(i)?r=r.slice(1):r=r.slice(1,-1)),v4(n,{href:r&&r.replace(this.rules.inline._escapes,"$1"),title:s&&s.replace(this.rules.inline._escapes,"$1")},n[0],this.lexer)}}reflink(t,n){let i;if((i=this.rules.inline.reflink.exec(t))||(i=this.rules.inline.nolink.exec(t))){let r=(i[2]||i[1]).replace(/\s+/g," ");if(r=n[r.toLowerCase()],!r){const s=i[0].charAt(0);return{type:"text",raw:s,text:s}}return v4(i,r,i[0],this.lexer)}}emStrong(t,n,i=""){let r=this.rules.inline.emStrong.lDelim.exec(t);if(!r||r[3]&&i.match(/[\p{L}\p{N}]/u))return;if(!(r[1]||r[2]||"")||!i||this.rules.inline.punctuation.exec(i)){const o=r[0].length-1;let a,u,l=o,c=0;const f=r[0][0]==="*"?this.rules.inline.emStrong.rDelimAst:this.rules.inline.emStrong.rDelimUnd;for(f.lastIndex=0,n=n.slice(-1*t.length+o);(r=f.exec(n))!=null;){if(a=r[1]||r[2]||r[3]||r[4]||r[5]||r[6],!a)continue;if(u=a.length,r[3]||r[4]){l+=u;continue}else if((r[5]||r[6])&&o%3&&!((o+u)%3)){c+=u;continue}if(l-=u,l>0)continue;u=Math.min(u,u+l+c);const d=t.slice(0,o+r.index+u+1);if(Math.min(o,u)%2){const p=d.slice(1,-1);return{type:"em",raw:d,text:p,tokens:this.lexer.inlineTokens(p)}}const h=d.slice(2,-2);return{type:"strong",raw:d,text:h,tokens:this.lexer.inlineTokens(h)}}}}codespan(t){const n=this.rules.inline.code.exec(t);if(n){let i=n[2].replace(/\n/g," ");const r=/[^ ]/.test(i),s=/^ /.test(i)&&/ $/.test(i);return r&&s&&(i=i.substring(1,i.length-1)),i=_n(i,!0),{type:"codespan",raw:n[0],text:i}}}br(t){const n=this.rules.inline.br.exec(t);if(n)return{type:"br",raw:n[0]}}del(t){const n=this.rules.inline.del.exec(t);if(n)return{type:"del",raw:n[0],text:n[2],tokens:this.lexer.inlineTokens(n[2])}}autolink(t,n){const i=this.rules.inline.autolink.exec(t);if(i){let r,s;return i[2]==="@"?(r=_n(this.options.mangle?n(i[1]):i[1]),s="mailto:"+r):(r=_n(i[1]),s=r),{type:"link",raw:i[0],text:r,href:s,tokens:[{type:"text",raw:r,text:r}]}}}url(t,n){let i;if(i=this.rules.inline.url.exec(t)){let r,s;if(i[2]==="@")r=_n(this.options.mangle?n(i[0]):i[0]),s="mailto:"+r;else{let o;do o=i[0],i[0]=this.rules.inline._backpedal.exec(i[0])[0];while(o!==i[0]);r=_n(i[0]),i[1]==="www."?s="http://"+i[0]:s=i[0]}return{type:"link",raw:i[0],text:r,href:s,tokens:[{type:"text",raw:r,text:r}]}}}inlineText(t,n){const i=this.rules.inline.text.exec(t);if(i){let r;return this.lexer.state.inRawBlock?r=this.options.sanitize?this.options.sanitizer?this.options.sanitizer(i[0]):_n(i[0]):i[0]:r=_n(this.options.smartypants?n(i[0]):i[0]),{type:"text",raw:i[0],text:r}}}}const Ce={newline:/^(?: *(?:\n|$))+/,code:/^( {4}[^\n]+(?:\n(?: *(?:\n|$))*)?)+/,fences:/^ {0,3}(`{3,}(?=[^`\n]*(?:\n|$))|~{3,})([^\n]*)(?:\n|$)(?:|([\s\S]*?)(?:\n|$))(?: {0,3}\1[~`]* *(?=\n|$)|$)/,hr:/^ {0,3}((?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/,heading:/^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/,blockquote:/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/,list:/^( {0,3}bull)([ \t][^\n]+?)?(?:\n|$)/,html:"^ {0,3}(?:<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?(?:\\?>\\n*|$)|\\n*|$)|\\n*|$)|)[\\s\\S]*?(?:(?:\\n *)+\\n|$)|<(?!script|pre|style|textarea)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n *)+\\n|$)|(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n *)+\\n|$))",def:/^ {0,3}\[(label)\]: *(?:\n *)?([^<\s][^\s]*|<.*?>)(?:(?: +(?:\n *)?| *\n *)(title))? *(?:\n+|$)/,table:Uh,lheading:/^((?:(?!^bull ).|\n(?!\n|bull ))+?)\n {0,3}(=+|-+) *(?:\n+|$)/,_paragraph:/^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html|table| +\n)[^\n]+)*)/,text:/^[^\n]+/};Ce._label=/(?!\s*\])(?:\\.|[^\[\]\\])+/,Ce._title=/(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/,Ce.def=it(Ce.def).replace("label",Ce._label).replace("title",Ce._title).getRegex(),Ce.bullet=/(?:[*+-]|\d{1,9}[.)])/,Ce.listItemStart=it(/^( *)(bull) */).replace("bull",Ce.bullet).getRegex(),Ce.list=it(Ce.list).replace(/bull/g,Ce.bullet).replace("hr","\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))").replace("def","\\n+(?="+Ce.def.source+")").getRegex(),Ce._tag="address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul",Ce._comment=/|$)/,Ce.html=it(Ce.html,"i").replace("comment",Ce._comment).replace("tag",Ce._tag).replace("attribute",/ +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(),Ce.lheading=it(Ce.lheading).replace(/bull/g,Ce.bullet).getRegex(),Ce.paragraph=it(Ce._paragraph).replace("hr",Ce.hr).replace("heading"," {0,3}#{1,6} ").replace("|lheading","").replace("|table","").replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",Ce._tag).getRegex(),Ce.blockquote=it(Ce.blockquote).replace("paragraph",Ce.paragraph).getRegex(),Ce.normal={...Ce},Ce.gfm={...Ce.normal,table:"^ *([^\\n ].*\\|.*)\\n {0,3}(?:\\| *)?(:?-+:? *(?:\\| *:?-+:? *)*)(?:\\| *)?(?:\\n((?:(?! *\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)"},Ce.gfm.table=it(Ce.gfm.table).replace("hr",Ce.hr).replace("heading"," {0,3}#{1,6} ").replace("blockquote"," {0,3}>").replace("code"," {4}[^\\n]").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",Ce._tag).getRegex(),Ce.gfm.paragraph=it(Ce._paragraph).replace("hr",Ce.hr).replace("heading"," {0,3}#{1,6} ").replace("|lheading","").replace("table",Ce.gfm.table).replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",Ce._tag).getRegex(),Ce.pedantic={...Ce.normal,html:it(`^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+? *(?:\\n{2,}|\\s*$)|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))`).replace("comment",Ce._comment).replace(/tag/g,"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(),def:/^ *\[([^\]]+)\]: *]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,heading:/^(#{1,6})(.*)(?:\n+|$)/,fences:Uh,lheading:/^(.+?)\n {0,3}(=+|-+) *(?:\n+|$)/,paragraph:it(Ce.normal._paragraph).replace("hr",Ce.hr).replace("heading",` *#{1,6} *[^ +]`).replace("lheading",Ce.lheading).replace("blockquote"," {0,3}>").replace("|fences","").replace("|list","").replace("|html","").getRegex()};const ae={escape:/^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,autolink:/^<(scheme:[^\s\x00-\x1f<>]*|email)>/,url:Uh,tag:"^comment|^|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^|^",link:/^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/,reflink:/^!?\[(label)\]\[(ref)\]/,nolink:/^!?\[(ref)\](?:\[\])?/,reflinkSearch:"reflink|nolink(?!\\()",emStrong:{lDelim:/^(?:\*+(?:((?!\*)[punct])|[^\s*]))|^_+(?:((?!_)[punct])|([^\s_]))/,rDelimAst:/^[^_*]*?__[^_*]*?\*[^_*]*?(?=__)|[^*]+(?=[^*])|(?!\*)[punct](\*+)(?=[\s]|$)|[^punct\s](\*+)(?!\*)(?=[punct\s]|$)|(?!\*)[punct\s](\*+)(?=[^punct\s])|[\s](\*+)(?!\*)(?=[punct])|(?!\*)[punct](\*+)(?!\*)(?=[punct])|[^punct\s](\*+)(?=[^punct\s])/,rDelimUnd:/^[^_*]*?\*\*[^_*]*?_[^_*]*?(?=\*\*)|[^_]+(?=[^_])|(?!_)[punct](_+)(?=[\s]|$)|[^punct\s](_+)(?!_)(?=[punct\s]|$)|(?!_)[punct\s](_+)(?=[^punct\s])|[\s](_+)(?!_)(?=[punct])|(?!_)[punct](_+)(?!_)(?=[punct])/},code:/^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,br:/^( {2,}|\\)\n(?!\s*$)/,del:Uh,text:/^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\`^|~",ae.punctuation=it(ae.punctuation,"u").replace(/punctuation/g,ae._punctuation).getRegex(),ae.blockSkip=/\[[^[\]]*?\]\([^\(\)]*?\)|`[^`]*?`|<[^<>]*?>/g,ae.anyPunctuation=/\\[punct]/g,ae._escapes=/\\([punct])/g,ae._comment=it(Ce._comment).replace("(?:-->|$)","-->").getRegex(),ae.emStrong.lDelim=it(ae.emStrong.lDelim,"u").replace(/punct/g,ae._punctuation).getRegex(),ae.emStrong.rDelimAst=it(ae.emStrong.rDelimAst,"gu").replace(/punct/g,ae._punctuation).getRegex(),ae.emStrong.rDelimUnd=it(ae.emStrong.rDelimUnd,"gu").replace(/punct/g,ae._punctuation).getRegex(),ae.anyPunctuation=it(ae.anyPunctuation,"gu").replace(/punct/g,ae._punctuation).getRegex(),ae._escapes=it(ae._escapes,"gu").replace(/punct/g,ae._punctuation).getRegex(),ae._scheme=/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/,ae._email=/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/,ae.autolink=it(ae.autolink).replace("scheme",ae._scheme).replace("email",ae._email).getRegex(),ae._attribute=/\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/,ae.tag=it(ae.tag).replace("comment",ae._comment).replace("attribute",ae._attribute).getRegex(),ae._label=/(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/,ae._href=/<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/,ae._title=/"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/,ae.link=it(ae.link).replace("label",ae._label).replace("href",ae._href).replace("title",ae._title).getRegex(),ae.reflink=it(ae.reflink).replace("label",ae._label).replace("ref",Ce._label).getRegex(),ae.nolink=it(ae.nolink).replace("ref",Ce._label).getRegex(),ae.reflinkSearch=it(ae.reflinkSearch,"g").replace("reflink",ae.reflink).replace("nolink",ae.nolink).getRegex(),ae.normal={...ae},ae.pedantic={...ae.normal,strong:{start:/^__|\*\*/,middle:/^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,endAst:/\*\*(?!\*)/g,endUnd:/__(?!_)/g},em:{start:/^_|\*/,middle:/^()\*(?=\S)([\s\S]*?\S)\*(?!\*)|^_(?=\S)([\s\S]*?\S)_(?!_)/,endAst:/\*(?!\*)/g,endUnd:/_(?!_)/g},link:it(/^!?\[(label)\]\((.*?)\)/).replace("label",ae._label).getRegex(),reflink:it(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace("label",ae._label).getRegex()},ae.gfm={...ae.normal,escape:it(ae.escape).replace("])","~|])").getRegex(),_extended_email:/[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/,url:/^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,_backpedal:/(?:[^?!.,:;*_'"~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_'"~)]+(?!$))+/,del:/^(~~?)(?=[^\s~])([\s\S]*?[^\s~])\1(?=[^~]|$)/,text:/^([`~]+|[^`~])(?:(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\.5&&(i="x"+i.toString(16)),t+="&#"+i+";";return t}class ur{constructor(t){this.tokens=[],this.tokens.links=Object.create(null),this.options=t||yo,this.options.tokenizer=this.options.tokenizer||new qh,this.tokenizer=this.options.tokenizer,this.tokenizer.options=this.options,this.tokenizer.lexer=this,this.inlineQueue=[],this.state={inLink:!1,inRawBlock:!1,top:!0};const n={block:Ce.normal,inline:ae.normal};this.options.pedantic?(n.block=Ce.pedantic,n.inline=ae.pedantic):this.options.gfm&&(n.block=Ce.gfm,this.options.breaks?n.inline=ae.breaks:n.inline=ae.gfm),this.tokenizer.rules=n}static get rules(){return{block:Ce,inline:ae}}static lex(t,n){return new ur(n).lex(t)}static lexInline(t,n){return new ur(n).inlineTokens(t)}lex(t){t=t.replace(/\r\n|\r/g,` +`),this.blockTokens(t,this.tokens);let n;for(;n=this.inlineQueue.shift();)this.inlineTokens(n.src,n.tokens);return this.tokens}blockTokens(t,n=[]){this.options.pedantic?t=t.replace(/\t/g," ").replace(/^ +$/gm,""):t=t.replace(/^( *)(\t+)/gm,(a,u,l)=>u+" ".repeat(l.length));let i,r,s,o;for(;t;)if(!(this.options.extensions&&this.options.extensions.block&&this.options.extensions.block.some(a=>(i=a.call({lexer:this},t,n))?(t=t.substring(i.raw.length),n.push(i),!0):!1))){if(i=this.tokenizer.space(t)){t=t.substring(i.raw.length),i.raw.length===1&&n.length>0?n[n.length-1].raw+=` +`:n.push(i);continue}if(i=this.tokenizer.code(t)){t=t.substring(i.raw.length),r=n[n.length-1],r&&(r.type==="paragraph"||r.type==="text")?(r.raw+=` +`+i.raw,r.text+=` +`+i.text,this.inlineQueue[this.inlineQueue.length-1].src=r.text):n.push(i);continue}if(i=this.tokenizer.fences(t)){t=t.substring(i.raw.length),n.push(i);continue}if(i=this.tokenizer.heading(t)){t=t.substring(i.raw.length),n.push(i);continue}if(i=this.tokenizer.hr(t)){t=t.substring(i.raw.length),n.push(i);continue}if(i=this.tokenizer.blockquote(t)){t=t.substring(i.raw.length),n.push(i);continue}if(i=this.tokenizer.list(t)){t=t.substring(i.raw.length),n.push(i);continue}if(i=this.tokenizer.html(t)){t=t.substring(i.raw.length),n.push(i);continue}if(i=this.tokenizer.def(t)){t=t.substring(i.raw.length),r=n[n.length-1],r&&(r.type==="paragraph"||r.type==="text")?(r.raw+=` +`+i.raw,r.text+=` +`+i.raw,this.inlineQueue[this.inlineQueue.length-1].src=r.text):this.tokens.links[i.tag]||(this.tokens.links[i.tag]={href:i.href,title:i.title});continue}if(i=this.tokenizer.table(t)){t=t.substring(i.raw.length),n.push(i);continue}if(i=this.tokenizer.lheading(t)){t=t.substring(i.raw.length),n.push(i);continue}if(s=t,this.options.extensions&&this.options.extensions.startBlock){let a=1/0;const u=t.slice(1);let l;this.options.extensions.startBlock.forEach(function(c){l=c.call({lexer:this},u),typeof l=="number"&&l>=0&&(a=Math.min(a,l))}),a<1/0&&a>=0&&(s=t.substring(0,a+1))}if(this.state.top&&(i=this.tokenizer.paragraph(s))){r=n[n.length-1],o&&r.type==="paragraph"?(r.raw+=` +`+i.raw,r.text+=` +`+i.text,this.inlineQueue.pop(),this.inlineQueue[this.inlineQueue.length-1].src=r.text):n.push(i),o=s.length!==t.length,t=t.substring(i.raw.length);continue}if(i=this.tokenizer.text(t)){t=t.substring(i.raw.length),r=n[n.length-1],r&&r.type==="text"?(r.raw+=` +`+i.raw,r.text+=` +`+i.text,this.inlineQueue.pop(),this.inlineQueue[this.inlineQueue.length-1].src=r.text):n.push(i);continue}if(t){const a="Infinite loop on byte: "+t.charCodeAt(0);if(this.options.silent){console.error(a);break}else throw new Error(a)}}return this.state.top=!0,n}inline(t,n=[]){return this.inlineQueue.push({src:t,tokens:n}),n}inlineTokens(t,n=[]){let i,r,s,o=t,a,u,l;if(this.tokens.links){const c=Object.keys(this.tokens.links);if(c.length>0)for(;(a=this.tokenizer.rules.inline.reflinkSearch.exec(o))!=null;)c.includes(a[0].slice(a[0].lastIndexOf("[")+1,-1))&&(o=o.slice(0,a.index)+"["+"a".repeat(a[0].length-2)+"]"+o.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex))}for(;(a=this.tokenizer.rules.inline.blockSkip.exec(o))!=null;)o=o.slice(0,a.index)+"["+"a".repeat(a[0].length-2)+"]"+o.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);for(;(a=this.tokenizer.rules.inline.anyPunctuation.exec(o))!=null;)o=o.slice(0,a.index)+"++"+o.slice(this.tokenizer.rules.inline.anyPunctuation.lastIndex);for(;t;)if(u||(l=""),u=!1,!(this.options.extensions&&this.options.extensions.inline&&this.options.extensions.inline.some(c=>(i=c.call({lexer:this},t,n))?(t=t.substring(i.raw.length),n.push(i),!0):!1))){if(i=this.tokenizer.escape(t)){t=t.substring(i.raw.length),n.push(i);continue}if(i=this.tokenizer.tag(t)){t=t.substring(i.raw.length),r=n[n.length-1],r&&i.type==="text"&&r.type==="text"?(r.raw+=i.raw,r.text+=i.text):n.push(i);continue}if(i=this.tokenizer.link(t)){t=t.substring(i.raw.length),n.push(i);continue}if(i=this.tokenizer.reflink(t,this.tokens.links)){t=t.substring(i.raw.length),r=n[n.length-1],r&&i.type==="text"&&r.type==="text"?(r.raw+=i.raw,r.text+=i.text):n.push(i);continue}if(i=this.tokenizer.emStrong(t,o,l)){t=t.substring(i.raw.length),n.push(i);continue}if(i=this.tokenizer.codespan(t)){t=t.substring(i.raw.length),n.push(i);continue}if(i=this.tokenizer.br(t)){t=t.substring(i.raw.length),n.push(i);continue}if(i=this.tokenizer.del(t)){t=t.substring(i.raw.length),n.push(i);continue}if(i=this.tokenizer.autolink(t,x4)){t=t.substring(i.raw.length),n.push(i);continue}if(!this.state.inLink&&(i=this.tokenizer.url(t,x4))){t=t.substring(i.raw.length),n.push(i);continue}if(s=t,this.options.extensions&&this.options.extensions.startInline){let c=1/0;const f=t.slice(1);let d;this.options.extensions.startInline.forEach(function(h){d=h.call({lexer:this},f),typeof d=="number"&&d>=0&&(c=Math.min(c,d))}),c<1/0&&c>=0&&(s=t.substring(0,c+1))}if(i=this.tokenizer.inlineText(s,xU)){t=t.substring(i.raw.length),i.raw.slice(-1)!=="_"&&(l=i.raw.slice(-1)),u=!0,r=n[n.length-1],r&&r.type==="text"?(r.raw+=i.raw,r.text+=i.text):n.push(i);continue}if(t){const c="Infinite loop on byte: "+t.charCodeAt(0);if(this.options.silent){console.error(c);break}else throw new Error(c)}}return n}}let Wh=class{constructor(t){this.options=t||yo}code(t,n,i){const r=(n||"").match(/\S*/)[0];if(this.options.highlight){const s=this.options.highlight(t,r);s!=null&&s!==t&&(i=!0,t=s)}return t=t.replace(/\n$/,"")+` +`,r?'
    '+(i?t:_n(t,!0))+`
    +`:"
    "+(i?t:_n(t,!0))+`
    +`}blockquote(t){return`
    +${t}
    +`}html(t,n){return t}heading(t,n,i,r){if(this.options.headerIds){const s=this.options.headerPrefix+r.slug(i);return`${t} +`}return`${t} +`}hr(){return this.options.xhtml?`
    +`:`
    +`}list(t,n,i){const r=n?"ol":"ul",s=n&&i!==1?' start="'+i+'"':"";return"<"+r+s+`> +`+t+" +`}listitem(t){return`
  • ${t}
  • +`}checkbox(t){return" "}paragraph(t){return`

    ${t}

    +`}table(t,n){return n&&(n=`${n}`),` + +`+t+` +`+n+`
    +`}tablerow(t){return` +${t} +`}tablecell(t,n){const i=n.header?"th":"td";return(n.align?`<${i} align="${n.align}">`:`<${i}>`)+t+` +`}strong(t){return`${t}`}em(t){return`${t}`}codespan(t){return`${t}`}br(){return this.options.xhtml?"
    ":"
    "}del(t){return`${t}`}link(t,n,i){if(t=y4(this.options.sanitize,this.options.baseUrl,t),t===null)return i;let r='
    ",r}image(t,n,i){if(t=y4(this.options.sanitize,this.options.baseUrl,t),t===null)return i;let r=`${i}":">",r}text(t){return t}};class m2{strong(t){return t}em(t){return t}codespan(t){return t}del(t){return t}html(t){return t}text(t){return t}link(t,n,i){return""+i}image(t,n,i){return""+i}br(){return""}}class Hh{constructor(){this.seen={}}serialize(t){return t.toLowerCase().trim().replace(/<[!\/a-z].*?>/ig,"").replace(/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g,"").replace(/\s/g,"-")}getNextSafeSlug(t,n){let i=t,r=0;if(this.seen.hasOwnProperty(i)){r=this.seen[t];do r++,i=t+"-"+r;while(this.seen.hasOwnProperty(i))}return n||(this.seen[t]=r,this.seen[i]=0),i}slug(t,n={}){const i=this.serialize(t);return this.getNextSafeSlug(i,n.dryrun)}}class Ur{constructor(t){this.options=t||yo,this.options.renderer=this.options.renderer||new Wh,this.renderer=this.options.renderer,this.renderer.options=this.options,this.textRenderer=new m2,this.slugger=new Hh}static parse(t,n){return new Ur(n).parse(t)}static parseInline(t,n){return new Ur(n).parseInline(t)}parse(t,n=!0){let i="",r,s,o,a,u,l,c,f,d,h,p,g,m,y,b,v,x,_,E;const w=t.length;for(r=0;r0&&b.tokens[0].type==="paragraph"?(b.tokens[0].text=_+" "+b.tokens[0].text,b.tokens[0].tokens&&b.tokens[0].tokens.length>0&&b.tokens[0].tokens[0].type==="text"&&(b.tokens[0].tokens[0].text=_+" "+b.tokens[0].tokens[0].text)):b.tokens.unshift({type:"text",text:_}):y+=_),y+=this.parse(b.tokens,m),d+=this.renderer.listitem(y,x,v);i+=this.renderer.list(d,p,g);continue}case"html":{i+=this.renderer.html(h.text,h.block);continue}case"paragraph":{i+=this.renderer.paragraph(this.parseInline(h.tokens));continue}case"text":{for(d=h.tokens?this.parseInline(h.tokens):h.text;r+1{i=i.concat(this.walkTokens(r[s],n))}):r.tokens&&(i=i.concat(this.walkTokens(r.tokens,n)))}return i}use(...t){const n=this.defaults.extensions||{renderers:{},childTokens:{}};return t.forEach(i=>{const r={...i};if(r.async=this.defaults.async||r.async||!1,i.extensions&&(i.extensions.forEach(s=>{if(!s.name)throw new Error("extension name required");if(s.renderer){const o=n.renderers[s.name];o?n.renderers[s.name]=function(...a){let u=s.renderer.apply(this,a);return u===!1&&(u=o.apply(this,a)),u}:n.renderers[s.name]=s.renderer}if(s.tokenizer){if(!s.level||s.level!=="block"&&s.level!=="inline")throw new Error("extension level must be 'block' or 'inline'");n[s.level]?n[s.level].unshift(s.tokenizer):n[s.level]=[s.tokenizer],s.start&&(s.level==="block"?n.startBlock?n.startBlock.push(s.start):n.startBlock=[s.start]:s.level==="inline"&&(n.startInline?n.startInline.push(s.start):n.startInline=[s.start]))}s.childTokens&&(n.childTokens[s.name]=s.childTokens)}),r.extensions=n),i.renderer){const s=this.defaults.renderer||new Wh(this.defaults);for(const o in i.renderer){const a=s[o];s[o]=(...u)=>{let l=i.renderer[o].apply(s,u);return l===!1&&(l=a.apply(s,u)),l}}r.renderer=s}if(i.tokenizer){const s=this.defaults.tokenizer||new qh(this.defaults);for(const o in i.tokenizer){const a=s[o];s[o]=(...u)=>{let l=i.tokenizer[o].apply(s,u);return l===!1&&(l=a.apply(s,u)),l}}r.tokenizer=s}if(i.hooks){const s=this.defaults.hooks||new Gc;for(const o in i.hooks){const a=s[o];Gc.passThroughHooks.has(o)?s[o]=u=>{if(this.defaults.async)return Promise.resolve(i.hooks[o].call(s,u)).then(c=>a.call(s,c));const l=i.hooks[o].call(s,u);return a.call(s,l)}:s[o]=(...u)=>{let l=i.hooks[o].apply(s,u);return l===!1&&(l=a.apply(s,u)),l}}r.hooks=s}if(i.walkTokens){const s=this.defaults.walkTokens;r.walkTokens=function(o){let a=[];return a.push(i.walkTokens.call(this,o)),s&&(a=a.concat(s.call(this,o))),a}}this.defaults={...this.defaults,...r}}),this}setOptions(t){return this.defaults={...this.defaults,...t},this}}Yu=new WeakSet,g5=function(t,n){return(i,r,s)=>{typeof r=="function"&&(s=r,r=null);const o={...r};r={...this.defaults,...o};const a=s2(this,Yu,kz).call(this,r.silent,r.async,s);if(typeof i>"u"||i===null)return a(new Error("marked(): input parameter is undefined or null"));if(typeof i!="string")return a(new Error("marked(): input parameter is of type "+Object.prototype.toString.call(i)+", string expected"));if(bU(r,s),r.hooks&&(r.hooks.options=r),s){const u=r.highlight;let l;try{r.hooks&&(i=r.hooks.preprocess(i)),l=t(i,r)}catch(d){return a(d)}const c=d=>{let h;if(!d)try{r.walkTokens&&this.walkTokens(l,r.walkTokens),h=n(l,r),r.hooks&&(h=r.hooks.postprocess(h))}catch(p){d=p}return r.highlight=u,d?a(d):s(null,h)};if(!u||u.length<3||(delete r.highlight,!l.length))return c();let f=0;this.walkTokens(l,d=>{d.type==="code"&&(f++,setTimeout(()=>{u(d.text,d.lang,(h,p)=>{if(h)return c(h);p!=null&&p!==d.text&&(d.text=p,d.escaped=!0),f--,f===0&&c()})},0))}),f===0&&c();return}if(r.async)return Promise.resolve(r.hooks?r.hooks.preprocess(i):i).then(u=>t(u,r)).then(u=>r.walkTokens?Promise.all(this.walkTokens(u,r.walkTokens)).then(()=>u):u).then(u=>n(u,r)).then(u=>r.hooks?r.hooks.postprocess(u):u).catch(a);try{r.hooks&&(i=r.hooks.preprocess(i));const u=t(i,r);r.walkTokens&&this.walkTokens(u,r.walkTokens);let l=n(u,r);return r.hooks&&(l=r.hooks.postprocess(l)),l}catch(u){return a(u)}}},kz=function(t,n,i){return r=>{if(r.message+=` +Please report this to https://github.com/markedjs/marked.`,t){const s="

    An error occurred:

    "+_n(r.message+"",!0)+"
    ";if(n)return Promise.resolve(s);if(i){i(null,s);return}return s}if(n)return Promise.reject(r);if(i){i(r);return}throw r}};const ka=new _U(yo);function st(e,t,n){return ka.parse(e,t,n)}st.options=st.setOptions=function(e){return ka.setOptions(e),st.defaults=ka.defaults,d4(st.defaults),st},st.getDefaults=g2,st.defaults=yo,st.use=function(...e){return ka.use(...e),st.defaults=ka.defaults,d4(st.defaults),st},st.walkTokens=function(e,t){return ka.walkTokens(e,t)},st.parseInline=ka.parseInline,st.Parser=Ur,st.parser=Ur.parse,st.Renderer=Wh,st.TextRenderer=m2,st.Lexer=ur,st.lexer=ur.lex,st.Tokenizer=qh,st.Slugger=Hh,st.Hooks=Gc,st.parse=st,st.options,st.setOptions,st.use,st.walkTokens,st.parseInline,Ur.parse,ur.lex;const _4={};function wU(e){let t;return{c(){t=Be(e[1])},m(n,i){I(n,t,i)},p(n,i){i&2&&ft(t,n[1])},i:ie,o:ie,d(n){n&&L(t)}}}function EU(e){let t,n;const i=e[5].default,r=mt(i,e,e[4],null);return{c(){t=G("h6"),r&&r.c(),M(t,"id",e[2])},m(s,o){I(s,t,o),r&&r.m(t,null),n=!0},p(s,o){r&&r.p&&(!n||o&16)&&bt(r,i,s,s[4],n?yt(i,s[4],o,null):vt(s[4]),null),(!n||o&4)&&M(t,"id",s[2])},i(s){n||(D(r,s),n=!0)},o(s){N(r,s),n=!1},d(s){s&&L(t),r&&r.d(s)}}}function CU(e){let t,n;const i=e[5].default,r=mt(i,e,e[4],null);return{c(){t=G("h5"),r&&r.c(),M(t,"id",e[2])},m(s,o){I(s,t,o),r&&r.m(t,null),n=!0},p(s,o){r&&r.p&&(!n||o&16)&&bt(r,i,s,s[4],n?yt(i,s[4],o,null):vt(s[4]),null),(!n||o&4)&&M(t,"id",s[2])},i(s){n||(D(r,s),n=!0)},o(s){N(r,s),n=!1},d(s){s&&L(t),r&&r.d(s)}}}function kU(e){let t,n;const i=e[5].default,r=mt(i,e,e[4],null);return{c(){t=G("h4"),r&&r.c(),M(t,"id",e[2])},m(s,o){I(s,t,o),r&&r.m(t,null),n=!0},p(s,o){r&&r.p&&(!n||o&16)&&bt(r,i,s,s[4],n?yt(i,s[4],o,null):vt(s[4]),null),(!n||o&4)&&M(t,"id",s[2])},i(s){n||(D(r,s),n=!0)},o(s){N(r,s),n=!1},d(s){s&&L(t),r&&r.d(s)}}}function AU(e){let t,n;const i=e[5].default,r=mt(i,e,e[4],null);return{c(){t=G("h3"),r&&r.c(),M(t,"id",e[2])},m(s,o){I(s,t,o),r&&r.m(t,null),n=!0},p(s,o){r&&r.p&&(!n||o&16)&&bt(r,i,s,s[4],n?yt(i,s[4],o,null):vt(s[4]),null),(!n||o&4)&&M(t,"id",s[2])},i(s){n||(D(r,s),n=!0)},o(s){N(r,s),n=!1},d(s){s&&L(t),r&&r.d(s)}}}function $U(e){let t,n;const i=e[5].default,r=mt(i,e,e[4],null);return{c(){t=G("h2"),r&&r.c(),M(t,"id",e[2])},m(s,o){I(s,t,o),r&&r.m(t,null),n=!0},p(s,o){r&&r.p&&(!n||o&16)&&bt(r,i,s,s[4],n?yt(i,s[4],o,null):vt(s[4]),null),(!n||o&4)&&M(t,"id",s[2])},i(s){n||(D(r,s),n=!0)},o(s){N(r,s),n=!1},d(s){s&&L(t),r&&r.d(s)}}}function SU(e){let t,n;const i=e[5].default,r=mt(i,e,e[4],null);return{c(){t=G("h1"),r&&r.c(),M(t,"id",e[2])},m(s,o){I(s,t,o),r&&r.m(t,null),n=!0},p(s,o){r&&r.p&&(!n||o&16)&&bt(r,i,s,s[4],n?yt(i,s[4],o,null):vt(s[4]),null),(!n||o&4)&&M(t,"id",s[2])},i(s){n||(D(r,s),n=!0)},o(s){N(r,s),n=!1},d(s){s&&L(t),r&&r.d(s)}}}function FU(e){let t,n,i,r;const s=[SU,$U,AU,kU,CU,EU,wU],o=[];function a(u,l){return u[0]===1?0:u[0]===2?1:u[0]===3?2:u[0]===4?3:u[0]===5?4:u[0]===6?5:6}return t=a(e),n=o[t]=s[t](e),{c(){n.c(),i=Ne()},m(u,l){o[t].m(u,l),I(u,i,l),r=!0},p(u,[l]){let c=t;t=a(u),t===c?o[t].p(u,l):(Se(),N(o[c],1,1,()=>{o[c]=null}),Fe(),n=o[t],n?n.p(u,l):(n=o[t]=s[t](u),n.c()),D(n,1),n.m(i.parentNode,i))},i(u){r||(D(n),r=!0)},o(u){N(n),r=!1},d(u){u&&L(i),o[t].d(u)}}}function DU(e,t,n){let i,{$$slots:r={},$$scope:s}=t,{depth:o}=t,{raw:a}=t,{text:u}=t;const{slug:l,getOptions:c}=C5(_4),f=c();return e.$$set=d=>{"depth"in d&&n(0,o=d.depth),"raw"in d&&n(1,a=d.raw),"text"in d&&n(3,u=d.text),"$$scope"in d&&n(4,s=d.$$scope)},e.$$.update=()=>{e.$$.dirty&8&&n(2,i=f.headerIds?f.headerPrefix+l(u):void 0)},[o,a,i,u,s,r]}class TU extends xe{constructor(t){super(),ve(this,t,DU,FU,ye,{depth:0,raw:1,text:3})}}function MU(e){let t,n;const i=e[1].default,r=mt(i,e,e[0],null);return{c(){t=G("p"),r&&r.c()},m(s,o){I(s,t,o),r&&r.m(t,null),n=!0},p(s,[o]){r&&r.p&&(!n||o&1)&&bt(r,i,s,s[0],n?yt(i,s[0],o,null):vt(s[0]),null)},i(s){n||(D(r,s),n=!0)},o(s){N(r,s),n=!1},d(s){s&&L(t),r&&r.d(s)}}}function NU(e,t,n){let{$$slots:i={},$$scope:r}=t;return e.$$set=s=>{"$$scope"in s&&n(0,r=s.$$scope)},[r,i]}class RU extends xe{constructor(t){super(),ve(this,t,NU,MU,ye,{})}}function OU(e){let t;const n=e[3].default,i=mt(n,e,e[2],null);return{c(){i&&i.c()},m(r,s){i&&i.m(r,s),t=!0},p(r,[s]){i&&i.p&&(!t||s&4)&&bt(i,n,r,r[2],t?yt(n,r[2],s,null):vt(r[2]),null)},i(r){t||(D(i,r),t=!0)},o(r){N(i,r),t=!1},d(r){i&&i.d(r)}}}function LU(e,t,n){let{$$slots:i={},$$scope:r}=t,{text:s}=t,{raw:o}=t;return e.$$set=a=>{"text"in a&&n(0,s=a.text),"raw"in a&&n(1,o=a.raw),"$$scope"in a&&n(2,r=a.$$scope)},[s,o,r,i]}let IU=class extends xe{constructor(t){super(),ve(this,t,LU,OU,ye,{text:0,raw:1})}};function PU(e){let t,n;return{c(){t=G("img"),Ih(t.src,n=e[0])||M(t,"src",n),M(t,"title",e[1]),M(t,"alt",e[2])},m(i,r){I(i,t,r)},p(i,[r]){r&1&&!Ih(t.src,n=i[0])&&M(t,"src",n),r&2&&M(t,"title",i[1]),r&4&&M(t,"alt",i[2])},i:ie,o:ie,d(i){i&&L(t)}}}function zU(e,t,n){let{href:i=""}=t,{title:r=void 0}=t,{text:s=""}=t;return e.$$set=o=>{"href"in o&&n(0,i=o.href),"title"in o&&n(1,r=o.title),"text"in o&&n(2,s=o.text)},[i,r,s]}let BU=class extends xe{constructor(t){super(),ve(this,t,zU,PU,ye,{href:0,title:1,text:2})}};function UU(e){let t,n;const i=e[3].default,r=mt(i,e,e[2],null);return{c(){t=G("a"),r&&r.c(),M(t,"href",e[0]),M(t,"title",e[1])},m(s,o){I(s,t,o),r&&r.m(t,null),n=!0},p(s,[o]){r&&r.p&&(!n||o&4)&&bt(r,i,s,s[2],n?yt(i,s[2],o,null):vt(s[2]),null),(!n||o&1)&&M(t,"href",s[0]),(!n||o&2)&&M(t,"title",s[1])},i(s){n||(D(r,s),n=!0)},o(s){N(r,s),n=!1},d(s){s&&L(t),r&&r.d(s)}}}function jU(e,t,n){let{$$slots:i={},$$scope:r}=t,{href:s=""}=t,{title:o=void 0}=t;return e.$$set=a=>{"href"in a&&n(0,s=a.href),"title"in a&&n(1,o=a.title),"$$scope"in a&&n(2,r=a.$$scope)},[s,o,r,i]}class qU extends xe{constructor(t){super(),ve(this,t,jU,UU,ye,{href:0,title:1})}}function WU(e){let t,n;const i=e[1].default,r=mt(i,e,e[0],null);return{c(){t=G("em"),r&&r.c()},m(s,o){I(s,t,o),r&&r.m(t,null),n=!0},p(s,[o]){r&&r.p&&(!n||o&1)&&bt(r,i,s,s[0],n?yt(i,s[0],o,null):vt(s[0]),null)},i(s){n||(D(r,s),n=!0)},o(s){N(r,s),n=!1},d(s){s&&L(t),r&&r.d(s)}}}function HU(e,t,n){let{$$slots:i={},$$scope:r}=t;return e.$$set=s=>{"$$scope"in s&&n(0,r=s.$$scope)},[r,i]}class GU extends xe{constructor(t){super(),ve(this,t,HU,WU,ye,{})}}function VU(e){let t,n;const i=e[1].default,r=mt(i,e,e[0],null);return{c(){t=G("del"),r&&r.c()},m(s,o){I(s,t,o),r&&r.m(t,null),n=!0},p(s,[o]){r&&r.p&&(!n||o&1)&&bt(r,i,s,s[0],n?yt(i,s[0],o,null):vt(s[0]),null)},i(s){n||(D(r,s),n=!0)},o(s){N(r,s),n=!1},d(s){s&&L(t),r&&r.d(s)}}}function YU(e,t,n){let{$$slots:i={},$$scope:r}=t;return e.$$set=s=>{"$$scope"in s&&n(0,r=s.$$scope)},[r,i]}class XU extends xe{constructor(t){super(),ve(this,t,YU,VU,ye,{})}}function KU(e){let t,n=e[0].replace(/`/g,"")+"",i;return{c(){t=G("code"),i=Be(n)},m(r,s){I(r,t,s),V(t,i)},p(r,[s]){s&1&&n!==(n=r[0].replace(/`/g,"")+"")&&ft(i,n)},i:ie,o:ie,d(r){r&&L(t)}}}function ZU(e,t,n){let{raw:i}=t;return e.$$set=r=>{"raw"in r&&n(0,i=r.raw)},[i]}class JU extends xe{constructor(t){super(),ve(this,t,ZU,KU,ye,{raw:0})}}function QU(e){let t,n;const i=e[1].default,r=mt(i,e,e[0],null);return{c(){t=G("strong"),r&&r.c()},m(s,o){I(s,t,o),r&&r.m(t,null),n=!0},p(s,[o]){r&&r.p&&(!n||o&1)&&bt(r,i,s,s[0],n?yt(i,s[0],o,null):vt(s[0]),null)},i(s){n||(D(r,s),n=!0)},o(s){N(r,s),n=!1},d(s){s&&L(t),r&&r.d(s)}}}function ej(e,t,n){let{$$slots:i={},$$scope:r}=t;return e.$$set=s=>{"$$scope"in s&&n(0,r=s.$$scope)},[r,i]}class tj extends xe{constructor(t){super(),ve(this,t,ej,QU,ye,{})}}function nj(e){let t,n;const i=e[1].default,r=mt(i,e,e[0],null);return{c(){t=G("table"),r&&r.c()},m(s,o){I(s,t,o),r&&r.m(t,null),n=!0},p(s,[o]){r&&r.p&&(!n||o&1)&&bt(r,i,s,s[0],n?yt(i,s[0],o,null):vt(s[0]),null)},i(s){n||(D(r,s),n=!0)},o(s){N(r,s),n=!1},d(s){s&&L(t),r&&r.d(s)}}}function ij(e,t,n){let{$$slots:i={},$$scope:r}=t;return e.$$set=s=>{"$$scope"in s&&n(0,r=s.$$scope)},[r,i]}let rj=class extends xe{constructor(t){super(),ve(this,t,ij,nj,ye,{})}};function sj(e){let t,n;const i=e[1].default,r=mt(i,e,e[0],null);return{c(){t=G("thead"),r&&r.c()},m(s,o){I(s,t,o),r&&r.m(t,null),n=!0},p(s,[o]){r&&r.p&&(!n||o&1)&&bt(r,i,s,s[0],n?yt(i,s[0],o,null):vt(s[0]),null)},i(s){n||(D(r,s),n=!0)},o(s){N(r,s),n=!1},d(s){s&&L(t),r&&r.d(s)}}}function oj(e,t,n){let{$$slots:i={},$$scope:r}=t;return e.$$set=s=>{"$$scope"in s&&n(0,r=s.$$scope)},[r,i]}class aj extends xe{constructor(t){super(),ve(this,t,oj,sj,ye,{})}}function uj(e){let t,n;const i=e[1].default,r=mt(i,e,e[0],null);return{c(){t=G("tbody"),r&&r.c()},m(s,o){I(s,t,o),r&&r.m(t,null),n=!0},p(s,[o]){r&&r.p&&(!n||o&1)&&bt(r,i,s,s[0],n?yt(i,s[0],o,null):vt(s[0]),null)},i(s){n||(D(r,s),n=!0)},o(s){N(r,s),n=!1},d(s){s&&L(t),r&&r.d(s)}}}function lj(e,t,n){let{$$slots:i={},$$scope:r}=t;return e.$$set=s=>{"$$scope"in s&&n(0,r=s.$$scope)},[r,i]}class cj extends xe{constructor(t){super(),ve(this,t,lj,uj,ye,{})}}function fj(e){let t,n;const i=e[1].default,r=mt(i,e,e[0],null);return{c(){t=G("tr"),r&&r.c()},m(s,o){I(s,t,o),r&&r.m(t,null),n=!0},p(s,[o]){r&&r.p&&(!n||o&1)&&bt(r,i,s,s[0],n?yt(i,s[0],o,null):vt(s[0]),null)},i(s){n||(D(r,s),n=!0)},o(s){N(r,s),n=!1},d(s){s&&L(t),r&&r.d(s)}}}function dj(e,t,n){let{$$slots:i={},$$scope:r}=t;return e.$$set=s=>{"$$scope"in s&&n(0,r=s.$$scope)},[r,i]}class hj extends xe{constructor(t){super(),ve(this,t,dj,fj,ye,{})}}function pj(e){let t,n;const i=e[3].default,r=mt(i,e,e[2],null);return{c(){t=G("td"),r&&r.c(),M(t,"align",e[1])},m(s,o){I(s,t,o),r&&r.m(t,null),n=!0},p(s,o){r&&r.p&&(!n||o&4)&&bt(r,i,s,s[2],n?yt(i,s[2],o,null):vt(s[2]),null),(!n||o&2)&&M(t,"align",s[1])},i(s){n||(D(r,s),n=!0)},o(s){N(r,s),n=!1},d(s){s&&L(t),r&&r.d(s)}}}function gj(e){let t,n;const i=e[3].default,r=mt(i,e,e[2],null);return{c(){t=G("th"),r&&r.c(),M(t,"align",e[1])},m(s,o){I(s,t,o),r&&r.m(t,null),n=!0},p(s,o){r&&r.p&&(!n||o&4)&&bt(r,i,s,s[2],n?yt(i,s[2],o,null):vt(s[2]),null),(!n||o&2)&&M(t,"align",s[1])},i(s){n||(D(r,s),n=!0)},o(s){N(r,s),n=!1},d(s){s&&L(t),r&&r.d(s)}}}function mj(e){let t,n,i,r;const s=[gj,pj],o=[];function a(u,l){return u[0]?0:1}return t=a(e),n=o[t]=s[t](e),{c(){n.c(),i=Ne()},m(u,l){o[t].m(u,l),I(u,i,l),r=!0},p(u,[l]){let c=t;t=a(u),t===c?o[t].p(u,l):(Se(),N(o[c],1,1,()=>{o[c]=null}),Fe(),n=o[t],n?n.p(u,l):(n=o[t]=s[t](u),n.c()),D(n,1),n.m(i.parentNode,i))},i(u){r||(D(n),r=!0)},o(u){N(n),r=!1},d(u){u&&L(i),o[t].d(u)}}}function yj(e,t,n){let{$$slots:i={},$$scope:r}=t,{header:s}=t,{align:o}=t;return e.$$set=a=>{"header"in a&&n(0,s=a.header),"align"in a&&n(1,o=a.align),"$$scope"in a&&n(2,r=a.$$scope)},[s,o,r,i]}class bj extends xe{constructor(t){super(),ve(this,t,yj,mj,ye,{header:0,align:1})}}function vj(e){let t,n;const i=e[3].default,r=mt(i,e,e[2],null);return{c(){t=G("ul"),r&&r.c()},m(s,o){I(s,t,o),r&&r.m(t,null),n=!0},p(s,o){r&&r.p&&(!n||o&4)&&bt(r,i,s,s[2],n?yt(i,s[2],o,null):vt(s[2]),null)},i(s){n||(D(r,s),n=!0)},o(s){N(r,s),n=!1},d(s){s&&L(t),r&&r.d(s)}}}function xj(e){let t,n;const i=e[3].default,r=mt(i,e,e[2],null);return{c(){t=G("ol"),r&&r.c(),M(t,"start",e[1])},m(s,o){I(s,t,o),r&&r.m(t,null),n=!0},p(s,o){r&&r.p&&(!n||o&4)&&bt(r,i,s,s[2],n?yt(i,s[2],o,null):vt(s[2]),null),(!n||o&2)&&M(t,"start",s[1])},i(s){n||(D(r,s),n=!0)},o(s){N(r,s),n=!1},d(s){s&&L(t),r&&r.d(s)}}}function _j(e){let t,n,i,r;const s=[xj,vj],o=[];function a(u,l){return u[0]?0:1}return t=a(e),n=o[t]=s[t](e),{c(){n.c(),i=Ne()},m(u,l){o[t].m(u,l),I(u,i,l),r=!0},p(u,[l]){let c=t;t=a(u),t===c?o[t].p(u,l):(Se(),N(o[c],1,1,()=>{o[c]=null}),Fe(),n=o[t],n?n.p(u,l):(n=o[t]=s[t](u),n.c()),D(n,1),n.m(i.parentNode,i))},i(u){r||(D(n),r=!0)},o(u){N(n),r=!1},d(u){u&&L(i),o[t].d(u)}}}function wj(e,t,n){let{$$slots:i={},$$scope:r}=t,{ordered:s}=t,{start:o}=t;return e.$$set=a=>{"ordered"in a&&n(0,s=a.ordered),"start"in a&&n(1,o=a.start),"$$scope"in a&&n(2,r=a.$$scope)},[s,o,r,i]}class Ej extends xe{constructor(t){super(),ve(this,t,wj,_j,ye,{ordered:0,start:1})}}function Cj(e){let t,n;const i=e[1].default,r=mt(i,e,e[0],null);return{c(){t=G("li"),r&&r.c()},m(s,o){I(s,t,o),r&&r.m(t,null),n=!0},p(s,[o]){r&&r.p&&(!n||o&1)&&bt(r,i,s,s[0],n?yt(i,s[0],o,null):vt(s[0]),null)},i(s){n||(D(r,s),n=!0)},o(s){N(r,s),n=!1},d(s){s&&L(t),r&&r.d(s)}}}function kj(e,t,n){let{$$slots:i={},$$scope:r}=t;return e.$$set=s=>{"$$scope"in s&&n(0,r=s.$$scope)},[r,i]}class Aj extends xe{constructor(t){super(),ve(this,t,kj,Cj,ye,{})}}function $j(e){let t;return{c(){t=G("hr")},m(n,i){I(n,t,i)},p:ie,i:ie,o:ie,d(n){n&&L(t)}}}class Sj extends xe{constructor(t){super(),ve(this,t,null,$j,ye,{})}}function Fj(e){let t,n;return{c(){t=new Mz(!1),n=Ne(),t.a=n},m(i,r){t.m(e[0],i,r),I(i,n,r)},p(i,[r]){r&1&&t.p(i[0])},i:ie,o:ie,d(i){i&&(L(n),t.d())}}}function Dj(e,t,n){let{text:i}=t;return e.$$set=r=>{"text"in r&&n(0,i=r.text)},[i]}class Tj extends xe{constructor(t){super(),ve(this,t,Dj,Fj,ye,{text:0})}}function Mj(e){let t,n;const i=e[1].default,r=mt(i,e,e[0],null);return{c(){t=G("blockquote"),r&&r.c()},m(s,o){I(s,t,o),r&&r.m(t,null),n=!0},p(s,[o]){r&&r.p&&(!n||o&1)&&bt(r,i,s,s[0],n?yt(i,s[0],o,null):vt(s[0]),null)},i(s){n||(D(r,s),n=!0)},o(s){N(r,s),n=!1},d(s){s&&L(t),r&&r.d(s)}}}function Nj(e,t,n){let{$$slots:i={},$$scope:r}=t;return e.$$set=s=>{"$$scope"in s&&n(0,r=s.$$scope)},[r,i]}class Rj extends xe{constructor(t){super(),ve(this,t,Nj,Mj,ye,{})}}function Oj(e){let t,n,i;return{c(){t=G("pre"),n=G("code"),i=Be(e[1]),M(t,"class",e[0])},m(r,s){I(r,t,s),V(t,n),V(n,i)},p(r,[s]){s&2&&ft(i,r[1]),s&1&&M(t,"class",r[0])},i:ie,o:ie,d(r){r&&L(t)}}}function Lj(e,t,n){let{lang:i}=t,{text:r}=t;return e.$$set=s=>{"lang"in s&&n(0,i=s.lang),"text"in s&&n(1,r=s.text)},[i,r]}class Ij extends xe{constructor(t){super(),ve(this,t,Lj,Oj,ye,{lang:0,text:1})}}function Pj(e){let t,n;const i=e[1].default,r=mt(i,e,e[0],null);return{c(){t=G("br"),r&&r.c()},m(s,o){I(s,t,o),r&&r.m(s,o),n=!0},p(s,[o]){r&&r.p&&(!n||o&1)&&bt(r,i,s,s[0],n?yt(i,s[0],o,null):vt(s[0]),null)},i(s){n||(D(r,s),n=!0)},o(s){N(r,s),n=!1},d(s){s&&L(t),r&&r.d(s)}}}function zj(e,t,n){let{$$slots:i={},$$scope:r}=t;return e.$$set=s=>{"$$scope"in s&&n(0,r=s.$$scope)},[r,i]}class Bj extends xe{constructor(t){super(),ve(this,t,zj,Pj,ye,{})}}const Uj={heading:TU,paragraph:RU,text:IU,image:BU,link:qU,em:GU,strong:tj,codespan:JU,del:XU,table:rj,tablehead:aj,tablebody:cj,tablerow:hj,tablecell:bj,list:Ej,orderedlistitem:null,unorderedlistitem:null,listitem:Aj,hr:Sj,html:Tj,blockquote:Rj,code:Ij,br:Bj},jj={baseUrl:null,breaks:!1,gfm:!0,headerIds:!0,headerPrefix:"",highlight:null,langPrefix:"language-",mangle:!0,pedantic:!1,renderer:null,sanitize:!1,sanitizer:null,silent:!1,smartLists:!1,smartypants:!1,tokenizer:null,xhtml:!1};function qj(e){let t,n;return t=new Ca({props:{tokens:e[0],renderers:e[1]}}),{c(){he(t.$$.fragment)},m(i,r){ce(t,i,r),n=!0},p(i,[r]){const s={};r&1&&(s.tokens=i[0]),r&2&&(s.renderers=i[1]),t.$set(s)},i(i){n||(D(t.$$.fragment,i),n=!0)},o(i){N(t.$$.fragment,i),n=!1},d(i){fe(t,i)}}}function Wj(e,t,n){let i,r,s,o,{source:a=[]}=t,{renderers:u={}}=t,{options:l={}}=t,{isInline:c=!1}=t;const f=a2();let d,h,p;return E5(_4,{slug:g=>r?r.slug(g):"",getOptions:()=>s}),Wc(()=>{n(7,p=!0)}),e.$$set=g=>{"source"in g&&n(2,a=g.source),"renderers"in g&&n(3,u=g.renderers),"options"in g&&n(4,l=g.options),"isInline"in g&&n(5,c=g.isInline)},e.$$.update=()=>{e.$$.dirty&4&&n(8,i=Array.isArray(a)),e.$$.dirty&4&&(r=a?new Hh:void 0),e.$$.dirty&16&&n(9,s={...jj,...l}),e.$$.dirty&869&&(i?n(0,d=a):(n(6,h=new ur(s)),n(0,d=c?h.inlineTokens(a):h.lex(a)),f("parsed",{tokens:d}))),e.$$.dirty&8&&n(1,o={...Uj,...u}),e.$$.dirty&385&&p&&!i&&f("parsed",{tokens:d})},[d,o,a,u,l,c,h,p,i,s]}class Hj extends xe{constructor(t){super(),ve(this,t,Wj,qj,ye,{source:2,renderers:3,options:4,isInline:5})}}function Gj(e){let t,n;return t=new Hj({props:{source:e[0].source}}),{c(){he(t.$$.fragment)},m(i,r){ce(t,i,r),n=!0},p(i,[r]){const s={};r&1&&(s.source=i[0].source),t.$set(s)},i(i){n||(D(t.$$.fragment,i),n=!0)},o(i){N(t.$$.fragment,i),n=!1},d(i){fe(t,i)}}}function Vj(e,t,n){let{componentData:i}=t;return e.$$set=r=>{"componentData"in r&&n(0,i=r.componentData)},[i]}class w4 extends xe{constructor(t){super(),ve(this,t,Vj,Gj,ye,{componentData:0})}}function Yj(e){let t,n,i;const r=e[2].default,s=mt(r,e,e[1],null);return{c(){var o;t=G("div"),s&&s.c(),M(t,"id",n=`page-${((o=e[0])==null?void 0:o.title)||"No Title"}`),M(t,"class","page svelte-v7ihqd"),M(t,"data-component","page")},m(o,a){I(o,t,a),s&&s.m(t,null),i=!0},p(o,[a]){var u;s&&s.p&&(!i||a&2)&&bt(s,r,o,o[1],i?yt(r,o[1],a,null):vt(o[1]),null),(!i||a&1&&n!==(n=`page-${((u=o[0])==null?void 0:u.title)||"No Title"}`))&&M(t,"id",n)},i(o){i||(D(s,o),i=!0)},o(o){N(s,o),i=!1},d(o){o&&L(t),s&&s.d(o)}}}function Xj(e,t,n){let{$$slots:i={},$$scope:r}=t,{componentData:s}=t;return e.$$set=o=>{"componentData"in o&&n(0,s=o.componentData),"$$scope"in o&&n(1,r=o.$$scope)},[s,r,i]}class Kj extends xe{constructor(t){super(),ve(this,t,Xj,Yj,ye,{componentData:0})}}function E4(e){let t,n,i=e[5]&&C4(e),r=e[4]&&k4(e);return{c(){t=G("div"),i&&i.c(),n=He(),r&&r.c(),M(t,"class","info svelte-ljrmzp")},m(s,o){I(s,t,o),i&&i.m(t,null),V(t,n),r&&r.m(t,null)},p(s,o){s[5]?i?i.p(s,o):(i=C4(s),i.c(),i.m(t,n)):i&&(i.d(1),i=null),s[4]?r?r.p(s,o):(r=k4(s),r.c(),r.m(t,null)):r&&(r.d(1),r=null)},d(s){s&&L(t),i&&i.d(),r&&r.d()}}}function C4(e){let t,n,i,r,s;return{c(){t=G("label"),n=Be(e[5]),i=He(),r=G("span"),s=Be(e[3]),M(r,"class","labelValue svelte-ljrmzp"),M(t,"for",e[6]),M(t,"class","svelte-ljrmzp")},m(o,a){I(o,t,a),V(t,n),V(t,i),V(t,r),V(r,s)},p(o,a){a&32&&ft(n,o[5]),a&8&&ft(s,o[3]),a&64&&M(t,"for",o[6])},d(o){o&&L(t)}}}function k4(e){let t,n;return{c(){t=G("span"),n=Be(e[4]),M(t,"title",e[4]),M(t,"class","details svelte-ljrmzp")},m(i,r){I(i,t,r),V(t,n)},p(i,r){r&16&&ft(n,i[4]),r&16&&M(t,"title",i[4])},d(i){i&&L(t)}}}function Zj(e){let t,n,i,r,s,o=(e[0]||"")+"",a,u=(e[5]||e[4])&&E4(e);return{c(){t=G("div"),n=G("div"),u&&u.c(),i=He(),r=G("progress"),s=Be(e[1]),a=Be(o),M(r,"id",e[6]),M(r,"max",e[2]),r.value=e[1],M(r,"style","color: red !important"),M(r,"class","svelte-ljrmzp"),M(n,"class","inner svelte-ljrmzp"),M(t,"class","container svelte-ljrmzp")},m(l,c){I(l,t,c),V(t,n),u&&u.m(n,null),V(n,i),V(n,r),V(r,s),V(r,a)},p(l,[c]){l[5]||l[4]?u?u.p(l,c):(u=E4(l),u.c(),u.m(n,i)):u&&(u.d(1),u=null),c&2&&ft(s,l[1]),c&1&&o!==(o=(l[0]||"")+"")&&ft(a,o),c&64&&M(r,"id",l[6]),c&4&&M(r,"max",l[2]),c&2&&(r.value=l[1])},i:ie,o:ie,d(l){l&&L(t),u&&u.d()}}}function Jj(e,t,n){let i,r,s,o,a,u,{componentData:l}=t;s==null&&(s=0);let c=s.toString();return e.$$set=f=>{"componentData"in f&&n(7,l=f.componentData)},e.$$.update=()=>{e.$$.dirty&128&&n(2,{max:i,id:r,value:s,label:o,unit:a,details:u}=l,i,(n(6,r),n(7,l)),(n(1,s),n(7,l)),(n(5,o),n(7,l)),(n(0,a),n(7,l)),(n(4,u),n(7,l))),e.$$.dirty&7&&(i?n(3,c=`${s}/${i}`):a&&n(3,c=`${s} ${a}`))},[a,s,i,c,u,o,r,l]}class A4 extends xe{constructor(t){super(),ve(this,t,Jj,Zj,ye,{componentData:7})}}function $4(e){let t,n;return{c(){t=G("h3"),n=Be(e[3])},m(i,r){I(i,t,r),V(t,n)},p(i,r){r&8&&ft(n,i[3])},d(i){i&&L(t)}}}function S4(e){let t,n;return{c(){t=G("p"),n=Be(e[2]),M(t,"class","description")},m(i,r){I(i,t,r),V(t,n)},p(i,r){r&4&&ft(n,i[2])},d(i){i&&L(t)}}}function Qj(e){let t,n,i,r,s,o,a,u,l=e[3]&&$4(e),c=e[2]&&S4(e);const f=e[6].default,d=mt(f,e,e[5],null);return{c(){t=G("section"),n=G("div"),l&&l.c(),i=He(),c&&c.c(),r=He(),s=G("div"),d&&d.c(),o=He(),a=G("hr"),M(n,"class","heading svelte-17n0qr8"),M(s,"class","sectionItems svelte-17n0qr8"),M(s,"style",e[0]),M(a,"class","svelte-17n0qr8"),M(t,"class","container svelte-17n0qr8"),M(t,"data-component","section"),M(t,"data-section-id",e[3]),ni(t,"columns",e[1])},m(h,p){I(h,t,p),V(t,n),l&&l.m(n,null),V(n,i),c&&c.m(n,null),V(t,r),V(t,s),d&&d.m(s,null),V(t,o),V(t,a),u=!0},p(h,[p]){h[3]?l?l.p(h,p):(l=$4(h),l.c(),l.m(n,i)):l&&(l.d(1),l=null),h[2]?c?c.p(h,p):(c=S4(h),c.c(),c.m(n,null)):c&&(c.d(1),c=null),d&&d.p&&(!u||p&32)&&bt(d,f,h,h[5],u?yt(f,h[5],p,null):vt(h[5]),null),(!u||p&1)&&M(s,"style",h[0]),(!u||p&8)&&M(t,"data-section-id",h[3]),(!u||p&2)&&ni(t,"columns",h[1])},i(h){u||(D(d,h),u=!0)},o(h){N(d,h),u=!1},d(h){h&&L(t),l&&l.d(),c&&c.d(),d&&d.d(h)}}}function eq(e,t,n){let i,r,s,{$$slots:o={},$$scope:a}=t,{componentData:u}=t,l;return s&&(l=`grid-template-columns: repeat(${s||1}, 1fr);`),e.$$set=c=>{"componentData"in c&&n(4,u=c.componentData),"$$scope"in c&&n(5,a=c.$$scope)},e.$$.update=()=>{e.$$.dirty&16&&n(3,{title:i,subtitle:r,columns:s}=u,i,(n(2,r),n(4,u)),(n(1,s),n(4,u)))},[l,s,r,i,u,a,o]}class tq extends xe{constructor(t){super(),ve(this,t,eq,Qj,ye,{componentData:4})}}const nq=/("(?:[^\\"]|\\.)*")|[:,]/g;function y2(e,t={}){const n=JSON.stringify([1],void 0,t.indent===void 0?2:t.indent).slice(2,-3),i=n===""?1/0:t.maxLength===void 0?80:t.maxLength;let{replacer:r}=t;return function s(o,a,u){o&&typeof o.toJSON=="function"&&(o=o.toJSON());const l=JSON.stringify(o,r);if(l===void 0)return l;const c=i-a.length-u;if(l.length<=c){const f=l.replace(nq,(d,h)=>h||`${d} `);if(f.length<=c)return f}if(r!=null&&(o=JSON.parse(l),r=void 0),typeof o=="object"&&o!==null){const f=a+n,d=[];let h=0,p,g;if(Array.isArray(o)){p="[",g="]";const{length:m}=o;for(;h0)return[p,n+d.join(`, +${f}`),g].join(` +${a}`)}return l}(e,"",0)}function ii(e,t,n){return e.fields=t||[],e.fname=n,e}function Tt(e){return e==null?null:e.fname}function wn(e){return e==null?null:e.fields}function F4(e){return e.length===1?iq(e[0]):rq(e)}const iq=e=>function(t){return t[e]},rq=e=>{const t=e.length;return function(n){for(let i=0;io?l():o=a+1:u==="["?(a>o&&l(),r=o=a+1):u==="]"&&(r||q("Access path missing open bracket: "+e),r>0&&l(),r=0,o=a+1)}return r&&q("Access path missing closing bracket: "+e),i&&q("Access path missing closing quote: "+e),a>o&&(a++,l()),t}function Bi(e,t,n){const i=jr(e);return e=i.length===1?i[0]:e,ii((n&&n.get||F4)(i),[e],t||e)}const Vc=Bi("id"),En=ii(e=>e,[],"identity"),bo=ii(()=>0,[],"zero"),tl=ii(()=>1,[],"one"),Ui=ii(()=>!0,[],"true"),vo=ii(()=>!1,[],"false");function sq(e,t,n){const i=[t].concat([].slice.call(n));console[e].apply(console,i)}const D4=0,b2=1,v2=2,T4=3,M4=4;function x2(e,t){let n=arguments.length>2&&arguments[2]!==void 0?arguments[2]:sq,i=e||D4;return{level(r){return arguments.length?(i=+r,this):i},error(){return i>=b2&&n(t||"error","ERROR",arguments),this},warn(){return i>=v2&&n(t||"warn","WARN",arguments),this},info(){return i>=T4&&n(t||"log","INFO",arguments),this},debug(){return i>=M4&&n(t||"log","DEBUG",arguments),this}}}var W=Array.isArray;function re(e){return e===Object(e)}const N4=e=>e!=="__proto__";function nl(){for(var e=arguments.length,t=new Array(e),n=0;n{for(const s in r)if(s==="signals")i.signals=oq(i.signals,r.signals);else{const o=s==="legend"?{layout:1}:s==="style"?!0:null;il(i,s,r[s],o)}return i},{})}function il(e,t,n,i){if(!N4(t))return;let r,s;if(re(n)&&!W(n)){s=re(e[t])?e[t]:e[t]={};for(r in n)i&&(i===!0||i[r])?il(s,r,n[r]):N4(r)&&(s[r]=n[r])}else e[t]=n}function oq(e,t){if(e==null)return t;const n={},i=[];function r(s){n[s.name]||(n[s.name]=1,i.push(s))}return t.forEach(r),e.forEach(r),i}function Ge(e){return e[e.length-1]}function Cn(e){return e==null||e===""?null:+e}const R4=e=>t=>e*Math.exp(t),O4=e=>t=>Math.log(e*t),L4=e=>t=>Math.sign(t)*Math.log1p(Math.abs(t/e)),I4=e=>t=>Math.sign(t)*Math.expm1(Math.abs(t))*e,Gh=e=>t=>t<0?-Math.pow(-t,e):Math.pow(t,e);function Vh(e,t,n,i){const r=n(e[0]),s=n(Ge(e)),o=(s-r)*t;return[i(r-o),i(s-o)]}function P4(e,t){return Vh(e,t,Cn,En)}function z4(e,t){var n=Math.sign(e[0]);return Vh(e,t,O4(n),R4(n))}function B4(e,t,n){return Vh(e,t,Gh(n),Gh(1/n))}function U4(e,t,n){return Vh(e,t,L4(n),I4(n))}function Yh(e,t,n,i,r){const s=i(e[0]),o=i(Ge(e)),a=t!=null?i(t):(s+o)/2;return[r(a+(s-a)*n),r(a+(o-a)*n)]}function _2(e,t,n){return Yh(e,t,n,Cn,En)}function w2(e,t,n){const i=Math.sign(e[0]);return Yh(e,t,n,O4(i),R4(i))}function Xh(e,t,n,i){return Yh(e,t,n,Gh(i),Gh(1/i))}function E2(e,t,n,i){return Yh(e,t,n,L4(i),I4(i))}function j4(e){return 1+~~(new Date(e).getMonth()/3)}function q4(e){return 1+~~(new Date(e).getUTCMonth()/3)}function oe(e){return e!=null?W(e)?e:[e]:[]}function W4(e,t,n){let i=e[0],r=e[1],s;return r=n-t?[t,n]:[i=Math.min(Math.max(i,t),n-s),i+s]}function Ie(e){return typeof e=="function"}const aq="descending";function C2(e,t,n){n=n||{},t=oe(t)||[];const i=[],r=[],s={},o=n.comparator||uq;return oe(e).forEach((a,u)=>{a!=null&&(i.push(t[u]===aq?-1:1),r.push(a=Ie(a)?a:Bi(a,null,n)),(wn(a)||[]).forEach(l=>s[l]=1))}),r.length===0?null:ii(o(r,i),Object.keys(s))}const rl=(e,t)=>(et||t==null)&&e!=null?1:(t=t instanceof Date?+t:t,(e=e instanceof Date?+e:e)!==e&&t===t?-1:t!==t&&e===e?1:0),uq=(e,t)=>e.length===1?lq(e[0],t[0]):cq(e,t,e.length),lq=(e,t)=>function(n,i){return rl(e(n),e(i))*t},cq=(e,t,n)=>(t.push(0),function(i,r){let s,o=0,a=-1;for(;o===0&&++ae}function k2(e,t){let n;return i=>{n&&clearTimeout(n),n=setTimeout(()=>(t(i),n=null),e)}}function Oe(e){for(let t,n,i=1,r=arguments.length;io&&(o=r))}else{for(r=t(e[n]);no&&(o=r))}return[s,o]}function H4(e,t){const n=e.length;let i=-1,r,s,o,a,u;if(t==null){for(;++i=s){r=o=s;break}if(i===n)return[-1,-1];for(a=u=i;++is&&(r=s,a=i),o=s){r=o=s;break}if(i===n)return[-1,-1];for(a=u=i;++is&&(r=s,a=i),o{r.set(s,e[s])}),r}function G4(e,t,n,i,r,s){if(!n&&n!==0)return s;const o=+n;let a=e[0],u=Ge(e),l;us&&(o=r,r=s,s=o),n=n===void 0||n,i=i===void 0||i,(n?r<=e:ra.replace(/\\(.)/g,"$1")):oe(e));const i=e&&e.length,r=n&&n.get||F4,s=a=>r(t?[a]:jr(a));let o;if(!i)o=function(){return""};else if(i===1){const a=s(e[0]);o=function(u){return""+a(u)}}else{const a=e.map(s);o=function(u){let l=""+a[0](u),c=0;for(;++c{t={},n={},i=0},s=(o,a)=>(++i>e&&(n=t,t={},i=1),t[o]=a);return r(),{clear:r,has:o=>ue(t,o)||ue(n,o),get:o=>ue(t,o)?t[o]:ue(n,o)?s(o,n[o]):void 0,set:(o,a)=>ue(t,o)?t[o]=a:s(o,a)}}function K4(e,t,n,i){const r=t.length,s=n.length;if(!s)return t;if(!r)return n;const o=i||new t.constructor(r+s);let a=0,u=0,l=0;for(;a0?n[u++]:t[a++];for(;a=0;)n+=e;return n}function Z4(e,t,n,i){const r=n||" ",s=e+"",o=t-s.length;return o<=0?s:i==="left"?Yc(r,o)+s:i==="center"?Yc(r,~~(o/2))+s+Yc(r,Math.ceil(o/2)):s+Yc(r,o)}function Xc(e){return e&&Ge(e)-e[0]||0}function Q(e){return W(e)?"["+e.map(Q)+"]":re(e)||se(e)?JSON.stringify(e).replace("\u2028","\\u2028").replace("\u2029","\\u2029"):e}function S2(e){return e==null||e===""?null:!e||e==="false"||e==="0"?!1:!!e}const dq=e=>Ze(e)||_o(e)?e:Date.parse(e);function F2(e,t){return t=t||dq,e==null||e===""?null:t(e)}function D2(e){return e==null||e===""?null:e+""}function lr(e){const t={},n=e.length;for(let i=0;i9999?"+"+ri(e,6):ri(e,4)}function gq(e){var t=e.getUTCHours(),n=e.getUTCMinutes(),i=e.getUTCSeconds(),r=e.getUTCMilliseconds();return isNaN(e)?"Invalid Date":pq(e.getUTCFullYear())+"-"+ri(e.getUTCMonth()+1,2)+"-"+ri(e.getUTCDate(),2)+(r?"T"+ri(t,2)+":"+ri(n,2)+":"+ri(i,2)+"."+ri(r,3)+"Z":i?"T"+ri(t,2)+":"+ri(n,2)+":"+ri(i,2)+"Z":n||t?"T"+ri(t,2)+":"+ri(n,2)+"Z":"")}function mq(e){var t=new RegExp('["'+e+` +\r]`),n=e.charCodeAt(0);function i(f,d){var h,p,g=r(f,function(m,y){if(h)return h(m,y-1);p=m,h=d?hq(m,d):e8(m)});return g.columns=p||[],g}function r(f,d){var h=[],p=f.length,g=0,m=0,y,b=p<=0,v=!1;f.charCodeAt(p-1)===Kc&&--p,f.charCodeAt(p-1)===N2&&--p;function x(){if(b)return T2;if(v)return v=!1,Q4;var E,w=g,C;if(f.charCodeAt(w)===M2){for(;g++=p?b=!0:(C=f.charCodeAt(g++))===Kc?v=!0:C===N2&&(v=!0,f.charCodeAt(g)===Kc&&++g),f.slice(w+1,E-1).replace(/""/g,'"')}for(;g1)i=Cq(e,t,n);else for(r=0,i=new Array(s=e.arcs.length);rt?1:e>=t?0:NaN}function kq(e,t){return e==null||t==null?NaN:te?1:t>=e?0:NaN}function al(e){let t,n,i;e.length!==2?(t=Ds,n=(a,u)=>Ds(e(a),u),i=(a,u)=>e(a)-u):(t=e===Ds||e===kq?e:Aq,n=e,i=e);function r(a,u,l=0,c=a.length){if(l>>1;n(a[f],u)<0?l=f+1:c=f}while(l>>1;n(a[f],u)<=0?l=f+1:c=f}while(ll&&i(a[f-1],u)>-i(a[f],u)?f-1:f}return{left:r,center:o,right:s}}function Aq(){return 0}function r8(e){return e===null?NaN:+e}function*$q(e,t){if(t===void 0)for(let n of e)n!=null&&(n=+n)>=n&&(yield n);else{let n=-1;for(let i of e)(i=t(i,++n,e))!=null&&(i=+i)>=i&&(yield i)}}const s8=al(Ds),Eo=s8.right,Sq=s8.left;al(r8).center;function Fq(e,t){let n=0,i,r=0,s=0;if(t===void 0)for(let o of e)o!=null&&(o=+o)>=o&&(i=o-r,r+=i/++n,s+=i*(o-r));else{let o=-1;for(let a of e)(a=t(a,++o,e))!=null&&(a=+a)>=a&&(i=a-r,r+=i/++n,s+=i*(a-r))}if(n>1)return s/(n-1)}function Dq(e,t){const n=Fq(e,t);return n&&Math.sqrt(n)}class zn{constructor(){this._partials=new Float64Array(32),this._n=0}add(t){const n=this._partials;let i=0;for(let r=0;r0){for(o=t[--n];n>0&&(i=o,r=t[--n],o=i+r,s=r-(o-i),!s););n>0&&(s<0&&t[n-1]<0||s>0&&t[n-1]>0)&&(r=s*2,i=o+r,r==i-o&&(o=i))}return o}}class o8 extends Map{constructor(t,n=l8){if(super(),Object.defineProperties(this,{_intern:{value:new Map},_key:{value:n}}),t!=null)for(const[i,r]of t)this.set(i,r)}get(t){return super.get(R2(this,t))}has(t){return super.has(R2(this,t))}set(t,n){return super.set(a8(this,t),n)}delete(t){return super.delete(u8(this,t))}}class Zh extends Set{constructor(t,n=l8){if(super(),Object.defineProperties(this,{_intern:{value:new Map},_key:{value:n}}),t!=null)for(const i of t)this.add(i)}has(t){return super.has(R2(this,t))}add(t){return super.add(a8(this,t))}delete(t){return super.delete(u8(this,t))}}function R2({_intern:e,_key:t},n){const i=t(n);return e.has(i)?e.get(i):n}function a8({_intern:e,_key:t},n){const i=t(n);return e.has(i)?e.get(i):(e.set(i,n),n)}function u8({_intern:e,_key:t},n){const i=t(n);return e.has(i)&&(n=e.get(i),e.delete(i)),n}function l8(e){return e!==null&&typeof e=="object"?e.valueOf():e}function Tq(e,t){return Array.from(t,n=>e[n])}function Mq(e=Ds){if(e===Ds)return c8;if(typeof e!="function")throw new TypeError("compare is not a function");return(t,n)=>{const i=e(t,n);return i||i===0?i:(e(n,n)===0)-(e(t,t)===0)}}function c8(e,t){return(e==null||!(e>=e))-(t==null||!(t>=t))||(et?1:0)}const Nq=Math.sqrt(50),Rq=Math.sqrt(10),Oq=Math.sqrt(2);function Jh(e,t,n){const i=(t-e)/Math.max(0,n),r=Math.floor(Math.log10(i)),s=i/Math.pow(10,r),o=s>=Nq?10:s>=Rq?5:s>=Oq?2:1;let a,u,l;return r<0?(l=Math.pow(10,-r)/o,a=Math.round(e*l),u=Math.round(t*l),a/lt&&--u,l=-l):(l=Math.pow(10,r)*o,a=Math.round(e/l),u=Math.round(t/l),a*lt&&--u),u0))return[];if(e===t)return[e];const i=t=r))return[];const a=s-r+1,u=new Array(a);if(i)if(o<0)for(let l=0;l=i)&&(n=i);else{let i=-1;for(let r of e)(r=t(r,++i,e))!=null&&(n=r)&&(n=r)}return n}function I2(e,t){let n;if(t===void 0)for(const i of e)i!=null&&(n>i||n===void 0&&i>=i)&&(n=i);else{let i=-1;for(let r of e)(r=t(r,++i,e))!=null&&(n>r||n===void 0&&r>=r)&&(n=r)}return n}function f8(e,t,n=0,i=1/0,r){if(t=Math.floor(t),n=Math.floor(Math.max(0,n)),i=Math.floor(Math.min(e.length-1,i)),!(n<=t&&t<=i))return e;for(r=r===void 0?c8:Mq(r);i>n;){if(i-n>600){const u=i-n+1,l=t-n+1,c=Math.log(u),f=.5*Math.exp(2*c/3),d=.5*Math.sqrt(c*f*(u-f)/u)*(l-u/2<0?-1:1),h=Math.max(n,Math.floor(t-l*f/u+d)),p=Math.min(i,Math.floor(t+(u-l)*f/u+d));f8(e,t,h,p,r)}const s=e[t];let o=n,a=i;for(Zc(e,n,t),r(e[i],s)>0&&Zc(e,n,i);o0;)--a}r(e[n],s)===0?Zc(e,n,a):(++a,Zc(e,a,i)),a<=t&&(n=a+1),t<=a&&(i=a-1)}return e}function Zc(e,t,n){const i=e[t];e[t]=e[n],e[n]=i}function P2(e,t,n){if(e=Float64Array.from($q(e,n)),!(!(i=e.length)||isNaN(t=+t))){if(t<=0||i<2)return I2(e);if(t>=1)return Aa(e);var i,r=(i-1)*t,s=Math.floor(r),o=Aa(f8(e,s).subarray(0,s+1)),a=I2(e.subarray(s+1));return o+(a-o)*(r-s)}}function d8(e,t,n=r8){if(!(!(i=e.length)||isNaN(t=+t))){if(t<=0||i<2)return+n(e[0],0,e);if(t>=1)return+n(e[i-1],i-1,e);var i,r=(i-1)*t,s=Math.floor(r),o=+n(e[s],s,e),a=+n(e[s+1],s+1,e);return o+(a-o)*(r-s)}}function Lq(e,t){let n=0,i=0;if(t===void 0)for(let r of e)r!=null&&(r=+r)>=r&&(++n,i+=r);else{let r=-1;for(let s of e)(s=t(s,++r,e))!=null&&(s=+s)>=s&&(++n,i+=s)}if(n)return i/n}function h8(e,t){return P2(e,.5,t)}function*Iq(e){for(const t of e)yield*t}function p8(e){return Array.from(Iq(e))}function Ei(e,t,n){e=+e,t=+t,n=(r=arguments.length)<2?(t=e,e=0,1):r<3?1:+n;for(var i=-1,r=Math.max(0,Math.ceil((t-e)/n))|0,s=new Array(r);++i=1e21?e.toLocaleString("en").replace(/,/g,""):e.toString(10)}function Qh(e,t){if((n=(e=t?e.toExponential(t-1):e.toExponential()).indexOf("e"))<0)return null;var n,i=e.slice(0,n);return[i.length>1?i[0]+i.slice(2):i,+e.slice(n+1)]}function ul(e){return e=Qh(Math.abs(e)),e?e[1]:NaN}function jq(e,t){return function(n,i){for(var r=n.length,s=[],o=0,a=e[0],u=0;r>0&&a>0&&(u+a+1>i&&(a=Math.max(1,i-u)),s.push(n.substring(r-=a,r+a)),!((u+=a+1)>i));)a=e[o=(o+1)%e.length];return s.reverse().join(t)}}function qq(e){return function(t){return t.replace(/[0-9]/g,function(n){return e[+n]})}}var Wq=/^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;function $a(e){if(!(t=Wq.exec(e)))throw new Error("invalid format: "+e);var t;return new z2({fill:t[1],align:t[2],sign:t[3],symbol:t[4],zero:t[5],width:t[6],comma:t[7],precision:t[8]&&t[8].slice(1),trim:t[9],type:t[10]})}$a.prototype=z2.prototype;function z2(e){this.fill=e.fill===void 0?" ":e.fill+"",this.align=e.align===void 0?">":e.align+"",this.sign=e.sign===void 0?"-":e.sign+"",this.symbol=e.symbol===void 0?"":e.symbol+"",this.zero=!!e.zero,this.width=e.width===void 0?void 0:+e.width,this.comma=!!e.comma,this.precision=e.precision===void 0?void 0:+e.precision,this.trim=!!e.trim,this.type=e.type===void 0?"":e.type+""}z2.prototype.toString=function(){return this.fill+this.align+this.sign+this.symbol+(this.zero?"0":"")+(this.width===void 0?"":Math.max(1,this.width|0))+(this.comma?",":"")+(this.precision===void 0?"":"."+Math.max(0,this.precision|0))+(this.trim?"~":"")+this.type};function Hq(e){e:for(var t=e.length,n=1,i=-1,r;n0&&(i=0);break}return i>0?e.slice(0,i)+e.slice(r+1):e}var m8;function Gq(e,t){var n=Qh(e,t);if(!n)return e+"";var i=n[0],r=n[1],s=r-(m8=Math.max(-8,Math.min(8,Math.floor(r/3)))*3)+1,o=i.length;return s===o?i:s>o?i+new Array(s-o+1).join("0"):s>0?i.slice(0,s)+"."+i.slice(s):"0."+new Array(1-s).join("0")+Qh(e,Math.max(0,t+s-1))[0]}function y8(e,t){var n=Qh(e,t);if(!n)return e+"";var i=n[0],r=n[1];return r<0?"0."+new Array(-r).join("0")+i:i.length>r+1?i.slice(0,r+1)+"."+i.slice(r+1):i+new Array(r-i.length+2).join("0")}const b8={"%":(e,t)=>(e*100).toFixed(t),b:e=>Math.round(e).toString(2),c:e=>e+"",d:Uq,e:(e,t)=>e.toExponential(t),f:(e,t)=>e.toFixed(t),g:(e,t)=>e.toPrecision(t),o:e=>Math.round(e).toString(8),p:(e,t)=>y8(e*100,t),r:y8,s:Gq,X:e=>Math.round(e).toString(16).toUpperCase(),x:e=>Math.round(e).toString(16)};function v8(e){return e}var x8=Array.prototype.map,_8=["y","z","a","f","p","n","Β΅","m","","k","M","G","T","P","E","Z","Y"];function w8(e){var t=e.grouping===void 0||e.thousands===void 0?v8:jq(x8.call(e.grouping,Number),e.thousands+""),n=e.currency===void 0?"":e.currency[0]+"",i=e.currency===void 0?"":e.currency[1]+"",r=e.decimal===void 0?".":e.decimal+"",s=e.numerals===void 0?v8:qq(x8.call(e.numerals,String)),o=e.percent===void 0?"%":e.percent+"",a=e.minus===void 0?"βˆ’":e.minus+"",u=e.nan===void 0?"NaN":e.nan+"";function l(f){f=$a(f);var d=f.fill,h=f.align,p=f.sign,g=f.symbol,m=f.zero,y=f.width,b=f.comma,v=f.precision,x=f.trim,_=f.type;_==="n"?(b=!0,_="g"):b8[_]||(v===void 0&&(v=12),x=!0,_="g"),(m||d==="0"&&h==="=")&&(m=!0,d="0",h="=");var E=g==="$"?n:g==="#"&&/[boxX]/.test(_)?"0"+_.toLowerCase():"",w=g==="$"?i:/[%p]/.test(_)?o:"",C=b8[_],k=/[defgprs%]/.test(_);v=v===void 0?6:/[gprs]/.test(_)?Math.max(1,Math.min(21,v)):Math.max(0,Math.min(20,v));function S($){var O=E,R=w,F,A,T;if(_==="c")R=C($)+R,$="";else{$=+$;var B=$<0||1/$<0;if($=isNaN($)?u:C(Math.abs($),v),x&&($=Hq($)),B&&+$==0&&p!=="+"&&(B=!1),O=(B?p==="("?p:a:p==="-"||p==="("?"":p)+O,R=(_==="s"?_8[8+m8/3]:"")+R+(B&&p==="("?")":""),k){for(F=-1,A=$.length;++FT||T>57){R=(T===46?r+$.slice(F+1):$.slice(F))+R,$=$.slice(0,F);break}}}b&&!m&&($=t($,1/0));var H=O.length+$.length+R.length,z=H>1)+O+$+R+z.slice(H);break;default:$=z+O+$+R;break}return s($)}return S.toString=function(){return f+""},S}function c(f,d){var h=l((f=$a(f),f.type="f",f)),p=Math.max(-8,Math.min(8,Math.floor(ul(d)/3)))*3,g=Math.pow(10,-p),m=_8[8+p/3];return function(y){return h(g*y)+m}}return{format:l,formatPrefix:c}}var e0,t0,B2;Vq({thousands:",",grouping:[3],currency:["$",""]});function Vq(e){return e0=w8(e),t0=e0.format,B2=e0.formatPrefix,e0}function E8(e){return Math.max(0,-ul(Math.abs(e)))}function C8(e,t){return Math.max(0,Math.max(-8,Math.min(8,Math.floor(ul(t)/3)))*3-ul(Math.abs(e)))}function k8(e,t){return e=Math.abs(e),t=Math.abs(t)-e,Math.max(0,ul(t)-ul(e))+1}const U2=new Date,j2=new Date;function qt(e,t,n,i){function r(s){return e(s=arguments.length===0?new Date:new Date(+s)),s}return r.floor=s=>(e(s=new Date(+s)),s),r.ceil=s=>(e(s=new Date(s-1)),t(s,1),e(s),s),r.round=s=>{const o=r(s),a=r.ceil(s);return s-o(t(s=new Date(+s),o==null?1:Math.floor(o)),s),r.range=(s,o,a)=>{const u=[];if(s=r.ceil(s),a=a==null?1:Math.floor(a),!(s0))return u;let l;do u.push(l=new Date(+s)),t(s,a),e(s);while(lqt(o=>{if(o>=o)for(;e(o),!s(o);)o.setTime(o-1)},(o,a)=>{if(o>=o)if(a<0)for(;++a<=0;)for(;t(o,-1),!s(o););else for(;--a>=0;)for(;t(o,1),!s(o););}),n&&(r.count=(s,o)=>(U2.setTime(+s),j2.setTime(+o),e(U2),e(j2),Math.floor(n(U2,j2))),r.every=s=>(s=Math.floor(s),!isFinite(s)||!(s>0)?null:s>1?r.filter(i?o=>i(o)%s===0:o=>r.count(0,o)%s===0):r)),r}const ll=qt(()=>{},(e,t)=>{e.setTime(+e+t)},(e,t)=>t-e);ll.every=e=>(e=Math.floor(e),!isFinite(e)||!(e>0)?null:e>1?qt(t=>{t.setTime(Math.floor(t/e)*e)},(t,n)=>{t.setTime(+t+n*e)},(t,n)=>(n-t)/e):ll),ll.range;const Ts=1e3,ji=Ts*60,Ms=ji*60,Ns=Ms*24,q2=Ns*7,A8=Ns*30,W2=Ns*365,Rs=qt(e=>{e.setTime(e-e.getMilliseconds())},(e,t)=>{e.setTime(+e+t*Ts)},(e,t)=>(t-e)/Ts,e=>e.getUTCSeconds());Rs.range;const n0=qt(e=>{e.setTime(e-e.getMilliseconds()-e.getSeconds()*Ts)},(e,t)=>{e.setTime(+e+t*ji)},(e,t)=>(t-e)/ji,e=>e.getMinutes());n0.range;const i0=qt(e=>{e.setUTCSeconds(0,0)},(e,t)=>{e.setTime(+e+t*ji)},(e,t)=>(t-e)/ji,e=>e.getUTCMinutes());i0.range;const r0=qt(e=>{e.setTime(e-e.getMilliseconds()-e.getSeconds()*Ts-e.getMinutes()*ji)},(e,t)=>{e.setTime(+e+t*Ms)},(e,t)=>(t-e)/Ms,e=>e.getHours());r0.range;const s0=qt(e=>{e.setUTCMinutes(0,0,0)},(e,t)=>{e.setTime(+e+t*Ms)},(e,t)=>(t-e)/Ms,e=>e.getUTCHours());s0.range;const Os=qt(e=>e.setHours(0,0,0,0),(e,t)=>e.setDate(e.getDate()+t),(e,t)=>(t-e-(t.getTimezoneOffset()-e.getTimezoneOffset())*ji)/Ns,e=>e.getDate()-1);Os.range;const ko=qt(e=>{e.setUTCHours(0,0,0,0)},(e,t)=>{e.setUTCDate(e.getUTCDate()+t)},(e,t)=>(t-e)/Ns,e=>e.getUTCDate()-1);ko.range;const $8=qt(e=>{e.setUTCHours(0,0,0,0)},(e,t)=>{e.setUTCDate(e.getUTCDate()+t)},(e,t)=>(t-e)/Ns,e=>Math.floor(e/Ns));$8.range;function Sa(e){return qt(t=>{t.setDate(t.getDate()-(t.getDay()+7-e)%7),t.setHours(0,0,0,0)},(t,n)=>{t.setDate(t.getDate()+n*7)},(t,n)=>(n-t-(n.getTimezoneOffset()-t.getTimezoneOffset())*ji)/q2)}const cl=Sa(0),o0=Sa(1),Yq=Sa(2),Xq=Sa(3),fl=Sa(4),Kq=Sa(5),Zq=Sa(6);cl.range,o0.range,Yq.range,Xq.range,fl.range,Kq.range,Zq.range;function Fa(e){return qt(t=>{t.setUTCDate(t.getUTCDate()-(t.getUTCDay()+7-e)%7),t.setUTCHours(0,0,0,0)},(t,n)=>{t.setUTCDate(t.getUTCDate()+n*7)},(t,n)=>(n-t)/q2)}const dl=Fa(0),a0=Fa(1),Jq=Fa(2),Qq=Fa(3),hl=Fa(4),eW=Fa(5),tW=Fa(6);dl.range,a0.range,Jq.range,Qq.range,hl.range,eW.range,tW.range;const Jc=qt(e=>{e.setDate(1),e.setHours(0,0,0,0)},(e,t)=>{e.setMonth(e.getMonth()+t)},(e,t)=>t.getMonth()-e.getMonth()+(t.getFullYear()-e.getFullYear())*12,e=>e.getMonth());Jc.range;const Qc=qt(e=>{e.setUTCDate(1),e.setUTCHours(0,0,0,0)},(e,t)=>{e.setUTCMonth(e.getUTCMonth()+t)},(e,t)=>t.getUTCMonth()-e.getUTCMonth()+(t.getUTCFullYear()-e.getUTCFullYear())*12,e=>e.getUTCMonth());Qc.range;const Wr=qt(e=>{e.setMonth(0,1),e.setHours(0,0,0,0)},(e,t)=>{e.setFullYear(e.getFullYear()+t)},(e,t)=>t.getFullYear()-e.getFullYear(),e=>e.getFullYear());Wr.every=e=>!isFinite(e=Math.floor(e))||!(e>0)?null:qt(t=>{t.setFullYear(Math.floor(t.getFullYear()/e)*e),t.setMonth(0,1),t.setHours(0,0,0,0)},(t,n)=>{t.setFullYear(t.getFullYear()+n*e)}),Wr.range;const Hr=qt(e=>{e.setUTCMonth(0,1),e.setUTCHours(0,0,0,0)},(e,t)=>{e.setUTCFullYear(e.getUTCFullYear()+t)},(e,t)=>t.getUTCFullYear()-e.getUTCFullYear(),e=>e.getUTCFullYear());Hr.every=e=>!isFinite(e=Math.floor(e))||!(e>0)?null:qt(t=>{t.setUTCFullYear(Math.floor(t.getUTCFullYear()/e)*e),t.setUTCMonth(0,1),t.setUTCHours(0,0,0,0)},(t,n)=>{t.setUTCFullYear(t.getUTCFullYear()+n*e)}),Hr.range;function S8(e,t,n,i,r,s){const o=[[Rs,1,Ts],[Rs,5,5*Ts],[Rs,15,15*Ts],[Rs,30,30*Ts],[s,1,ji],[s,5,5*ji],[s,15,15*ji],[s,30,30*ji],[r,1,Ms],[r,3,3*Ms],[r,6,6*Ms],[r,12,12*Ms],[i,1,Ns],[i,2,2*Ns],[n,1,q2],[t,1,A8],[t,3,3*A8],[e,1,W2]];function a(l,c,f){const d=cm).right(o,d);if(h===o.length)return e.every(Co(l/W2,c/W2,f));if(h===0)return ll.every(Math.max(Co(l,c,f),1));const[p,g]=o[d/o[h-1][2](e[t]=1+n,e),{});function V2(e){const t=oe(e).slice(),n={};return t.length||q("Missing time unit."),t.forEach(r=>{ue(G2,r)?n[r]=1:q(`Invalid time unit: ${r}.`)}),(n[Wt]||n[$n]?1:0)+(n[si]||n[An]||n[oi]?1:0)+(n[Gr]?1:0)>1&&q(`Incompatible time units: ${e}`),t.sort((r,s)=>G2[r]-G2[s]),t}const oW={[cn]:"%Y ",[si]:"Q%q ",[An]:"%b ",[oi]:"%d ",[Wt]:"W%U ",[$n]:"%a ",[Gr]:"%j ",[Ci]:"%H:00",[ki]:"00:%M",[qi]:":%S",[cr]:".%L",[`${cn}-${An}`]:"%Y-%m ",[`${cn}-${An}-${oi}`]:"%Y-%m-%d ",[`${Ci}-${ki}`]:"%H:%M"};function F8(e,t){const n=Oe({},oW,t),i=V2(e),r=i.length;let s="",o=0,a,u;for(o=0;oo;--a)if(u=i.slice(o,a).join("-"),n[u]!=null){s+=n[u],o=a;break}return s.trim()}const Da=new Date;function Y2(e){return Da.setFullYear(e),Da.setMonth(0),Da.setDate(1),Da.setHours(0,0,0,0),Da}function D8(e){return M8(new Date(e))}function T8(e){return X2(new Date(e))}function M8(e){return Os.count(Y2(e.getFullYear())-1,e)}function X2(e){return cl.count(Y2(e.getFullYear())-1,e)}function K2(e){return Y2(e).getDay()}function aW(e,t,n,i,r,s,o){if(0<=e&&e<100){const a=new Date(-1,t,n,i,r,s,o);return a.setFullYear(e),a}return new Date(e,t,n,i,r,s,o)}function N8(e){return O8(new Date(e))}function R8(e){return Z2(new Date(e))}function O8(e){const t=Date.UTC(e.getUTCFullYear(),0,1);return ko.count(t-1,e)}function Z2(e){const t=Date.UTC(e.getUTCFullYear(),0,1);return dl.count(t-1,e)}function J2(e){return Da.setTime(Date.UTC(e,0,1)),Da.getUTCDay()}function uW(e,t,n,i,r,s,o){if(0<=e&&e<100){const a=new Date(Date.UTC(-1,t,n,i,r,s,o));return a.setUTCFullYear(n.y),a}return new Date(Date.UTC(e,t,n,i,r,s,o))}function L8(e,t,n,i,r){const s=t||1,o=Ge(e),a=(y,b,v)=>(v=v||y,lW(n[v],i[v],y===o&&s,b)),u=new Date,l=lr(e),c=l[cn]?a(cn):kn(2012),f=l[An]?a(An):l[si]?a(si):bo,d=l[Wt]&&l[$n]?a($n,1,Wt+$n):l[Wt]?a(Wt,1):l[$n]?a($n,1):l[oi]?a(oi,1):l[Gr]?a(Gr,1):tl,h=l[Ci]?a(Ci):bo,p=l[ki]?a(ki):bo,g=l[qi]?a(qi):bo,m=l[cr]?a(cr):bo;return function(y){u.setTime(+y);const b=c(u);return r(b,f(u),d(u,b),h(u),p(u),g(u),m(u))}}function lW(e,t,n,i){const r=n<=1?e:i?(s,o)=>i+n*Math.floor((e(s,o)-i)/n):(s,o)=>n*Math.floor(e(s,o)/n);return t?(s,o)=>t(r(s,o),o):r}function pl(e,t,n){return t+e*7-(n+6)%7}const cW={[cn]:e=>e.getFullYear(),[si]:e=>Math.floor(e.getMonth()/3),[An]:e=>e.getMonth(),[oi]:e=>e.getDate(),[Ci]:e=>e.getHours(),[ki]:e=>e.getMinutes(),[qi]:e=>e.getSeconds(),[cr]:e=>e.getMilliseconds(),[Gr]:e=>M8(e),[Wt]:e=>X2(e),[Wt+$n]:(e,t)=>pl(X2(e),e.getDay(),K2(t)),[$n]:(e,t)=>pl(1,e.getDay(),K2(t))},fW={[si]:e=>3*e,[Wt]:(e,t)=>pl(e,0,K2(t))};function I8(e,t){return L8(e,t||1,cW,fW,aW)}const dW={[cn]:e=>e.getUTCFullYear(),[si]:e=>Math.floor(e.getUTCMonth()/3),[An]:e=>e.getUTCMonth(),[oi]:e=>e.getUTCDate(),[Ci]:e=>e.getUTCHours(),[ki]:e=>e.getUTCMinutes(),[qi]:e=>e.getUTCSeconds(),[cr]:e=>e.getUTCMilliseconds(),[Gr]:e=>O8(e),[Wt]:e=>Z2(e),[$n]:(e,t)=>pl(1,e.getUTCDay(),J2(t)),[Wt+$n]:(e,t)=>pl(Z2(e),e.getUTCDay(),J2(t))},hW={[si]:e=>3*e,[Wt]:(e,t)=>pl(e,0,J2(t))};function P8(e,t){return L8(e,t||1,dW,hW,uW)}const pW={[cn]:Wr,[si]:Jc.every(3),[An]:Jc,[Wt]:cl,[oi]:Os,[$n]:Os,[Gr]:Os,[Ci]:r0,[ki]:n0,[qi]:Rs,[cr]:ll},gW={[cn]:Hr,[si]:Qc.every(3),[An]:Qc,[Wt]:dl,[oi]:ko,[$n]:ko,[Gr]:ko,[Ci]:s0,[ki]:i0,[qi]:Rs,[cr]:ll};function gl(e){return pW[e]}function ml(e){return gW[e]}function z8(e,t,n){return e?e.offset(t,n):void 0}function B8(e,t,n){return z8(gl(e),t,n)}function U8(e,t,n){return z8(ml(e),t,n)}function j8(e,t,n,i){return e?e.range(t,n,i):void 0}function q8(e,t,n,i){return j8(gl(e),t,n,i)}function W8(e,t,n,i){return j8(ml(e),t,n,i)}const ef=1e3,tf=ef*60,nf=tf*60,u0=nf*24,mW=u0*7,H8=u0*30,Q2=u0*365,G8=[cn,An,oi,Ci,ki,qi,cr],rf=G8.slice(0,-1),sf=rf.slice(0,-1),of=sf.slice(0,-1),yW=of.slice(0,-1),bW=[cn,Wt],V8=[cn,An],Y8=[cn],af=[[rf,1,ef],[rf,5,5*ef],[rf,15,15*ef],[rf,30,30*ef],[sf,1,tf],[sf,5,5*tf],[sf,15,15*tf],[sf,30,30*tf],[of,1,nf],[of,3,3*nf],[of,6,6*nf],[of,12,12*nf],[yW,1,u0],[bW,1,mW],[V8,1,H8],[V8,3,3*H8],[Y8,1,Q2]];function X8(e){const t=e.extent,n=e.maxbins||40,i=Math.abs(Xc(t))/n;let r=al(a=>a[2]).right(af,i),s,o;return r===af.length?(s=Y8,o=Co(t[0]/Q2,t[1]/Q2,n)):r?(r=af[i/af[r-1][2]53)return null;"w"in K||(K.w=1),"Z"in K?(Xe=ty(uf(K.y,0,1)),Pn=Xe.getUTCDay(),Xe=Pn>4||Pn===0?a0.ceil(Xe):a0(Xe),Xe=ko.offset(Xe,(K.V-1)*7),K.y=Xe.getUTCFullYear(),K.m=Xe.getUTCMonth(),K.d=Xe.getUTCDate()+(K.w+6)%7):(Xe=ey(uf(K.y,0,1)),Pn=Xe.getDay(),Xe=Pn>4||Pn===0?o0.ceil(Xe):o0(Xe),Xe=Os.offset(Xe,(K.V-1)*7),K.y=Xe.getFullYear(),K.m=Xe.getMonth(),K.d=Xe.getDate()+(K.w+6)%7)}else("W"in K||"U"in K)&&("w"in K||(K.w="u"in K?K.u%7:"W"in K?1:0),Pn="Z"in K?ty(uf(K.y,0,1)).getUTCDay():ey(uf(K.y,0,1)).getDay(),K.m=0,K.d="W"in K?(K.w+6)%7+K.W*7-(Pn+5)%7:K.w+K.U*7-(Pn+6)%7);return"Z"in K?(K.H+=K.Z/100|0,K.M+=K.Z%100,ty(K)):ey(K)}}function C(le,Re,Le,K){for(var Kt=0,Xe=Re.length,Pn=Le.length,ln,Fs;Kt=Pn)return-1;if(ln=Re.charCodeAt(Kt++),ln===37){if(ln=Re.charAt(Kt++),Fs=_[ln in Z8?Re.charAt(Kt++):ln],!Fs||(K=Fs(le,Le,K))<0)return-1}else if(ln!=Le.charCodeAt(K++))return-1}return K}function k(le,Re,Le){var K=l.exec(Re.slice(Le));return K?(le.p=c.get(K[0].toLowerCase()),Le+K[0].length):-1}function S(le,Re,Le){var K=h.exec(Re.slice(Le));return K?(le.w=p.get(K[0].toLowerCase()),Le+K[0].length):-1}function $(le,Re,Le){var K=f.exec(Re.slice(Le));return K?(le.w=d.get(K[0].toLowerCase()),Le+K[0].length):-1}function O(le,Re,Le){var K=y.exec(Re.slice(Le));return K?(le.m=b.get(K[0].toLowerCase()),Le+K[0].length):-1}function R(le,Re,Le){var K=g.exec(Re.slice(Le));return K?(le.m=m.get(K[0].toLowerCase()),Le+K[0].length):-1}function F(le,Re,Le){return C(le,t,Re,Le)}function A(le,Re,Le){return C(le,n,Re,Le)}function T(le,Re,Le){return C(le,i,Re,Le)}function B(le){return o[le.getDay()]}function H(le){return s[le.getDay()]}function z(le){return u[le.getMonth()]}function ne(le){return a[le.getMonth()]}function be(le){return r[+(le.getHours()>=12)]}function de(le){return 1+~~(le.getMonth()/3)}function Te(le){return o[le.getUTCDay()]}function ct(le){return s[le.getUTCDay()]}function $e(le){return u[le.getUTCMonth()]}function Ot(le){return a[le.getUTCMonth()]}function or(le){return r[+(le.getUTCHours()>=12)]}function Ii(le){return 1+~~(le.getUTCMonth()/3)}return{format:function(le){var Re=E(le+="",v);return Re.toString=function(){return le},Re},parse:function(le){var Re=w(le+="",!1);return Re.toString=function(){return le},Re},utcFormat:function(le){var Re=E(le+="",x);return Re.toString=function(){return le},Re},utcParse:function(le){var Re=w(le+="",!0);return Re.toString=function(){return le},Re}}}var Z8={"-":"",_:" ",0:"0"},Zt=/^\s*\d+/,vW=/^%/,xW=/[\\^$*+?|[\]().{}]/g;function Je(e,t,n){var i=e<0?"-":"",r=(i?-e:e)+"",s=r.length;return i+(s[t.toLowerCase(),n]))}function wW(e,t,n){var i=Zt.exec(t.slice(n,n+1));return i?(e.w=+i[0],n+i[0].length):-1}function EW(e,t,n){var i=Zt.exec(t.slice(n,n+1));return i?(e.u=+i[0],n+i[0].length):-1}function CW(e,t,n){var i=Zt.exec(t.slice(n,n+2));return i?(e.U=+i[0],n+i[0].length):-1}function kW(e,t,n){var i=Zt.exec(t.slice(n,n+2));return i?(e.V=+i[0],n+i[0].length):-1}function AW(e,t,n){var i=Zt.exec(t.slice(n,n+2));return i?(e.W=+i[0],n+i[0].length):-1}function J8(e,t,n){var i=Zt.exec(t.slice(n,n+4));return i?(e.y=+i[0],n+i[0].length):-1}function Q8(e,t,n){var i=Zt.exec(t.slice(n,n+2));return i?(e.y=+i[0]+(+i[0]>68?1900:2e3),n+i[0].length):-1}function $W(e,t,n){var i=/^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(t.slice(n,n+6));return i?(e.Z=i[1]?0:-(i[2]+(i[3]||"00")),n+i[0].length):-1}function SW(e,t,n){var i=Zt.exec(t.slice(n,n+1));return i?(e.q=i[0]*3-3,n+i[0].length):-1}function FW(e,t,n){var i=Zt.exec(t.slice(n,n+2));return i?(e.m=i[0]-1,n+i[0].length):-1}function eE(e,t,n){var i=Zt.exec(t.slice(n,n+2));return i?(e.d=+i[0],n+i[0].length):-1}function DW(e,t,n){var i=Zt.exec(t.slice(n,n+3));return i?(e.m=0,e.d=+i[0],n+i[0].length):-1}function tE(e,t,n){var i=Zt.exec(t.slice(n,n+2));return i?(e.H=+i[0],n+i[0].length):-1}function TW(e,t,n){var i=Zt.exec(t.slice(n,n+2));return i?(e.M=+i[0],n+i[0].length):-1}function MW(e,t,n){var i=Zt.exec(t.slice(n,n+2));return i?(e.S=+i[0],n+i[0].length):-1}function NW(e,t,n){var i=Zt.exec(t.slice(n,n+3));return i?(e.L=+i[0],n+i[0].length):-1}function RW(e,t,n){var i=Zt.exec(t.slice(n,n+6));return i?(e.L=Math.floor(i[0]/1e3),n+i[0].length):-1}function OW(e,t,n){var i=vW.exec(t.slice(n,n+1));return i?n+i[0].length:-1}function LW(e,t,n){var i=Zt.exec(t.slice(n));return i?(e.Q=+i[0],n+i[0].length):-1}function IW(e,t,n){var i=Zt.exec(t.slice(n));return i?(e.s=+i[0],n+i[0].length):-1}function nE(e,t){return Je(e.getDate(),t,2)}function PW(e,t){return Je(e.getHours(),t,2)}function zW(e,t){return Je(e.getHours()%12||12,t,2)}function BW(e,t){return Je(1+Os.count(Wr(e),e),t,3)}function iE(e,t){return Je(e.getMilliseconds(),t,3)}function UW(e,t){return iE(e,t)+"000"}function jW(e,t){return Je(e.getMonth()+1,t,2)}function qW(e,t){return Je(e.getMinutes(),t,2)}function WW(e,t){return Je(e.getSeconds(),t,2)}function HW(e){var t=e.getDay();return t===0?7:t}function GW(e,t){return Je(cl.count(Wr(e)-1,e),t,2)}function rE(e){var t=e.getDay();return t>=4||t===0?fl(e):fl.ceil(e)}function VW(e,t){return e=rE(e),Je(fl.count(Wr(e),e)+(Wr(e).getDay()===4),t,2)}function YW(e){return e.getDay()}function XW(e,t){return Je(o0.count(Wr(e)-1,e),t,2)}function KW(e,t){return Je(e.getFullYear()%100,t,2)}function ZW(e,t){return e=rE(e),Je(e.getFullYear()%100,t,2)}function JW(e,t){return Je(e.getFullYear()%1e4,t,4)}function QW(e,t){var n=e.getDay();return e=n>=4||n===0?fl(e):fl.ceil(e),Je(e.getFullYear()%1e4,t,4)}function eH(e){var t=e.getTimezoneOffset();return(t>0?"-":(t*=-1,"+"))+Je(t/60|0,"0",2)+Je(t%60,"0",2)}function sE(e,t){return Je(e.getUTCDate(),t,2)}function tH(e,t){return Je(e.getUTCHours(),t,2)}function nH(e,t){return Je(e.getUTCHours()%12||12,t,2)}function iH(e,t){return Je(1+ko.count(Hr(e),e),t,3)}function oE(e,t){return Je(e.getUTCMilliseconds(),t,3)}function rH(e,t){return oE(e,t)+"000"}function sH(e,t){return Je(e.getUTCMonth()+1,t,2)}function oH(e,t){return Je(e.getUTCMinutes(),t,2)}function aH(e,t){return Je(e.getUTCSeconds(),t,2)}function uH(e){var t=e.getUTCDay();return t===0?7:t}function lH(e,t){return Je(dl.count(Hr(e)-1,e),t,2)}function aE(e){var t=e.getUTCDay();return t>=4||t===0?hl(e):hl.ceil(e)}function cH(e,t){return e=aE(e),Je(hl.count(Hr(e),e)+(Hr(e).getUTCDay()===4),t,2)}function fH(e){return e.getUTCDay()}function dH(e,t){return Je(a0.count(Hr(e)-1,e),t,2)}function hH(e,t){return Je(e.getUTCFullYear()%100,t,2)}function pH(e,t){return e=aE(e),Je(e.getUTCFullYear()%100,t,2)}function gH(e,t){return Je(e.getUTCFullYear()%1e4,t,4)}function mH(e,t){var n=e.getUTCDay();return e=n>=4||n===0?hl(e):hl.ceil(e),Je(e.getUTCFullYear()%1e4,t,4)}function yH(){return"+0000"}function uE(){return"%"}function lE(e){return+e}function cE(e){return Math.floor(+e/1e3)}var yl,ny,fE,iy,dE;bH({dateTime:"%x, %X",date:"%-m/%-d/%Y",time:"%-I:%M:%S %p",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});function bH(e){return yl=K8(e),ny=yl.format,fE=yl.parse,iy=yl.utcFormat,dE=yl.utcParse,yl}function ff(e){const t={};return n=>t[n]||(t[n]=e(n))}function vH(e,t){return n=>{const i=e(n),r=i.indexOf(t);if(r<0)return i;let s=xH(i,r);const o=sr;)if(i[s]!=="0"){++s;break}return i.slice(0,s)+o}}function xH(e,t){let n=e.lastIndexOf("e"),i;if(n>0)return n;for(n=e.length;--n>t;)if(i=e.charCodeAt(n),i>=48&&i<=57)return n+1}function hE(e){const t=ff(e.format),n=e.formatPrefix;return{format:t,formatPrefix:n,formatFloat(i){const r=$a(i||",");if(r.precision==null){switch(r.precision=12,r.type){case"%":r.precision-=2;break;case"e":r.precision-=1;break}return vH(t(r),t(".1f")(1)[1])}else return t(r)},formatSpan(i,r,s,o){o=$a(o??",f");const a=Co(i,r,s),u=Math.max(Math.abs(i),Math.abs(r));let l;if(o.precision==null)switch(o.type){case"s":return isNaN(l=C8(a,u))||(o.precision=l),n(o,u);case"":case"e":case"g":case"p":case"r":{isNaN(l=k8(a,u))||(o.precision=l-(o.type==="e"));break}case"f":case"%":{isNaN(l=E8(a))||(o.precision=l-(o.type==="%")*2);break}}return t(o)}}}let ry;pE();function pE(){return ry=hE({format:t0,formatPrefix:B2})}function gE(e){return hE(w8(e))}function l0(e){return arguments.length?ry=gE(e):ry}function mE(e,t,n){n=n||{},re(n)||q(`Invalid time multi-format specifier: ${n}`);const i=t(qi),r=t(ki),s=t(Ci),o=t(oi),a=t(Wt),u=t(An),l=t(si),c=t(cn),f=e(n[cr]||".%L"),d=e(n[qi]||":%S"),h=e(n[ki]||"%I:%M"),p=e(n[Ci]||"%I %p"),g=e(n[oi]||n[$n]||"%a %d"),m=e(n[Wt]||"%b %d"),y=e(n[An]||"%B"),b=e(n[si]||"%B"),v=e(n[cn]||"%Y");return x=>(i(x)se(i)?t(i):mE(t,gl,i),utcFormat:i=>se(i)?n(i):mE(n,ml,i),timeParse:ff(e.parse),utcParse:ff(e.utcParse)}}let sy;bE();function bE(){return sy=yE({format:ny,parse:fE,utcFormat:iy,utcParse:dE})}function vE(e){return yE(K8(e))}function df(e){return arguments.length?sy=vE(e):sy}const oy=(e,t)=>Oe({},e,t);function xE(e,t){const n=e?gE(e):l0(),i=t?vE(t):df();return oy(n,i)}function ay(e,t){const n=arguments.length;return n&&n!==2&&q("defaultLocale expects either zero or two arguments."),n?oy(l0(e),df(t)):oy(l0(),df())}function _H(){return pE(),bE(),ay()}const wH=/^(data:|([A-Za-z]+:)?\/\/)/,EH=/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp|file|data):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i,CH=/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205f\u3000]/g,_E="file://";function kH(e,t){return n=>({options:n||{},sanitize:$H,load:AH,fileAccess:!1,file:SH(t),http:DH(e)})}async function AH(e,t){const n=await this.sanitize(e,t),i=n.href;return n.localFile?this.file(i):this.http(i,t)}async function $H(e,t){t=Oe({},this.options,t);const n=this.fileAccess,i={href:null};let r,s,o;const a=EH.test(e.replace(CH,""));(e==null||typeof e!="string"||!a)&&q("Sanitize failure, invalid URI: "+Q(e));const u=wH.test(e);return(o=t.baseURL)&&!u&&(!e.startsWith("/")&&!o.endsWith("/")&&(e="/"+e),e=o+e),s=(r=e.startsWith(_E))||t.mode==="file"||t.mode!=="http"&&!u&&n,r?e=e.slice(_E.length):e.startsWith("//")&&(t.defaultProtocol==="file"?(e=e.slice(2),s=!0):e=(t.defaultProtocol||"http")+":"+e),Object.defineProperty(i,"localFile",{value:!!s}),i.href=e,t.target&&(i.target=t.target+""),t.rel&&(i.rel=t.rel+""),t.context==="image"&&t.crossOrigin&&(i.crossOrigin=t.crossOrigin+""),i}function SH(e){return e?t=>new Promise((n,i)=>{e.readFile(t,(r,s)=>{r?i(r):n(s)})}):FH}async function FH(){q("No file system access.")}function DH(e){return e?async function(t,n){const i=Oe({},this.options.http,n),r=n&&n.response,s=await e(t,i);return s.ok?Ie(s[r])?s[r]():s.text():q(s.status+""+s.statusText)}:TH}async function TH(){q("No HTTP fetch method available.")}const MH=e=>e!=null&&e===e,NH=e=>e==="true"||e==="false"||e===!0||e===!1,RH=e=>!Number.isNaN(Date.parse(e)),wE=e=>!Number.isNaN(+e)&&!(e instanceof Date),OH=e=>wE(e)&&Number.isInteger(+e),uy={boolean:S2,integer:Cn,number:Cn,date:F2,string:D2,unknown:En},c0=[NH,OH,wE,RH],LH=["boolean","integer","number","date"];function EE(e,t){if(!e||!e.length)return"unknown";const n=e.length,i=c0.length,r=c0.map((s,o)=>o+1);for(let s=0,o=0,a,u;ss===0?o:s,0)-1]}function CE(e,t){return t.reduce((n,i)=>(n[i]=EE(e,i),n),{})}function kE(e){const t=function(n,i){const r={delimiter:e};return ly(n,i?Oe(i,r):r)};return t.responseType="text",t}function ly(e,t){return t.header&&(e=t.header.map(Q).join(t.delimiter)+` +`+e),mq(t.delimiter).parse(e+"")}ly.responseType="text";function IH(e){return typeof Buffer=="function"&&Ie(Buffer.isBuffer)?Buffer.isBuffer(e):!1}function cy(e,t){const n=t&&t.property?Bi(t.property):En;return re(e)&&!IH(e)?PH(n(e),t):n(JSON.parse(e))}cy.responseType="json";function PH(e,t){return!W(e)&&V4(e)&&(e=[...e]),t&&t.copy?JSON.parse(JSON.stringify(e)):e}const zH={interior:(e,t)=>e!==t,exterior:(e,t)=>e===t};function AE(e,t){let n,i,r,s;return e=cy(e,t),t&&t.feature?(n=xq,r=t.feature):t&&t.mesh?(n=wq,r=t.mesh,s=zH[t.filter]):q("Missing TopoJSON feature or mesh parameter."),i=(i=e.objects[r])?n(e,i,s):q("Invalid TopoJSON object: "+r),i&&i.features||[i]}AE.responseType="json";const f0={dsv:ly,csv:kE(","),tsv:kE(" "),json:cy,topojson:AE};function fy(e,t){return arguments.length>1?(f0[e]=t,this):ue(f0,e)?f0[e]:null}function $E(e){const t=fy(e);return t&&t.responseType||"text"}function SE(e,t,n,i){t=t||{};const r=fy(t.type||"json");return r||q("Unknown data format type: "+t.type),e=r(e,t),t.parse&&BH(e,t.parse,n,i),ue(e,"columns")&&delete e.columns,e}function BH(e,t,n,i){if(!e.length)return;const r=df();n=n||r.timeParse,i=i||r.utcParse;let s=e.columns||Object.keys(e[0]),o,a,u,l,c,f;t==="auto"&&(t=CE(e,s)),s=Object.keys(t);const d=s.map(h=>{const p=t[h];let g,m;if(p&&(p.startsWith("date:")||p.startsWith("utc:")))return g=p.split(/:(.+)?/,2),m=g[1],(m[0]==="'"&&m[m.length-1]==="'"||m[0]==='"'&&m[m.length-1]==='"')&&(m=m.slice(1,-1)),(g[0]==="utc"?i:n)(m);if(!uy[p])throw Error("Illegal format pattern: "+h+":"+p);return uy[p]});for(u=0,c=e.length,f=s.length;u{const s=t(r);return i[s]||(i[s]=1,n.push(r)),n},n.remove=r=>{const s=t(r);if(i[s]){i[s]=0;const o=n.indexOf(r);o>=0&&n.splice(o,1)}return n},n}async function p0(e,t){try{await t(e)}catch(n){e.error(n)}}const FE=Symbol("vega_id");let UH=1;function g0(e){return!!(e&&_e(e))}function _e(e){return e[FE]}function DE(e,t){return e[FE]=t,e}function nt(e){const t=e===Object(e)?e:{data:e};return _e(t)?t:DE(t,UH++)}function dy(e){return m0(e,nt({}))}function m0(e,t){for(const n in e)t[n]=e[n];return t}function TE(e,t){return DE(t,_e(e))}function Ta(e,t){return e?t?(n,i)=>e(n,i)||_e(t(n))-_e(t(i)):(n,i)=>e(n,i)||_e(n)-_e(i):null}function ME(e){return e&&e.constructor===Ao}function Ao(){const e=[],t=[],n=[],i=[],r=[];let s=null,o=!1;return{constructor:Ao,insert(a){const u=oe(a),l=u.length;for(let c=0;c{p(b)&&(l[_e(b)]=-1)});for(f=0,d=e.length;f0&&(y(g,p,h.value),a.modifies(p));for(f=0,d=r.length;f{p(b)&&l[_e(b)]>0&&y(b,h.field,h.value)}),a.modifies(h.field);if(o)a.mod=t.length||i.length?u.filter(b=>l[_e(b)]>0):u.slice();else for(m in c)a.mod.push(c[m]);return(s||s==null&&(t.length||i.length))&&a.clean(!0),a}}}const y0="_:mod:_";function b0(){Object.defineProperty(this,y0,{writable:!0,value:{}})}b0.prototype={set(e,t,n,i){const r=this,s=r[e],o=r[y0];return t!=null&&t>=0?(s[t]!==n||i)&&(s[t]=n,o[t+":"+e]=-1,o[e]=-1):(s!==n||i)&&(r[e]=n,o[e]=W(n)?1+n.length:-1),r},modified(e,t){const n=this[y0];if(arguments.length){if(W(e)){for(let i=0;i=0?t+1{h instanceof xt?(h!==this&&(t&&h.targets().add(this),s.push(h)),r.push({op:h,name:f,index:d})):i.set(f,d,h)};for(o in e)if(a=e[o],o===qH)oe(a).forEach(f=>{f instanceof xt?f!==this&&(f.targets().add(this),s.push(f)):q("Pulse parameters must be operator instances.")}),this.source=a;else if(W(a))for(i.set(o,-1,Array(u=a.length)),l=0;l{const n=Date.now();return n-t>e?(t=n,1):0})},debounce(e){const t=$o();return this.targets().add($o(null,null,k2(e,n=>{const i=n.dataflow;t.receive(n),i&&i.run&&i.run()}))),t},between(e,t){let n=!1;return e.targets().add($o(null,null,()=>n=!0)),t.targets().add($o(null,null,()=>n=!1)),this.filter(()=>n)},detach(){this._filter=Ui,this._targets=null}};function KH(e,t,n,i){const r=this,s=$o(n,i),o=function(l){l.dataflow=r;try{s.receive(l)}catch(c){r.error(c)}finally{r.run()}};let a;typeof e=="string"&&typeof document<"u"?a=document.querySelectorAll(e):a=oe(e);const u=a.length;for(let l=0;lt=i);return n.requests=0,n.done=()=>{--n.requests===0&&(e._pending=null,t(e))},e._pending=n}const nG={skip:!0};function iG(e,t,n,i,r){return(e instanceof xt?sG:rG)(this,e,t,n,i,r),this}function rG(e,t,n,i,r,s){const o=Oe({},s,nG);let a,u;Ie(n)||(n=kn(n)),i===void 0?a=l=>e.touch(n(l)):Ie(i)?(u=new xt(null,i,r,!1),a=l=>{u.evaluate(l);const c=n(l),f=u.value;ME(f)?e.pulse(c,f,s):e.update(c,f,o)}):a=l=>e.update(n(l),i,o),t.apply(a)}function sG(e,t,n,i,r,s){if(i===void 0)t.targets().add(n);else{const o=s||{},a=new xt(null,oG(n,i),r,!1);a.modified(o.force),a.rank=t.rank,t.targets().add(a),n&&(a.skip(!0),a.value=n.value,a.targets().add(n),e.connect(n,[a]))}}function oG(e,t){return t=Ie(t)?t:kn(t),e?function(n,i){const r=t(n,i);return e.skip()||(e.skip(r!==this.value).value=r),r}:t}function aG(e){e.rank=++this._rank}function uG(e){const t=[e];let n,i,r;for(;t.length;)if(this.rank(n=t.pop()),i=n._targets)for(r=i.length;--r>=0;)t.push(n=i[r]),n===e&&q("Cycle detected in dataflow graph.")}const x0={},Vr=1,So=2,Ls=4,lG=Vr|So,RE=Vr|Ls,bl=Vr|So|Ls,OE=8,hf=16,LE=32,IE=64;function Fo(e,t,n){this.dataflow=e,this.stamp=t??-1,this.add=[],this.rem=[],this.mod=[],this.fields=null,this.encode=n||null}function hy(e,t){const n=[];return wo(e,t,i=>n.push(i)),n}function PE(e,t){const n={};return e.visit(t,i=>{n[_e(i)]=1}),i=>n[_e(i)]?null:i}function _0(e,t){return e?(n,i)=>e(n,i)&&t(n,i):t}Fo.prototype={StopPropagation:x0,ADD:Vr,REM:So,MOD:Ls,ADD_REM:lG,ADD_MOD:RE,ALL:bl,REFLOW:OE,SOURCE:hf,NO_SOURCE:LE,NO_FIELDS:IE,fork(e){return new Fo(this.dataflow).init(this,e)},clone(){const e=this.fork(bl);return e.add=e.add.slice(),e.rem=e.rem.slice(),e.mod=e.mod.slice(),e.source&&(e.source=e.source.slice()),e.materialize(bl|hf)},addAll(){let e=this;return!e.source||e.add===e.rem||!e.rem.length&&e.source.length===e.add.length||(e=new Fo(this.dataflow).init(this),e.add=e.source,e.rem=[]),e},init(e,t){const n=this;return n.stamp=e.stamp,n.encode=e.encode,e.fields&&!(t&IE)&&(n.fields=e.fields),t&Vr?(n.addF=e.addF,n.add=e.add):(n.addF=null,n.add=[]),t&So?(n.remF=e.remF,n.rem=e.rem):(n.remF=null,n.rem=[]),t&Ls?(n.modF=e.modF,n.mod=e.mod):(n.modF=null,n.mod=[]),t&LE?(n.srcF=null,n.source=null):(n.srcF=e.srcF,n.source=e.source,e.cleans&&(n.cleans=e.cleans)),n},runAfter(e){this.dataflow.runAfter(e)},changed(e){const t=e||bl;return t&Vr&&this.add.length||t&So&&this.rem.length||t&Ls&&this.mod.length},reflow(e){if(e)return this.fork(bl).reflow();const t=this.add.length,n=this.source&&this.source.length;return n&&n!==t&&(this.mod=this.source,t&&this.filter(Ls,PE(this,Vr))),this},clean(e){return arguments.length?(this.cleans=!!e,this):this.cleans},modifies(e){const t=this.fields||(this.fields={});return W(e)?e.forEach(n=>t[n]=!0):t[e]=!0,this},modified(e,t){const n=this.fields;return(t||this.mod.length)&&n?arguments.length?W(e)?e.some(i=>n[i]):n[e]:!!n:!1},filter(e,t){const n=this;return e&Vr&&(n.addF=_0(n.addF,t)),e&So&&(n.remF=_0(n.remF,t)),e&Ls&&(n.modF=_0(n.modF,t)),e&hf&&(n.srcF=_0(n.srcF,t)),n},materialize(e){e=e||bl;const t=this;return e&Vr&&t.addF&&(t.add=hy(t.add,t.addF),t.addF=null),e&So&&t.remF&&(t.rem=hy(t.rem,t.remF),t.remF=null),e&Ls&&t.modF&&(t.mod=hy(t.mod,t.modF),t.modF=null),e&hf&&t.srcF&&(t.source=t.source.filter(t.srcF),t.srcF=null),t},visit(e,t){const n=this,i=t;if(e&hf)return wo(n.source,n.srcF,i),n;e&Vr&&wo(n.add,n.addF,i),e&So&&wo(n.rem,n.remF,i),e&Ls&&wo(n.mod,n.modF,i);const r=n.source;if(e&OE&&r){const s=n.add.length+n.mod.length;s===r.length||(s?wo(r,PE(n,RE),i):wo(r,n.srcF,i))}return n}};function py(e,t,n,i){const r=this;let s=0;this.dataflow=e,this.stamp=t,this.fields=null,this.encode=i||null,this.pulses=n;for(const o of n)if(o.stamp===t){if(o.fields){const a=r.fields||(r.fields={});for(const u in o.fields)a[u]=1}o.changed(r.ADD)&&(s|=r.ADD),o.changed(r.REM)&&(s|=r.REM),o.changed(r.MOD)&&(s|=r.MOD)}this.changes=s}ee(py,Fo,{fork(e){const t=new Fo(this.dataflow).init(this,e&this.NO_FIELDS);return e!==void 0&&(e&t.ADD&&this.visit(t.ADD,n=>t.add.push(n)),e&t.REM&&this.visit(t.REM,n=>t.rem.push(n)),e&t.MOD&&this.visit(t.MOD,n=>t.mod.push(n))),t},changed(e){return this.changes&e},modified(e){const t=this,n=t.fields;return n&&t.changes&t.MOD?W(e)?e.some(i=>n[i]):n[e]:0},filter(){q("MultiPulse does not support filtering.")},materialize(){q("MultiPulse does not support materialization.")},visit(e,t){const n=this,i=n.pulses,r=i.length;let s=0;if(e&n.SOURCE)for(;si._enqueue(c,!0)),i._touched=h0(Vc);let o=0,a,u,l;try{for(;i._heap.size()>0;){if(a=i._heap.pop(),a.rank!==a.qrank){i._enqueue(a,!0);continue}u=a.run(i._getPulse(a,e)),u.then?u=await u:u.async&&(r.push(u.async),u=x0),u!==x0&&a._targets&&a._targets.forEach(c=>i._enqueue(c)),++o}}catch(c){i._heap.clear(),l=c}if(i._input={},i._pulse=null,i.debug(`Pulse ${s}: ${o} operators`),l&&(i._postrun=[],i.error(l)),i._postrun.length){const c=i._postrun.sort((f,d)=>d.priority-f.priority);i._postrun=[];for(let f=0;fi.runAsync(null,()=>{c.forEach(f=>{try{f(i)}catch(d){i.error(d)}})})),i}async function fG(e,t,n){for(;this._running;)await this._running;const i=()=>this._running=null;return(this._running=this.evaluate(e,t,n)).then(i,i),this._running}function dG(e,t,n){return this._pulse?zE(this):(this.evaluate(e,t,n),this)}function hG(e,t,n){if(this._pulse||t)this._postrun.push({priority:n||0,callback:e});else try{e(this)}catch(i){this.error(i)}}function zE(e){return e.error("Dataflow already running. Use runAsync() to chain invocations."),e}function pG(e,t){const n=e.stampr.pulse),t):this._input[e.id]||mG(this._pulse,n&&n.pulse)}function mG(e,t){return t&&t.stamp===e.stamp?t:(e=e.fork(),t&&t!==x0&&(e.source=t.source),e)}const gy={skip:!1,force:!1};function yG(e,t){const n=t||gy;return this._pulse?this._enqueue(e):this._touched.add(e),n.skip&&e.skip(!0),this}function bG(e,t,n){const i=n||gy;return(e.set(t)||i.force)&&this.touch(e,i),this}function vG(e,t,n){this.touch(e,n||gy);const i=new Fo(this,this._clock+(this._pulse?0:1)),r=e.pulse&&e.pulse.source||[];return i.target=e,this._input[e.id]=t.pulse(i,r),this}function xG(e){let t=[];return{clear:()=>t=[],size:()=>t.length,peek:()=>t[0],push:n=>(t.push(n),BE(t,0,t.length-1,e)),pop:()=>{const n=t.pop();let i;return t.length?(i=t[0],t[0]=n,_G(t,0,e)):i=n,i}}}function BE(e,t,n,i){let r,s;const o=e[n];for(;n>t;){if(s=n-1>>1,r=e[s],i(o,r)<0){e[n]=r,n=s;continue}break}return e[n]=o}function _G(e,t,n){const i=t,r=e.length,s=e[t];let o=(t<<1)+1,a;for(;o=0&&(o=a),e[t]=e[o],t=o,o=(t<<1)+1;return e[t]=s,BE(e,i,t,n)}function vl(){this.logger(x2()),this.logLevel(b2),this._clock=0,this._rank=0,this._locale=ay();try{this._loader=d0()}catch{}this._touched=h0(Vc),this._input={},this._pulse=null,this._heap=xG((e,t)=>e.qrank-t.qrank),this._postrun=[]}function pf(e){return function(){return this._log[e].apply(this,arguments)}}vl.prototype={stamp(){return this._clock},loader(e){return arguments.length?(this._loader=e,this):this._loader},locale(e){return arguments.length?(this._locale=e,this):this._locale},logger(e){return arguments.length?(this._log=e,this):this._log},error:pf("error"),warn:pf("warn"),info:pf("info"),debug:pf("debug"),logLevel:pf("level"),cleanThreshold:1e4,add:VH,connect:YH,rank:aG,rerank:uG,pulse:vG,touch:yG,update:bG,changeset:Ao,ingest:JH,parse:ZH,preload:eG,request:QH,events:KH,on:iG,evaluate:cG,run:dG,runAsync:fG,runAfter:hG,_enqueue:pG,_getPulse:gG};function P(e,t){xt.call(this,e,null,t)}ee(P,xt,{run(e){if(e.stampthis.pulse=n):t!==e.StopPropagation&&(this.pulse=t),t},evaluate(e){const t=this.marshall(e.stamp),n=this.transform(t,e);return t.clear(),n},transform(){}});const xl={};function UE(e){const t=jE(e);return t&&t.Definition||null}function jE(e){return e=e&&e.toLowerCase(),ue(xl,e)?xl[e]:null}function*qE(e,t){if(t==null)for(let n of e)n!=null&&n!==""&&(n=+n)>=n&&(yield n);else{let n=-1;for(let i of e)i=t(i,++n,e),i!=null&&i!==""&&(i=+i)>=i&&(yield i)}}function my(e,t,n){const i=Float64Array.from(qE(e,n));return i.sort(Ds),t.map(r=>d8(i,r))}function yy(e,t){return my(e,[.25,.5,.75],t)}function by(e,t){const n=e.length,i=Dq(e,t),r=yy(e,t),s=(r[2]-r[0])/1.34;return 1.06*(Math.min(i,s)||i||Math.abs(r[0])||1)*Math.pow(n,-.2)}function WE(e){const t=e.maxbins||20,n=e.base||10,i=Math.log(n),r=e.divide||[5,2];let s=e.extent[0],o=e.extent[1],a,u,l,c,f,d;const h=e.span||o-s||Math.abs(s)||1;if(e.step)a=e.step;else if(e.steps){for(c=h/t,f=0,d=e.steps.length;ft;)a*=n;for(f=0,d=r.length;f=l&&h/c<=t&&(a=c)}c=Math.log(a);const p=c>=0?0:~~(-c/i)+1,g=Math.pow(n,-p-1);return(e.nice||e.nice===void 0)&&(c=Math.floor(s/a+g)*a,s=sd);const r=e.length,s=new Float64Array(r);let o=0,a=1,u=i(e[0]),l=u,c=u+t,f;for(;a=c){for(l=(u+l)/2;o>1);or;)e[o--]=e[i]}i=r,r=s}return e}function CG(e){return function(){return e=(1103515245*e+12345)%2147483647,e/2147483647}}function kG(e,t){t==null&&(t=e,e=0);let n,i,r;const s={min(o){return arguments.length?(n=o||0,r=i-n,s):n},max(o){return arguments.length?(i=o||0,r=i-n,s):i},sample(){return n+Math.floor(r*Wi())},pdf(o){return o===Math.floor(o)&&o>=n&&o=i?1:(a-n+1)/r},icdf(o){return o>=0&&o<=1?n-1+Math.floor(o*r):NaN}};return s.min(e).max(t)}const VE=Math.sqrt(2*Math.PI),AG=Math.SQRT2;let gf=NaN;function w0(e,t){e=e||0,t=t??1;let n=0,i=0,r,s;if(gf===gf)n=gf,gf=NaN;else{do n=Wi()*2-1,i=Wi()*2-1,r=n*n+i*i;while(r===0||r>1);s=Math.sqrt(-2*Math.log(r)/r),n*=s,gf=i*s}return e+n*t}function vy(e,t,n){n=n??1;const i=(e-(t||0))/n;return Math.exp(-.5*i*i)/(n*VE)}function E0(e,t,n){t=t||0,n=n??1;const i=(e-t)/n,r=Math.abs(i);let s;if(r>37)s=0;else{const o=Math.exp(-r*r/2);let a;r<7.07106781186547?(a=.0352624965998911*r+.700383064443688,a=a*r+6.37396220353165,a=a*r+33.912866078383,a=a*r+112.079291497871,a=a*r+221.213596169931,a=a*r+220.206867912376,s=o*a,a=.0883883476483184*r+1.75566716318264,a=a*r+16.064177579207,a=a*r+86.7807322029461,a=a*r+296.564248779674,a=a*r+637.333633378831,a=a*r+793.826512519948,a=a*r+440.413735824752,s=s/a):(a=r+.65,a=r+4/a,a=r+3/a,a=r+2/a,a=r+1/a,s=o/a/2.506628274631)}return i>0?1-s:s}function C0(e,t,n){return e<0||e>1?NaN:(t||0)+(n??1)*AG*$G(2*e-1)}function $G(e){let t=-Math.log((1-e)*(1+e)),n;return t<6.25?(t-=3.125,n=-364441206401782e-35,n=-16850591381820166e-35+n*t,n=128584807152564e-32+n*t,n=11157877678025181e-33+n*t,n=-1333171662854621e-31+n*t,n=20972767875968562e-33+n*t,n=6637638134358324e-30+n*t,n=-4054566272975207e-29+n*t,n=-8151934197605472e-29+n*t,n=26335093153082323e-28+n*t,n=-12975133253453532e-27+n*t,n=-5415412054294628e-26+n*t,n=10512122733215323e-25+n*t,n=-4112633980346984e-24+n*t,n=-29070369957882005e-24+n*t,n=42347877827932404e-23+n*t,n=-13654692000834679e-22+n*t,n=-13882523362786469e-21+n*t,n=.00018673420803405714+n*t,n=-.000740702534166267+n*t,n=-.006033670871430149+n*t,n=.24015818242558962+n*t,n=1.6536545626831027+n*t):t<16?(t=Math.sqrt(t)-3.25,n=22137376921775787e-25,n=9075656193888539e-23+n*t,n=-27517406297064545e-23+n*t,n=18239629214389228e-24+n*t,n=15027403968909828e-22+n*t,n=-4013867526981546e-21+n*t,n=29234449089955446e-22+n*t,n=12475304481671779e-21+n*t,n=-47318229009055734e-21+n*t,n=6828485145957318e-20+n*t,n=24031110387097894e-21+n*t,n=-.0003550375203628475+n*t,n=.0009532893797373805+n*t,n=-.0016882755560235047+n*t,n=.002491442096107851+n*t,n=-.003751208507569241+n*t,n=.005370914553590064+n*t,n=1.0052589676941592+n*t,n=3.0838856104922208+n*t):Number.isFinite(t)?(t=Math.sqrt(t)-5,n=-27109920616438573e-27,n=-2555641816996525e-25+n*t,n=15076572693500548e-25+n*t,n=-3789465440126737e-24+n*t,n=761570120807834e-23+n*t,n=-1496002662714924e-23+n*t,n=2914795345090108e-23+n*t,n=-6771199775845234e-23+n*t,n=22900482228026655e-23+n*t,n=-99298272942317e-20+n*t,n=4526062597223154e-21+n*t,n=-1968177810553167e-20+n*t,n=7599527703001776e-20+n*t,n=-.00021503011930044477+n*t,n=-.00013871931833623122+n*t,n=1.0103004648645344+n*t,n=4.849906401408584+n*t):n=1/0,n*e}function xy(e,t){let n,i;const r={mean(s){return arguments.length?(n=s||0,r):n},stdev(s){return arguments.length?(i=s??1,r):i},sample:()=>w0(n,i),pdf:s=>vy(s,n,i),cdf:s=>E0(s,n,i),icdf:s=>C0(s,n,i)};return r.mean(e).stdev(t)}function _y(e,t){const n=xy();let i=0;const r={data(s){return arguments.length?(e=s,i=s?s.length:0,r.bandwidth(t)):e},bandwidth(s){return arguments.length?(t=s,!t&&e&&(t=by(e)),r):t},sample(){return e[~~(Wi()*i)]+t*n.sample()},pdf(s){let o=0,a=0;for(;awy(n,i),pdf:s=>Ey(s,n,i),cdf:s=>Cy(s,n,i),icdf:s=>ky(s,n,i)};return r.mean(e).stdev(t)}function XE(e,t){let n=0,i;function r(o){const a=[];let u=0,l;for(l=0;l=t&&e<=n?1/(n-t):0}function Sy(e,t,n){return n==null&&(n=t??1,t=0),en?1:(e-t)/(n-t)}function Fy(e,t,n){return n==null&&(n=t??1,t=0),e>=0&&e<=1?t+e*(n-t):NaN}function KE(e,t){let n,i;const r={min(s){return arguments.length?(n=s||0,r):n},max(s){return arguments.length?(i=s??1,r):i},sample:()=>Ay(n,i),pdf:s=>$y(s,n,i),cdf:s=>Sy(s,n,i),icdf:s=>Fy(s,n,i)};return t==null&&(t=e??1,e=0),r.min(e).max(t)}function Dy(e,t,n){let i=0,r=0;for(const s of e){const o=n(s);t(s)==null||o==null||isNaN(o)||(i+=(o-i)/++r)}return{coef:[i],predict:()=>i,rSquared:0}}function mf(e,t,n,i){const r=i-e*e,s=Math.abs(r)<1e-24?0:(n-e*t)/r;return[t-s*e,s]}function k0(e,t,n,i){e=e.filter(h=>{let p=t(h),g=n(h);return p!=null&&(p=+p)>=p&&g!=null&&(g=+g)>=g}),i&&e.sort((h,p)=>t(h)-t(p));const r=e.length,s=new Float64Array(r),o=new Float64Array(r);let a=0,u=0,l=0,c,f,d;for(d of e)s[a]=c=+t(d),o[a]=f=+n(d),++a,u+=(c-u)/a,l+=(f-l)/a;for(a=0;a=s&&o!=null&&(o=+o)>=o&&i(s,o,++r)}function _l(e,t,n,i,r){let s=0,o=0;return yf(e,t,n,(a,u)=>{const l=u-r(a),c=u-i;s+=l*l,o+=c*c}),1-s/o}function Ty(e,t,n){let i=0,r=0,s=0,o=0,a=0;yf(e,t,n,(c,f)=>{++a,i+=(c-i)/a,r+=(f-r)/a,s+=(c*f-s)/a,o+=(c*c-o)/a});const u=mf(i,r,s,o),l=c=>u[0]+u[1]*c;return{coef:u,predict:l,rSquared:_l(e,t,n,r,l)}}function ZE(e,t,n){let i=0,r=0,s=0,o=0,a=0;yf(e,t,n,(c,f)=>{++a,c=Math.log(c),i+=(c-i)/a,r+=(f-r)/a,s+=(c*f-s)/a,o+=(c*c-o)/a});const u=mf(i,r,s,o),l=c=>u[0]+u[1]*Math.log(c);return{coef:u,predict:l,rSquared:_l(e,t,n,r,l)}}function JE(e,t,n){const[i,r,s,o]=k0(e,t,n);let a=0,u=0,l=0,c=0,f=0,d,h,p;yf(e,t,n,(b,v)=>{d=i[f++],h=Math.log(v),p=d*v,a+=(v*h-a)/f,u+=(p-u)/f,l+=(p*h-l)/f,c+=(d*p-c)/f});const[g,m]=mf(u/o,a/o,l/o,c/o),y=b=>Math.exp(g+m*(b-s));return{coef:[Math.exp(g-m*s),m],predict:y,rSquared:_l(e,t,n,o,y)}}function QE(e,t,n){let i=0,r=0,s=0,o=0,a=0,u=0;yf(e,t,n,(f,d)=>{const h=Math.log(f),p=Math.log(d);++u,i+=(h-i)/u,r+=(p-r)/u,s+=(h*p-s)/u,o+=(h*h-o)/u,a+=(d-a)/u});const l=mf(i,r,s,o),c=f=>l[0]*Math.pow(f,l[1]);return l[0]=Math.exp(l[0]),{coef:l,predict:c,rSquared:_l(e,t,n,a,c)}}function My(e,t,n){const[i,r,s,o]=k0(e,t,n),a=i.length;let u=0,l=0,c=0,f=0,d=0,h,p,g,m;for(h=0;h(w=w-s,v*w*w+x*w+_+o);return{coef:[_-x*s+v*s*s+o,x-2*v*s,v],predict:E,rSquared:_l(e,t,n,o,E)}}function e9(e,t,n,i){if(i===0)return Dy(e,t,n);if(i===1)return Ty(e,t,n);if(i===2)return My(e,t,n);const[r,s,o,a]=k0(e,t,n),u=r.length,l=[],c=[],f=i+1;let d,h,p,g,m;for(d=0;d{v-=o;let x=a+y[0]+y[1]*v+y[2]*v*v;for(d=3;d=0;--s)for(a=t[s],u=1,r[s]+=a,o=1;o<=s;++o)u*=(s+1-o)/o,r[s-o]+=a*Math.pow(n,o)*u;return r[0]+=i,r}function FG(e){const t=e.length-1,n=[];let i,r,s,o,a;for(i=0;iMath.abs(e[i][o])&&(o=r);for(s=i;s=i;s--)e[s][r]-=e[s][i]*e[i][r]/e[i][i]}for(r=t-1;r>=0;--r){for(a=0,s=r+1;sr[v]-y?b:v;let _=0,E=0,w=0,C=0,k=0;const S=1/Math.abs(r[x]-y||1);for(let R=b;R<=v;++R){const F=r[R],A=s[R],T=DG(Math.abs(y-F)*S)*d[R],B=F*T;_+=T,E+=B,w+=A*T,C+=A*B,k+=F*B}const[$,O]=mf(E/_,w/_,C/_,k/_);c[m]=$+O*y,f[m]=Math.abs(s[m]-c[m]),TG(r,m+1,p)}if(h===t9)break;const g=h8(f);if(Math.abs(g)=1?n9:(b=1-y*y)*b}return MG(r,c,o,a)}function DG(e){return(e=1-e*e*e)*e*e}function TG(e,t,n){const i=e[t];let r=n[0],s=n[1]+1;if(!(s>=e.length))for(;t>r&&e[s]-i<=i-e[r];)n[0]=++r,n[1]=s,++s}function MG(e,t,n,i){const r=e.length,s=[];let o=0,a=0,u=[],l;for(;o[g,e(g)],s=t[0],o=t[1],a=o-s,u=a/i,l=[r(s)],c=[];if(n===i){for(let g=1;g0;)c.push(r(s+g/n*a))}let f=l[0],d=c[c.length-1];const h=1/a,p=RG(f[1],c);for(;d;){const g=r((f[0]+d[0])/2);g[0]-f[0]>=u&&OG(f,g,d,h,p)>NG?c.push(g):(f=d,l.push(d),c.pop()),d=c[c.length-1]}return l}function RG(e,t){let n=e,i=e;const r=t.length;for(let s=0;si&&(i=o)}return 1/(i-n)}function OG(e,t,n,i,r){const s=Math.atan2(r*(n[1]-e[1]),i*(n[0]-e[0])),o=Math.atan2(r*(t[1]-e[1]),i*(t[0]-e[0]));return Math.abs(s-o)}function LG(e){return t=>{const n=e.length;let i=1,r=String(e[0](t));for(;i{},IG={init:Ry,add:Ry,rem:Ry,idx:0},bf={values:{init:e=>e.cell.store=!0,value:e=>e.cell.data.values(),idx:-1},count:{value:e=>e.cell.num},__count__:{value:e=>e.missing+e.valid},missing:{value:e=>e.missing},valid:{value:e=>e.valid},sum:{init:e=>e.sum=0,value:e=>e.valid?e.sum:void 0,add:(e,t)=>e.sum+=+t,rem:(e,t)=>e.sum-=t},product:{init:e=>e.product=1,value:e=>e.valid?e.product:void 0,add:(e,t)=>e.product*=t,rem:(e,t)=>e.product/=t},mean:{init:e=>e.mean=0,value:e=>e.valid?e.mean:void 0,add:(e,t)=>(e.mean_d=t-e.mean,e.mean+=e.mean_d/e.valid),rem:(e,t)=>(e.mean_d=t-e.mean,e.mean-=e.valid?e.mean_d/e.valid:e.mean)},average:{value:e=>e.valid?e.mean:void 0,req:["mean"],idx:1},variance:{init:e=>e.dev=0,value:e=>e.valid>1?e.dev/(e.valid-1):void 0,add:(e,t)=>e.dev+=e.mean_d*(t-e.mean),rem:(e,t)=>e.dev-=e.mean_d*(t-e.mean),req:["mean"],idx:1},variancep:{value:e=>e.valid>1?e.dev/e.valid:void 0,req:["variance"],idx:2},stdev:{value:e=>e.valid>1?Math.sqrt(e.dev/(e.valid-1)):void 0,req:["variance"],idx:2},stdevp:{value:e=>e.valid>1?Math.sqrt(e.dev/e.valid):void 0,req:["variance"],idx:2},stderr:{value:e=>e.valid>1?Math.sqrt(e.dev/(e.valid*(e.valid-1))):void 0,req:["variance"],idx:2},distinct:{value:e=>e.cell.data.distinct(e.get),req:["values"],idx:3},ci0:{value:e=>e.cell.data.ci0(e.get),req:["values"],idx:3},ci1:{value:e=>e.cell.data.ci1(e.get),req:["values"],idx:3},median:{value:e=>e.cell.data.q2(e.get),req:["values"],idx:3},q1:{value:e=>e.cell.data.q1(e.get),req:["values"],idx:3},q3:{value:e=>e.cell.data.q3(e.get),req:["values"],idx:3},min:{init:e=>e.min=void 0,value:e=>e.min=Number.isNaN(e.min)?e.cell.data.min(e.get):e.min,add:(e,t)=>{(t{t<=e.min&&(e.min=NaN)},req:["values"],idx:4},max:{init:e=>e.max=void 0,value:e=>e.max=Number.isNaN(e.max)?e.cell.data.max(e.get):e.max,add:(e,t)=>{(t>e.max||e.max===void 0)&&(e.max=t)},rem:(e,t)=>{t>=e.max&&(e.max=NaN)},req:["values"],idx:4},argmin:{init:e=>e.argmin=void 0,value:e=>e.argmin||e.cell.data.argmin(e.get),add:(e,t,n)=>{t{t<=e.min&&(e.argmin=void 0)},req:["min","values"],idx:3},argmax:{init:e=>e.argmax=void 0,value:e=>e.argmax||e.cell.data.argmax(e.get),add:(e,t,n)=>{t>e.max&&(e.argmax=n)},rem:(e,t)=>{t>=e.max&&(e.argmax=void 0)},req:["max","values"],idx:3},exponential:{init:(e,t)=>{e.exp=0,e.exp_r=t},value:e=>e.valid?e.exp*(1-e.exp_r)/(1-e.exp_r**e.valid):void 0,add:(e,t)=>e.exp=e.exp_r*e.exp+t,rem:(e,t)=>e.exp=(e.exp-t/e.exp_r**(e.valid-1))/e.exp_r},exponentialb:{value:e=>e.valid?e.exp*(1-e.exp_r):void 0,req:["exponential"],idx:1}},vf=Object.keys(bf).filter(e=>e!=="__count__");function PG(e,t){return(n,i)=>Oe({name:e,aggregate_param:i,out:n||e},IG,t)}[...vf,"__count__"].forEach(e=>{bf[e]=PG(e,bf[e])});function s9(e,t,n){return bf[e](n,t)}function o9(e,t){return e.idx-t.idx}function zG(e){const t={};e.forEach(i=>t[i.name]=i);const n=i=>{i.req&&i.req.forEach(r=>{t[r]||n(t[r]=bf[r]())})};return e.forEach(n),Object.values(t).sort(o9)}function BG(){this.valid=0,this.missing=0,this._ops.forEach(e=>e.aggregate_param==null?e.init(this):e.init(this,e.aggregate_param))}function UG(e,t){if(e==null||e===""){++this.missing;return}e===e&&(++this.valid,this._ops.forEach(n=>n.add(this,e,t)))}function jG(e,t){if(e==null||e===""){--this.missing;return}e===e&&(--this.valid,this._ops.forEach(n=>n.rem(this,e,t)))}function qG(e){return this._out.forEach(t=>e[t.out]=t.value(this)),e}function a9(e,t){const n=t||En,i=zG(e),r=e.slice().sort(o9);function s(o){this._ops=i,this._out=r,this.cell=o,this.init()}return s.prototype.init=BG,s.prototype.add=UG,s.prototype.rem=jG,s.prototype.set=qG,s.prototype.get=n,s.fields=e.map(o=>o.out),s}function Oy(e){this._key=e?Bi(e):_e,this.reset()}const fn=Oy.prototype;fn.reset=function(){this._add=[],this._rem=[],this._ext=null,this._get=null,this._q=null},fn.add=function(e){this._add.push(e)},fn.rem=function(e){this._rem.push(e)},fn.values=function(){if(this._get=null,this._rem.length===0)return this._add;const e=this._add,t=this._rem,n=this._key,i=e.length,r=t.length,s=Array(i-r),o={};let a,u,l;for(a=0;a=0;)s=e(t[i])+"",ue(n,s)||(n[s]=1,++r);return r},fn.extent=function(e){if(this._get!==e||!this._ext){const t=this.values(),n=H4(t,e);this._ext=[t[n[0]],t[n[1]]],this._get=e}return this._ext},fn.argmin=function(e){return this.extent(e)[0]||{}},fn.argmax=function(e){return this.extent(e)[1]||{}},fn.min=function(e){const t=this.extent(e)[0];return t!=null?e(t):void 0},fn.max=function(e){const t=this.extent(e)[1];return t!=null?e(t):void 0},fn.quartile=function(e){return(this._get!==e||!this._q)&&(this._q=yy(this.values(),e),this._get=e),this._q},fn.q1=function(e){return this.quartile(e)[0]},fn.q2=function(e){return this.quartile(e)[1]},fn.q3=function(e){return this.quartile(e)[2]},fn.ci=function(e){return(this._get!==e||!this._ci)&&(this._ci=HE(this.values(),1e3,.05,e),this._get=e),this._ci},fn.ci0=function(e){return this.ci(e)[0]},fn.ci1=function(e){return this.ci(e)[1]};function Do(e){P.call(this,null,e),this._adds=[],this._mods=[],this._alen=0,this._mlen=0,this._drop=!0,this._cross=!1,this._dims=[],this._dnames=[],this._measures=[],this._countOnly=!1,this._counts=null,this._prev=null,this._inputs=null,this._outputs=null}Do.Definition={type:"Aggregate",metadata:{generates:!0,changes:!0},params:[{name:"groupby",type:"field",array:!0},{name:"ops",type:"enum",array:!0,values:vf},{name:"aggregate_params",type:"number",null:!0,array:!0},{name:"fields",type:"field",null:!0,array:!0},{name:"as",type:"string",null:!0,array:!0},{name:"drop",type:"boolean",default:!0},{name:"cross",type:"boolean",default:!1},{name:"key",type:"field"}]},ee(Do,P,{transform(e,t){const n=this,i=t.fork(t.NO_SOURCE|t.NO_FIELDS),r=e.modified();return n.stamp=i.stamp,n.value&&(r||t.modified(n._inputs,!0))?(n._prev=n.value,n.value=r?n.init(e):Object.create(null),t.visit(t.SOURCE,s=>n.add(s))):(n.value=n.value||n.init(e),t.visit(t.REM,s=>n.rem(s)),t.visit(t.ADD,s=>n.add(s))),i.modifies(n._outputs),n._drop=e.drop!==!1,e.cross&&n._dims.length>1&&(n._drop=!1,n.cross()),t.clean()&&n._drop&&i.clean(!0).runAfter(()=>this.clean()),n.changes(i)},cross(){const e=this,t=e.value,n=e._dnames,i=n.map(()=>({})),r=n.length;function s(a){let u,l,c,f;for(u in a)for(c=a[u].tuple,l=0;l{const v=Tt(b);return r(b),n.push(v),v}),this.cellkey=e.key?e.key:Ny(this._dims),this._countOnly=!0,this._counts=[],this._measures=[];const s=e.fields||[null],o=e.ops||["count"],a=e.aggregate_params||[null],u=e.as||[],l=s.length,c={};let f,d,h,p,g,m,y;for(l!==o.length&&q("Unmatched number of fields and aggregate ops."),y=0;ya9(b,b.field)),Object.create(null)},cellkey:Ny(),cell(e,t){let n=this.value[e];return n?n.num===0&&this._drop&&n.stamp{const f=i(c);c[a]=f,c[u]=f==null?null:r+s*(1+(f-r)/s)}:c=>c[a]=i(c)),t.modifies(n?o:a)},_bins(e){if(this.value&&!e.modified())return this.value;const t=e.field,n=WE(e),i=n.step;let r=n.start,s=r+Math.ceil((n.stop-r)/i)*i,o,a;(o=e.anchor)!=null&&(a=o-(r+i*Math.floor((o-r)/i)),r+=a,s+=a);const u=function(l){let c=Cn(t(l));return c==null?null:cs?1/0:(c=Math.max(r,Math.min(c,s-i)),r+i*Math.floor(WG+(c-r)/i))};return u.start=r,u.stop=n.stop,u.step=i,this.value=ii(u,wn(t),e.name||"bin_"+Tt(t))}});function u9(e,t,n){const i=e;let r=t||[],s=n||[],o={},a=0;return{add:u=>s.push(u),remove:u=>o[i(u)]=++a,size:()=>r.length,data:(u,l)=>(a&&(r=r.filter(c=>!o[i(c)]),o={},a=0),l&&u&&r.sort(u),s.length&&(r=u?K4(u,r,s.sort(u)):r.concat(s),s=[]),r)}}function Iy(e){P.call(this,[],e)}Iy.Definition={type:"Collect",metadata:{source:!0},params:[{name:"sort",type:"compare"}]},ee(Iy,P,{transform(e,t){const n=t.fork(t.ALL),i=u9(_e,this.value,n.materialize(n.ADD).add),r=e.sort,s=t.changed()||r&&(e.modified("sort")||t.modified(r.fields));return n.visit(n.REM,i.remove),this.modified(s),this.value=n.source=i.data(Ta(r),s),t.source&&t.source.root&&(this.value.root=t.source.root),n}});function l9(e){xt.call(this,null,HG,e)}ee(l9,xt);function HG(e){return this.value&&!e.modified()?this.value:C2(e.fields,e.orders)}function Py(e){P.call(this,null,e)}Py.Definition={type:"CountPattern",metadata:{generates:!0,changes:!0},params:[{name:"field",type:"field",required:!0},{name:"case",type:"enum",values:["upper","lower","mixed"],default:"mixed"},{name:"pattern",type:"string",default:'[\\w"]+'},{name:"stopwords",type:"string",default:""},{name:"as",type:"string",array:!0,length:2,default:["text","count"]}]};function GG(e,t,n){switch(t){case"upper":e=e.toUpperCase();break;case"lower":e=e.toLowerCase();break}return e.match(n)}ee(Py,P,{transform(e,t){const n=f=>d=>{for(var h=GG(a(d),e.case,s)||[],p,g=0,m=h.length;gr[f]=1+(r[f]||0)),c=n(f=>r[f]-=1);return i?t.visit(t.SOURCE,l):(t.visit(t.ADD,l),t.visit(t.REM,c)),this._finish(t,u)},_parameterCheck(e,t){let n=!1;return(e.modified("stopwords")||!this._stop)&&(this._stop=new RegExp("^"+(e.stopwords||"")+"$","i"),n=!0),(e.modified("pattern")||!this._match)&&(this._match=new RegExp(e.pattern||"[\\w']+","g"),n=!0),(e.modified("field")||t.modified(e.field.fields))&&(n=!0),n&&(this._counts={}),n},_finish(e,t){const n=this._counts,i=this._tuples||(this._tuples={}),r=t[0],s=t[1],o=e.fork(e.NO_SOURCE|e.NO_FIELDS);let a,u,l;for(a in n)u=i[a],l=n[a]||0,!u&&l?(i[a]=u=nt({}),u[r]=a,u[s]=l,o.add.push(u)):l===0?(u&&o.rem.push(u),n[a]=null,i[a]=null):u[s]!==l&&(u[s]=l,o.mod.push(u));return o.modifies(t)}});function zy(e){P.call(this,null,e)}zy.Definition={type:"Cross",metadata:{generates:!0},params:[{name:"filter",type:"expr"},{name:"as",type:"string",array:!0,length:2,default:["a","b"]}]},ee(zy,P,{transform(e,t){const n=t.fork(t.NO_SOURCE),i=e.as||["a","b"],r=i[0],s=i[1],o=!this.value||t.changed(t.ADD_REM)||e.modified("as")||e.modified("filter");let a=this.value;return o?(a&&(n.rem=a),a=t.materialize(t.SOURCE).source,n.add=this.value=VG(a,r,s,e.filter||Ui)):n.mod=a,n.source=this.value,n.modifies(i)}});function VG(e,t,n,i){for(var r=[],s={},o=e.length,a=0,u,l;ad9(s,t))):typeof i[r]===f9&&i[r](e[r]);return i}function By(e){P.call(this,null,e)}const h9=[{key:{function:"normal"},params:[{name:"mean",type:"number",default:0},{name:"stdev",type:"number",default:1}]},{key:{function:"lognormal"},params:[{name:"mean",type:"number",default:0},{name:"stdev",type:"number",default:1}]},{key:{function:"uniform"},params:[{name:"min",type:"number",default:0},{name:"max",type:"number",default:1}]},{key:{function:"kde"},params:[{name:"field",type:"field",required:!0},{name:"from",type:"data"},{name:"bandwidth",type:"number",default:0}]}],KG={key:{function:"mixture"},params:[{name:"distributions",type:"param",array:!0,params:h9},{name:"weights",type:"number",array:!0}]};By.Definition={type:"Density",metadata:{generates:!0},params:[{name:"extent",type:"number",array:!0,length:2},{name:"steps",type:"number"},{name:"minsteps",type:"number",default:25},{name:"maxsteps",type:"number",default:200},{name:"method",type:"string",default:"pdf",values:["pdf","cdf"]},{name:"distribution",type:"param",params:h9.concat(KG)},{name:"as",type:"string",array:!0,default:["value","density"]}]},ee(By,P,{transform(e,t){const n=t.fork(t.NO_SOURCE|t.NO_FIELDS);if(!this.value||t.changed()||e.modified()){const i=d9(e.distribution,ZG(t)),r=e.steps||e.minsteps||25,s=e.steps||e.maxsteps||200;let o=e.method||"pdf";o!=="pdf"&&o!=="cdf"&&q("Invalid density method: "+o),!e.extent&&!i.data&&q("Missing density extent parameter."),o=i[o];const a=e.as||["value","density"],u=e.extent||qr(i.data()),l=A0(o,u,r,s).map(c=>{const f={};return f[a[0]]=c[0],f[a[1]]=c[1],nt(f)});this.value&&(n.rem=this.value),this.value=n.add=n.source=l}return n}});function ZG(e){return()=>e.materialize(e.SOURCE).source}function p9(e,t){return e?e.map((n,i)=>t[i]||Tt(n)):null}function Uy(e,t,n){const i=[],r=f=>f(u);let s,o,a,u,l,c;if(t==null)i.push(e.map(n));else for(s={},o=0,a=e.length;oXc(qr(e,t))/30;ee(jy,P,{transform(e,t){if(this.value&&!(e.modified()||t.changed()))return t;const n=t.materialize(t.SOURCE).source,i=Uy(t.source,e.groupby,En),r=e.smooth||!1,s=e.field,o=e.step||JG(n,s),a=Ta((p,g)=>s(p)-s(g)),u=e.as||g9,l=i.length;let c=1/0,f=-1/0,d=0,h;for(;df&&(f=g),p[++h][u]=g}return this.value={start:c,stop:f,step:o},t.reflow(!0).modifies(u)}});function m9(e){xt.call(this,null,QG,e),this.modified(!0)}ee(m9,xt);function QG(e){const t=e.expr;return this.value&&!e.modified("expr")?this.value:ii(n=>t(n,e),wn(t),Tt(t))}function qy(e){P.call(this,[void 0,void 0],e)}qy.Definition={type:"Extent",metadata:{},params:[{name:"field",type:"field",required:!0}]},ee(qy,P,{transform(e,t){const n=this.value,i=e.field,r=t.changed()||t.modified(i.fields)||e.modified("field");let s=n[0],o=n[1];if((r||s==null)&&(s=1/0,o=-1/0),t.visit(r?t.SOURCE:t.ADD,a=>{const u=Cn(i(a));u!=null&&(uo&&(o=u))}),!Number.isFinite(s)||!Number.isFinite(o)){let a=Tt(i);a&&(a=` for field "${a}"`),t.dataflow.warn(`Infinite extent${a}: [${s}, ${o}]`),s=o=void 0}this.value=[s,o]}});function Wy(e,t){xt.call(this,e),this.parent=t,this.count=0}ee(Wy,xt,{connect(e){return this.detachSubflow=e.detachSubflow,this.targets().add(e),e.source=this},add(e){this.count+=1,this.value.add.push(e)},rem(e){this.count-=1,this.value.rem.push(e)},mod(e){this.value.mod.push(e)},init(e){this.value.init(e,e.NO_SOURCE)},evaluate(){return this.value}});function $0(e){P.call(this,{},e),this._keys=sl();const t=this._targets=[];t.active=0,t.forEach=n=>{for(let i=0,r=t.active;ii&&i.count>0);this.initTargets(n)}},initTargets(e){const t=this._targets,n=t.length,i=e?e.length:0;let r=0;for(;rthis.subflow(u,r,t);return this._group=e.group||{},this.initTargets(),t.visit(t.REM,u=>{const l=_e(u),c=s.get(l);c!==void 0&&(s.delete(l),a(c).rem(u))}),t.visit(t.ADD,u=>{const l=i(u);s.set(_e(u),l),a(l).add(u)}),o||t.modified(i.fields)?t.visit(t.MOD,u=>{const l=_e(u),c=s.get(l),f=i(u);c===f?a(f).mod(u):(s.set(l,f),a(c).rem(u),a(f).add(u))}):t.changed(t.MOD)&&t.visit(t.MOD,u=>{a(s.get(_e(u))).mod(u)}),o&&t.visit(t.REFLOW,u=>{const l=_e(u),c=s.get(l),f=i(u);c!==f&&(s.set(l,f),a(c).rem(u),a(f).add(u))}),t.clean()?n.runAfter(()=>{this.clean(),s.clean()}):s.empty>n.cleanThreshold&&n.runAfter(s.clean),t}});function y9(e){xt.call(this,null,eV,e)}ee(y9,xt);function eV(e){return this.value&&!e.modified()?this.value:W(e.name)?oe(e.name).map(t=>Bi(t)):Bi(e.name,e.as)}function Hy(e){P.call(this,sl(),e)}Hy.Definition={type:"Filter",metadata:{changes:!0},params:[{name:"expr",type:"expr",required:!0}]},ee(Hy,P,{transform(e,t){const n=t.dataflow,i=this.value,r=t.fork(),s=r.add,o=r.rem,a=r.mod,u=e.expr;let l=!0;t.visit(t.REM,f=>{const d=_e(f);i.has(d)?i.delete(d):o.push(f)}),t.visit(t.ADD,f=>{u(f,e)?s.push(f):i.set(_e(f),1)});function c(f){const d=_e(f),h=u(f,e),p=i.get(d);h&&p?(i.delete(d),s.push(f)):!h&&!p?(i.set(d,1),o.push(f)):l&&h&&!p&&a.push(f)}return t.visit(t.MOD,c),e.modified()&&(l=!1,t.visit(t.REFLOW,c)),i.empty>n.cleanThreshold&&n.runAfter(i.clean),r}});function Gy(e){P.call(this,[],e)}Gy.Definition={type:"Flatten",metadata:{generates:!0},params:[{name:"fields",type:"field",array:!0,required:!0},{name:"index",type:"string"},{name:"as",type:"string",array:!0}]},ee(Gy,P,{transform(e,t){const n=t.fork(t.NO_SOURCE),i=e.fields,r=p9(i,e.as||[]),s=e.index||null,o=r.length;return n.rem=this.value,t.visit(t.SOURCE,a=>{const u=i.map(p=>p(a)),l=u.reduce((p,g)=>Math.max(p,g.length),0);let c=0,f,d,h;for(;c{for(let c=0,f;co[i]=n(o,e))}});function b9(e){P.call(this,[],e)}ee(b9,P,{transform(e,t){const n=t.fork(t.ALL),i=e.generator;let r=this.value,s=e.size-r.length,o,a,u;if(s>0){for(o=[];--s>=0;)o.push(u=nt(i(e))),r.push(u);n.add=n.add.length?n.materialize(n.ADD).add.concat(o):o}else a=r.slice(0,-s),n.rem=n.rem.length?n.materialize(n.REM).rem.concat(a):a,r=r.slice(-s);return n.source=this.value=r,n}});const S0={value:"value",median:h8,mean:Lq,min:I2,max:Aa},tV=[];function Xy(e){P.call(this,[],e)}Xy.Definition={type:"Impute",metadata:{changes:!0},params:[{name:"field",type:"field",required:!0},{name:"key",type:"field",required:!0},{name:"keyvals",array:!0},{name:"groupby",type:"field",array:!0},{name:"method",type:"enum",default:"value",values:["value","mean","median","max","min"]},{name:"value",default:0}]};function nV(e){var t=e.method||S0.value,n;if(S0[t]==null)q("Unrecognized imputation method: "+t);else return t===S0.value?(n=e.value!==void 0?e.value:0,()=>n):S0[t]}function iV(e){const t=e.field;return n=>n?t(n):NaN}ee(Xy,P,{transform(e,t){var n=t.fork(t.ALL),i=nV(e),r=iV(e),s=Tt(e.field),o=Tt(e.key),a=(e.groupby||[]).map(Tt),u=rV(t.source,e.groupby,e.key,e.keyvals),l=[],c=this.value,f=u.domain.length,d,h,p,g,m,y,b,v,x,_;for(m=0,v=u.length;my(m),s=[],o=i?i.slice():[],a={},u={},l,c,f,d,h,p,g,m;for(o.forEach((y,b)=>a[y]=b+1),d=0,g=e.length;dn.add(s))):(r=n.value=n.value||this.init(e),t.visit(t.REM,s=>n.rem(s)),t.visit(t.ADD,s=>n.add(s))),n.changes(),t.visit(t.SOURCE,s=>{Oe(s,r[n.cellkey(s)].tuple)}),t.reflow(i).modifies(this._outputs)},changes(){const e=this._adds,t=this._mods;let n,i;for(n=0,i=this._alen;n{const p=_y(h,o)[a],g=e.counts?h.length:1,m=c||qr(h);A0(p,m,f,d).forEach(y=>{const b={};for(let v=0;v(this._pending=oe(r.data),s=>s.touch(this)))}:n.request(e.url,e.format).then(i=>Jy(this,t,oe(i.data)))}});function oV(e){return e.modified("async")&&!(e.modified("values")||e.modified("url")||e.modified("format"))}function Jy(e,t,n){n.forEach(nt);const i=t.fork(t.NO_FIELDS&t.NO_SOURCE);return i.rem=e.value,e.value=i.source=i.add=n,e._pending=null,i.rem.length&&i.clean(!0),i}function Qy(e){P.call(this,{},e)}Qy.Definition={type:"Lookup",metadata:{modifies:!0},params:[{name:"index",type:"index",params:[{name:"from",type:"data",required:!0},{name:"key",type:"field",required:!0}]},{name:"values",type:"field",array:!0},{name:"fields",type:"field",array:!0,required:!0},{name:"as",type:"string",array:!0},{name:"default",default:null}]},ee(Qy,P,{transform(e,t){const n=e.fields,i=e.index,r=e.values,s=e.default==null?null:e.default,o=e.modified(),a=n.length;let u=o?t.SOURCE:t.ADD,l=t,c=e.as,f,d,h;return r?(d=r.length,a>1&&!c&&q('Multi-field lookup requires explicit "as" parameter.'),c&&c.length!==a*d&&q('The "as" parameter has too few output field names.'),c=c||r.map(Tt),f=function(p){for(var g=0,m=0,y,b;gt.modified(p.fields)),u|=h?t.MOD:0),t.visit(u,f),l.modifies(c)}});function _9(e){xt.call(this,null,aV,e)}ee(_9,xt);function aV(e){if(this.value&&!e.modified())return this.value;const t=e.extents,n=t.length;let i=1/0,r=-1/0,s,o;for(s=0;sr&&(r=o[1]);return[i,r]}function w9(e){xt.call(this,null,uV,e)}ee(w9,xt);function uV(e){return this.value&&!e.modified()?this.value:e.values.reduce((t,n)=>t.concat(n),[])}function E9(e){P.call(this,null,e)}ee(E9,P,{transform(e,t){return this.modified(e.modified()),this.value=e,t.fork(t.NO_SOURCE|t.NO_FIELDS)}});function eb(e){Do.call(this,e)}eb.Definition={type:"Pivot",metadata:{generates:!0,changes:!0},params:[{name:"groupby",type:"field",array:!0},{name:"field",type:"field",required:!0},{name:"value",type:"field",required:!0},{name:"op",type:"enum",values:vf,default:"sum"},{name:"limit",type:"number",default:0},{name:"key",type:"field"}]},ee(eb,Do,{_transform:Do.prototype.transform,transform(e,t){return this._transform(lV(e,t),t)}});function lV(e,t){const n=e.field,i=e.value,r=(e.op==="count"?"__count__":e.op)||"sum",s=wn(n).concat(wn(i)),o=fV(n,e.limit||0,t);return t.changed()&&e.set("__pivot__",null,null,!0),{key:e.key,groupby:e.groupby,ops:o.map(()=>r),fields:o.map(a=>cV(a,n,i,s)),as:o.map(a=>a+""),modified:e.modified.bind(e)}}function cV(e,t,n,i){return ii(r=>t(r)===e?n(r):NaN,i,e+"")}function fV(e,t,n){const i={},r=[];return n.visit(n.SOURCE,s=>{const o=e(s);i[o]||(i[o]=1,r.push(o))}),r.sort(rl),t?r.slice(0,t):r}function C9(e){$0.call(this,e)}ee(C9,$0,{transform(e,t){const n=e.subflow,i=e.field,r=s=>this.subflow(_e(s),n,t,s);return(e.modified("field")||i&&t.modified(wn(i)))&&q("PreFacet does not support field modification."),this.initTargets(),i?(t.visit(t.MOD,s=>{const o=r(s);i(s).forEach(a=>o.mod(a))}),t.visit(t.ADD,s=>{const o=r(s);i(s).forEach(a=>o.add(nt(a)))}),t.visit(t.REM,s=>{const o=r(s);i(s).forEach(a=>o.rem(a))})):(t.visit(t.MOD,s=>r(s).mod(s)),t.visit(t.ADD,s=>r(s).add(s)),t.visit(t.REM,s=>r(s).rem(s))),t.clean()&&t.runAfter(()=>this.clean()),t}});function tb(e){P.call(this,null,e)}tb.Definition={type:"Project",metadata:{generates:!0,changes:!0},params:[{name:"fields",type:"field",array:!0},{name:"as",type:"string",null:!0,array:!0}]},ee(tb,P,{transform(e,t){const n=t.fork(t.NO_SOURCE),i=e.fields,r=p9(e.fields,e.as||[]),s=i?(a,u)=>dV(a,u,i,r):m0;let o;return this.value?o=this.value:(t=t.addAll(),o=this.value={}),t.visit(t.REM,a=>{const u=_e(a);n.rem.push(o[u]),o[u]=null}),t.visit(t.ADD,a=>{const u=s(a,nt({}));o[_e(a)]=u,n.add.push(u)}),t.visit(t.MOD,a=>{n.mod.push(s(a,o[_e(a)]))}),n}});function dV(e,t,n,i){for(let r=0,s=n.length;r{const d=my(f,l);for(let h=0;h{const s=_e(r);n.rem.push(i[s]),i[s]=null}),t.visit(t.ADD,r=>{const s=dy(r);i[_e(r)]=s,n.add.push(s)}),t.visit(t.MOD,r=>{const s=i[_e(r)];for(const o in r)s[o]=r[o],n.modifies(o);n.mod.push(s)})),n}});function ib(e){P.call(this,[],e),this.count=0}ib.Definition={type:"Sample",metadata:{},params:[{name:"size",type:"number",default:1e3}]},ee(ib,P,{transform(e,t){const n=t.fork(t.NO_SOURCE),i=e.modified("size"),r=e.size,s=this.value.reduce((c,f)=>(c[_e(f)]=1,c),{});let o=this.value,a=this.count,u=0;function l(c){let f,d;o.length=u&&(f=o[d],s[_e(f)]&&n.rem.push(f),o[d]=c)),++a}if(t.rem.length&&(t.visit(t.REM,c=>{const f=_e(c);s[f]&&(s[f]=-1,n.rem.push(c)),--a}),o=o.filter(c=>s[_e(c)]!==-1)),(t.rem.length||i)&&o.length{s[_e(c)]||l(c)}),u=-1),i&&o.length>r){const c=o.length-r;for(let f=0;f{s[_e(c)]&&n.mod.push(c)}),t.add.length&&t.visit(t.ADD,l),(t.add.length||u<0)&&(n.add=o.filter(c=>!s[_e(c)])),this.count=a,this.value=n.source=o,n}});function rb(e){P.call(this,null,e)}rb.Definition={type:"Sequence",metadata:{generates:!0,changes:!0},params:[{name:"start",type:"number",required:!0},{name:"stop",type:"number",required:!0},{name:"step",type:"number",default:1},{name:"as",type:"string",default:"data"}]},ee(rb,P,{transform(e,t){if(this.value&&!e.modified())return;const n=t.materialize().fork(t.MOD),i=e.as||"data";return n.rem=this.value?t.rem.concat(this.value):t.rem,this.value=Ei(e.start,e.stop,e.step||1).map(r=>{const s={};return s[i]=r,nt(s)}),n.add=t.add.concat(this.value),n}});function $9(e){P.call(this,null,e),this.modified(!0)}ee($9,P,{transform(e,t){return this.value=t.source,t.changed()?t.fork(t.NO_SOURCE|t.NO_FIELDS):t.StopPropagation}});function sb(e){P.call(this,null,e)}const S9=["unit0","unit1"];sb.Definition={type:"TimeUnit",metadata:{modifies:!0},params:[{name:"field",type:"field",required:!0},{name:"interval",type:"boolean",default:!0},{name:"units",type:"enum",values:H2,array:!0},{name:"step",type:"number",default:1},{name:"maxbins",type:"number",default:40},{name:"extent",type:"date",array:!0},{name:"timezone",type:"enum",default:"local",values:["local","utc"]},{name:"as",type:"string",array:!0,length:2,default:S9}]},ee(sb,P,{transform(e,t){const n=e.field,i=e.interval!==!1,r=e.timezone==="utc",s=this._floor(e,t),o=(r?ml:gl)(s.unit).offset,a=e.as||S9,u=a[0],l=a[1],c=s.step;let f=s.start||1/0,d=s.stop||-1/0,h=t.ADD;return(e.modified()||t.changed(t.REM)||t.modified(wn(n)))&&(t=t.reflow(!0),h=t.SOURCE,f=1/0,d=-1/0),t.visit(h,p=>{const g=n(p);let m,y;g==null?(p[u]=null,i&&(p[l]=null)):(p[u]=m=y=s(g),i&&(p[l]=y=o(m,c)),md&&(d=y))}),s.start=f,s.stop=d,t.modifies(i?a:u)},_floor(e,t){const n=e.timezone==="utc",{units:i,step:r}=e.units?{units:e.units,step:e.step||1}:X8({extent:e.extent||qr(t.materialize(t.SOURCE).source,e.field),maxbins:e.maxbins}),s=V2(i),o=this.value||{},a=(n?P8:I8)(s,r);return a.unit=Ge(s),a.units=s,a.step=r,a.start=o.start,a.stop=o.stop,this.value=a}});function F9(e){P.call(this,sl(),e)}ee(F9,P,{transform(e,t){const n=t.dataflow,i=e.field,r=this.value,s=a=>r.set(i(a),a);let o=!0;return e.modified("field")||t.modified(i.fields)?(r.clear(),t.visit(t.SOURCE,s)):t.changed()?(t.visit(t.REM,a=>r.delete(i(a))),t.visit(t.ADD,s)):o=!1,this.modified(o),r.empty>n.cleanThreshold&&n.runAfter(r.clean),t.fork()}});function D9(e){P.call(this,null,e)}ee(D9,P,{transform(e,t){(!this.value||e.modified("field")||e.modified("sort")||t.changed()||e.sort&&t.modified(e.sort.fields))&&(this.value=(e.sort?t.source.slice().sort(Ta(e.sort)):t.source).map(e.field))}});function pV(e,t,n,i){const r=xf[e](t,n);return{init:r.init||bo,update:function(s,o){o[i]=r.next(s)}}}const xf={row_number:function(){return{next:e=>e.index+1}},rank:function(){let e;return{init:()=>e=1,next:t=>{const n=t.index,i=t.data;return n&&t.compare(i[n-1],i[n])?e=n+1:e}}},dense_rank:function(){let e;return{init:()=>e=1,next:t=>{const n=t.index,i=t.data;return n&&t.compare(i[n-1],i[n])?++e:e}}},percent_rank:function(){const e=xf.rank(),t=e.next;return{init:e.init,next:n=>(t(n)-1)/(n.data.length-1)}},cume_dist:function(){let e;return{init:()=>e=0,next:t=>{const n=t.data,i=t.compare;let r=t.index;if(e0||q("ntile num must be greater than zero.");const n=xf.cume_dist(),i=n.next;return{init:n.init,next:r=>Math.ceil(t*i(r))}},lag:function(e,t){return t=+t||1,{next:n=>{const i=n.index-t;return i>=0?e(n.data[i]):null}}},lead:function(e,t){return t=+t||1,{next:n=>{const i=n.index+t,r=n.data;return ie(t.data[t.i0])}},last_value:function(e){return{next:t=>e(t.data[t.i1-1])}},nth_value:function(e,t){return t=+t,t>0||q("nth_value nth must be greater than zero."),{next:n=>{const i=n.i0+(t-1);return it=null,next:n=>{const i=e(n.data[n.index]);return i!=null?t=i:t}}},next_value:function(e){let t,n;return{init:()=>(t=null,n=-1),next:i=>{const r=i.data;return i.index<=n?t:(n=gV(e,r,i.index))<0?(n=r.length,t=null):t=e(r[n])}}}};function gV(e,t,n){for(let i=t.length;nu[g]=1)}h(e.sort),t.forEach((p,g)=>{const m=n[g],y=i[g],b=r[g]||null,v=Tt(m),x=r9(p,v,s[g]);if(h(m),o.push(x),ue(xf,p))a.push(pV(p,m,y,x));else{if(m==null&&p!=="count"&&q("Null aggregate field specified."),p==="count"){c.push(x);return}d=!1;let _=l[v];_||(_=l[v]=[],_.field=m,f.push(_)),_.push(s9(p,b,x))}}),(c.length||f.length)&&(this.cell=yV(f,c,d)),this.inputs=Object.keys(u)}const M9=T9.prototype;M9.init=function(){this.windows.forEach(e=>e.init()),this.cell&&this.cell.init()},M9.update=function(e,t){const n=this.cell,i=this.windows,r=e.data,s=i&&i.length;let o;if(n){for(o=e.p0;oa9(u,u.field));const i={num:0,agg:null,store:!1,count:t};if(!n)for(var r=e.length,s=i.agg=Array(r),o=0;othis.group(r(a));let o=this.state;(!o||n)&&(o=this.state=new T9(e)),n||t.modified(o.inputs)?(this.value={},t.visit(t.SOURCE,a=>s(a).add(a))):(t.visit(t.REM,a=>s(a).remove(a)),t.visit(t.ADD,a=>s(a).add(a)));for(let a=0,u=this._mlen;a0&&!r(s[n],s[n-1])&&(e.i0=t.left(s,s[n])),i1?0:e<-1?wl:Math.acos(e)}function O9(e){return e>=1?F0:e<=-1?-F0:Math.asin(e)}const ub=Math.PI,lb=2*ub,Ra=1e-6,CV=lb-Ra;function L9(e){this._+=e[0];for(let t=1,n=e.length;t=0))throw new Error(`invalid digits: ${e}`);if(t>15)return L9;const n=10**t;return function(i){this._+=i[0];for(let r=1,s=i.length;rRa)if(!(Math.abs(f*u-l*c)>Ra)||!s)this._append`L${this._x1=t},${this._y1=n}`;else{let h=i-o,p=r-a,g=u*u+l*l,m=h*h+p*p,y=Math.sqrt(g),b=Math.sqrt(d),v=s*Math.tan((ub-Math.acos((g+d-m)/(2*y*b)))/2),x=v/b,_=v/y;Math.abs(x-1)>Ra&&this._append`L${t+x*c},${n+x*f}`,this._append`A${s},${s},0,0,${+(f*h>c*p)},${this._x1=t+_*u},${this._y1=n+_*l}`}}arc(t,n,i,r,s,o){if(t=+t,n=+n,i=+i,o=!!o,i<0)throw new Error(`negative radius: ${i}`);let a=i*Math.cos(r),u=i*Math.sin(r),l=t+a,c=n+u,f=1^o,d=o?r-s:s-r;this._x1===null?this._append`M${l},${c}`:(Math.abs(this._x1-l)>Ra||Math.abs(this._y1-c)>Ra)&&this._append`L${l},${c}`,i&&(d<0&&(d=d%lb+lb),d>CV?this._append`A${i},${i},0,1,${f},${t-a},${n-u}A${i},${i},0,1,${f},${this._x1=l},${this._y1=c}`:d>Ra&&this._append`A${i},${i},0,${+(d>=ub)},${f},${this._x1=t+i*Math.cos(s)},${this._y1=n+i*Math.sin(s)}`)}rect(t,n,i,r){this._append`M${this._x0=this._x1=+t},${this._y0=this._y1=+n}h${i=+i}v${+r}h${-i}Z`}toString(){return this._}};function D0(){return new cb}D0.prototype=cb.prototype;function T0(e){let t=3;return e.digits=function(n){if(!arguments.length)return t;if(n==null)t=null;else{const i=Math.floor(n);if(!(i>=0))throw new RangeError(`invalid digits: ${n}`);t=i}return e},()=>new cb(t)}function AV(e){return e.innerRadius}function $V(e){return e.outerRadius}function SV(e){return e.startAngle}function FV(e){return e.endAngle}function DV(e){return e&&e.padAngle}function TV(e,t,n,i,r,s,o,a){var u=n-e,l=i-t,c=o-r,f=a-s,d=f*u-c*l;if(!(d*dF*F+A*A&&(C=S,k=$),{cx:C,cy:k,x01:-c,y01:-f,x11:C*(r/_-1),y11:k*(r/_-1)}}function MV(){var e=AV,t=$V,n=rt(0),i=null,r=SV,s=FV,o=DV,a=null,u=T0(l);function l(){var c,f,d=+e.apply(this,arguments),h=+t.apply(this,arguments),p=r.apply(this,arguments)-F0,g=s.apply(this,arguments)-F0,m=N9(g-p),y=g>p;if(a||(a=c=u()),hFn))a.moveTo(0,0);else if(m>R9-Fn)a.moveTo(h*Ma(p),h*Yr(p)),a.arc(0,0,h,p,g,!y),d>Fn&&(a.moveTo(d*Ma(g),d*Yr(g)),a.arc(0,0,d,g,p,y));else{var b=p,v=g,x=p,_=g,E=m,w=m,C=o.apply(this,arguments)/2,k=C>Fn&&(i?+i.apply(this,arguments):Na(d*d+h*h)),S=ab(N9(h-d)/2,+n.apply(this,arguments)),$=S,O=S,R,F;if(k>Fn){var A=O9(k/d*Yr(C)),T=O9(k/h*Yr(C));(E-=A*2)>Fn?(A*=y?1:-1,x+=A,_-=A):(E=0,x=_=(p+g)/2),(w-=T*2)>Fn?(T*=y?1:-1,b+=T,v-=T):(w=0,b=v=(p+g)/2)}var B=h*Ma(b),H=h*Yr(b),z=d*Ma(_),ne=d*Yr(_);if(S>Fn){var be=h*Ma(v),de=h*Yr(v),Te=d*Ma(x),ct=d*Yr(x),$e;if(mFn?O>Fn?(R=M0(Te,ct,B,H,h,O,y),F=M0(be,de,z,ne,h,O,y),a.moveTo(R.cx+R.x01,R.cy+R.y01),OFn)||!(E>Fn)?a.lineTo(z,ne):$>Fn?(R=M0(z,ne,be,de,d,-$,y),F=M0(B,H,Te,ct,d,-$,y),a.lineTo(R.cx+R.x01,R.cy+R.y01),$=h;--p)a.point(v[p],x[p]);a.lineEnd(),a.areaEnd()}y&&(v[d]=+e(m,d,f),x[d]=+t(m,d,f),a.point(i?+i(m,d,f):v[d],n?+n(m,d,f):x[d]))}if(b)return a=null,b+""||null}function c(){return U9().defined(r).curve(o).context(s)}return l.x=function(f){return arguments.length?(e=typeof f=="function"?f:rt(+f),i=null,l):e},l.x0=function(f){return arguments.length?(e=typeof f=="function"?f:rt(+f),l):e},l.x1=function(f){return arguments.length?(i=f==null?null:typeof f=="function"?f:rt(+f),l):i},l.y=function(f){return arguments.length?(t=typeof f=="function"?f:rt(+f),n=null,l):t},l.y0=function(f){return arguments.length?(t=typeof f=="function"?f:rt(+f),l):t},l.y1=function(f){return arguments.length?(n=f==null?null:typeof f=="function"?f:rt(+f),l):n},l.lineX0=l.lineY0=function(){return c().x(e).y(t)},l.lineY1=function(){return c().x(e).y(n)},l.lineX1=function(){return c().x(i).y(t)},l.defined=function(f){return arguments.length?(r=typeof f=="function"?f:rt(!!f),l):r},l.curve=function(f){return arguments.length?(o=f,s!=null&&(a=o(s)),l):o},l.context=function(f){return arguments.length?(f==null?s=a=null:a=o(s=f),l):s},l}const NV={draw(e,t){const n=Na(t/wl);e.moveTo(n,0),e.arc(0,0,n,0,R9)}};function RV(e,t){let n=null,i=T0(r);e=typeof e=="function"?e:rt(e||NV),t=typeof t=="function"?t:rt(t===void 0?64:+t);function r(){let s;if(n||(n=s=i()),e.apply(this,arguments).draw(n,+t.apply(this,arguments)),s)return n=null,s+""||null}return r.type=function(s){return arguments.length?(e=typeof s=="function"?s:rt(s),r):e},r.size=function(s){return arguments.length?(t=typeof s=="function"?s:rt(+s),r):t},r.context=function(s){return arguments.length?(n=s??null,r):n},r}function To(){}function N0(e,t,n){e._context.bezierCurveTo((2*e._x0+e._x1)/3,(2*e._y0+e._y1)/3,(e._x0+2*e._x1)/3,(e._y0+2*e._y1)/3,(e._x0+4*e._x1+t)/6,(e._y0+4*e._y1+n)/6)}function R0(e){this._context=e}R0.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){switch(this._point){case 3:N0(this,this._x1,this._y1);case 2:this._context.lineTo(this._x1,this._y1);break}(this._line||this._line!==0&&this._point===1)&&this._context.closePath(),this._line=1-this._line},point:function(e,t){switch(e=+e,t=+t,this._point){case 0:this._point=1,this._line?this._context.lineTo(e,t):this._context.moveTo(e,t);break;case 1:this._point=2;break;case 2:this._point=3,this._context.lineTo((5*this._x0+this._x1)/6,(5*this._y0+this._y1)/6);default:N0(this,e,t);break}this._x0=this._x1,this._x1=e,this._y0=this._y1,this._y1=t}};function OV(e){return new R0(e)}function q9(e){this._context=e}q9.prototype={areaStart:To,areaEnd:To,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._y0=this._y1=this._y2=this._y3=this._y4=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:{this._context.moveTo(this._x2,this._y2),this._context.closePath();break}case 2:{this._context.moveTo((this._x2+2*this._x3)/3,(this._y2+2*this._y3)/3),this._context.lineTo((this._x3+2*this._x2)/3,(this._y3+2*this._y2)/3),this._context.closePath();break}case 3:{this.point(this._x2,this._y2),this.point(this._x3,this._y3),this.point(this._x4,this._y4);break}}},point:function(e,t){switch(e=+e,t=+t,this._point){case 0:this._point=1,this._x2=e,this._y2=t;break;case 1:this._point=2,this._x3=e,this._y3=t;break;case 2:this._point=3,this._x4=e,this._y4=t,this._context.moveTo((this._x0+4*this._x1+e)/6,(this._y0+4*this._y1+t)/6);break;default:N0(this,e,t);break}this._x0=this._x1,this._x1=e,this._y0=this._y1,this._y1=t}};function LV(e){return new q9(e)}function W9(e){this._context=e}W9.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){(this._line||this._line!==0&&this._point===3)&&this._context.closePath(),this._line=1-this._line},point:function(e,t){switch(e=+e,t=+t,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3;var n=(this._x0+4*this._x1+e)/6,i=(this._y0+4*this._y1+t)/6;this._line?this._context.lineTo(n,i):this._context.moveTo(n,i);break;case 3:this._point=4;default:N0(this,e,t);break}this._x0=this._x1,this._x1=e,this._y0=this._y1,this._y1=t}};function IV(e){return new W9(e)}function H9(e,t){this._basis=new R0(e),this._beta=t}H9.prototype={lineStart:function(){this._x=[],this._y=[],this._basis.lineStart()},lineEnd:function(){var e=this._x,t=this._y,n=e.length-1;if(n>0)for(var i=e[0],r=t[0],s=e[n]-i,o=t[n]-r,a=-1,u;++a<=n;)u=a/n,this._basis.point(this._beta*e[a]+(1-this._beta)*(i+u*s),this._beta*t[a]+(1-this._beta)*(r+u*o));this._x=this._y=null,this._basis.lineEnd()},point:function(e,t){this._x.push(+e),this._y.push(+t)}};const PV=function e(t){function n(i){return t===1?new R0(i):new H9(i,t)}return n.beta=function(i){return e(+i)},n}(.85);function O0(e,t,n){e._context.bezierCurveTo(e._x1+e._k*(e._x2-e._x0),e._y1+e._k*(e._y2-e._y0),e._x2+e._k*(e._x1-t),e._y2+e._k*(e._y1-n),e._x2,e._y2)}function db(e,t){this._context=e,this._k=(1-t)/6}db.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:O0(this,this._x1,this._y1);break}(this._line||this._line!==0&&this._point===1)&&this._context.closePath(),this._line=1-this._line},point:function(e,t){switch(e=+e,t=+t,this._point){case 0:this._point=1,this._line?this._context.lineTo(e,t):this._context.moveTo(e,t);break;case 1:this._point=2,this._x1=e,this._y1=t;break;case 2:this._point=3;default:O0(this,e,t);break}this._x0=this._x1,this._x1=this._x2,this._x2=e,this._y0=this._y1,this._y1=this._y2,this._y2=t}};const zV=function e(t){function n(i){return new db(i,t)}return n.tension=function(i){return e(+i)},n}(0);function hb(e,t){this._context=e,this._k=(1-t)/6}hb.prototype={areaStart:To,areaEnd:To,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:{this._context.moveTo(this._x3,this._y3),this._context.closePath();break}case 2:{this._context.lineTo(this._x3,this._y3),this._context.closePath();break}case 3:{this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5);break}}},point:function(e,t){switch(e=+e,t=+t,this._point){case 0:this._point=1,this._x3=e,this._y3=t;break;case 1:this._point=2,this._context.moveTo(this._x4=e,this._y4=t);break;case 2:this._point=3,this._x5=e,this._y5=t;break;default:O0(this,e,t);break}this._x0=this._x1,this._x1=this._x2,this._x2=e,this._y0=this._y1,this._y1=this._y2,this._y2=t}};const BV=function e(t){function n(i){return new hb(i,t)}return n.tension=function(i){return e(+i)},n}(0);function pb(e,t){this._context=e,this._k=(1-t)/6}pb.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},lineEnd:function(){(this._line||this._line!==0&&this._point===3)&&this._context.closePath(),this._line=1-this._line},point:function(e,t){switch(e=+e,t=+t,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:O0(this,e,t);break}this._x0=this._x1,this._x1=this._x2,this._x2=e,this._y0=this._y1,this._y1=this._y2,this._y2=t}};const UV=function e(t){function n(i){return new pb(i,t)}return n.tension=function(i){return e(+i)},n}(0);function gb(e,t,n){var i=e._x1,r=e._y1,s=e._x2,o=e._y2;if(e._l01_a>Fn){var a=2*e._l01_2a+3*e._l01_a*e._l12_a+e._l12_2a,u=3*e._l01_a*(e._l01_a+e._l12_a);i=(i*a-e._x0*e._l12_2a+e._x2*e._l01_2a)/u,r=(r*a-e._y0*e._l12_2a+e._y2*e._l01_2a)/u}if(e._l23_a>Fn){var l=2*e._l23_2a+3*e._l23_a*e._l12_a+e._l12_2a,c=3*e._l23_a*(e._l23_a+e._l12_a);s=(s*l+e._x1*e._l23_2a-t*e._l12_2a)/c,o=(o*l+e._y1*e._l23_2a-n*e._l12_2a)/c}e._context.bezierCurveTo(i,r,s,o,e._x2,e._y2)}function G9(e,t){this._context=e,this._alpha=t}G9.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:this.point(this._x2,this._y2);break}(this._line||this._line!==0&&this._point===1)&&this._context.closePath(),this._line=1-this._line},point:function(e,t){if(e=+e,t=+t,this._point){var n=this._x2-e,i=this._y2-t;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(n*n+i*i,this._alpha))}switch(this._point){case 0:this._point=1,this._line?this._context.lineTo(e,t):this._context.moveTo(e,t);break;case 1:this._point=2;break;case 2:this._point=3;default:gb(this,e,t);break}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=e,this._y0=this._y1,this._y1=this._y2,this._y2=t}};const jV=function e(t){function n(i){return t?new G9(i,t):new db(i,0)}return n.alpha=function(i){return e(+i)},n}(.5);function V9(e,t){this._context=e,this._alpha=t}V9.prototype={areaStart:To,areaEnd:To,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){switch(this._point){case 1:{this._context.moveTo(this._x3,this._y3),this._context.closePath();break}case 2:{this._context.lineTo(this._x3,this._y3),this._context.closePath();break}case 3:{this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5);break}}},point:function(e,t){if(e=+e,t=+t,this._point){var n=this._x2-e,i=this._y2-t;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(n*n+i*i,this._alpha))}switch(this._point){case 0:this._point=1,this._x3=e,this._y3=t;break;case 1:this._point=2,this._context.moveTo(this._x4=e,this._y4=t);break;case 2:this._point=3,this._x5=e,this._y5=t;break;default:gb(this,e,t);break}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=e,this._y0=this._y1,this._y1=this._y2,this._y2=t}};const qV=function e(t){function n(i){return t?new V9(i,t):new hb(i,0)}return n.alpha=function(i){return e(+i)},n}(.5);function Y9(e,t){this._context=e,this._alpha=t}Y9.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){(this._line||this._line!==0&&this._point===3)&&this._context.closePath(),this._line=1-this._line},point:function(e,t){if(e=+e,t=+t,this._point){var n=this._x2-e,i=this._y2-t;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(n*n+i*i,this._alpha))}switch(this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:gb(this,e,t);break}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=e,this._y0=this._y1,this._y1=this._y2,this._y2=t}};const WV=function e(t){function n(i){return t?new Y9(i,t):new pb(i,0)}return n.alpha=function(i){return e(+i)},n}(.5);function X9(e){this._context=e}X9.prototype={areaStart:To,areaEnd:To,lineStart:function(){this._point=0},lineEnd:function(){this._point&&this._context.closePath()},point:function(e,t){e=+e,t=+t,this._point?this._context.lineTo(e,t):(this._point=1,this._context.moveTo(e,t))}};function HV(e){return new X9(e)}function K9(e){return e<0?-1:1}function Z9(e,t,n){var i=e._x1-e._x0,r=t-e._x1,s=(e._y1-e._y0)/(i||r<0&&-0),o=(n-e._y1)/(r||i<0&&-0),a=(s*r+o*i)/(i+r);return(K9(s)+K9(o))*Math.min(Math.abs(s),Math.abs(o),.5*Math.abs(a))||0}function J9(e,t){var n=e._x1-e._x0;return n?(3*(e._y1-e._y0)/n-t)/2:t}function mb(e,t,n){var i=e._x0,r=e._y0,s=e._x1,o=e._y1,a=(s-i)/3;e._context.bezierCurveTo(i+a,r+a*t,s-a,o-a*n,s,o)}function L0(e){this._context=e}L0.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=this._t0=NaN,this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x1,this._y1);break;case 3:mb(this,this._t0,J9(this,this._t0));break}(this._line||this._line!==0&&this._point===1)&&this._context.closePath(),this._line=1-this._line},point:function(e,t){var n=NaN;if(e=+e,t=+t,!(e===this._x1&&t===this._y1)){switch(this._point){case 0:this._point=1,this._line?this._context.lineTo(e,t):this._context.moveTo(e,t);break;case 1:this._point=2;break;case 2:this._point=3,mb(this,J9(this,n=Z9(this,e,t)),n);break;default:mb(this,this._t0,n=Z9(this,e,t));break}this._x0=this._x1,this._x1=e,this._y0=this._y1,this._y1=t,this._t0=n}}};function Q9(e){this._context=new eC(e)}(Q9.prototype=Object.create(L0.prototype)).point=function(e,t){L0.prototype.point.call(this,t,e)};function eC(e){this._context=e}eC.prototype={moveTo:function(e,t){this._context.moveTo(t,e)},closePath:function(){this._context.closePath()},lineTo:function(e,t){this._context.lineTo(t,e)},bezierCurveTo:function(e,t,n,i,r,s){this._context.bezierCurveTo(t,e,i,n,s,r)}};function GV(e){return new L0(e)}function VV(e){return new Q9(e)}function tC(e){this._context=e}tC.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x=[],this._y=[]},lineEnd:function(){var e=this._x,t=this._y,n=e.length;if(n)if(this._line?this._context.lineTo(e[0],t[0]):this._context.moveTo(e[0],t[0]),n===2)this._context.lineTo(e[1],t[1]);else for(var i=nC(e),r=nC(t),s=0,o=1;o=0;--t)r[t]=(o[t]-r[t+1])/s[t];for(s[n-1]=(e[n]+r[n-1])/2,t=0;t=0&&(this._t=1-this._t,this._line=1-this._line)},point:function(e,t){switch(e=+e,t=+t,this._point){case 0:this._point=1,this._line?this._context.lineTo(e,t):this._context.moveTo(e,t);break;case 1:this._point=2;default:{if(this._t<=0)this._context.lineTo(this._x,t),this._context.lineTo(e,t);else{var n=this._x*(1-this._t)+e*this._t;this._context.lineTo(n,this._y),this._context.lineTo(n,t)}break}}this._x=e,this._y=t}};function XV(e){return new I0(e,.5)}function KV(e){return new I0(e,0)}function ZV(e){return new I0(e,1)}function Mo(e,t){if(typeof document<"u"&&document.createElement){const n=document.createElement("canvas");if(n&&n.getContext)return n.width=e,n.height=t,n}return null}const JV=()=>typeof Image<"u"?Image:null;function Xr(e,t){switch(arguments.length){case 0:break;case 1:this.range(e);break;default:this.range(t).domain(e);break}return this}function No(e,t){switch(arguments.length){case 0:break;case 1:{typeof e=="function"?this.interpolator(e):this.range(e);break}default:{this.domain(e),typeof t=="function"?this.interpolator(t):this.range(t);break}}return this}const yb=Symbol("implicit");function bb(){var e=new o8,t=[],n=[],i=yb;function r(s){let o=e.get(s);if(o===void 0){if(i!==yb)return i;e.set(s,o=t.push(s)-1)}return n[o%n.length]}return r.domain=function(s){if(!arguments.length)return t.slice();t=[],e=new o8;for(const o of s)e.has(o)||e.set(o,t.push(o)-1);return r},r.range=function(s){return arguments.length?(n=Array.from(s),r):n.slice()},r.unknown=function(s){return arguments.length?(i=s,r):i},r.copy=function(){return bb(t,n).unknown(i)},Xr.apply(r,arguments),r}function El(e,t,n){e.prototype=t.prototype=n,n.constructor=e}function _f(e,t){var n=Object.create(e.prototype);for(var i in t)n[i]=t[i];return n}function Ro(){}var Oa=.7,Cl=1/Oa,kl="\\s*([+-]?\\d+)\\s*",wf="\\s*([+-]?(?:\\d*\\.)?\\d+(?:[eE][+-]?\\d+)?)\\s*",Kr="\\s*([+-]?(?:\\d*\\.)?\\d+(?:[eE][+-]?\\d+)?)%\\s*",QV=/^#([0-9a-f]{3,8})$/,eY=new RegExp(`^rgb\\(${kl},${kl},${kl}\\)$`),tY=new RegExp(`^rgb\\(${Kr},${Kr},${Kr}\\)$`),nY=new RegExp(`^rgba\\(${kl},${kl},${kl},${wf}\\)$`),iY=new RegExp(`^rgba\\(${Kr},${Kr},${Kr},${wf}\\)$`),rY=new RegExp(`^hsl\\(${wf},${Kr},${Kr}\\)$`),sY=new RegExp(`^hsla\\(${wf},${Kr},${Kr},${wf}\\)$`),iC={aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074};El(Ro,Ef,{copy(e){return Object.assign(new this.constructor,this,e)},displayable(){return this.rgb().displayable()},hex:rC,formatHex:rC,formatHex8:oY,formatHsl:aY,formatRgb:sC,toString:sC});function rC(){return this.rgb().formatHex()}function oY(){return this.rgb().formatHex8()}function aY(){return cC(this).formatHsl()}function sC(){return this.rgb().formatRgb()}function Ef(e){var t,n;return e=(e+"").trim().toLowerCase(),(t=QV.exec(e))?(n=t[1].length,t=parseInt(t[1],16),n===6?oC(t):n===3?new Jt(t>>8&15|t>>4&240,t>>4&15|t&240,(t&15)<<4|t&15,1):n===8?P0(t>>24&255,t>>16&255,t>>8&255,(t&255)/255):n===4?P0(t>>12&15|t>>8&240,t>>8&15|t>>4&240,t>>4&15|t&240,((t&15)<<4|t&15)/255):null):(t=eY.exec(e))?new Jt(t[1],t[2],t[3],1):(t=tY.exec(e))?new Jt(t[1]*255/100,t[2]*255/100,t[3]*255/100,1):(t=nY.exec(e))?P0(t[1],t[2],t[3],t[4]):(t=iY.exec(e))?P0(t[1]*255/100,t[2]*255/100,t[3]*255/100,t[4]):(t=rY.exec(e))?lC(t[1],t[2]/100,t[3]/100,1):(t=sY.exec(e))?lC(t[1],t[2]/100,t[3]/100,t[4]):iC.hasOwnProperty(e)?oC(iC[e]):e==="transparent"?new Jt(NaN,NaN,NaN,0):null}function oC(e){return new Jt(e>>16&255,e>>8&255,e&255,1)}function P0(e,t,n,i){return i<=0&&(e=t=n=NaN),new Jt(e,t,n,i)}function vb(e){return e instanceof Ro||(e=Ef(e)),e?(e=e.rgb(),new Jt(e.r,e.g,e.b,e.opacity)):new Jt}function Oo(e,t,n,i){return arguments.length===1?vb(e):new Jt(e,t,n,i??1)}function Jt(e,t,n,i){this.r=+e,this.g=+t,this.b=+n,this.opacity=+i}El(Jt,Oo,_f(Ro,{brighter(e){return e=e==null?Cl:Math.pow(Cl,e),new Jt(this.r*e,this.g*e,this.b*e,this.opacity)},darker(e){return e=e==null?Oa:Math.pow(Oa,e),new Jt(this.r*e,this.g*e,this.b*e,this.opacity)},rgb(){return this},clamp(){return new Jt(La(this.r),La(this.g),La(this.b),z0(this.opacity))},displayable(){return-.5<=this.r&&this.r<255.5&&-.5<=this.g&&this.g<255.5&&-.5<=this.b&&this.b<255.5&&0<=this.opacity&&this.opacity<=1},hex:aC,formatHex:aC,formatHex8:uY,formatRgb:uC,toString:uC}));function aC(){return`#${Ia(this.r)}${Ia(this.g)}${Ia(this.b)}`}function uY(){return`#${Ia(this.r)}${Ia(this.g)}${Ia(this.b)}${Ia((isNaN(this.opacity)?1:this.opacity)*255)}`}function uC(){const e=z0(this.opacity);return`${e===1?"rgb(":"rgba("}${La(this.r)}, ${La(this.g)}, ${La(this.b)}${e===1?")":`, ${e})`}`}function z0(e){return isNaN(e)?1:Math.max(0,Math.min(1,e))}function La(e){return Math.max(0,Math.min(255,Math.round(e)||0))}function Ia(e){return e=La(e),(e<16?"0":"")+e.toString(16)}function lC(e,t,n,i){return i<=0?e=t=n=NaN:n<=0||n>=1?e=t=NaN:t<=0&&(e=NaN),new fr(e,t,n,i)}function cC(e){if(e instanceof fr)return new fr(e.h,e.s,e.l,e.opacity);if(e instanceof Ro||(e=Ef(e)),!e)return new fr;if(e instanceof fr)return e;e=e.rgb();var t=e.r/255,n=e.g/255,i=e.b/255,r=Math.min(t,n,i),s=Math.max(t,n,i),o=NaN,a=s-r,u=(s+r)/2;return a?(t===s?o=(n-i)/a+(n0&&u<1?0:o,new fr(o,a,u,e.opacity)}function B0(e,t,n,i){return arguments.length===1?cC(e):new fr(e,t,n,i??1)}function fr(e,t,n,i){this.h=+e,this.s=+t,this.l=+n,this.opacity=+i}El(fr,B0,_f(Ro,{brighter(e){return e=e==null?Cl:Math.pow(Cl,e),new fr(this.h,this.s,this.l*e,this.opacity)},darker(e){return e=e==null?Oa:Math.pow(Oa,e),new fr(this.h,this.s,this.l*e,this.opacity)},rgb(){var e=this.h%360+(this.h<0)*360,t=isNaN(e)||isNaN(this.s)?0:this.s,n=this.l,i=n+(n<.5?n:1-n)*t,r=2*n-i;return new Jt(xb(e>=240?e-240:e+120,r,i),xb(e,r,i),xb(e<120?e+240:e-120,r,i),this.opacity)},clamp(){return new fr(fC(this.h),U0(this.s),U0(this.l),z0(this.opacity))},displayable(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1},formatHsl(){const e=z0(this.opacity);return`${e===1?"hsl(":"hsla("}${fC(this.h)}, ${U0(this.s)*100}%, ${U0(this.l)*100}%${e===1?")":`, ${e})`}`}}));function fC(e){return e=(e||0)%360,e<0?e+360:e}function U0(e){return Math.max(0,Math.min(1,e||0))}function xb(e,t,n){return(e<60?t+(n-t)*e/60:e<180?n:e<240?t+(n-t)*(240-e)/60:t)*255}const dC=Math.PI/180,hC=180/Math.PI,j0=18,pC=.96422,gC=1,mC=.82521,yC=4/29,Al=6/29,bC=3*Al*Al,lY=Al*Al*Al;function vC(e){if(e instanceof Zr)return new Zr(e.l,e.a,e.b,e.opacity);if(e instanceof Is)return xC(e);e instanceof Jt||(e=vb(e));var t=Cb(e.r),n=Cb(e.g),i=Cb(e.b),r=_b((.2225045*t+.7168786*n+.0606169*i)/gC),s,o;return t===n&&n===i?s=o=r:(s=_b((.4360747*t+.3850649*n+.1430804*i)/pC),o=_b((.0139322*t+.0971045*n+.7141733*i)/mC)),new Zr(116*r-16,500*(s-r),200*(r-o),e.opacity)}function q0(e,t,n,i){return arguments.length===1?vC(e):new Zr(e,t,n,i??1)}function Zr(e,t,n,i){this.l=+e,this.a=+t,this.b=+n,this.opacity=+i}El(Zr,q0,_f(Ro,{brighter(e){return new Zr(this.l+j0*(e??1),this.a,this.b,this.opacity)},darker(e){return new Zr(this.l-j0*(e??1),this.a,this.b,this.opacity)},rgb(){var e=(this.l+16)/116,t=isNaN(this.a)?e:e+this.a/500,n=isNaN(this.b)?e:e-this.b/200;return t=pC*wb(t),e=gC*wb(e),n=mC*wb(n),new Jt(Eb(3.1338561*t-1.6168667*e-.4906146*n),Eb(-.9787684*t+1.9161415*e+.033454*n),Eb(.0719453*t-.2289914*e+1.4052427*n),this.opacity)}}));function _b(e){return e>lY?Math.pow(e,1/3):e/bC+yC}function wb(e){return e>Al?e*e*e:bC*(e-yC)}function Eb(e){return 255*(e<=.0031308?12.92*e:1.055*Math.pow(e,1/2.4)-.055)}function Cb(e){return(e/=255)<=.04045?e/12.92:Math.pow((e+.055)/1.055,2.4)}function cY(e){if(e instanceof Is)return new Is(e.h,e.c,e.l,e.opacity);if(e instanceof Zr||(e=vC(e)),e.a===0&&e.b===0)return new Is(NaN,0=1?(n=1,t-1):Math.floor(n*t),r=e[i],s=e[i+1],o=i>0?e[i-1]:2*r-s,a=i()=>e;function SC(e,t){return function(n){return e+n*t}}function dY(e,t,n){return e=Math.pow(e,n),t=Math.pow(t,n)-e,n=1/n,function(i){return Math.pow(e+i*t,n)}}function V0(e,t){var n=t-e;return n?SC(e,n>180||n<-180?n-360*Math.round(n/360):n):G0(isNaN(e)?t:e)}function hY(e){return(e=+e)==1?Qt:function(t,n){return n-t?dY(t,n,e):G0(isNaN(t)?n:t)}}function Qt(e,t){var n=t-e;return n?SC(e,n):G0(isNaN(e)?t:e)}const Sb=function e(t){var n=hY(t);function i(r,s){var o=n((r=Oo(r)).r,(s=Oo(s)).r),a=n(r.g,s.g),u=n(r.b,s.b),l=Qt(r.opacity,s.opacity);return function(c){return r.r=o(c),r.g=a(c),r.b=u(c),r.opacity=l(c),r+""}}return i.gamma=e,i}(1);function FC(e){return function(t){var n=t.length,i=new Array(n),r=new Array(n),s=new Array(n),o,a;for(o=0;on&&(s=t.slice(n,s),a[o]?a[o]+=s:a[++o]=s),(i=i[0])===(r=r[0])?a[o]?a[o]+=r:a[++o]=r:(a[++o]=null,u.push({i:o,x:dr(i,r)})),n=Tb.lastIndex;return n180?c+=360:c-l>180&&(l+=360),d.push({i:f.push(r(f)+"rotate(",null,i)-2,x:dr(l,c)})):c&&f.push(r(f)+"rotate("+c+i)}function a(l,c,f,d){l!==c?d.push({i:f.push(r(f)+"skewX(",null,i)-2,x:dr(l,c)}):c&&f.push(r(f)+"skewX("+c+i)}function u(l,c,f,d,h,p){if(l!==f||c!==d){var g=h.push(r(h)+"scale(",null,",",null,")");p.push({i:g-4,x:dr(l,f)},{i:g-2,x:dr(c,d)})}else(f!==1||d!==1)&&h.push(r(h)+"scale("+f+","+d+")")}return function(l,c){var f=[],d=[];return l=e(l),c=e(c),s(l.translateX,l.translateY,c.translateX,c.translateY,f,d),o(l.rotate,c.rotate,f,d),a(l.skewX,c.skewX,f,d),u(l.scaleX,l.scaleY,c.scaleX,c.scaleY,f,d),l=c=null,function(h){for(var p=-1,g=d.length,m;++pt&&(n=e,e=t,t=n),function(i){return Math.max(e,Math.min(t,i))}}function BY(e,t,n){var i=e[0],r=e[1],s=t[0],o=t[1];return r2?UY:BY,u=l=null,f}function f(d){return d==null||isNaN(d=+d)?s:(u||(u=a(e.map(i),t,n)))(i(o(d)))}return f.invert=function(d){return o(r((l||(l=a(t,e.map(i),dr)))(d)))},f.domain=function(d){return arguments.length?(e=Array.from(d,Rb),c()):e.slice()},f.range=function(d){return arguments.length?(t=Array.from(d),c()):t.slice()},f.rangeRound=function(d){return t=Array.from(d),n=kf,c()},f.clamp=function(d){return arguments.length?(o=d?!0:ai,c()):o!==ai},f.interpolate=function(d){return arguments.length?(n=d,c()):n},f.unknown=function(d){return arguments.length?(s=d,f):s},function(d,h){return i=d,r=h,c()}}function qC(){return X0()(ai,ai)}function WC(e,t,n,i){var r=Co(e,t,n),s;switch(i=$a(i??",f"),i.type){case"s":{var o=Math.max(Math.abs(e),Math.abs(t));return i.precision==null&&!isNaN(s=C8(r,o))&&(i.precision=s),B2(i,o)}case"":case"e":case"g":case"p":case"r":{i.precision==null&&!isNaN(s=k8(r,Math.max(Math.abs(e),Math.abs(t))))&&(i.precision=s-(i.type==="e"));break}case"f":case"%":{i.precision==null&&!isNaN(s=E8(r))&&(i.precision=s-(i.type==="%")*2);break}}return t0(i)}function za(e){var t=e.domain;return e.ticks=function(n){var i=t();return O2(i[0],i[i.length-1],n??10)},e.tickFormat=function(n,i){var r=t();return WC(r[0],r[r.length-1],n??10,i)},e.nice=function(n){n==null&&(n=10);var i=t(),r=0,s=i.length-1,o=i[r],a=i[s],u,l,c=10;for(a0;){if(l=L2(o,a,n),l===u)return i[r]=o,i[s]=a,t(i);if(l>0)o=Math.floor(o/l)*l,a=Math.ceil(a/l)*l;else if(l<0)o=Math.ceil(o*l)/l,a=Math.floor(a*l)/l;else break;u=l}return e},e}function HC(){var e=qC();return e.copy=function(){return Af(e,HC())},Xr.apply(e,arguments),za(e)}function GC(e){var t;function n(i){return i==null||isNaN(i=+i)?t:i}return n.invert=n,n.domain=n.range=function(i){return arguments.length?(e=Array.from(i,Rb),n):e.slice()},n.unknown=function(i){return arguments.length?(t=i,n):t},n.copy=function(){return GC(e).unknown(t)},e=arguments.length?Array.from(e,Rb):[0,1],za(n)}function VC(e,t){e=e.slice();var n=0,i=e.length-1,r=e[n],s=e[i],o;return sMath.pow(e,t)}function GY(e){return e===Math.E?Math.log:e===10&&Math.log10||e===2&&Math.log2||(e=Math.log(e),t=>Math.log(t)/e)}function KC(e){return(t,n)=>-e(-t,n)}function Lb(e){const t=e(YC,XC),n=t.domain;let i=10,r,s;function o(){return r=GY(i),s=HY(i),n()[0]<0?(r=KC(r),s=KC(s),e(jY,qY)):e(YC,XC),t}return t.base=function(a){return arguments.length?(i=+a,o()):i},t.domain=function(a){return arguments.length?(n(a),o()):n()},t.ticks=a=>{const u=n();let l=u[0],c=u[u.length-1];const f=c0){for(;d<=h;++d)for(p=1;pc)break;y.push(g)}}else for(;d<=h;++d)for(p=i-1;p>=1;--p)if(g=d>0?p/s(-d):p*s(d),!(gc)break;y.push(g)}y.length*2{if(a==null&&(a=10),u==null&&(u=i===10?"s":","),typeof u!="function"&&(!(i%1)&&(u=$a(u)).precision==null&&(u.trim=!0),u=t0(u)),a===1/0)return u;const l=Math.max(1,i*a/t.ticks().length);return c=>{let f=c/s(Math.round(r(c)));return f*in(VC(n(),{floor:a=>s(Math.floor(r(a))),ceil:a=>s(Math.ceil(r(a)))})),t}function ZC(){const e=Lb(X0()).domain([1,10]);return e.copy=()=>Af(e,ZC()).base(e.base()),Xr.apply(e,arguments),e}function JC(e){return function(t){return Math.sign(t)*Math.log1p(Math.abs(t/e))}}function QC(e){return function(t){return Math.sign(t)*Math.expm1(Math.abs(t))*e}}function Ib(e){var t=1,n=e(JC(t),QC(t));return n.constant=function(i){return arguments.length?e(JC(t=+i),QC(t)):t},za(n)}function ek(){var e=Ib(X0());return e.copy=function(){return Af(e,ek()).constant(e.constant())},Xr.apply(e,arguments)}function tk(e){return function(t){return t<0?-Math.pow(-t,e):Math.pow(t,e)}}function VY(e){return e<0?-Math.sqrt(-e):Math.sqrt(e)}function YY(e){return e<0?-e*e:e*e}function Pb(e){var t=e(ai,ai),n=1;function i(){return n===1?e(ai,ai):n===.5?e(VY,YY):e(tk(n),tk(1/n))}return t.exponent=function(r){return arguments.length?(n=+r,i()):n},za(t)}function zb(){var e=Pb(X0());return e.copy=function(){return Af(e,zb()).exponent(e.exponent())},Xr.apply(e,arguments),e}function XY(){return zb.apply(null,arguments).exponent(.5)}function nk(){var e=[],t=[],n=[],i;function r(){var o=0,a=Math.max(1,t.length);for(n=new Array(a-1);++o0?n[a-1]:e[0],a=n?[i[n-1],t]:[i[l-1],i[l]]},o.unknown=function(u){return arguments.length&&(s=u),o},o.thresholds=function(){return i.slice()},o.copy=function(){return ik().domain([e,t]).range(r).unknown(s)},Xr.apply(za(o),arguments)}function rk(){var e=[.5],t=[0,1],n,i=1;function r(s){return s!=null&&s<=s?t[Eo(e,s,0,i)]:n}return r.domain=function(s){return arguments.length?(e=Array.from(s),i=Math.min(e.length,t.length-1),r):e.slice()},r.range=function(s){return arguments.length?(t=Array.from(s),i=Math.min(e.length,t.length-1),r):t.slice()},r.invertExtent=function(s){var o=t.indexOf(s);return[e[o-1],e[o]]},r.unknown=function(s){return arguments.length?(n=s,r):n},r.copy=function(){return rk().domain(e).range(t).unknown(n)},Xr.apply(r,arguments)}function KY(e){return new Date(e)}function ZY(e){return e instanceof Date?+e:+new Date(+e)}function Bb(e,t,n,i,r,s,o,a,u,l){var c=qC(),f=c.invert,d=c.domain,h=l(".%L"),p=l(":%S"),g=l("%I:%M"),m=l("%I %p"),y=l("%a %d"),b=l("%b %d"),v=l("%B"),x=l("%Y");function _(E){return(u(E)0?i:1:0}const dX="identity",$l="linear",Ps="log",$f="pow",Sf="sqrt",J0="symlog",Ba="time",Ua="utc",Qr="sequential",Sl="diverging",Fl="quantile",Q0="quantize",ep="threshold",Hb="ordinal",Gb="point",ck="band",Vb="bin-ordinal",Ht="continuous",Ff="discrete",Df="discretizing",Hi="interpolating",Yb="temporal";function hX(e){return function(t){let n=t[0],i=t[1],r;return i=i&&n[u]<=r&&(s<0&&(s=u),o=u);if(!(s<0))return i=e.invertExtent(n[s]),r=e.invertExtent(n[o]),[i[0]===void 0?i[1]:i[0],r[1]===void 0?r[0]:r[1]]}}function Xb(){const e=bb().unknown(void 0),t=e.domain,n=e.range;let i=[0,1],r,s,o=!1,a=0,u=0,l=.5;delete e.unknown;function c(){const f=t().length,d=i[1]g+r*y);return n(d?m.reverse():m)}return e.domain=function(f){return arguments.length?(t(f),c()):t()},e.range=function(f){return arguments.length?(i=[+f[0],+f[1]],c()):i.slice()},e.rangeRound=function(f){return i=[+f[0],+f[1]],o=!0,c()},e.bandwidth=function(){return s},e.step=function(){return r},e.round=function(f){return arguments.length?(o=!!f,c()):o},e.padding=function(f){return arguments.length?(u=Math.max(0,Math.min(1,f)),a=u,c()):a},e.paddingInner=function(f){return arguments.length?(a=Math.max(0,Math.min(1,f)),c()):a},e.paddingOuter=function(f){return arguments.length?(u=Math.max(0,Math.min(1,f)),c()):u},e.align=function(f){return arguments.length?(l=Math.max(0,Math.min(1,f)),c()):l},e.invertRange=function(f){if(f[0]==null||f[1]==null)return;const d=i[1]i[1-d])))return y=Math.max(0,Eo(h,g)-1),b=g===m?y:Eo(h,m)-1,g-h[y]>s+1e-10&&++y,d&&(v=y,y=p-b,b=p-v),y>b?void 0:t().slice(y,b+1)},e.invert=function(f){const d=e.invertRange([f,f]);return d&&d[0]},e.copy=function(){return Xb().domain(t()).range(i).round(o).paddingInner(a).paddingOuter(u).align(l)},c()}function fk(e){const t=e.copy;return e.padding=e.paddingOuter,delete e.paddingInner,e.copy=function(){return fk(t())},e}function gX(){return fk(Xb().paddingInner(1))}var mX=Array.prototype.map;function yX(e){return mX.call(e,Cn)}const bX=Array.prototype.slice;function dk(){let e=[],t=[];function n(i){return i==null||i!==i?void 0:t[(Eo(e,i)-1)%t.length]}return n.domain=function(i){return arguments.length?(e=yX(i),n):e.slice()},n.range=function(i){return arguments.length?(t=bX.call(i),n):t.slice()},n.tickFormat=function(i,r){return WC(e[0],Ge(e),i??10,r)},n.copy=function(){return dk().domain(n.domain()).range(n.range())},n}const tp=new Map,hk=Symbol("vega_scale");function pk(e){return e[hk]=!0,e}function vX(e){return e&&e[hk]===!0}function xX(e,t,n){const i=function(){const s=t();return s.invertRange||(s.invertRange=s.invert?hX(s):s.invertExtent?pX(s):void 0),s.type=e,pk(s)};return i.metadata=lr(oe(n)),i}function Qe(e,t,n){return arguments.length>1?(tp.set(e,xX(e,t,n)),this):gk(e)?tp.get(e):void 0}Qe(dX,GC),Qe($l,HC,Ht),Qe(Ps,ZC,[Ht,Ps]),Qe($f,zb,Ht),Qe(Sf,XY,Ht),Qe(J0,ek,Ht),Qe(Ba,JY,[Ht,Yb]),Qe(Ua,QY,[Ht,Yb]),Qe(Qr,Ub,[Ht,Hi]),Qe(`${Qr}-${$l}`,Ub,[Ht,Hi]),Qe(`${Qr}-${Ps}`,sk,[Ht,Hi,Ps]),Qe(`${Qr}-${$f}`,jb,[Ht,Hi]),Qe(`${Qr}-${Sf}`,eX,[Ht,Hi]),Qe(`${Qr}-${J0}`,ok,[Ht,Hi]),Qe(`${Sl}-${$l}`,ak,[Ht,Hi]),Qe(`${Sl}-${Ps}`,uk,[Ht,Hi,Ps]),Qe(`${Sl}-${$f}`,qb,[Ht,Hi]),Qe(`${Sl}-${Sf}`,tX,[Ht,Hi]),Qe(`${Sl}-${J0}`,lk,[Ht,Hi]),Qe(Fl,nk,[Df,Fl]),Qe(Q0,ik,Df),Qe(ep,rk,Df),Qe(Vb,dk,[Ff,Df]),Qe(Hb,bb,Ff),Qe(ck,Xb,Ff),Qe(Gb,gX,Ff);function gk(e){return tp.has(e)}function ja(e,t){const n=tp.get(e);return n&&n.metadata[t]}function Kb(e){return ja(e,Ht)}function Dl(e){return ja(e,Ff)}function Zb(e){return ja(e,Df)}function mk(e){return ja(e,Ps)}function _X(e){return ja(e,Yb)}function yk(e){return ja(e,Hi)}function bk(e){return ja(e,Fl)}const wX=["clamp","base","constant","exponent"];function vk(e,t){const n=t[0],i=Ge(t)-n;return function(r){return e(n+r*i)}}function np(e,t,n){return Nb(Jb(t||"rgb",n),e)}function xk(e,t){const n=new Array(t),i=t+1;for(let r=0;re[a]?o[a](e[a]()):0),o)}function Jb(e,t){const n=IY[EX(e)];return t!=null&&n&&n.gamma?n.gamma(t):n}function EX(e){return"interpolate"+e.toLowerCase().split("-").map(t=>t[0].toUpperCase()+t.slice(1)).join("")}const CX={blues:"cfe1f2bed8eca8cee58fc1de74b2d75ba3cf4592c63181bd206fb2125ca40a4a90",greens:"d3eecdc0e6baabdda594d3917bc77d60ba6c46ab5e329a512089430e7735036429",greys:"e2e2e2d4d4d4c4c4c4b1b1b19d9d9d8888887575756262624d4d4d3535351e1e1e",oranges:"fdd8b3fdc998fdb87bfda55efc9244f87f2cf06b18e4580bd14904b93d029f3303",purples:"e2e1efd4d4e8c4c5e0b4b3d6a3a0cc928ec3827cb97566ae684ea25c3696501f8c",reds:"fdc9b4fcb49afc9e80fc8767fa7051f6573fec3f2fdc2a25c81b1db21218970b13",blueGreen:"d5efedc1e8e0a7ddd18bd2be70c6a958ba9144ad77319c5d2089460e7736036429",bluePurple:"ccddecbad0e4a8c2dd9ab0d4919cc98d85be8b6db28a55a6873c99822287730f71",greenBlue:"d3eecec5e8c3b1e1bb9bd8bb82cec269c2ca51b2cd3c9fc7288abd1675b10b60a1",orangeRed:"fddcaffdcf9bfdc18afdad77fb9562f67d53ee6545e24932d32d1ebf130da70403",purpleBlue:"dbdaebc8cee4b1c3de97b7d87bacd15b9fc93a90c01e7fb70b70ab056199045281",purpleBlueGreen:"dbd8eac8cee4b0c3de93b7d872acd1549fc83892bb1c88a3097f8702736b016353",purpleRed:"dcc9e2d3b3d7ce9eccd186c0da6bb2e14da0e23189d91e6fc61159ab07498f023a",redPurple:"fccfccfcbec0faa9b8f98faff571a5ec539ddb3695c41b8aa908808d0179700174",yellowGreen:"e4f4acd1eca0b9e2949ed68880c97c62bb6e47aa5e3297502083440e723b036034",yellowOrangeBrown:"feeaa1fedd84fecc63feb746fca031f68921eb7215db5e0bc54c05ab3d038f3204",yellowOrangeRed:"fee087fed16ffebd59fea849fd903efc7335f9522bee3423de1b20ca0b22af0225",blueOrange:"134b852f78b35da2cb9dcae1d2e5eff2f0ebfce0bafbbf74e8932fc5690d994a07",brownBlueGreen:"704108a0651ac79548e3c78af3e6c6eef1eac9e9e48ed1c74da79e187a72025147",purpleGreen:"5b1667834792a67fb6c9aed3e6d6e8eff0efd9efd5aedda971bb75368e490e5e29",purpleOrange:"4114696647968f83b7b9b4d6dadbebf3eeeafce0bafbbf74e8932fc5690d994a07",redBlue:"8c0d25bf363adf745ef4ae91fbdbc9f2efeed2e5ef9dcae15da2cb2f78b3134b85",redGrey:"8c0d25bf363adf745ef4ae91fcdccbfaf4f1e2e2e2c0c0c0969696646464343434",yellowGreenBlue:"eff9bddbf1b4bde5b594d5b969c5be45b4c22c9ec02182b82163aa23479c1c3185",redYellowBlue:"a50026d4322cf16e43fcac64fedd90faf8c1dcf1ecabd6e875abd04a74b4313695",redYellowGreen:"a50026d4322cf16e43fcac63fedd8df9f7aed7ee8ea4d86e64bc6122964f006837",pinkYellowGreen:"8e0152c0267edd72adf0b3d6faddedf5f3efe1f2cab6de8780bb474f9125276419",spectral:"9e0142d13c4bf0704afcac63fedd8dfbf8b0e0f3a1a9dda269bda94288b55e4fa2",viridis:"440154470e61481a6c482575472f7d443a834144873d4e8a39568c35608d31688e2d708e2a788e27818e23888e21918d1f988b1fa08822a8842ab07f35b77943bf7154c56866cc5d7ad1518fd744a5db36bcdf27d2e21be9e51afde725",magma:"0000040404130b0924150e3720114b2c11603b0f704a107957157e651a80721f817f24828c29819a2e80a8327db6377ac43c75d1426fde4968e95462f1605df76f5cfa7f5efc8f65fe9f6dfeaf78febf84fece91fddea0fcedaffcfdbf",inferno:"0000040403130c0826170c3b240c4f330a5f420a68500d6c5d126e6b176e781c6d86216b932667a12b62ae305cbb3755c73e4cd24644dd513ae65c30ed6925f3771af8850ffb9506fca50afcb519fac62df6d645f2e661f3f484fcffa4",plasma:"0d088723069033059742039d5002a25d01a66a00a87801a88405a7900da49c179ea72198b12a90ba3488c33d80cb4779d35171da5a69e16462e76e5bed7953f2834cf68f44fa9a3dfca636fdb32ffec029fcce25f9dc24f5ea27f0f921",cividis:"00205100235800265d002961012b65042e670831690d346b11366c16396d1c3c6e213f6e26426e2c456e31476e374a6e3c4d6e42506e47536d4c566d51586e555b6e5a5e6e5e616e62646f66676f6a6a706e6d717270717573727976737c79747f7c75827f758682768985778c8877908b78938e789691789a94789e9778a19b78a59e77a9a177aea575b2a874b6ab73bbaf71c0b26fc5b66dc9b96acebd68d3c065d8c462ddc85fe2cb5ce7cf58ebd355f0d652f3da4ff7de4cfae249fce647",rainbow:"6e40aa883eb1a43db3bf3cafd83fa4ee4395fe4b83ff576eff6659ff7847ff8c38f3a130e2b72fcfcc36bee044aff05b8ff4576ff65b52f6673af27828ea8d1ddfa319d0b81cbecb23abd82f96e03d82e14c6edb5a5dd0664dbf6e40aa",sinebow:"ff4040fc582af47218e78d0bd5a703bfbf00a7d5038de70b72f41858fc2a40ff402afc5818f4720be78d03d5a700bfbf03a7d50b8de71872f42a58fc4040ff582afc7218f48d0be7a703d5bf00bfd503a7e70b8df41872fc2a58ff4040",turbo:"23171b32204a3e2a71453493493eae4b49c54a53d7485ee44569ee4074f53c7ff8378af93295f72e9ff42ba9ef28b3e926bce125c5d925cdcf27d5c629dcbc2de3b232e9a738ee9d3ff39347f68950f9805afc7765fd6e70fe667cfd5e88fc5795fb51a1f84badf545b9f140c5ec3cd0e637dae034e4d931ecd12ef4c92bfac029ffb626ffad24ffa223ff9821ff8d1fff821dff771cfd6c1af76118f05616e84b14df4111d5380fcb2f0dc0260ab61f07ac1805a313029b0f00950c00910b00",browns:"eedbbdecca96e9b97ae4a865dc9856d18954c7784cc0673fb85536ad44339f3632",tealBlues:"bce4d89dd3d181c3cb65b3c245a2b9368fae347da0306a932c5985",teals:"bbdfdfa2d4d58ac9c975bcbb61b0af4da5a43799982b8b8c1e7f7f127273006667",warmGreys:"dcd4d0cec5c1c0b8b4b3aaa7a59c9998908c8b827f7e7673726866665c5a59504e",goldGreen:"f4d166d5ca60b6c35c98bb597cb25760a6564b9c533f8f4f33834a257740146c36",goldOrange:"f4d166f8be5cf8aa4cf5983bf3852aef701be2621fd65322c54923b142239e3a26",goldRed:"f4d166f6be59f9aa51fc964ef6834bee734ae56249db5247cf4244c43141b71d3e",lightGreyRed:"efe9e6e1dad7d5cbc8c8bdb9bbaea9cd967ddc7b43e15f19df4011dc000b",lightGreyTeal:"e4eaead6dcddc8ced2b7c2c7a6b4bc64b0bf22a6c32295c11f85be1876bc",lightMulti:"e0f1f2c4e9d0b0de9fd0e181f6e072f6c053f3993ef77440ef4a3c",lightOrange:"f2e7daf7d5baf9c499fab184fa9c73f68967ef7860e8645bde515bd43d5b",lightTealBlue:"e3e9e0c0dccf9aceca7abfc859afc0389fb9328dad2f7ca0276b95255988",darkBlue:"3232322d46681a5c930074af008cbf05a7ce25c0dd38daed50f3faffffff",darkGold:"3c3c3c584b37725e348c7631ae8b2bcfa424ecc31ef9de30fff184ffffff",darkGreen:"3a3a3a215748006f4d048942489e4276b340a6c63dd2d836ffeb2cffffaa",darkMulti:"3737371f5287197d8c29a86995ce3fffe800ffffff",darkRed:"3434347036339e3c38cc4037e75d1eec8620eeab29f0ce32ffeb2c"},kX={accent:iX,category10:nX,category20:"1f77b4aec7e8ff7f0effbb782ca02c98df8ad62728ff98969467bdc5b0d58c564bc49c94e377c2f7b6d27f7f7fc7c7c7bcbd22dbdb8d17becf9edae5",category20b:"393b795254a36b6ecf9c9ede6379398ca252b5cf6bcedb9c8c6d31bd9e39e7ba52e7cb94843c39ad494ad6616be7969c7b4173a55194ce6dbdde9ed6",category20c:"3182bd6baed69ecae1c6dbefe6550dfd8d3cfdae6bfdd0a231a35474c476a1d99bc7e9c0756bb19e9ac8bcbddcdadaeb636363969696bdbdbdd9d9d9",dark2:rX,observable10:sX,paired:oX,pastel1:aX,pastel2:uX,set1:lX,set2:cX,set3:fX,tableau10:"4c78a8f58518e4575672b7b254a24beeca3bb279a2ff9da69d755dbab0ac",tableau20:"4c78a89ecae9f58518ffbf7954a24b88d27ab79a20f2cf5b43989483bcb6e45756ff9d9879706ebab0acd67195fcbfd2b279a2d6a5c99e765fd8b5a5"};function wk(e){if(W(e))return e;const t=e.length/6|0,n=new Array(t);for(let i=0;inp(wk(e)));function Qb(e,t){return e=e&&e.toLowerCase(),arguments.length>1?(Ck[e]=t,this):Ck[e]}const ip="symbol",AX="discrete",$X="gradient",SX=e=>W(e)?e.map(t=>String(t)):String(e),FX=(e,t)=>e[1]-t[1],DX=(e,t)=>t[1]-e[1];function e3(e,t,n){let i;return Ze(t)&&(e.bins&&(t=Math.max(t,e.bins.length)),n!=null&&(t=Math.min(t,Math.floor(Xc(e.domain())/n||1)+1))),re(t)&&(i=t.step,t=t.interval),se(t)&&(t=e.type===Ba?gl(t):e.type==Ua?ml(t):q("Only time and utc scales accept interval strings."),i&&(t=t.every(i))),t}function kk(e,t,n){let i=e.range(),r=i[0],s=Ge(i),o=FX;if(r>s&&(i=s,s=r,r=i,o=DX),r=Math.floor(r),s=Math.ceil(s),t=t.map(a=>[a,e(a)]).filter(a=>r<=a[1]&&a[1]<=s).sort(o).map(a=>a[0]),n>0&&t.length>1){const a=[t[0],Ge(t)];for(;t.length>n&&t.length>=3;)t=t.filter((u,l)=>!(l%2));t.length<3&&(t=a)}return t}function t3(e,t){return e.bins?kk(e,e.bins,t):e.ticks?e.ticks(t):e.domain()}function Ak(e,t,n,i,r,s){const o=t.type;let a=SX;if(o===Ba||r===Ba)a=e.timeFormat(i);else if(o===Ua||r===Ua)a=e.utcFormat(i);else if(mk(o)){const u=e.formatFloat(i);if(s||t.bins)a=u;else{const l=$k(t,n,!1);a=c=>l(c)?u(c):""}}else if(t.tickFormat){const u=t.domain();a=e.formatSpan(u[0],u[u.length-1],n,i)}else i&&(a=e.format(i));return a}function $k(e,t,n){const i=t3(e,t),r=e.base(),s=Math.log(r),o=Math.max(1,r*t/i.length),a=u=>{let l=u/Math.pow(r,Math.round(Math.log(u)/s));return l*r1?i[1]-i[0]:i[0],o;for(o=1;on3[e.type]||e.bins;function Dk(e,t,n,i,r,s,o){const a=Sk[t.type]&&s!==Ba&&s!==Ua?TX(e,t,r):Ak(e,t,n,r,s,o);return i===ip&&RX(t)?OX(a):i===AX?LX(a):IX(a)}const OX=e=>(t,n,i)=>{const r=Tk(i[n+1],Tk(i.max,1/0)),s=Mk(t,e),o=Mk(r,e);return s&&o?s+" – "+o:o?"< "+o:"β‰₯ "+s},Tk=(e,t)=>e??t,LX=e=>(t,n)=>n?e(t):null,IX=e=>t=>e(t),Mk=(e,t)=>Number.isFinite(e)?t(e):null;function PX(e){const t=e.domain(),n=t.length-1;let i=+t[0],r=+Ge(t),s=r-i;if(e.type===ep){const o=n?s/n:.1;i-=o,r+=o,s=r-i}return o=>(o-i)/s}function zX(e,t,n,i){const r=i||t.type;return se(n)&&_X(r)&&(n=n.replace(/%a/g,"%A").replace(/%b/g,"%B")),!n&&r===Ba?e.timeFormat("%A, %d %B %Y, %X"):!n&&r===Ua?e.utcFormat("%A, %d %B %Y, %X UTC"):Dk(e,t,5,null,n,i,!0)}function Nk(e,t,n){n=n||{};const i=Math.max(3,n.maxlen||7),r=zX(e,t,n.format,n.formatType);if(Zb(t.type)){const s=Fk(t).slice(1).map(r),o=s.length;return`${o} boundar${o===1?"y":"ies"}: ${s.join(", ")}`}else if(Dl(t.type)){const s=t.domain(),o=s.length,a=o>i?s.slice(0,i-2).map(r).join(", ")+", ending with "+s.slice(-1).map(r):s.map(r).join(", ");return`${o} value${o===1?"":"s"}: ${a}`}else{const s=t.domain();return`values from ${r(s[0])} to ${r(Ge(s))}`}}let Rk=0;function BX(){Rk=0}const rp="p_";function i3(e){return e&&e.gradient}function Ok(e,t,n){const i=e.gradient;let r=e.id,s=i==="radial"?rp:"";return r||(r=e.id="gradient_"+Rk++,i==="radial"?(e.x1=es(e.x1,.5),e.y1=es(e.y1,.5),e.r1=es(e.r1,0),e.x2=es(e.x2,.5),e.y2=es(e.y2,.5),e.r2=es(e.r2,.5),s=rp):(e.x1=es(e.x1,0),e.y1=es(e.y1,0),e.x2=es(e.x2,1),e.y2=es(e.y2,0))),t[r]=e,"url("+(n||"")+"#"+s+r+")"}function es(e,t){return e??t}function Lk(e,t){var n=[],i;return i={gradient:"linear",x1:e?e[0]:0,y1:e?e[1]:0,x2:t?t[0]:1,y2:t?t[1]:0,stops:n,stop:function(r,s){return n.push({offset:r,color:s}),i}}}const Ik={basis:{curve:OV},"basis-closed":{curve:LV},"basis-open":{curve:IV},bundle:{curve:PV,tension:"beta",value:.85},cardinal:{curve:zV,tension:"tension",value:0},"cardinal-open":{curve:UV,tension:"tension",value:0},"cardinal-closed":{curve:BV,tension:"tension",value:0},"catmull-rom":{curve:jV,tension:"alpha",value:.5},"catmull-rom-closed":{curve:qV,tension:"alpha",value:.5},"catmull-rom-open":{curve:WV,tension:"alpha",value:.5},linear:{curve:fb},"linear-closed":{curve:HV},monotone:{horizontal:VV,vertical:GV},natural:{curve:YV},step:{curve:XV},"step-after":{curve:ZV},"step-before":{curve:KV}};function r3(e,t,n){var i=ue(Ik,e)&&Ik[e],r=null;return i&&(r=i.curve||i[t||"vertical"],i.tension&&n!=null&&(r=r[i.tension](n))),r}const UX={m:2,l:2,h:1,v:1,z:0,c:6,s:4,q:4,t:2,a:7},jX=/[mlhvzcsqta]([^mlhvzcsqta]+|$)/gi,qX=/^[+-]?(([0-9]*\.[0-9]+)|([0-9]+\.)|([0-9]+))([eE][+-]?[0-9]+)?/,WX=/^((\s+,?\s*)|(,\s*))/,HX=/^[01]/;function Tl(e){const t=[];return(e.match(jX)||[]).forEach(i=>{let r=i[0];const s=r.toLowerCase(),o=UX[s],a=GX(s,o,i.slice(1).trim()),u=a.length;if(u1&&(g=Math.sqrt(g),n*=g,i*=g);const m=d/n,y=f/n,b=-f/i,v=d/i,x=m*a+y*u,_=b*a+v*u,E=m*e+y*t,w=b*e+v*t;let k=1/((E-x)*(E-x)+(w-_)*(w-_))-.25;k<0&&(k=0);let S=Math.sqrt(k);s==r&&(S=-S);const $=.5*(x+E)-S*(w-_),O=.5*(_+w)+S*(E-x),R=Math.atan2(_-O,x-$);let A=Math.atan2(w-O,E-$)-R;A<0&&s===1?A+=ts:A>0&&s===0&&(A-=ts);const T=Math.ceil(Math.abs(A/(qa+.001))),B=[];for(let H=0;H+e}function sp(e,t,n){return Math.max(t,Math.min(e,n))}function qk(){var e=JX,t=QX,n=eK,i=tK,r=zs(0),s=r,o=r,a=r,u=null;function l(c,f,d){var h,p=f??+e.call(this,c),g=d??+t.call(this,c),m=+n.call(this,c),y=+i.call(this,c),b=Math.min(m,y)/2,v=sp(+r.call(this,c),0,b),x=sp(+s.call(this,c),0,b),_=sp(+o.call(this,c),0,b),E=sp(+a.call(this,c),0,b);if(u||(u=h=D0()),v<=0&&x<=0&&_<=0&&E<=0)u.rect(p,g,m,y);else{var w=p+m,C=g+y;u.moveTo(p+v,g),u.lineTo(w-x,g),u.bezierCurveTo(w-zo*x,g,w,g+zo*x,w,g+x),u.lineTo(w,C-E),u.bezierCurveTo(w,C-zo*E,w-zo*E,C,w-E,C),u.lineTo(p+_,C),u.bezierCurveTo(p+zo*_,C,p,C-zo*_,p,C-_),u.lineTo(p,g+v),u.bezierCurveTo(p,g+zo*v,p+zo*v,g,p+v,g),u.closePath()}if(h)return u=null,h+""||null}return l.x=function(c){return arguments.length?(e=zs(c),l):e},l.y=function(c){return arguments.length?(t=zs(c),l):t},l.width=function(c){return arguments.length?(n=zs(c),l):n},l.height=function(c){return arguments.length?(i=zs(c),l):i},l.cornerRadius=function(c,f,d,h){return arguments.length?(r=zs(c),s=f!=null?zs(f):r,a=d!=null?zs(d):r,o=h!=null?zs(h):s,l):r},l.context=function(c){return arguments.length?(u=c??null,l):u},l}function Wk(){var e,t,n,i,r=null,s,o,a,u;function l(f,d,h){const p=h/2;if(s){var g=a-d,m=f-o;if(g||m){var y=Math.hypot(g,m),b=(g/=y)*u,v=(m/=y)*u,x=Math.atan2(m,g);r.moveTo(o-b,a-v),r.lineTo(f-g*p,d-m*p),r.arc(f,d,p,x-Math.PI,x),r.lineTo(o+b,a+v),r.arc(o,a,u,x,x+Math.PI)}else r.arc(f,d,p,0,ts);r.closePath()}else s=1;o=f,a=d,u=p}function c(f){var d,h=f.length,p,g=!1,m;for(r==null&&(r=m=D0()),d=0;d<=h;++d)!(de.x||0,Rf=e=>e.y||0,nK=e=>e.width||0,iK=e=>e.height||0,rK=e=>(e.x||0)+(e.width||0),sK=e=>(e.y||0)+(e.height||0),oK=e=>e.startAngle||0,aK=e=>e.endAngle||0,uK=e=>e.padAngle||0,lK=e=>e.innerRadius||0,cK=e=>e.outerRadius||0,fK=e=>e.cornerRadius||0,dK=e=>Mf(e.cornerRadiusTopLeft,e.cornerRadius)||0,hK=e=>Mf(e.cornerRadiusTopRight,e.cornerRadius)||0,pK=e=>Mf(e.cornerRadiusBottomRight,e.cornerRadius)||0,gK=e=>Mf(e.cornerRadiusBottomLeft,e.cornerRadius)||0,mK=e=>Mf(e.size,64),yK=e=>e.size||1,op=e=>e.defined!==!1,bK=e=>jk(e.shape||"circle"),vK=MV().startAngle(oK).endAngle(aK).padAngle(uK).innerRadius(lK).outerRadius(cK).cornerRadius(fK),xK=j9().x(Nf).y1(Rf).y0(sK).defined(op),_K=j9().y(Rf).x1(Nf).x0(rK).defined(op),wK=U9().x(Nf).y(Rf).defined(op),EK=qk().x(Nf).y(Rf).width(nK).height(iK).cornerRadius(dK,hK,pK,gK),CK=RV().type(bK).size(mK),kK=Wk().x(Nf).y(Rf).defined(op).size(yK);function u3(e){return e.cornerRadius||e.cornerRadiusTopLeft||e.cornerRadiusTopRight||e.cornerRadiusBottomRight||e.cornerRadiusBottomLeft}function AK(e,t){return vK.context(e)(t)}function $K(e,t){const n=t[0],i=n.interpolate||"linear";return(n.orient==="horizontal"?_K:xK).curve(r3(i,n.orient,n.tension)).context(e)(t)}function SK(e,t){const n=t[0],i=n.interpolate||"linear";return wK.curve(r3(i,n.orient,n.tension)).context(e)(t)}function Nl(e,t,n,i){return EK.context(e)(t,n,i)}function FK(e,t){return(t.mark.shape||t.shape).context(e)(t)}function DK(e,t){return CK.context(e)(t)}function TK(e,t){return kK.context(e)(t)}var Hk=1;function Gk(){Hk=1}function l3(e,t,n){var i=t.clip,r=e._defs,s=t.clip_id||(t.clip_id="clip"+Hk++),o=r.clipping[s]||(r.clipping[s]={id:s});return Ie(i)?o.path=i(null):u3(n)?o.path=Nl(null,n,0,0):(o.width=n.width||0,o.height=n.height||0),"url(#"+s+")"}function Ut(e){this.clear(),e&&this.union(e)}Ut.prototype={clone(){return new Ut(this)},clear(){return this.x1=+Number.MAX_VALUE,this.y1=+Number.MAX_VALUE,this.x2=-Number.MAX_VALUE,this.y2=-Number.MAX_VALUE,this},empty(){return this.x1===+Number.MAX_VALUE&&this.y1===+Number.MAX_VALUE&&this.x2===-Number.MAX_VALUE&&this.y2===-Number.MAX_VALUE},equals(e){return this.x1===e.x1&&this.y1===e.y1&&this.x2===e.x2&&this.y2===e.y2},set(e,t,n,i){return nthis.x2&&(this.x2=e),t>this.y2&&(this.y2=t),this},expand(e){return this.x1-=e,this.y1-=e,this.x2+=e,this.y2+=e,this},round(){return this.x1=Math.floor(this.x1),this.y1=Math.floor(this.y1),this.x2=Math.ceil(this.x2),this.y2=Math.ceil(this.y2),this},scale(e){return this.x1*=e,this.y1*=e,this.x2*=e,this.y2*=e,this},translate(e,t){return this.x1+=e,this.x2+=e,this.y1+=t,this.y2+=t,this},rotate(e,t,n){const i=this.rotatedPoints(e,t,n);return this.clear().add(i[0],i[1]).add(i[2],i[3]).add(i[4],i[5]).add(i[6],i[7])},rotatedPoints(e,t,n){var{x1:i,y1:r,x2:s,y2:o}=this,a=Math.cos(e),u=Math.sin(e),l=t-t*a+n*u,c=n-t*u-n*a;return[a*i-u*r+l,u*i+a*r+c,a*i-u*o+l,u*i+a*o+c,a*s-u*r+l,u*s+a*r+c,a*s-u*o+l,u*s+a*o+c]},union(e){return e.x1this.x2&&(this.x2=e.x2),e.y2>this.y2&&(this.y2=e.y2),this},intersect(e){return e.x1>this.x1&&(this.x1=e.x1),e.y1>this.y1&&(this.y1=e.y1),e.x2=e.x2&&this.y1<=e.y1&&this.y2>=e.y2},alignsWith(e){return e&&(this.x1==e.x1||this.x2==e.x2||this.y1==e.y1||this.y2==e.y2)},intersects(e){return e&&!(this.x2e.x2||this.y2e.y2)},contains(e,t){return!(ethis.x2||tthis.y2)},width(){return this.x2-this.x1},height(){return this.y2-this.y1}};function ap(e){this.mark=e,this.bounds=this.bounds||new Ut}function up(e){ap.call(this,e),this.items=this.items||[]}ee(up,ap);class Vk{constructor(t){this._pending=0,this._loader=t||d0()}pending(){return this._pending}sanitizeURL(t){const n=this;return Yk(n),n._loader.sanitize(t,{context:"href"}).then(i=>(Of(n),i)).catch(()=>(Of(n),null))}loadImage(t){const n=this,i=JV();return Yk(n),n._loader.sanitize(t,{context:"image"}).then(r=>{const s=r.href;if(!s||!i)throw{url:s};const o=new i,a=ue(r,"crossOrigin")?r.crossOrigin:"anonymous";return a!=null&&(o.crossOrigin=a),o.onload=()=>Of(n),o.onerror=()=>Of(n),o.src=s,o}).catch(r=>(Of(n),{complete:!1,width:0,height:0,src:r&&r.url||""}))}ready(){const t=this;return new Promise(n=>{function i(r){t.pending()?setTimeout(()=>{i(!0)},10):n(r)}i(!1)})}}function Yk(e){e._pending+=1}function Of(e){e._pending-=1}function Bs(e,t,n){if(t.stroke&&t.opacity!==0&&t.strokeOpacity!==0){const i=t.strokeWidth!=null?+t.strokeWidth:1;e.expand(i+(n?MK(t,i):0))}return e}function MK(e,t){return e.strokeJoin&&e.strokeJoin!=="miter"?0:t}const NK=ts-1e-8;let lp,cp,fp,Wa,c3,dp,f3,d3;const Bo=(e,t)=>lp.add(e,t),hp=(e,t)=>Bo(cp=e,fp=t),Xk=e=>Bo(e,lp.y1),Kk=e=>Bo(lp.x1,e),Ha=(e,t)=>c3*e+f3*t,Ga=(e,t)=>dp*e+d3*t,h3=(e,t)=>Bo(Ha(e,t),Ga(e,t)),p3=(e,t)=>hp(Ha(e,t),Ga(e,t));function Lf(e,t){return lp=e,t?(Wa=t*Po,c3=d3=Math.cos(Wa),dp=Math.sin(Wa),f3=-dp):(c3=d3=1,Wa=dp=f3=0),RK}const RK={beginPath(){},closePath(){},moveTo:p3,lineTo:p3,rect(e,t,n,i){Wa?(h3(e+n,t),h3(e+n,t+i),h3(e,t+i),p3(e,t)):(Bo(e+n,t+i),hp(e,t))},quadraticCurveTo(e,t,n,i){const r=Ha(e,t),s=Ga(e,t),o=Ha(n,i),a=Ga(n,i);Zk(cp,r,o,Xk),Zk(fp,s,a,Kk),hp(o,a)},bezierCurveTo(e,t,n,i,r,s){const o=Ha(e,t),a=Ga(e,t),u=Ha(n,i),l=Ga(n,i),c=Ha(r,s),f=Ga(r,s);Jk(cp,o,u,c,Xk),Jk(fp,a,l,f,Kk),hp(c,f)},arc(e,t,n,i,r,s){if(i+=Wa,r+=Wa,cp=n*Math.cos(r)+e,fp=n*Math.sin(r)+t,Math.abs(r-i)>NK)Bo(e-n,t-n),Bo(e+n,t+n);else{const o=l=>Bo(n*Math.cos(l)+e,n*Math.sin(l)+t);let a,u;if(o(i),o(r),r!==i)if(i=i%ts,i<0&&(i+=ts),r=r%ts,r<0&&(r+=ts),rr;++u,a-=qa)o(a);else for(a=i-i%qa+qa,u=0;u<4&&aVX?(c=o*o+a*s,c>=0&&(c=Math.sqrt(c),u=(-o+c)/s,l=(-o-c)/s)):u=.5*a/o,0d)return!1;g>f&&(f=g)}else if(h>0){if(g0?(e.globalAlpha=n,e.fillStyle=iA(e,t,t.fill),!0):!1}var LK=[];function Ll(e,t,n){var i=(i=t.strokeWidth)!=null?i:1;return i<=0?!1:(n*=t.strokeOpacity==null?1:t.strokeOpacity,n>0?(e.globalAlpha=n,e.strokeStyle=iA(e,t,t.stroke),e.lineWidth=i,e.lineCap=t.strokeCap||"butt",e.lineJoin=t.strokeJoin||"miter",e.miterLimit=t.strokeMiterLimit||10,e.setLineDash&&(e.setLineDash(t.strokeDash||LK),e.lineDashOffset=t.strokeDashOffset||0),!0):!1)}function IK(e,t){return e.zindex-t.zindex||e.index-t.index}function b3(e){if(!e.zdirty)return e.zitems;var t=e.items,n=[],i,r,s;for(r=0,s=t.length;r=0;)if(i=t(n[r]))return i;if(n===s){for(n=e.items,r=n.length;--r>=0;)if(!n[r].zindex&&(i=t(n[r])))return i}return null}function v3(e){return function(t,n,i){pr(n,r=>{(!i||i.intersects(r.bounds))&&rA(e,t,r,r)})}}function PK(e){return function(t,n,i){n.items.length&&(!i||i.intersects(n.bounds))&&rA(e,t,n.items[0],n.items)}}function rA(e,t,n,i){var r=n.opacity==null?1:n.opacity;r!==0&&(e(t,i)||(Ol(t,n),n.fill&&pp(t,n,r)&&t.fill(),n.stroke&&Ll(t,n,r)&&t.stroke()))}function mp(e){return e=e||Ui,function(t,n,i,r,s,o){return i*=t.pixelRatio,r*=t.pixelRatio,gp(n,a=>{const u=a.bounds;if(!(u&&!u.contains(s,o)||!u)&&e(t,a,i,r,s,o))return a})}}function If(e,t){return function(n,i,r,s){var o=Array.isArray(i)?i[0]:i,a=t??o.fill,u=o.stroke&&n.isPointInStroke,l,c;return u&&(l=o.strokeWidth,c=o.strokeCap,n.lineWidth=l??1,n.lineCap=c??"butt"),e(n,i)?!1:a&&n.isPointInPath(r,s)||u&&n.isPointInStroke(r,s)}}function x3(e){return mp(If(e))}function Va(e,t){return"translate("+e+","+t+")"}function _3(e){return"rotate("+e+")"}function zK(e,t){return"scale("+e+","+t+")"}function sA(e){return Va(e.x||0,e.y||0)}function BK(e){return Va(e.x||0,e.y||0)+(e.angle?" "+_3(e.angle):"")}function UK(e){return Va(e.x||0,e.y||0)+(e.angle?" "+_3(e.angle):"")+(e.scaleX||e.scaleY?" "+zK(e.scaleX||1,e.scaleY||1):"")}function w3(e,t,n){function i(o,a){o("transform",BK(a)),o("d",t(null,a))}function r(o,a){return t(Lf(o,a.angle),a),Bs(o,a).translate(a.x||0,a.y||0)}function s(o,a){var u=a.x||0,l=a.y||0,c=a.angle||0;o.translate(u,l),c&&o.rotate(c*=Po),o.beginPath(),t(o,a),c&&o.rotate(-c),o.translate(-u,-l)}return{type:e,tag:"path",nested:!1,attr:i,bound:r,draw:v3(s),pick:x3(s),isect:n||m3(s)}}var jK=w3("arc",AK);function qK(e,t){for(var n=e[0].orient==="horizontal"?t[1]:t[0],i=e[0].orient==="horizontal"?"y":"x",r=e.length,s=1/0,o,a;--r>=0;)e[r].defined!==!1&&(a=Math.abs(e[r][i]-n),a=0;)if(e[i].defined!==!1&&(r=e[i].x-t[0],s=e[i].y-t[1],o=r*r+s*s,o=0;)if(e[n].defined!==!1&&(i=e[n].x-t[0],r=e[n].y-t[1],s=i*i+r*r,i=e[n].size||1,s.5&&t<1.5?.5-Math.abs(t-1):0}function YK(e,t){e("transform",sA(t))}function uA(e,t){const n=aA(t);e("d",Nl(null,t,n,n))}function XK(e,t){e("class","background"),e("aria-hidden",!0),uA(e,t)}function KK(e,t){e("class","foreground"),e("aria-hidden",!0),t.strokeForeground?uA(e,t):e("d","")}function ZK(e,t,n){const i=t.clip?l3(n,t,t):null;e("clip-path",i)}function JK(e,t){if(!t.clip&&t.items){const n=t.items,i=n.length;for(let r=0;r{const s=r.x||0,o=r.y||0,a=r.strokeForeground,u=r.opacity==null?1:r.opacity;(r.stroke||r.fill)&&u&&(Pf(e,r,s,o),Ol(e,r),r.fill&&pp(e,r,u)&&e.fill(),r.stroke&&!a&&Ll(e,r,u)&&e.stroke()),e.save(),e.translate(s,o),r.clip&&oA(e,r),n&&n.translate(-s,-o),pr(r,l=>{(l.marktype==="group"||i==null||i.includes(l.marktype))&&this.draw(e,l,n,i)}),n&&n.translate(s,o),e.restore(),a&&r.stroke&&u&&(Pf(e,r,s,o),Ol(e,r),Ll(e,r,u)&&e.stroke())})}function iZ(e,t,n,i,r,s){if(t.bounds&&!t.bounds.contains(r,s)||!t.items)return null;const o=n*e.pixelRatio,a=i*e.pixelRatio;return gp(t,u=>{let l,c,f;const d=u.bounds;if(d&&!d.contains(r,s))return;c=u.x||0,f=u.y||0;const h=c+(u.width||0),p=f+(u.height||0),g=u.clip;if(g&&(rh||sp))return;if(e.save(),e.translate(c,f),c=r-c,f=s-f,g&&u3(u)&&!tZ(e,u,o,a))return e.restore(),null;const m=u.strokeForeground,y=t.interactive!==!1;return y&&m&&u.stroke&&eZ(e,u,o,a)?(e.restore(),u):(l=gp(u,b=>rZ(b,c,f)?this.pick(b,n,i,c,f):null),!l&&y&&(u.fill||!m&&u.stroke)&&QK(e,u,o,a)&&(l=u),e.restore(),l||null)})}function rZ(e,t,n){return(e.interactive!==!1||e.marktype==="group")&&e.bounds&&e.bounds.contains(t,n)}var sZ={type:"group",tag:"g",nested:!1,attr:YK,bound:JK,draw:nZ,pick:iZ,isect:eA,content:ZK,background:XK,foreground:KK},zf={xmlns:"http://www.w3.org/2000/svg","xmlns:xlink":"http://www.w3.org/1999/xlink",version:"1.1"};function C3(e,t){var n=e.image;return(!n||e.url&&e.url!==n.url)&&(n={complete:!1,width:0,height:0},t.loadImage(e.url).then(i=>{e.image=i,e.image.url=e.url})),n}function k3(e,t){return e.width!=null?e.width:!t||!t.width?0:e.aspect!==!1&&e.height?e.height*t.width/t.height:t.width}function A3(e,t){return e.height!=null?e.height:!t||!t.height?0:e.aspect!==!1&&e.width?e.width*t.height/t.width:t.height}function yp(e,t){return e==="center"?t/2:e==="right"?t:0}function bp(e,t){return e==="middle"?t/2:e==="bottom"?t:0}function oZ(e,t,n){const i=C3(t,n),r=k3(t,i),s=A3(t,i),o=(t.x||0)-yp(t.align,r),a=(t.y||0)-bp(t.baseline,s),u=!i.src&&i.toDataURL?i.toDataURL():i.src||"";e("href",u,zf["xmlns:xlink"],"xlink:href"),e("transform",Va(o,a)),e("width",r),e("height",s),e("preserveAspectRatio",t.aspect===!1?"none":"xMidYMid")}function aZ(e,t){const n=t.image,i=k3(t,n),r=A3(t,n),s=(t.x||0)-yp(t.align,i),o=(t.y||0)-bp(t.baseline,r);return e.set(s,o,s+i,o+r)}function uZ(e,t,n){pr(t,i=>{if(n&&!n.intersects(i.bounds))return;const r=C3(i,this);let s=k3(i,r),o=A3(i,r);if(s===0||o===0)return;let a=(i.x||0)-yp(i.align,s),u=(i.y||0)-bp(i.baseline,o),l,c,f,d;i.aspect!==!1&&(c=r.width/r.height,f=i.width/i.height,c===c&&f===f&&c!==f&&(f{if(!(n&&!n.intersects(i.bounds))){var r=i.opacity==null?1:i.opacity;r&&cA(e,i,r)&&(Ol(e,i),e.stroke())}})}function xZ(e,t,n,i){return e.isPointInStroke?cA(e,t,1)&&e.isPointInStroke(n,i):!1}var _Z={type:"rule",tag:"line",nested:!1,attr:yZ,bound:bZ,draw:vZ,pick:mp(xZ),isect:tA},wZ=w3("shape",FK),EZ=w3("symbol",DK,y3);const fA=X4();var Ai={height:ns,measureWidth:$3,estimateWidth:xp,width:xp,canvas:dA};dA(!0);function dA(e){Ai.width=e&&Uo?$3:xp}function xp(e,t){return hA(qo(e,t),ns(e))}function hA(e,t){return~~(.8*e.length*t)}function $3(e,t){return ns(e)<=0||!(t=qo(e,t))?0:pA(t,_p(e))}function pA(e,t){const n=`(${t}) ${e}`;let i=fA.get(n);return i===void 0&&(Uo.font=t,i=Uo.measureText(e).width,fA.set(n,i)),i}function ns(e){return e.fontSize!=null?+e.fontSize||0:11}function jo(e){return e.lineHeight!=null?e.lineHeight:ns(e)+2}function CZ(e){return W(e)?e.length>1?e:e[0]:e}function Bf(e){return CZ(e.lineBreak&&e.text&&!W(e.text)?e.text.split(e.lineBreak):e.text)}function S3(e){const t=Bf(e);return(W(t)?t.length-1:0)*jo(e)}function qo(e,t){const n=t==null?"":(t+"").trim();return e.limit>0&&n.length?AZ(e,n):n}function kZ(e){if(Ai.width===$3){const t=_p(e);return n=>pA(n,t)}else if(Ai.width===xp){const t=ns(e);return n=>hA(n,t)}else return t=>Ai.width(e,t)}function AZ(e,t){var n=+e.limit,i=kZ(e);if(i(t)>>1,i(t.slice(u))>n?o=u+1:a=u;return r+t.slice(o)}else{for(;o>>1),i(t.slice(0,u))Math.max(d,Ai.width(t,h)),0)):f=Ai.width(t,c),r==="center"?u-=f/2:r==="right"&&(u-=f),e.set(u+=o,l+=a,u+f,l+i),t.angle&&!n)e.rotate(t.angle*Po,o,a);else if(n===2)return e.rotatedPoints(t.angle*Po,o,a);return e}function FZ(e,t,n){pr(t,i=>{var r=i.opacity==null?1:i.opacity,s,o,a,u,l,c,f;if(!(n&&!n.intersects(i.bounds)||r===0||i.fontSize<=0||i.text==null||i.text.length===0)){if(e.font=_p(i),e.textAlign=i.align||"left",s=wp(i),o=s.x1,a=s.y1,i.angle&&(e.save(),e.translate(o,a),e.rotate(i.angle*Po),o=a=0),o+=i.dx||0,a+=(i.dy||0)+F3(i),c=Bf(i),Ol(e,i),W(c))for(l=jo(i),u=0;ut;)e.removeChild(n[--i]);return e}function wA(e){return"mark-"+e.marktype+(e.role?" role-"+e.role:"")+(e.name?" "+e.name:"")}function Ep(e,t){const n=t.getBoundingClientRect();return[e.clientX-n.left-(t.clientLeft||0),e.clientY-n.top-(t.clientTop||0)]}function OZ(e,t,n,i){var r=e&&e.mark,s,o;if(r&&(s=$i[r.marktype]).tip){for(o=Ep(t,n),o[0]-=i[0],o[1]-=i[1];e=e.mark.group;)o[0]-=e.x||0,o[1]-=e.y||0;e=s.tip(r.items,o)}return e}let N3=class{constructor(t,n){this._active=null,this._handlers={},this._loader=t||d0(),this._tooltip=n||LZ}initialize(t,n,i){return this._el=t,this._obj=i||null,this.origin(n)}element(){return this._el}canvas(){return this._el&&this._el.firstChild}origin(t){return arguments.length?(this._origin=t||[0,0],this):this._origin.slice()}scene(t){return arguments.length?(this._scene=t,this):this._scene}on(){}off(){}_handlerIndex(t,n,i){for(let r=t?t.length:0;--r>=0;)if(t[r].type===n&&(!i||t[r].handler===i))return r;return-1}handlers(t){const n=this._handlers,i=[];if(t)i.push(...n[this.eventName(t)]);else for(const r in n)i.push(...n[r]);return i}eventName(t){const n=t.indexOf(".");return n<0?t:t.slice(0,n)}handleHref(t,n,i){this._loader.sanitize(i,{context:"href"}).then(r=>{const s=new MouseEvent(t.type,t),o=Wo(null,"a");for(const a in r)o.setAttribute(a,r[a]);o.dispatchEvent(s)}).catch(()=>{})}handleTooltip(t,n,i){if(n&&n.tooltip!=null){n=OZ(n,t,this.canvas(),this._origin);const r=i&&n&&n.tooltip||null;this._tooltip.call(this._obj,this,t,n,r)}}getItemBoundingClientRect(t){const n=this.canvas();if(!n)return;const i=n.getBoundingClientRect(),r=this._origin,s=t.bounds,o=s.width(),a=s.height();let u=s.x1+r[0]+i.left,l=s.y1+r[1]+i.top;for(;t.mark&&(t=t.mark.group);)u+=t.x||0,l+=t.y||0;return{x:u,y:l,width:o,height:a,left:u,top:l,right:u+o,bottom:l+a}}};function LZ(e,t,n,i){e.element().setAttribute("title",i||"")}class qf{constructor(t){this._el=null,this._bgcolor=null,this._loader=new Vk(t)}initialize(t,n,i,r,s){return this._el=t,this.resize(n,i,r,s)}element(){return this._el}canvas(){return this._el&&this._el.firstChild}background(t){return arguments.length===0?this._bgcolor:(this._bgcolor=t,this)}resize(t,n,i,r){return this._width=t,this._height=n,this._origin=i||[0,0],this._scale=r||1,this}dirty(){}render(t,n){const i=this;return i._call=function(){i._render(t,n)},i._call(),i._call=null,i}_render(){}renderAsync(t,n){const i=this.render(t,n);return this._ready?this._ready.then(()=>i):Promise.resolve(i)}_load(t,n){var i=this,r=i._loader[t](n);if(!i._ready){const s=i._call;i._ready=i._loader.ready().then(o=>{o&&s(),i._ready=null})}return r}sanitizeURL(t){return this._load("sanitizeURL",t)}loadImage(t){return this._load("loadImage",t)}}const IZ="keydown",PZ="keypress",zZ="keyup",EA="dragenter",Cp="dragleave",CA="dragover",R3="pointerdown",BZ="pointerup",kp="pointermove",Ap="pointerout",kA="pointerover",O3="mousedown",UZ="mouseup",AA="mousemove",$p="mouseout",$A="mouseover",Sp="click",jZ="dblclick",qZ="wheel",SA="mousewheel",Fp="touchstart",Dp="touchmove",Tp="touchend",WZ=[IZ,PZ,zZ,EA,Cp,CA,R3,BZ,kp,Ap,kA,O3,UZ,AA,$p,$A,Sp,jZ,qZ,SA,Fp,Dp,Tp],L3=kp,Wf=$p,I3=Sp;class Hf extends N3{constructor(t,n){super(t,n),this._down=null,this._touch=null,this._first=!0,this._events={},this.events=WZ,this.pointermove=DA([kp,AA],[kA,$A],[Ap,$p]),this.dragover=DA([CA],[EA],[Cp]),this.pointerout=TA([Ap,$p]),this.dragleave=TA([Cp])}initialize(t,n,i){return this._canvas=t&&M3(t,"canvas"),[Sp,O3,R3,kp,Ap,Cp].forEach(r=>FA(this,r)),super.initialize(t,n,i)}canvas(){return this._canvas}context(){return this._canvas.getContext("2d")}DOMMouseScroll(t){this.fire(SA,t)}pointerdown(t){this._down=this._active,this.fire(R3,t)}mousedown(t){this._down=this._active,this.fire(O3,t)}click(t){this._down===this._active&&(this.fire(Sp,t),this._down=null)}touchstart(t){this._touch=this.pickEvent(t.changedTouches[0]),this._first&&(this._active=this._touch,this._first=!1),this.fire(Fp,t,!0)}touchmove(t){this.fire(Dp,t,!0)}touchend(t){this.fire(Tp,t,!0),this._touch=null}fire(t,n,i){const r=i?this._touch:this._active,s=this._handlers[t];if(n.vegaType=t,t===I3&&r&&r.href?this.handleHref(n,r,r.href):(t===L3||t===Wf)&&this.handleTooltip(n,r,t!==Wf),s)for(let o=0,a=s.length;o=0&&r.splice(s,1),this}pickEvent(t){const n=Ep(t,this._canvas),i=this._origin;return this.pick(this._scene,n[0],n[1],n[0]-i[0],n[1]-i[1])}pick(t,n,i,r,s){const o=this.context();return $i[t.marktype].pick.call(this,o,t,n,i,r,s)}}const HZ=e=>e===Fp||e===Dp||e===Tp?[Fp,Dp,Tp]:[e];function FA(e,t){HZ(t).forEach(n=>GZ(e,n))}function GZ(e,t){const n=e.canvas();n&&!e._events[t]&&(e._events[t]=1,n.addEventListener(t,e[t]?i=>e[t](i):i=>e.fire(t,i)))}function Gf(e,t,n){t.forEach(i=>e.fire(i,n))}function DA(e,t,n){return function(i){const r=this._active,s=this.pickEvent(i);s===r?Gf(this,e,i):((!r||!r.exit)&&Gf(this,n,i),this._active=s,Gf(this,t,i),Gf(this,e,i))}}function TA(e){return function(t){Gf(this,e,t),this._active=null}}function VZ(){return typeof window<"u"&&window.devicePixelRatio||1}function YZ(e,t,n,i,r,s){const o=typeof HTMLElement<"u"&&e instanceof HTMLElement&&e.parentNode!=null,a=e.getContext("2d"),u=o?VZ():r;e.width=t*u,e.height=n*u;for(const l in s)a[l]=s[l];return o&&u!==1&&(e.style.width=t+"px",e.style.height=n+"px"),a.pixelRatio=u,a.setTransform(u,0,0,u,u*i[0],u*i[1]),e}class Mp extends qf{constructor(t){super(t),this._options={},this._redraw=!1,this._dirty=new Ut,this._tempb=new Ut}initialize(t,n,i,r,s,o){return this._options=o||{},this._canvas=this._options.externalContext?null:Mo(1,1,this._options.type),t&&this._canvas&&(Vi(t,0).appendChild(this._canvas),this._canvas.setAttribute("class","marks")),super.initialize(t,n,i,r,s)}resize(t,n,i,r){if(super.resize(t,n,i,r),this._canvas)YZ(this._canvas,this._width,this._height,this._origin,this._scale,this._options.context);else{const s=this._options.externalContext;s||q("CanvasRenderer is missing a valid canvas or context"),s.scale(this._scale,this._scale),s.translate(this._origin[0],this._origin[1])}return this._redraw=!0,this}canvas(){return this._canvas}context(){return this._options.externalContext||(this._canvas?this._canvas.getContext("2d"):null)}dirty(t){const n=this._tempb.clear().union(t.bounds);let i=t.mark.group;for(;i;)n.translate(i.x||0,i.y||0),i=i.mark.group;this._dirty.union(n)}_render(t,n){const i=this.context(),r=this._origin,s=this._width,o=this._height,a=this._dirty,u=XZ(r,s,o);i.save();const l=this._redraw||a.empty()?(this._redraw=!1,u.expand(1)):KZ(i,u.intersect(a),r);return this.clear(-r[0],-r[1],s,o),this.draw(i,t,l,n),i.restore(),a.clear(),this}draw(t,n,i,r){if(n.marktype!=="group"&&r!=null&&!r.includes(n.marktype))return;const s=$i[n.marktype];n.clip&&VK(t,n),s.draw.call(this,t,n,i,r),n.clip&&t.restore()}clear(t,n,i,r){const s=this._options,o=this.context();s.type!=="pdf"&&!s.externalContext&&o.clearRect(t,n,i,r),this._bgcolor!=null&&(o.fillStyle=this._bgcolor,o.fillRect(t,n,i,r))}}const XZ=(e,t,n)=>new Ut().set(0,0,t,n).translate(-e[0],-e[1]);function KZ(e,t,n){return t.expand(1).round(),e.pixelRatio%1&&t.scale(e.pixelRatio).round().scale(1/e.pixelRatio),t.translate(-(n[0]%1),-(n[1]%1)),e.beginPath(),e.rect(t.x1,t.y1,t.width(),t.height()),e.clip(),t}class MA extends N3{constructor(t,n){super(t,n);const i=this;i._hrefHandler=P3(i,(r,s)=>{s&&s.href&&i.handleHref(r,s,s.href)}),i._tooltipHandler=P3(i,(r,s)=>{i.handleTooltip(r,s,r.type!==Wf)})}initialize(t,n,i){let r=this._svg;return r&&(r.removeEventListener(I3,this._hrefHandler),r.removeEventListener(L3,this._tooltipHandler),r.removeEventListener(Wf,this._tooltipHandler)),this._svg=r=t&&M3(t,"svg"),r&&(r.addEventListener(I3,this._hrefHandler),r.addEventListener(L3,this._tooltipHandler),r.addEventListener(Wf,this._tooltipHandler)),super.initialize(t,n,i)}canvas(){return this._svg}on(t,n){const i=this.eventName(t),r=this._handlers;if(this._handlerIndex(r[i],t,n)<0){const o={type:t,handler:n,listener:P3(this,n)};(r[i]||(r[i]=[])).push(o),this._svg&&this._svg.addEventListener(i,o.listener)}return this}off(t,n){const i=this.eventName(t),r=this._handlers[i],s=this._handlerIndex(r,t,n);return s>=0&&(this._svg&&this._svg.removeEventListener(i,r[s].listener),r.splice(s,1)),this}}const P3=(e,t)=>n=>{let i=n.target.__data__;i=Array.isArray(i)?i[0]:i,n.vegaType=n.type,t.call(e._obj,n,i)},NA="aria-hidden",z3="aria-label",B3="role",U3="aria-roledescription",RA="graphics-object",j3="graphics-symbol",OA=(e,t,n)=>({[B3]:e,[U3]:t,[z3]:n||void 0}),ZZ=lr(["axis-domain","axis-grid","axis-label","axis-tick","axis-title","legend-band","legend-entry","legend-gradient","legend-label","legend-title","legend-symbol","title"]),LA={axis:{desc:"axis",caption:eJ},legend:{desc:"legend",caption:tJ},"title-text":{desc:"title",caption:e=>`Title text '${BA(e)}'`},"title-subtitle":{desc:"subtitle",caption:e=>`Subtitle text '${BA(e)}'`}},IA={ariaRole:B3,ariaRoleDescription:U3,description:z3};function PA(e,t){const n=t.aria===!1;if(e(NA,n||void 0),n||t.description==null)for(const i in IA)e(IA[i],void 0);else{const i=t.mark.marktype;e(z3,t.description),e(B3,t.ariaRole||(i==="group"?RA:j3)),e(U3,t.ariaRoleDescription||`${i} mark`)}}function zA(e){return e.aria===!1?{[NA]:!0}:ZZ[e.role]?null:LA[e.role]?QZ(e,LA[e.role]):JZ(e)}function JZ(e){const t=e.marktype,n=t==="group"||t==="text"||e.items.some(i=>i.description!=null&&i.aria!==!1);return OA(n?RA:j3,`${t} mark container`,e.description)}function QZ(e,t){try{const n=e.items[0],i=t.caption||(()=>"");return OA(t.role||j3,t.desc,n.description||i(n))}catch{return null}}function BA(e){return oe(e.text).join(" ")}function eJ(e){const t=e.datum,n=e.orient,i=t.title?UA(e):null,r=e.context,s=r.scales[t.scale].value,o=r.dataflow.locale(),a=s.type;return`${n==="left"||n==="right"?"Y":"X"}-axis`+(i?` titled '${i}'`:"")+` for a ${Dl(a)?"discrete":a} scale with ${Nk(o,s,e)}`}function tJ(e){const t=e.datum,n=t.title?UA(e):null,i=`${t.type||""} legend`.trim(),r=t.scales,s=Object.keys(r),o=e.context,a=o.scales[r[s[0]]].value,u=o.dataflow.locale();return iJ(i)+(n?` titled '${n}'`:"")+` for ${nJ(s)} with ${Nk(u,a,e)}`}function UA(e){try{return oe(Ge(e.items).items[0].text).join(" ")}catch{return null}}function nJ(e){return e=e.map(t=>t+(t==="fill"||t==="stroke"?" color":"")),e.length<2?e[0]:e.slice(0,-1).join(", ")+" and "+Ge(e)}function iJ(e){return e.length?e[0].toUpperCase()+e.slice(1):e}const jA=e=>(e+"").replace(/&/g,"&").replace(//g,">"),rJ=e=>jA(e).replace(/"/g,""").replace(/\t/g," ").replace(/\n/g," ").replace(/\r/g," ");function q3(){let e="",t="",n="";const i=[],r=()=>t=n="",s=u=>{t&&(e+=`${t}>${n}`,r()),i.push(u)},o=(u,l)=>(l!=null&&(t+=` ${u}="${rJ(l)}"`),a),a={open(u){s(u),t="<"+u;for(var l=arguments.length,c=new Array(l>1?l-1:0),f=1;f${n}`:"/>"):e+=``,r(),a},attr:o,text:u=>(n+=jA(u),a),toString:()=>e};return a}const qA=e=>WA(q3(),e)+"";function WA(e,t){if(e.open(t.tagName),t.hasAttributes()){const n=t.attributes,i=n.length;for(let r=0;r{c.dirty=n})),!r.zdirty){if(i.exit){o.nested&&r.items.length?(l=r.items[0],l._svg&&this._update(o,l._svg,l)):i._svg&&(l=i._svg.parentNode,l&&l.removeChild(i._svg)),i._svg=null;continue}i=o.nested?r.items[0]:i,i._update!==n&&(!i._svg||!i._svg.ownerSVGElement?(this._dirtyAll=!1,VA(i,n)):this._update(o,i._svg,i),i._update=n)}return!this._dirtyAll}mark(t,n,i,r){if(!this.isDirty(n))return n._svg;const s=this._svg,o=n.marktype,a=$i[o],u=n.interactive===!1?"none":null,l=a.tag==="g",c=YA(n,t,i,"g",s);if(o!=="group"&&r!=null&&!r.includes(o))return Vi(c,0),n._svg;c.setAttribute("class",wA(n));const f=zA(n);for(const g in f)Bn(c,g,f[g]);l||Bn(c,"pointer-events",u),Bn(c,"clip-path",n.clip?l3(this,n,n.group):null);let d=null,h=0;const p=g=>{const m=this.isDirty(g),y=YA(g,c,d,a.tag,s);m&&(this._update(a,y,g),l&&aJ(this,y,g,r)),d=y,++h};return a.nested?n.items.length&&p(n.items[0]):pr(n,p),Vi(c,h),c}_update(t,n,i){Us=n,Dn=n.__values__,PA(Yf,i),t.attr(Yf,i,this);const r=lJ[t.type];r&&r.call(this,t,n,i),Us&&this.style(Us,i)}style(t,n){if(n!=null){for(const i in Np){let r=i==="font"?Uf(n):n[i];if(r===Dn[i])continue;const s=Np[i];r==null?t.removeAttribute(s):(i3(r)&&(r=Ok(r,this._defs.gradient,XA())),t.setAttribute(s,r+"")),Dn[i]=r}for(const i in Rp)Op(t,Rp[i],n[i])}}defs(){const t=this._svg,n=this._defs;let i=n.el,r=0;for(const s in n.gradient)i||(n.el=i=Gt(t,Vf+1,"defs",Vt)),r=sJ(i,n.gradient[s],r);for(const s in n.clipping)i||(n.el=i=Gt(t,Vf+1,"defs",Vt)),r=oJ(i,n.clipping[s],r);i&&(r===0?(t.removeChild(i),n.el=null):Vi(i,r))}_clearDefs(){const t=this._defs;t.gradient={},t.clipping={}}}function VA(e,t){for(;e&&e.dirty!==t;e=e.mark.group)if(e.dirty=t,e.mark&&e.mark.dirty!==t)e.mark.dirty=t;else return}function sJ(e,t,n){let i,r,s;if(t.gradient==="radial"){let o=Gt(e,n++,"pattern",Vt);Ho(o,{id:rp+t.id,viewBox:"0,0,1,1",width:"100%",height:"100%",preserveAspectRatio:"xMidYMid slice"}),o=Gt(o,0,"rect",Vt),Ho(o,{width:1,height:1,fill:`url(${XA()}#${t.id})`}),e=Gt(e,n++,"radialGradient",Vt),Ho(e,{id:t.id,fx:t.x1,fy:t.y1,fr:t.r1,cx:t.x2,cy:t.y2,r:t.r2})}else e=Gt(e,n++,"linearGradient",Vt),Ho(e,{id:t.id,x1:t.x1,x2:t.x2,y1:t.y1,y2:t.y2});for(i=0,r=t.stops.length;i{r=e.mark(t,o,r,i),++s}),Vi(t,1+s)}function YA(e,t,n,i,r){let s=e._svg,o;if(!s&&(o=t.ownerDocument,s=Wo(o,i,Vt),e._svg=s,e.mark&&(s.__data__=e,s.__values__={fill:"default"},i==="g"))){const a=Wo(o,"path",Vt);s.appendChild(a),a.__data__=e;const u=Wo(o,"g",Vt);s.appendChild(u),u.__data__=e;const l=Wo(o,"path",Vt);s.appendChild(l),l.__data__=e,l.__values__={fill:"default"}}return(s.ownerSVGElement!==r||uJ(s,n))&&t.insertBefore(s,n?n.nextSibling:t.firstChild),s}function uJ(e,t){return e.parentNode&&e.parentNode.childNodes.length>1&&e.previousSibling!=t}let Us=null,Dn=null;const lJ={group(e,t,n){const i=Us=t.childNodes[2];Dn=i.__values__,e.foreground(Yf,n,this),Dn=t.__values__,Us=t.childNodes[1],e.content(Yf,n,this);const r=Us=t.childNodes[0];e.background(Yf,n,this);const s=n.mark.interactive===!1?"none":null;if(s!==Dn.events&&(Bn(i,"pointer-events",s),Bn(r,"pointer-events",s),Dn.events=s),n.strokeForeground&&n.stroke){const o=n.fill;Bn(i,"display",null),this.style(r,n),Bn(r,"stroke",null),o&&(n.fill=null),Dn=i.__values__,this.style(i,n),o&&(n.fill=o),Us=null}else Bn(i,"display","none")},image(e,t,n){n.smooth===!1?(Op(t,"image-rendering","optimizeSpeed"),Op(t,"image-rendering","pixelated")):Op(t,"image-rendering",null)},text(e,t,n){const i=Bf(n);let r,s,o,a;W(i)?(s=i.map(u=>qo(n,u)),r=s.join(` +`),r!==Dn.text&&(Vi(t,0),o=t.ownerDocument,a=jo(n),s.forEach((u,l)=>{const c=Wo(o,"tspan",Vt);c.__data__=n,c.textContent=u,l&&(c.setAttribute("x",0),c.setAttribute("dy",a)),t.appendChild(c)}),Dn.text=r)):(s=qo(n,i),s!==Dn.text&&(t.textContent=s,Dn.text=s)),Bn(t,"font-family",Uf(n)),Bn(t,"font-size",ns(n)+"px"),Bn(t,"font-style",n.fontStyle),Bn(t,"font-variant",n.fontVariant),Bn(t,"font-weight",n.fontWeight)}};function Yf(e,t,n){t!==Dn[e]&&(n?cJ(Us,e,t,n):Bn(Us,e,t),Dn[e]=t)}function Op(e,t,n){n!==Dn[t]&&(n==null?e.style.removeProperty(t):e.style.setProperty(t,n+""),Dn[t]=n)}function Ho(e,t){for(const n in t)Bn(e,n,t[n])}function Bn(e,t,n){n!=null?e.setAttribute(t,n):e.removeAttribute(t)}function cJ(e,t,n,i){n!=null?e.setAttributeNS(i,t,n):e.removeAttributeNS(i,t)}function XA(){let e;return typeof window>"u"?"":(e=window.location).hash?e.href.slice(0,-e.hash.length):e.href}class KA extends qf{constructor(t){super(t),this._text=null,this._defs={gradient:{},clipping:{}}}svg(){return this._text}_render(t){const n=q3();n.open("svg",Oe({},zf,{class:"marks",width:this._width*this._scale,height:this._height*this._scale,viewBox:`0 0 ${this._width} ${this._height}`}));const i=this._bgcolor;return i&&i!=="transparent"&&i!=="none"&&n.open("rect",{width:this._width,height:this._height,fill:i}).close(),n.open("g",HA,{transform:"translate("+this._origin+")"}),this.mark(n,t),n.close(),this.defs(n),this._text=n.close()+"",this}mark(t,n){const i=$i[n.marktype],r=i.tag,s=[PA,i.attr];t.open("g",{class:wA(n),"clip-path":n.clip?l3(this,n,n.group):null},zA(n),{"pointer-events":r!=="g"&&n.interactive===!1?"none":null});const o=a=>{const u=this.href(a);if(u&&t.open("a",u),t.open(r,this.attr(n,a,s,r!=="g"?r:null)),r==="text"){const l=Bf(a);if(W(l)){const c={x:0,dy:jo(a)};for(let f=0;fthis.mark(t,d)),t.close(),l&&f?(c&&(a.fill=null),a.stroke=f,t.open("path",this.attr(n,a,i.foreground,"bgrect")).close(),c&&(a.fill=c)):t.open("path",this.attr(n,a,i.foreground,"bgfore")).close()}t.close(),u&&t.close()};return i.nested?n.items&&n.items.length&&o(n.items[0]):pr(n,o),t.close()}href(t){const n=t.href;let i;if(n){if(i=this._hrefs&&this._hrefs[n])return i;this.sanitizeURL(n).then(r=>{r["xlink:href"]=r.href,r.href=null,(this._hrefs||(this._hrefs={}))[n]=r})}return null}attr(t,n,i,r){const s={},o=(a,u,l,c)=>{s[c||a]=u};return Array.isArray(i)?i.forEach(a=>a(o,n,this)):i(o,n,this),r&&fJ(s,n,t,r,this._defs),s}defs(t){const n=this._defs.gradient,i=this._defs.clipping;if(Object.keys(n).length+Object.keys(i).length!==0){t.open("defs");for(const s in n){const o=n[s],a=o.stops;o.gradient==="radial"?(t.open("pattern",{id:rp+s,viewBox:"0,0,1,1",width:"100%",height:"100%",preserveAspectRatio:"xMidYMid slice"}),t.open("rect",{width:"1",height:"1",fill:"url(#"+s+")"}).close(),t.close(),t.open("radialGradient",{id:s,fx:o.x1,fy:o.y1,fr:o.r1,cx:o.x2,cy:o.y2,r:o.r2})):t.open("linearGradient",{id:s,x1:o.x1,x2:o.x2,y1:o.y1,y2:o.y2});for(let u=0;u!gr.svgMarkTypes.includes(s));this._svgRenderer.render(t,gr.svgMarkTypes),this._canvasRenderer.render(t,r)}resize(t,n,i,r){return super.resize(t,n,i,r),this._svgRenderer.resize(t,n,i,r),this._canvasRenderer.resize(t,n,i,r),this}background(t){return gr.svgOnTop?this._canvasRenderer.background(t):this._svgRenderer.background(t),this}}class ZA extends Hf{constructor(t,n){super(t,n)}initialize(t,n,i){const r=Gt(Gt(t,0,"div"),gr.svgOnTop?0:1,"div");return super.initialize(r,n,i)}}const JA="canvas",QA="hybrid",e$="png",t$="svg",n$="none",Go={Canvas:JA,PNG:e$,SVG:t$,Hybrid:QA,None:n$},Ya={};Ya[JA]=Ya[e$]={renderer:Mp,headless:Mp,handler:Hf},Ya[t$]={renderer:W3,headless:KA,handler:MA},Ya[QA]={renderer:H3,headless:H3,handler:ZA},Ya[n$]={};function Lp(e,t){return e=String(e||"").toLowerCase(),arguments.length>1?(Ya[e]=t,this):Ya[e]}function i$(e,t,n){const i=[],r=new Ut().union(t),s=e.marktype;return s?r$(e,r,n,i):s==="group"?s$(e,r,n,i):q("Intersect scene must be mark node or group item.")}function r$(e,t,n,i){if(hJ(e,t,n)){const r=e.items,s=e.marktype,o=r.length;let a=0;if(s==="group")for(;a=0;s--)if(n[s]!=i[s])return!1;for(s=n.length-1;s>=0;s--)if(r=n[s],!V3(e[r],t[r],r))return!1;return typeof e==typeof t}function mJ(){Gk(),BX()}const Il="top",mr="left",yr="right",Vo="bottom",yJ="top-left",bJ="top-right",vJ="bottom-left",xJ="bottom-right",Y3="start",X3="middle",Un="end",_J="x",wJ="y",Ip="group",K3="axis",Z3="title",EJ="frame",CJ="scope",J3="legend",l$="row-header",c$="row-footer",f$="row-title",d$="column-header",h$="column-footer",p$="column-title",kJ="padding",AJ="symbol",g$="fit",m$="fit-x",y$="fit-y",$J="pad",Q3="none",Pp="all",ev="each",tv="flush",Yo="column",Xo="row";function b$(e){P.call(this,null,e)}ee(b$,P,{transform(e,t){const n=t.dataflow,i=e.mark,r=i.marktype,s=$i[r],o=s.bound;let a=i.bounds,u;if(s.nested)i.items.length&&n.dirty(i.items[0]),a=zp(i,o),i.items.forEach(l=>{l.bounds.clear().union(a)});else if(r===Ip||e.modified())switch(t.visit(t.MOD,l=>n.dirty(l)),a.clear(),i.items.forEach(l=>a.union(zp(l,o))),i.role){case K3:case J3:case Z3:t.reflow()}else u=t.changed(t.REM),t.visit(t.ADD,l=>{a.union(zp(l,o))}),t.visit(t.MOD,l=>{u=u||a.alignsWith(l.bounds),n.dirty(l),a.union(zp(l,o))}),u&&(a.clear(),i.items.forEach(l=>a.union(l.bounds)));return a$(i),t.modifies("bounds")}});function zp(e,t,n){return t(e.bounds.clear(),e,n)}const v$=":vega_identifier:";function nv(e){P.call(this,0,e)}nv.Definition={type:"Identifier",metadata:{modifies:!0},params:[{name:"as",type:"string",required:!0}]},ee(nv,P,{transform(e,t){const n=SJ(t.dataflow),i=e.as;let r=n.value;return t.visit(t.ADD,s=>s[i]=s[i]||++r),n.set(this.value=r),t}});function SJ(e){return e._signals[v$]||(e._signals[v$]=e.add(0))}function x$(e){P.call(this,null,e)}ee(x$,P,{transform(e,t){let n=this.value;n||(n=t.dataflow.scenegraph().mark(e.markdef,FJ(e),e.index),n.group.context=e.context,e.context.group||(e.context.group=n.group),n.source=this.source,n.clip=e.clip,n.interactive=e.interactive,this.value=n);const i=n.marktype===Ip?up:ap;return t.visit(t.ADD,r=>i.call(r,n)),(e.modified("clip")||e.modified("interactive"))&&(n.clip=e.clip,n.interactive=!!e.interactive,n.zdirty=!0,t.reflow()),n.items=t.source,t}});function FJ(e){const t=e.groups,n=e.parent;return t&&t.size===1?t.get(Object.keys(t.object)[0]):t&&n?t.lookup(n):null}function _$(e){P.call(this,null,e)}const w$={parity:e=>e.filter((t,n)=>n%2?t.opacity=0:1),greedy:(e,t)=>{let n;return e.filter((i,r)=>!r||!E$(n.bounds,i.bounds,t)?(n=i,1):i.opacity=0)}},E$=(e,t,n)=>n>Math.max(t.x1-e.x2,e.x1-t.x2,t.y1-e.y2,e.y1-t.y2),C$=(e,t)=>{for(var n=1,i=e.length,r=e[0].bounds,s;n{const t=e.bounds;return t.width()>1&&t.height()>1},TJ=(e,t,n)=>{var i=e.range(),r=new Ut;return t===Il||t===Vo?r.set(i[0],-1/0,i[1],1/0):r.set(-1/0,i[0],1/0,i[1]),r.expand(n||1),s=>r.encloses(s.bounds)},k$=e=>(e.forEach(t=>t.opacity=1),e),A$=(e,t)=>e.reflow(t.modified()).modifies("opacity");ee(_$,P,{transform(e,t){const n=w$[e.method]||w$.parity,i=e.separation||0;let r=t.materialize(t.SOURCE).source,s,o;if(!r||!r.length)return;if(!e.method)return e.modified("method")&&(k$(r),t=A$(t,e)),t;if(r=r.filter(DJ),!r.length)return;if(e.sort&&(r=r.slice().sort(e.sort)),s=k$(r),t=A$(t,e),s.length>=3&&C$(s,i)){do s=n(s,i);while(s.length>=3&&C$(s,i));s.length<3&&!Ge(r).opacity&&(s.length>1&&(Ge(s).opacity=0),Ge(r).opacity=1)}e.boundScale&&e.boundTolerance>=0&&(o=TJ(e.boundScale,e.boundOrient,+e.boundTolerance),r.forEach(u=>{o(u)||(u.opacity=0)}));const a=s[0].mark.bounds.clear();return r.forEach(u=>{u.opacity&&a.union(u.bounds)}),t}});function $$(e){P.call(this,null,e)}ee($$,P,{transform(e,t){const n=t.dataflow;if(t.visit(t.ALL,i=>n.dirty(i)),t.fields&&t.fields.zindex){const i=t.source&&t.source[0];i&&(i.mark.zdirty=!0)}}});const Tn=new Ut;function Pl(e,t,n){return e[t]===n?0:(e[t]=n,1)}function MJ(e){var t=e.items[0].orient;return t===mr||t===yr}function NJ(e){let t=+e.grid;return[e.ticks?t++:-1,e.labels?t++:-1,t+ +e.domain]}function RJ(e,t,n,i){var r=t.items[0],s=r.datum,o=r.translate!=null?r.translate:.5,a=r.orient,u=NJ(s),l=r.range,c=r.offset,f=r.position,d=r.minExtent,h=r.maxExtent,p=s.title&&r.items[u[2]].items[0],g=r.titlePadding,m=r.bounds,y=p&&S3(p),b=0,v=0,x,_;switch(Tn.clear().union(m),m.clear(),(x=u[0])>-1&&m.union(r.items[x].bounds),(x=u[1])>-1&&m.union(r.items[x].bounds),a){case Il:b=f||0,v=-c,_=Math.max(d,Math.min(h,-m.y1)),m.add(0,-_).add(l,0),p&&Bp(e,p,_,g,y,0,-1,m);break;case mr:b=-c,v=f||0,_=Math.max(d,Math.min(h,-m.x1)),m.add(-_,0).add(0,l),p&&Bp(e,p,_,g,y,1,-1,m);break;case yr:b=n+c,v=f||0,_=Math.max(d,Math.min(h,m.x2)),m.add(0,0).add(_,l),p&&Bp(e,p,_,g,y,1,1,m);break;case Vo:b=f||0,v=i+c,_=Math.max(d,Math.min(h,m.y2)),m.add(0,0).add(l,_),p&&Bp(e,p,_,g,0,0,1,m);break;default:b=r.x,v=r.y}return Bs(m.translate(b,v),r),Pl(r,"x",b+o)|Pl(r,"y",v+o)&&(r.bounds=Tn,e.dirty(r),r.bounds=m,e.dirty(r)),r.mark.bounds.clear().union(m)}function Bp(e,t,n,i,r,s,o,a){const u=t.bounds;if(t.auto){const l=o*(n+r+i);let c=0,f=0;e.dirty(t),s?c=(t.x||0)-(t.x=l):f=(t.y||0)-(t.y=l),t.mark.bounds.clear().union(u.translate(-c,-f)),e.dirty(t)}a.union(u)}const S$=(e,t)=>Math.floor(Math.min(e,t)),F$=(e,t)=>Math.ceil(Math.max(e,t));function OJ(e){var t=e.items,n=t.length,i=0,r,s;const o={marks:[],rowheaders:[],rowfooters:[],colheaders:[],colfooters:[],rowtitle:null,coltitle:null};for(;i1)for(w=0;w0&&(v[w]+=F/2);if(a&&kt(n.center,Xo)&&c!==1)for(w=0;w0&&(x[w]+=A/2);for(w=0;wr&&(e.warn("Grid headers exceed limit: "+r),t=t.slice(0,r)),g+=s,b=0,x=t.length;b=0&&(w=n[v])==null;v-=d);a?(C=h==null?w.x:Math.round(w.bounds.x1+h*w.bounds.width()),k=g):(C=g,k=h==null?w.y:Math.round(w.bounds.y1+h*w.bounds.height())),_.union(E.bounds.translate(C-(E.x||0),k-(E.y||0))),E.x=C,E.y=k,e.dirty(E),m=o(m,_[l])}return m}function M$(e,t,n,i,r,s){if(t){e.dirty(t);var o=n,a=n;i?o=Math.round(r.x1+s*r.width()):a=Math.round(r.y1+s*r.height()),t.bounds.translate(o-(t.x||0),a-(t.y||0)),t.mark.bounds.clear().union(t.bounds),t.x=o,t.y=a,e.dirty(t)}}function UJ(e,t){const n=e[t]||{};return(i,r)=>n[i]!=null?n[i]:e[i]!=null?e[i]:r}function jJ(e,t){let n=-1/0;return e.forEach(i=>{i.offset!=null&&(n=Math.max(n,i.offset))}),n>-1/0?n:t}function qJ(e,t,n,i,r,s,o){const a=UJ(n,t),u=jJ(e,a("offset",0)),l=a("anchor",Y3),c=l===Un?1:l===X3?.5:0,f={align:ev,bounds:a("bounds",tv),columns:a("direction")==="vertical"?1:e.length,padding:a("margin",8),center:a("center"),nodirty:!0};switch(t){case mr:f.anchor={x:Math.floor(i.x1)-u,column:Un,y:c*(o||i.height()+2*i.y1),row:l};break;case yr:f.anchor={x:Math.ceil(i.x2)+u,y:c*(o||i.height()+2*i.y1),row:l};break;case Il:f.anchor={y:Math.floor(r.y1)-u,row:Un,x:c*(s||r.width()+2*r.x1),column:l};break;case Vo:f.anchor={y:Math.ceil(r.y2)+u,x:c*(s||r.width()+2*r.x1),column:l};break;case yJ:f.anchor={x:u,y:u};break;case bJ:f.anchor={x:s-u,y:u,column:Un};break;case vJ:f.anchor={x:u,y:o-u,row:Un};break;case xJ:f.anchor={x:s-u,y:o-u,column:Un,row:Un};break}return f}function WJ(e,t){var n=t.items[0],i=n.datum,r=n.orient,s=n.bounds,o=n.x,a=n.y,u,l;return n._bounds?n._bounds.clear().union(s):n._bounds=s.clone(),s.clear(),GJ(e,n,n.items[0].items[0]),s=HJ(n,s),u=2*n.padding,l=2*n.padding,s.empty()||(u=Math.ceil(s.width()+u),l=Math.ceil(s.height()+l)),i.type===AJ&&VJ(n.items[0].items[0].items[0].items),r!==Q3&&(n.x=o=0,n.y=a=0),n.width=u,n.height=l,Bs(s.set(o,a,o+u,a+l),n),n.mark.bounds.clear().union(s),n}function HJ(e,t){return e.items.forEach(n=>t.union(n.bounds)),t.x1=e.padding,t.y1=e.padding,t}function GJ(e,t,n){var i=t.padding,r=i-n.x,s=i-n.y;if(!t.datum.title)(r||s)&&Xf(e,n,r,s);else{var o=t.items[1].items[0],a=o.anchor,u=t.titlePadding||0,l=i-o.x,c=i-o.y;switch(o.orient){case mr:r+=Math.ceil(o.bounds.width())+u;break;case yr:case Vo:break;default:s+=o.bounds.height()+u}switch((r||s)&&Xf(e,n,r,s),o.orient){case mr:c+=zl(t,n,o,a,1,1);break;case yr:l+=zl(t,n,o,Un,0,0)+u,c+=zl(t,n,o,a,1,1);break;case Vo:l+=zl(t,n,o,a,0,0),c+=zl(t,n,o,Un,-1,0,1)+u;break;default:l+=zl(t,n,o,a,0,0)}(l||c)&&Xf(e,o,l,c),(l=Math.round(o.bounds.x1-i))<0&&(Xf(e,n,-l,0),Xf(e,o,-l,0))}}function zl(e,t,n,i,r,s,o){const a=e.datum.type!=="symbol",u=n.datum.vgrad,l=a&&(s||!u)&&!o?t.items[0]:t,c=l.bounds[r?"y2":"x2"]-e.padding,f=u&&s?c:0,d=u&&s?0:c,h=r<=0?0:S3(n);return Math.round(i===Y3?f:i===Un?d-h:.5*(c-h))}function Xf(e,t,n,i){t.x+=n,t.y+=i,t.bounds.translate(n,i),t.mark.bounds.translate(n,i),e.dirty(t)}function VJ(e){const t=e.reduce((n,i)=>(n[i.column]=Math.max(i.bounds.x2-i.x,n[i.column]||0),n),{});e.forEach(n=>{n.width=t[n.column],n.height=n.bounds.y2-n.y})}function YJ(e,t,n,i,r){var s=t.items[0],o=s.frame,a=s.orient,u=s.anchor,l=s.offset,c=s.padding,f=s.items[0].items[0],d=s.items[1]&&s.items[1].items[0],h=a===mr||a===yr?i:n,p=0,g=0,m=0,y=0,b=0,v;if(o!==Ip?a===mr?(p=r.y2,h=r.y1):a===yr?(p=r.y1,h=r.y2):(p=r.x1,h=r.x2):a===mr&&(p=i,h=0),v=u===Y3?p:u===Un?h:(p+h)/2,d&&d.text){switch(a){case Il:case Vo:b=f.bounds.height()+c;break;case mr:y=f.bounds.width()+c;break;case yr:y=-f.bounds.width()-c;break}Tn.clear().union(d.bounds),Tn.translate(y-(d.x||0),b-(d.y||0)),Pl(d,"x",y)|Pl(d,"y",b)&&(e.dirty(d),d.bounds.clear().union(Tn),d.mark.bounds.clear().union(Tn),e.dirty(d)),Tn.clear().union(d.bounds)}else Tn.clear();switch(Tn.union(f.bounds),a){case Il:g=v,m=r.y1-Tn.height()-l;break;case mr:g=r.x1-Tn.width()-l,m=v;break;case yr:g=r.x2+Tn.width()+l,m=v;break;case Vo:g=v,m=r.y2+l;break;default:g=s.x,m=s.y}return Pl(s,"x",g)|Pl(s,"y",m)&&(Tn.translate(g,m),e.dirty(s),s.bounds.clear().union(Tn),t.bounds.clear().union(Tn),e.dirty(s)),s.bounds}function N$(e){P.call(this,null,e)}ee(N$,P,{transform(e,t){const n=t.dataflow;return e.mark.items.forEach(i=>{e.layout&&PJ(n,i,e.layout),KJ(n,i,e)}),XJ(e.mark.group)?t.reflow():t}});function XJ(e){return e&&e.mark.role!=="legend-entry"}function KJ(e,t,n){var i=t.items,r=Math.max(0,t.width||0),s=Math.max(0,t.height||0),o=new Ut().set(0,0,r,s),a=o.clone(),u=o.clone(),l=[],c,f,d,h,p,g;for(p=0,g=i.length;p{d=y.orient||yr,d!==Q3&&(m[d]||(m[d]=[])).push(y)});for(const y in m){const b=m[y];T$(e,b,qJ(b,y,n.legends,a,u,r,s))}l.forEach(y=>{const b=y.bounds;if(b.equals(y._bounds)||(y.bounds=y._bounds,e.dirty(y),y.bounds=b,e.dirty(y)),n.autosize&&(n.autosize.type===g$||n.autosize.type===m$||n.autosize.type===y$))switch(y.orient){case mr:case yr:o.add(b.x1,0).add(b.x2,0);break;case Il:case Vo:o.add(0,b.y1).add(0,b.y2)}else o.union(b)})}o.union(a).union(u),c&&o.union(YJ(e,c,r,s,o)),t.clip&&o.set(0,0,t.width||0,t.height||0),ZJ(e,t,o,n)}function ZJ(e,t,n,i){const r=i.autosize||{},s=r.type;if(e._autosize<1||!s)return;let o=e._width,a=e._height,u=Math.max(0,t.width||0),l=Math.max(0,Math.ceil(-n.x1)),c=Math.max(0,t.height||0),f=Math.max(0,Math.ceil(-n.y1));const d=Math.max(0,Math.ceil(n.x2-u)),h=Math.max(0,Math.ceil(n.y2-c));if(r.contains===kJ){const p=e.padding();o-=p.left+p.right,a-=p.top+p.bottom}s===Q3?(l=0,f=0,u=o,c=a):s===g$?(u=Math.max(0,o-l-d),c=Math.max(0,a-f-h)):s===m$?(u=Math.max(0,o-l-d),a=c+f+h):s===y$?(o=u+l+d,c=Math.max(0,a-f-h)):s===$J&&(o=u+l+d,a=c+f+h),e._resizeView(o,a,u,c,[l,f],r.resize)}const JJ=Object.freeze(Object.defineProperty({__proto__:null,bound:b$,identifier:nv,mark:x$,overlap:_$,render:$$,viewlayout:N$},Symbol.toStringTag,{value:"Module"}));function R$(e){P.call(this,null,e)}ee(R$,P,{transform(e,t){if(this.value&&!e.modified())return t.StopPropagation;var n=t.dataflow.locale(),i=t.fork(t.NO_SOURCE|t.NO_FIELDS),r=this.value,s=e.scale,o=e.count==null?e.values?e.values.length:10:e.count,a=e3(s,o,e.minstep),u=e.format||Ak(n,s,a,e.formatSpecifier,e.formatType,!!e.values),l=e.values?kk(s,e.values,a):t3(s,a);return r&&(i.rem=r),r=l.map((c,f)=>nt({index:f/(l.length-1||1),value:c,label:u(c)})),e.extra&&r.length&&r.push(nt({index:-1,extra:{value:r[0].value},label:""})),i.source=r,i.add=r,this.value=r,i}});function O$(e){P.call(this,null,e)}function QJ(){return nt({})}function eQ(e){const t=sl().test(n=>n.exit);return t.lookup=n=>t.get(e(n)),t}ee(O$,P,{transform(e,t){var n=t.dataflow,i=t.fork(t.NO_SOURCE|t.NO_FIELDS),r=e.item||QJ,s=e.key||_e,o=this.value;return W(i.encode)&&(i.encode=null),o&&(e.modified("key")||t.modified(s))&&q("DataJoin does not support modified key function or fields."),o||(t=t.addAll(),this.value=o=eQ(s)),t.visit(t.ADD,a=>{const u=s(a);let l=o.get(u);l?l.exit?(o.empty--,i.add.push(l)):i.mod.push(l):(l=r(a),o.set(u,l),i.add.push(l)),l.datum=a,l.exit=!1}),t.visit(t.MOD,a=>{const u=s(a),l=o.get(u);l&&(l.datum=a,i.mod.push(l))}),t.visit(t.REM,a=>{const u=s(a),l=o.get(u);a===l.datum&&!l.exit&&(i.rem.push(l),l.exit=!0,++o.empty)}),t.changed(t.ADD_MOD)&&i.modifies("datum"),(t.clean()||e.clean&&o.empty>n.cleanThreshold)&&n.runAfter(o.clean),i}});function L$(e){P.call(this,null,e)}ee(L$,P,{transform(e,t){var n=t.fork(t.ADD_REM),i=e.mod||!1,r=e.encoders,s=t.encode;if(W(s))if(n.changed()||s.every(f=>r[f]))s=s[0],n.encode=null;else return t.StopPropagation;var o=s==="enter",a=r.update||vo,u=r.enter||vo,l=r.exit||vo,c=(s&&!o?r[s]:a)||vo;if(t.changed(t.ADD)&&(t.visit(t.ADD,f=>{u(f,e),a(f,e)}),n.modifies(u.output),n.modifies(a.output),c!==vo&&c!==a&&(t.visit(t.ADD,f=>{c(f,e)}),n.modifies(c.output))),t.changed(t.REM)&&l!==vo&&(t.visit(t.REM,f=>{l(f,e)}),n.modifies(l.output)),o||c!==vo){const f=t.MOD|(e.modified()?t.REFLOW:0);o?(t.visit(f,d=>{const h=u(d,e)||i;(c(d,e)||h)&&n.mod.push(d)}),n.mod.length&&n.modifies(u.output)):t.visit(f,d=>{(c(d,e)||i)&&n.mod.push(d)}),n.mod.length&&n.modifies(c.output)}return n.changed()?n:t.StopPropagation}});function I$(e){P.call(this,[],e)}ee(I$,P,{transform(e,t){if(this.value!=null&&!e.modified())return t.StopPropagation;var n=t.dataflow.locale(),i=t.fork(t.NO_SOURCE|t.NO_FIELDS),r=this.value,s=e.type||ip,o=e.scale,a=+e.limit,u=e3(o,e.count==null?5:e.count,e.minstep),l=!!e.values||s===ip,c=e.format||Dk(n,o,u,s,e.formatSpecifier,e.formatType,l),f=e.values||Fk(o,u),d,h,p,g,m;return r&&(i.rem=r),s===ip?(a&&f.length>a?(t.dataflow.warn("Symbol legend count exceeds limit, filtering items."),r=f.slice(0,a-1),m=!0):r=f,Ie(p=e.size)?(!e.values&&o(r[0])===0&&(r=r.slice(1)),g=r.reduce((y,b)=>Math.max(y,p(b,e)),0)):p=kn(g=p||8),r=r.map((y,b)=>nt({index:b,label:c(y,b,r),value:y,offset:g,size:p(y,e)})),m&&(m=f[r.length],r.push(nt({index:r.length,label:`…${f.length-r.length} entries`,value:m,offset:g,size:p(m,e)})))):s===$X?(d=o.domain(),h=_k(o,d[0],Ge(d)),f.length<3&&!e.values&&d[0]!==Ge(d)&&(f=[d[0],Ge(d)]),r=f.map((y,b)=>nt({index:b,label:c(y,b,f),value:y,perc:h(y)}))):(p=f.length-1,h=PX(o),r=f.map((y,b)=>nt({index:b,label:c(y,b,f),value:y,perc:b?h(y):0,perc2:b===p?1:h(f[b+1])}))),i.source=r,i.add=r,this.value=r,i}});const tQ=e=>e.source.x,nQ=e=>e.source.y,iQ=e=>e.target.x,rQ=e=>e.target.y;function iv(e){P.call(this,{},e)}iv.Definition={type:"LinkPath",metadata:{modifies:!0},params:[{name:"sourceX",type:"field",default:"source.x"},{name:"sourceY",type:"field",default:"source.y"},{name:"targetX",type:"field",default:"target.x"},{name:"targetY",type:"field",default:"target.y"},{name:"orient",type:"enum",default:"vertical",values:["horizontal","vertical","radial"]},{name:"shape",type:"enum",default:"line",values:["line","arc","curve","diagonal","orthogonal"]},{name:"require",type:"signal"},{name:"as",type:"string",default:"path"}]},ee(iv,P,{transform(e,t){var n=e.sourceX||tQ,i=e.sourceY||nQ,r=e.targetX||iQ,s=e.targetY||rQ,o=e.as||"path",a=e.orient||"vertical",u=e.shape||"line",l=U$.get(u+"-"+a)||U$.get(u);return l||q("LinkPath unsupported type: "+e.shape+(e.orient?"-"+e.orient:"")),t.visit(t.SOURCE,c=>{c[o]=l(n(c),i(c),r(c),s(c))}),t.reflow(e.modified()).modifies(o)}});const P$=(e,t,n,i)=>"M"+e+","+t+"L"+n+","+i,sQ=(e,t,n,i)=>P$(t*Math.cos(e),t*Math.sin(e),i*Math.cos(n),i*Math.sin(n)),z$=(e,t,n,i)=>{var r=n-e,s=i-t,o=Math.hypot(r,s)/2,a=180*Math.atan2(s,r)/Math.PI;return"M"+e+","+t+"A"+o+","+o+" "+a+" 0 1 "+n+","+i},oQ=(e,t,n,i)=>z$(t*Math.cos(e),t*Math.sin(e),i*Math.cos(n),i*Math.sin(n)),B$=(e,t,n,i)=>{const r=n-e,s=i-t,o=.2*(r+s),a=.2*(s-r);return"M"+e+","+t+"C"+(e+o)+","+(t+a)+" "+(n+a)+","+(i-o)+" "+n+","+i},U$=sl({line:P$,"line-radial":sQ,arc:z$,"arc-radial":oQ,curve:B$,"curve-radial":(e,t,n,i)=>B$(t*Math.cos(e),t*Math.sin(e),i*Math.cos(n),i*Math.sin(n)),"orthogonal-horizontal":(e,t,n,i)=>"M"+e+","+t+"V"+i+"H"+n,"orthogonal-vertical":(e,t,n,i)=>"M"+e+","+t+"H"+n+"V"+i,"orthogonal-radial":(e,t,n,i)=>{const r=Math.cos(e),s=Math.sin(e),o=Math.cos(n),a=Math.sin(n),u=Math.abs(n-e)>Math.PI?n<=e:n>e;return"M"+t*r+","+t*s+"A"+t+","+t+" 0 0,"+(u?1:0)+" "+t*o+","+t*a+"L"+i*o+","+i*a},"diagonal-horizontal":(e,t,n,i)=>{const r=(e+n)/2;return"M"+e+","+t+"C"+r+","+t+" "+r+","+i+" "+n+","+i},"diagonal-vertical":(e,t,n,i)=>{const r=(t+i)/2;return"M"+e+","+t+"C"+e+","+r+" "+n+","+r+" "+n+","+i},"diagonal-radial":(e,t,n,i)=>{const r=Math.cos(e),s=Math.sin(e),o=Math.cos(n),a=Math.sin(n),u=(t+i)/2;return"M"+t*r+","+t*s+"C"+u*r+","+u*s+" "+u*o+","+u*a+" "+i*o+","+i*a}});function rv(e){P.call(this,null,e)}rv.Definition={type:"Pie",metadata:{modifies:!0},params:[{name:"field",type:"field"},{name:"startAngle",type:"number",default:0},{name:"endAngle",type:"number",default:6.283185307179586},{name:"sort",type:"boolean",default:!1},{name:"as",type:"string",array:!0,length:2,default:["startAngle","endAngle"]}]},ee(rv,P,{transform(e,t){var n=e.as||["startAngle","endAngle"],i=n[0],r=n[1],s=e.field||tl,o=e.startAngle||0,a=e.endAngle!=null?e.endAngle:2*Math.PI,u=t.source,l=u.map(s),c=l.length,f=o,d=(a-o)/g8(l),h=Ei(c),p,g,m;for(e.sort&&h.sort((y,b)=>l[y]-l[b]),p=0;p-1)return i;var r=t.domain,s=e.type,o=t.zero||t.zero===void 0&&uQ(e),a,u;if(!r)return 0;if((o||t.domainMin!=null||t.domainMax!=null||t.domainMid!=null)&&(a=(r=r.slice()).length-1||1,o&&(r[0]>0&&(r[0]=0),r[a]<0&&(r[a]=0)),t.domainMin!=null&&(r[0]=t.domainMin),t.domainMax!=null&&(r[a]=t.domainMax),t.domainMid!=null)){u=t.domainMid;const l=u>r[a]?a+1:ur+(s<0?-1:s>0?1:0),0));i!==t.length&&n.warn("Log scale domain includes zero: "+Q(t))}return t}function gQ(e,t,n){let i=t.bins;if(i&&!W(i)){const r=e.domain(),s=r[0],o=Ge(r),a=i.step;let u=i.start==null?s:i.start,l=i.stop==null?o:i.stop;a||q("Scale bins parameter missing step property."),uo&&(l=a*Math.floor(o/a)),i=Ei(u,l+a/2,a)}return i?e.bins=i:e.bins&&delete e.bins,e.type===Vb&&(i?!t.domain&&!t.domainRaw&&(e.domain(i),n=i.length):e.bins=e.domain()),n}function mQ(e,t,n){var i=e.type,r=t.round||!1,s=t.range;if(t.rangeStep!=null)s=yQ(i,t,n);else if(t.scheme&&(s=bQ(i,t,n),Ie(s))){if(e.interpolator)return e.interpolator(s);q(`Scale type ${i} does not support interpolating color schemes.`)}if(s&&yk(i))return e.interpolator(np(sv(s,t.reverse),t.interpolate,t.interpolateGamma));s&&t.interpolate&&e.interpolate?e.interpolate(Jb(t.interpolate,t.interpolateGamma)):Ie(e.round)?e.round(r):Ie(e.rangeRound)&&e.interpolate(r?kf:Lo),s&&e.range(sv(s,t.reverse))}function yQ(e,t,n){e!==ck&&e!==Gb&&q("Only band and point scales support rangeStep.");var i=(t.paddingOuter!=null?t.paddingOuter:t.padding)||0,r=e===Gb?1:(t.paddingInner!=null?t.paddingInner:t.padding)||0;return[0,t.rangeStep*Wb(n,r,i)]}function bQ(e,t,n){var i=t.schemeExtent,r,s;return W(t.scheme)?s=np(t.scheme,t.interpolate,t.interpolateGamma):(r=t.scheme.toLowerCase(),s=Qb(r),s||q(`Unrecognized scheme name: ${t.scheme}`)),n=e===ep?n+1:e===Vb?n-1:e===Fl||e===Q0?+t.schemeCount||aQ:n,yk(e)?H$(s,i,t.reverse):Ie(s)?xk(H$(s,i),n):e===Hb?s:s.slice(0,n)}function H$(e,t,n){return Ie(e)&&(t||n)?vk(e,sv(t||[0,1],n)):e}function sv(e,t){return t?e.slice().reverse():e}function G$(e){P.call(this,null,e)}ee(G$,P,{transform(e,t){const n=e.modified("sort")||t.changed(t.ADD)||t.modified(e.sort.fields)||t.modified("datum");return n&&t.source.sort(Ta(e.sort)),this.modified(n),t}});const V$="zero",Y$="center",X$="normalize",K$=["y0","y1"];function ov(e){P.call(this,null,e)}ov.Definition={type:"Stack",metadata:{modifies:!0},params:[{name:"field",type:"field"},{name:"groupby",type:"field",array:!0},{name:"sort",type:"compare"},{name:"offset",type:"enum",default:V$,values:[V$,Y$,X$]},{name:"as",type:"string",array:!0,length:2,default:K$}]},ee(ov,P,{transform(e,t){var n=e.as||K$,i=n[0],r=n[1],s=Ta(e.sort),o=e.field||tl,a=e.offset===Y$?vQ:e.offset===X$?xQ:_Q,u,l,c,f;for(u=wQ(t.source,e.groupby,s,o),l=0,c=u.length,f=u.max;lg(c),o,a,u,l,c,f,d,h,p;if(t==null)r.push(e.slice());else for(o={},a=0,u=e.length;ap&&(p=h),n&&d.sort(n)}return r.max=p,r}const EQ=Object.freeze(Object.defineProperty({__proto__:null,axisticks:R$,datajoin:O$,encode:L$,legendentries:I$,linkpath:iv,pie:rv,scale:q$,sortitems:G$,stack:ov},Symbol.toStringTag,{value:"Module"}));var Ae=1e-6,av=1e-12,je=Math.PI,Mt=je/2,jp=je/4,jn=je*2,It=180/je,ze=je/180,Ve=Math.abs,Bl=Math.atan,Yi=Math.atan2,ke=Math.cos,qp=Math.ceil,Z$=Math.exp,uv=Math.hypot,Wp=Math.log,lv=Math.pow,we=Math.sin,Xi=Math.sign||function(e){return e>0?1:e<0?-1:0},qn=Math.sqrt,cv=Math.tan;function J$(e){return e>1?0:e<-1?je:Math.acos(e)}function ui(e){return e>1?Mt:e<-1?-Mt:Math.asin(e)}function dn(){}function Hp(e,t){e&&eS.hasOwnProperty(e.type)&&eS[e.type](e,t)}var Q$={Feature:function(e,t){Hp(e.geometry,t)},FeatureCollection:function(e,t){for(var n=e.features,i=-1,r=n.length;++i=0?1:-1,r=i*n,s=ke(t),o=we(t),a=pv*o,u=hv*s+a*ke(r),l=a*i*we(r);Gp.add(Yi(l,u)),dv=e,hv=s,pv=o}function $Q(e){return Vp=new zn,js(e,is),Vp*2}function Yp(e){return[Yi(e[1],e[0]),ui(e[2])]}function Xa(e){var t=e[0],n=e[1],i=ke(n);return[i*ke(t),i*we(t),we(n)]}function Xp(e,t){return e[0]*t[0]+e[1]*t[1]+e[2]*t[2]}function Ul(e,t){return[e[1]*t[2]-e[2]*t[1],e[2]*t[0]-e[0]*t[2],e[0]*t[1]-e[1]*t[0]]}function gv(e,t){e[0]+=t[0],e[1]+=t[1],e[2]+=t[2]}function Kp(e,t){return[e[0]*t,e[1]*t,e[2]*t]}function Zp(e){var t=qn(e[0]*e[0]+e[1]*e[1]+e[2]*e[2]);e[0]/=t,e[1]/=t,e[2]/=t}var St,li,Nt,Si,Ka,sS,oS,jl,Kf,Ko,qs,Ws={point:mv,lineStart:uS,lineEnd:lS,polygonStart:function(){Ws.point=cS,Ws.lineStart=SQ,Ws.lineEnd=FQ,Kf=new zn,is.polygonStart()},polygonEnd:function(){is.polygonEnd(),Ws.point=mv,Ws.lineStart=uS,Ws.lineEnd=lS,Gp<0?(St=-(Nt=180),li=-(Si=90)):Kf>Ae?Si=90:Kf<-1e-6&&(li=-90),qs[0]=St,qs[1]=Nt},sphere:function(){St=-(Nt=180),li=-(Si=90)}};function mv(e,t){Ko.push(qs=[St=e,Nt=e]),tSi&&(Si=t)}function aS(e,t){var n=Xa([e*ze,t*ze]);if(jl){var i=Ul(jl,n),r=[i[1],-i[0],0],s=Ul(r,i);Zp(s),s=Yp(s);var o=e-Ka,a=o>0?1:-1,u=s[0]*It*a,l,c=Ve(o)>180;c^(a*KaSi&&(Si=l)):(u=(u+360)%360-180,c^(a*KaSi&&(Si=t))),c?eFi(St,Nt)&&(Nt=e):Fi(e,Nt)>Fi(St,Nt)&&(St=e):Nt>=St?(eNt&&(Nt=e)):e>Ka?Fi(St,e)>Fi(St,Nt)&&(Nt=e):Fi(e,Nt)>Fi(St,Nt)&&(St=e)}else Ko.push(qs=[St=e,Nt=e]);tSi&&(Si=t),jl=n,Ka=e}function uS(){Ws.point=aS}function lS(){qs[0]=St,qs[1]=Nt,Ws.point=mv,jl=null}function cS(e,t){if(jl){var n=e-Ka;Kf.add(Ve(n)>180?n+(n>0?360:-360):n)}else sS=e,oS=t;is.point(e,t),aS(e,t)}function SQ(){is.lineStart()}function FQ(){cS(sS,oS),is.lineEnd(),Ve(Kf)>Ae&&(St=-(Nt=180)),qs[0]=St,qs[1]=Nt,jl=null}function Fi(e,t){return(t-=e)<0?t+360:t}function DQ(e,t){return e[0]-t[0]}function fS(e,t){return e[0]<=e[1]?e[0]<=t&&t<=e[1]:tFi(i[0],i[1])&&(i[1]=r[1]),Fi(r[0],i[1])>Fi(i[0],i[1])&&(i[0]=r[0])):s.push(i=r);for(o=-1/0,n=s.length-1,t=0,i=s[n];t<=n;i=r,++t)r=s[t],(a=Fi(i[1],r[0]))>o&&(o=a,St=r[0],Nt=i[1])}return Ko=qs=null,St===1/0||li===1/0?[[NaN,NaN],[NaN,NaN]]:[[St,li],[Nt,Si]]}var Zf,Jp,Qp,eg,tg,ng,ig,rg,yv,bv,vv,dS,hS,Wn,Hn,Gn,br={sphere:dn,point:xv,lineStart:pS,lineEnd:gS,polygonStart:function(){br.lineStart=RQ,br.lineEnd=OQ},polygonEnd:function(){br.lineStart=pS,br.lineEnd=gS}};function xv(e,t){e*=ze,t*=ze;var n=ke(t);Jf(n*ke(e),n*we(e),we(t))}function Jf(e,t,n){++Zf,Qp+=(e-Qp)/Zf,eg+=(t-eg)/Zf,tg+=(n-tg)/Zf}function pS(){br.point=MQ}function MQ(e,t){e*=ze,t*=ze;var n=ke(t);Wn=n*ke(e),Hn=n*we(e),Gn=we(t),br.point=NQ,Jf(Wn,Hn,Gn)}function NQ(e,t){e*=ze,t*=ze;var n=ke(t),i=n*ke(e),r=n*we(e),s=we(t),o=Yi(qn((o=Hn*s-Gn*r)*o+(o=Gn*i-Wn*s)*o+(o=Wn*r-Hn*i)*o),Wn*i+Hn*r+Gn*s);Jp+=o,ng+=o*(Wn+(Wn=i)),ig+=o*(Hn+(Hn=r)),rg+=o*(Gn+(Gn=s)),Jf(Wn,Hn,Gn)}function gS(){br.point=xv}function RQ(){br.point=LQ}function OQ(){mS(dS,hS),br.point=xv}function LQ(e,t){dS=e,hS=t,e*=ze,t*=ze,br.point=mS;var n=ke(t);Wn=n*ke(e),Hn=n*we(e),Gn=we(t),Jf(Wn,Hn,Gn)}function mS(e,t){e*=ze,t*=ze;var n=ke(t),i=n*ke(e),r=n*we(e),s=we(t),o=Hn*s-Gn*r,a=Gn*i-Wn*s,u=Wn*r-Hn*i,l=uv(o,a,u),c=ui(l),f=l&&-c/l;yv.add(f*o),bv.add(f*a),vv.add(f*u),Jp+=c,ng+=c*(Wn+(Wn=i)),ig+=c*(Hn+(Hn=r)),rg+=c*(Gn+(Gn=s)),Jf(Wn,Hn,Gn)}function IQ(e){Zf=Jp=Qp=eg=tg=ng=ig=rg=0,yv=new zn,bv=new zn,vv=new zn,js(e,br);var t=+yv,n=+bv,i=+vv,r=uv(t,n,i);return rje&&(e-=Math.round(e/jn)*jn),[e,t]}wv.invert=wv;function yS(e,t,n){return(e%=jn)?t||n?_v(vS(e),xS(t,n)):vS(e):t||n?xS(t,n):wv}function bS(e){return function(t,n){return t+=e,Ve(t)>je&&(t-=Math.round(t/jn)*jn),[t,n]}}function vS(e){var t=bS(e);return t.invert=bS(-e),t}function xS(e,t){var n=ke(e),i=we(e),r=ke(t),s=we(t);function o(a,u){var l=ke(u),c=ke(a)*l,f=we(a)*l,d=we(u),h=d*n+c*i;return[Yi(f*r-h*s,c*n-d*i),ui(h*r+f*s)]}return o.invert=function(a,u){var l=ke(u),c=ke(a)*l,f=we(a)*l,d=we(u),h=d*r-f*s;return[Yi(f*r+d*s,c*n+h*i),ui(h*n-c*i)]},o}function PQ(e){e=yS(e[0]*ze,e[1]*ze,e.length>2?e[2]*ze:0);function t(n){return n=e(n[0]*ze,n[1]*ze),n[0]*=It,n[1]*=It,n}return t.invert=function(n){return n=e.invert(n[0]*ze,n[1]*ze),n[0]*=It,n[1]*=It,n},t}function zQ(e,t,n,i,r,s){if(n){var o=ke(t),a=we(t),u=i*n;r==null?(r=t+i*jn,s=t-u/2):(r=_S(o,r),s=_S(o,s),(i>0?rs)&&(r+=i*jn));for(var l,c=r;i>0?c>s:c1&&e.push(e.pop().concat(e.shift()))},result:function(){var n=e;return e=[],t=null,n}}}function sg(e,t){return Ve(e[0]-t[0])=0;--a)r.point((f=c[a])[0],f[1]);else i(d.x,d.p.x,-1,r);d=d.p}d=d.o,c=d.z,h=!h}while(!d.v);r.lineEnd()}}}function CS(e){if(t=e.length){for(var t,n=0,i=e[0],r;++n=0?1:-1,S=k*C,$=S>je,O=m*E;if(u.add(Yi(O*k*we(S),y*w+O*ke(S))),o+=$?C+k*jn:C,$^p>=n^x>=n){var R=Ul(Xa(h),Xa(v));Zp(R);var F=Ul(s,R);Zp(F);var A=($^C>=0?-1:1)*ui(F[2]);(i>A||i===A&&(R[0]||R[1]))&&(a+=$^C>=0?1:-1)}}return(o<-1e-6||o0){for(u||(r.polygonStart(),u=!0),r.lineStart(),E=0;E1&&x&2&&_.push(_.pop().concat(_.shift())),c.push(_.filter(UQ))}}return d}}function UQ(e){return e.length>1}function jQ(e,t){return((e=e.x)[0]<0?e[1]-Mt-Ae:Mt-e[1])-((t=t.x)[0]<0?t[1]-Mt-Ae:Mt-t[1])}const AS=kS(function(){return!0},qQ,HQ,[-je,-Mt]);function qQ(e){var t=NaN,n=NaN,i=NaN,r;return{lineStart:function(){e.lineStart(),r=1},point:function(s,o){var a=s>0?je:-je,u=Ve(s-t);Ve(u-je)0?Mt:-Mt),e.point(i,n),e.lineEnd(),e.lineStart(),e.point(a,n),e.point(s,n),r=0):i!==a&&u>=je&&(Ve(t-i)Ae?Bl((we(t)*(s=ke(i))*we(n)-we(i)*(r=ke(t))*we(e))/(r*s*o)):(t+i)/2}function HQ(e,t,n,i){var r;if(e==null)r=n*Mt,i.point(-je,r),i.point(0,r),i.point(je,r),i.point(je,0),i.point(je,-r),i.point(0,-r),i.point(-je,-r),i.point(-je,0),i.point(-je,r);else if(Ve(e[0]-t[0])>Ae){var s=e[0]0,r=Ve(t)>Ae;function s(c,f,d,h){zQ(h,e,n,d,c,f)}function o(c,f){return ke(c)*ke(f)>t}function a(c){var f,d,h,p,g;return{lineStart:function(){p=h=!1,g=1},point:function(m,y){var b=[m,y],v,x=o(m,y),_=i?x?0:l(m,y):x?l(m+(m<0?je:-je),y):0;if(!f&&(p=h=x)&&c.lineStart(),x!==h&&(v=u(f,b),(!v||sg(f,v)||sg(b,v))&&(b[2]=1)),x!==h)g=0,x?(c.lineStart(),v=u(b,f),c.point(v[0],v[1])):(v=u(f,b),c.point(v[0],v[1],2),c.lineEnd()),f=v;else if(r&&f&&i^x){var E;!(_&d)&&(E=u(b,f,!0))&&(g=0,i?(c.lineStart(),c.point(E[0][0],E[0][1]),c.point(E[1][0],E[1][1]),c.lineEnd()):(c.point(E[1][0],E[1][1]),c.lineEnd(),c.lineStart(),c.point(E[0][0],E[0][1],3)))}x&&(!f||!sg(f,b))&&c.point(b[0],b[1]),f=b,h=x,d=_},lineEnd:function(){h&&c.lineEnd(),f=null},clean:function(){return g|(p&&h)<<1}}}function u(c,f,d){var h=Xa(c),p=Xa(f),g=[1,0,0],m=Ul(h,p),y=Xp(m,m),b=m[0],v=y-b*b;if(!v)return!d&&c;var x=t*y/v,_=-t*b/v,E=Ul(g,m),w=Kp(g,x),C=Kp(m,_);gv(w,C);var k=E,S=Xp(w,k),$=Xp(k,k),O=S*S-$*(Xp(w,w)-1);if(!(O<0)){var R=qn(O),F=Kp(k,(-S-R)/$);if(gv(F,w),F=Yp(F),!d)return F;var A=c[0],T=f[0],B=c[1],H=f[1],z;T0^F[1]<(Ve(F[0]-A)je^(A<=F[0]&&F[0]<=T)){var Te=Kp(k,(-S+R)/$);return gv(Te,w),[F,Yp(Te)]}}}function l(c,f){var d=i?e:je-e,h=0;return c<-d?h|=1:c>d&&(h|=2),f<-d?h|=4:f>d&&(h|=8),h}return kS(o,a,s,i?[0,-e]:[-je,e-je])}function VQ(e,t,n,i,r,s){var o=e[0],a=e[1],u=t[0],l=t[1],c=0,f=1,d=u-o,h=l-a,p;if(p=n-o,!(!d&&p>0)){if(p/=d,d<0){if(p0){if(p>f)return;p>c&&(c=p)}if(p=r-o,!(!d&&p<0)){if(p/=d,d<0){if(p>f)return;p>c&&(c=p)}else if(d>0){if(p0)){if(p/=h,h<0){if(p0){if(p>f)return;p>c&&(c=p)}if(p=s-a,!(!h&&p<0)){if(p/=h,h<0){if(p>f)return;p>c&&(c=p)}else if(h>0){if(p0&&(e[0]=o+c*d,e[1]=a+c*h),f<1&&(t[0]=o+f*d,t[1]=a+f*h),!0}}}}}var ag=1e9,ug=-1e9;function $S(e,t,n,i){function r(l,c){return e<=l&&l<=n&&t<=c&&c<=i}function s(l,c,f,d){var h=0,p=0;if(l==null||(h=o(l,f))!==(p=o(c,f))||u(l,c)<0^f>0)do d.point(h===0||h===3?e:n,h>1?i:t);while((h=(h+f+4)%4)!==p);else d.point(c[0],c[1])}function o(l,c){return Ve(l[0]-e)0?0:3:Ve(l[0]-n)0?2:1:Ve(l[1]-t)0?1:0:c>0?3:2}function a(l,c){return u(l.x,c.x)}function u(l,c){var f=o(l,1),d=o(c,1);return f!==d?f-d:f===0?c[1]-l[1]:f===1?l[0]-c[0]:f===2?l[1]-c[1]:c[0]-l[0]}return function(l){var c=l,f=wS(),d,h,p,g,m,y,b,v,x,_,E,w={point:C,lineStart:O,lineEnd:R,polygonStart:S,polygonEnd:$};function C(A,T){r(A,T)&&c.point(A,T)}function k(){for(var A=0,T=0,B=h.length;Ti&&(ct-de)*(i-Te)>($e-Te)*(e-de)&&++A:$e<=i&&(ct-de)*(i-Te)<($e-Te)*(e-de)&&--A;return A}function S(){c=f,d=[],h=[],E=!0}function $(){var A=k(),T=E&&A,B=(d=p8(d)).length;(T||B)&&(l.polygonStart(),T&&(l.lineStart(),s(null,null,1,l),l.lineEnd()),B&&ES(d,a,A,s,l),l.polygonEnd()),c=l,d=h=p=null}function O(){w.point=F,h&&h.push(p=[]),_=!0,x=!1,b=v=NaN}function R(){d&&(F(g,m),y&&x&&f.rejoin(),d.push(f.result())),w.point=C,x&&c.lineEnd()}function F(A,T){var B=r(A,T);if(h&&p.push([A,T]),_)g=A,m=T,y=B,_=!1,B&&(c.lineStart(),c.point(A,T));else if(B&&x)c.point(A,T);else{var H=[b=Math.max(ug,Math.min(ag,b)),v=Math.max(ug,Math.min(ag,v))],z=[A=Math.max(ug,Math.min(ag,A)),T=Math.max(ug,Math.min(ag,T))];VQ(H,z,e,t,n,i)?(x||(c.lineStart(),c.point(H[0],H[1])),c.point(z[0],z[1]),B||c.lineEnd(),E=!1):B&&(c.lineStart(),c.point(A,T),E=!1)}b=A,v=T,x=B}return w}}function SS(e,t,n){var i=Ei(e,t-Ae,n).concat(t);return function(r){return i.map(function(s){return[r,s]})}}function FS(e,t,n){var i=Ei(e,t-Ae,n).concat(t);return function(r){return i.map(function(s){return[s,r]})}}function YQ(){var e,t,n,i,r,s,o,a,u=10,l=u,c=90,f=360,d,h,p,g,m=2.5;function y(){return{type:"MultiLineString",coordinates:b()}}function b(){return Ei(qp(i/c)*c,n,c).map(p).concat(Ei(qp(a/f)*f,o,f).map(g)).concat(Ei(qp(t/u)*u,e,u).filter(function(v){return Ve(v%c)>Ae}).map(d)).concat(Ei(qp(s/l)*l,r,l).filter(function(v){return Ve(v%f)>Ae}).map(h))}return y.lines=function(){return b().map(function(v){return{type:"LineString",coordinates:v}})},y.outline=function(){return{type:"Polygon",coordinates:[p(i).concat(g(o).slice(1),p(n).reverse().slice(1),g(a).reverse().slice(1))]}},y.extent=function(v){return arguments.length?y.extentMajor(v).extentMinor(v):y.extentMinor()},y.extentMajor=function(v){return arguments.length?(i=+v[0][0],n=+v[1][0],a=+v[0][1],o=+v[1][1],i>n&&(v=i,i=n,n=v),a>o&&(v=a,a=o,o=v),y.precision(m)):[[i,a],[n,o]]},y.extentMinor=function(v){return arguments.length?(t=+v[0][0],e=+v[1][0],s=+v[0][1],r=+v[1][1],t>e&&(v=t,t=e,e=v),s>r&&(v=s,s=r,r=v),y.precision(m)):[[t,s],[e,r]]},y.step=function(v){return arguments.length?y.stepMajor(v).stepMinor(v):y.stepMinor()},y.stepMajor=function(v){return arguments.length?(c=+v[0],f=+v[1],y):[c,f]},y.stepMinor=function(v){return arguments.length?(u=+v[0],l=+v[1],y):[u,l]},y.precision=function(v){return arguments.length?(m=+v,d=SS(s,r,90),h=FS(t,e,m),p=SS(a,o,90),g=FS(i,n,m),y):m},y.extentMajor([[-180,-90+Ae],[180,90-Ae]]).extentMinor([[-180,-80-Ae],[180,80+Ae]])}const Qf=e=>e;var Cv=new zn,kv=new zn,DS,TS,Av,$v,Hs={point:dn,lineStart:dn,lineEnd:dn,polygonStart:function(){Hs.lineStart=XQ,Hs.lineEnd=ZQ},polygonEnd:function(){Hs.lineStart=Hs.lineEnd=Hs.point=dn,Cv.add(Ve(kv)),kv=new zn},result:function(){var e=Cv/2;return Cv=new zn,e}};function XQ(){Hs.point=KQ}function KQ(e,t){Hs.point=MS,DS=Av=e,TS=$v=t}function MS(e,t){kv.add($v*e-Av*t),Av=e,$v=t}function ZQ(){MS(DS,TS)}var ql=1/0,lg=ql,ed=-ql,cg=ed,fg={point:JQ,lineStart:dn,lineEnd:dn,polygonStart:dn,polygonEnd:dn,result:function(){var e=[[ql,lg],[ed,cg]];return ed=cg=-(lg=ql=1/0),e}};function JQ(e,t){eed&&(ed=e),tcg&&(cg=t)}var Sv=0,Fv=0,td=0,dg=0,hg=0,Wl=0,Dv=0,Tv=0,nd=0,NS,RS,rs,ss,Ki={point:Za,lineStart:OS,lineEnd:LS,polygonStart:function(){Ki.lineStart=tee,Ki.lineEnd=nee},polygonEnd:function(){Ki.point=Za,Ki.lineStart=OS,Ki.lineEnd=LS},result:function(){var e=nd?[Dv/nd,Tv/nd]:Wl?[dg/Wl,hg/Wl]:td?[Sv/td,Fv/td]:[NaN,NaN];return Sv=Fv=td=dg=hg=Wl=Dv=Tv=nd=0,e}};function Za(e,t){Sv+=e,Fv+=t,++td}function OS(){Ki.point=QQ}function QQ(e,t){Ki.point=eee,Za(rs=e,ss=t)}function eee(e,t){var n=e-rs,i=t-ss,r=qn(n*n+i*i);dg+=r*(rs+e)/2,hg+=r*(ss+t)/2,Wl+=r,Za(rs=e,ss=t)}function LS(){Ki.point=Za}function tee(){Ki.point=iee}function nee(){IS(NS,RS)}function iee(e,t){Ki.point=IS,Za(NS=rs=e,RS=ss=t)}function IS(e,t){var n=e-rs,i=t-ss,r=qn(n*n+i*i);dg+=r*(rs+e)/2,hg+=r*(ss+t)/2,Wl+=r,r=ss*e-rs*t,Dv+=r*(rs+e),Tv+=r*(ss+t),nd+=r*3,Za(rs=e,ss=t)}function PS(e){this._context=e}PS.prototype={_radius:4.5,pointRadius:function(e){return this._radius=e,this},polygonStart:function(){this._line=0},polygonEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){this._line===0&&this._context.closePath(),this._point=NaN},point:function(e,t){switch(this._point){case 0:{this._context.moveTo(e,t),this._point=1;break}case 1:{this._context.lineTo(e,t);break}default:{this._context.moveTo(e+this._radius,t),this._context.arc(e,t,this._radius,0,jn);break}}},result:dn};var Mv=new zn,Nv,zS,BS,id,rd,sd={point:dn,lineStart:function(){sd.point=ree},lineEnd:function(){Nv&&US(zS,BS),sd.point=dn},polygonStart:function(){Nv=!0},polygonEnd:function(){Nv=null},result:function(){var e=+Mv;return Mv=new zn,e}};function ree(e,t){sd.point=US,zS=id=e,BS=rd=t}function US(e,t){id-=e,rd-=t,Mv.add(qn(id*id+rd*rd)),id=e,rd=t}let jS,pg,qS,WS;class HS{constructor(t){this._append=t==null?GS:see(t),this._radius=4.5,this._=""}pointRadius(t){return this._radius=+t,this}polygonStart(){this._line=0}polygonEnd(){this._line=NaN}lineStart(){this._point=0}lineEnd(){this._line===0&&(this._+="Z"),this._point=NaN}point(t,n){switch(this._point){case 0:{this._append`M${t},${n}`,this._point=1;break}case 1:{this._append`L${t},${n}`;break}default:{if(this._append`M${t},${n}`,this._radius!==qS||this._append!==pg){const i=this._radius,r=this._;this._="",this._append`m0,${i}a${i},${i} 0 1,1 0,${-2*i}a${i},${i} 0 1,1 0,${2*i}z`,qS=i,pg=this._append,WS=this._,this._=r}this._+=WS;break}}}result(){const t=this._;return this._="",t.length?t:null}}function GS(e){let t=1;this._+=e[0];for(const n=e.length;t=0))throw new RangeError(`invalid digits: ${e}`);if(t>15)return GS;if(t!==jS){const n=10**t;jS=t,pg=function(r){let s=1;this._+=r[0];for(const o=r.length;s=0))throw new RangeError(`invalid digits: ${a}`);n=u}return t===null&&(s=new HS(n)),o},o.projection(e).digits(n).context(t)}function gg(e){return function(t){var n=new Rv;for(var i in e)n[i]=e[i];return n.stream=t,n}}function Rv(){}Rv.prototype={constructor:Rv,point:function(e,t){this.stream.point(e,t)},sphere:function(){this.stream.sphere()},lineStart:function(){this.stream.lineStart()},lineEnd:function(){this.stream.lineEnd()},polygonStart:function(){this.stream.polygonStart()},polygonEnd:function(){this.stream.polygonEnd()}};function Ov(e,t,n){var i=e.clipExtent&&e.clipExtent();return e.scale(150).translate([0,0]),i!=null&&e.clipExtent(null),js(n,e.stream(fg)),t(fg.result()),i!=null&&e.clipExtent(i),e}function mg(e,t,n){return Ov(e,function(i){var r=t[1][0]-t[0][0],s=t[1][1]-t[0][1],o=Math.min(r/(i[1][0]-i[0][0]),s/(i[1][1]-i[0][1])),a=+t[0][0]+(r-o*(i[1][0]+i[0][0]))/2,u=+t[0][1]+(s-o*(i[1][1]+i[0][1]))/2;e.scale(150*o).translate([a,u])},n)}function Lv(e,t,n){return mg(e,[[0,0],t],n)}function Iv(e,t,n){return Ov(e,function(i){var r=+t,s=r/(i[1][0]-i[0][0]),o=(r-s*(i[1][0]+i[0][0]))/2,a=-s*i[0][1];e.scale(150*s).translate([o,a])},n)}function Pv(e,t,n){return Ov(e,function(i){var r=+t,s=r/(i[1][1]-i[0][1]),o=-s*i[0][0],a=(r-s*(i[1][1]+i[0][1]))/2;e.scale(150*s).translate([o,a])},n)}var YS=16,oee=ke(30*ze);function XS(e,t){return+t?uee(e,t):aee(e)}function aee(e){return gg({point:function(t,n){t=e(t,n),this.stream.point(t[0],t[1])}})}function uee(e,t){function n(i,r,s,o,a,u,l,c,f,d,h,p,g,m){var y=l-i,b=c-r,v=y*y+b*b;if(v>4*t&&g--){var x=o+d,_=a+h,E=u+p,w=qn(x*x+_*_+E*E),C=ui(E/=w),k=Ve(Ve(E)-1)t||Ve((y*R+b*F)/v-.5)>.3||o*d+a*h+u*p2?A[2]%360*ze:0,R()):[a*It,u*It,l*It]},$.angle=function(A){return arguments.length?(f=A%360*ze,R()):f*It},$.reflectX=function(A){return arguments.length?(d=A?-1:1,R()):d<0},$.reflectY=function(A){return arguments.length?(h=A?-1:1,R()):h<0},$.precision=function(A){return arguments.length?(E=XS(w,_=A*A),F()):qn(_)},$.fitExtent=function(A,T){return mg($,A,T)},$.fitSize=function(A,T){return Lv($,A,T)},$.fitWidth=function(A,T){return Iv($,A,T)},$.fitHeight=function(A,T){return Pv($,A,T)};function R(){var A=KS(n,0,0,d,h,f).apply(null,t(s,o)),T=KS(n,i-A[0],r-A[1],d,h,f);return c=yS(a,u,l),w=_v(t,T),C=_v(c,w),E=XS(w,_),F()}function F(){return k=S=null,$}return function(){return t=e.apply(this,arguments),$.invert=t.invert&&O,R()}}function zv(e){var t=0,n=je/3,i=ZS(e),r=i(t,n);return r.parallels=function(s){return arguments.length?i(t=s[0]*ze,n=s[1]*ze):[t*It,n*It]},r}function dee(e){var t=ke(e);function n(i,r){return[i*t,we(r)/t]}return n.invert=function(i,r){return[i/t,ui(r*t)]},n}function hee(e,t){var n=we(e),i=(n+we(t))/2;if(Ve(i)=.12&&m<.234&&g>=-.425&&g<-.214?r:m>=.166&&m<.234&&g>=-.214&&g<-.115?o:n).invert(d)},c.stream=function(d){return e&&t===d?e:e=pee([n.stream(t=d),r.stream(d),o.stream(d)])},c.precision=function(d){return arguments.length?(n.precision(d),r.precision(d),o.precision(d),f()):n.precision()},c.scale=function(d){return arguments.length?(n.scale(d),r.scale(d*.35),o.scale(d),c.translate(n.translate())):n.scale()},c.translate=function(d){if(!arguments.length)return n.translate();var h=n.scale(),p=+d[0],g=+d[1];return i=n.translate(d).clipExtent([[p-.455*h,g-.238*h],[p+.455*h,g+.238*h]]).stream(l),s=r.translate([p-.307*h,g+.201*h]).clipExtent([[p-.425*h+Ae,g+.12*h+Ae],[p-.214*h-Ae,g+.234*h-Ae]]).stream(l),a=o.translate([p-.205*h,g+.212*h]).clipExtent([[p-.214*h+Ae,g+.166*h+Ae],[p-.115*h-Ae,g+.234*h-Ae]]).stream(l),f()},c.fitExtent=function(d,h){return mg(c,d,h)},c.fitSize=function(d,h){return Lv(c,d,h)},c.fitWidth=function(d,h){return Iv(c,d,h)},c.fitHeight=function(d,h){return Pv(c,d,h)};function f(){return e=t=null,c}return c.scale(1070)}function QS(e){return function(t,n){var i=ke(t),r=ke(n),s=e(i*r);return s===1/0?[2,0]:[s*r*we(t),s*we(n)]}}function od(e){return function(t,n){var i=qn(t*t+n*n),r=e(i),s=we(r),o=ke(r);return[Yi(t*s,i*o),ui(i&&n*s/i)]}}var eF=QS(function(e){return qn(2/(1+e))});eF.invert=od(function(e){return 2*ui(e/2)});function mee(){return os(eF).scale(124.75).clipAngle(180-.001)}var tF=QS(function(e){return(e=J$(e))&&e/we(e)});tF.invert=od(function(e){return e});function yee(){return os(tF).scale(79.4188).clipAngle(180-.001)}function bg(e,t){return[e,Wp(cv((Mt+t)/2))]}bg.invert=function(e,t){return[e,2*Bl(Z$(t))-Mt]};function bee(){return nF(bg).scale(961/jn)}function nF(e){var t=os(e),n=t.center,i=t.scale,r=t.translate,s=t.clipExtent,o=null,a,u,l;t.scale=function(f){return arguments.length?(i(f),c()):i()},t.translate=function(f){return arguments.length?(r(f),c()):r()},t.center=function(f){return arguments.length?(n(f),c()):n()},t.clipExtent=function(f){return arguments.length?(f==null?o=a=u=l=null:(o=+f[0][0],a=+f[0][1],u=+f[1][0],l=+f[1][1]),c()):o==null?null:[[o,a],[u,l]]};function c(){var f=je*i(),d=t(PQ(t.rotate()).invert([0,0]));return s(o==null?[[d[0]-f,d[1]-f],[d[0]+f,d[1]+f]]:e===bg?[[Math.max(d[0]-f,o),a],[Math.min(d[0]+f,u),l]]:[[o,Math.max(d[1]-f,a)],[u,Math.min(d[1]+f,l)]])}return c()}function vg(e){return cv((Mt+e)/2)}function vee(e,t){var n=ke(e),i=e===t?we(e):Wp(n/ke(t))/Wp(vg(t)/vg(e)),r=n*lv(vg(e),i)/i;if(!i)return bg;function s(o,a){r>0?a<-Mt+Ae&&(a=-Mt+Ae):a>Mt-Ae&&(a=Mt-Ae);var u=r/lv(vg(a),i);return[u*we(i*o),r-u*ke(i*o)]}return s.invert=function(o,a){var u=r-a,l=Xi(i)*qn(o*o+u*u),c=Yi(o,Ve(u))*Xi(u);return u*i<0&&(c-=je*Xi(o)*Xi(u)),[c/i,2*Bl(lv(r/l,1/i))-Mt]},s}function xee(){return zv(vee).scale(109.5).parallels([30,30])}function xg(e,t){return[e,t]}xg.invert=xg;function _ee(){return os(xg).scale(152.63)}function wee(e,t){var n=ke(e),i=e===t?we(e):(n-ke(t))/(t-e),r=n/i+e;if(Ve(i)Ae&&--i>0);return[e/(.8707+(s=n*n)*(-.131979+s*(-.013791+s*s*s*(.003971-.001529*s)))),n]};function See(){return os(sF).scale(175.295)}function oF(e,t){return[ke(t)*we(e),we(t)]}oF.invert=od(ui);function Fee(){return os(oF).scale(249.5).clipAngle(90+Ae)}function aF(e,t){var n=ke(t),i=1+ke(e)*n;return[n*we(e)/i,we(t)/i]}aF.invert=od(function(e){return 2*Bl(e)});function Dee(){return os(aF).scale(250).clipAngle(142)}function uF(e,t){return[Wp(cv((Mt+t)/2)),-e]}uF.invert=function(e,t){return[-t,2*Bl(Z$(e))-Mt]};function Tee(){var e=nF(uF),t=e.center,n=e.rotate;return e.center=function(i){return arguments.length?t([-i[1],i[0]]):(i=t(),[i[1],-i[0]])},e.rotate=function(i){return arguments.length?n([i[0],i[1],i.length>2?i[2]+90:90]):(i=n(),[i[0],i[1],i[2]-90])},n([0,0,90]).scale(159.155)}var Mee=Math.abs,Bv=Math.cos,wg=Math.sin,Nee=1e-6,lF=Math.PI,Uv=lF/2,cF=Ree(2);function fF(e){return e>1?Uv:e<-1?-Uv:Math.asin(e)}function Ree(e){return e>0?Math.sqrt(e):0}function Oee(e,t){var n=e*wg(t),i=30,r;do t-=r=(t+wg(t)-n)/(1+Bv(t));while(Mee(r)>Nee&&--i>0);return t/2}function Lee(e,t,n){function i(r,s){return[e*r*Bv(s=Oee(n,s)),t*wg(s)]}return i.invert=function(r,s){return s=fF(s/t),[r/(e*Bv(s)),fF((2*s+wg(2*s))/n)]},i}var Iee=Lee(cF/Uv,cF,lF);function Pee(){return os(Iee).scale(169.529)}const zee=VS(),jv=["clipAngle","clipExtent","scale","translate","center","rotate","parallels","precision","reflectX","reflectY","coefficient","distance","fraction","lobes","parallel","radius","ratio","spacing","tilt"];function Bee(e,t){return function n(){const i=t();return i.type=e,i.path=VS().projection(i),i.copy=i.copy||function(){const r=n();return jv.forEach(s=>{i[s]&&r[s](i[s]())}),r.path.pointRadius(i.path.pointRadius()),r},pk(i)}}function qv(e,t){if(!e||typeof e!="string")throw new Error("Projection type must be a name string.");return e=e.toLowerCase(),arguments.length>1?(Eg[e]=Bee(e,t),this):Eg[e]||null}function dF(e){return e&&e.path||zee}const Eg={albers:JS,albersusa:gee,azimuthalequalarea:mee,azimuthalequidistant:yee,conicconformal:xee,conicequalarea:yg,conicequidistant:Eee,equalEarth:kee,equirectangular:_ee,gnomonic:Aee,identity:$ee,mercator:bee,mollweide:Pee,naturalEarth1:See,orthographic:Fee,stereographic:Dee,transversemercator:Tee};for(const e in Eg)qv(e,Eg[e]);function Uee(){}const Gs=[[],[[[1,1.5],[.5,1]]],[[[1.5,1],[1,1.5]]],[[[1.5,1],[.5,1]]],[[[1,.5],[1.5,1]]],[[[1,1.5],[.5,1]],[[1,.5],[1.5,1]]],[[[1,.5],[1,1.5]]],[[[1,.5],[.5,1]]],[[[.5,1],[1,.5]]],[[[1,1.5],[1,.5]]],[[[.5,1],[1,.5]],[[1.5,1],[1,1.5]]],[[[1.5,1],[1,.5]]],[[[.5,1],[1.5,1]]],[[[1,1.5],[1.5,1]]],[[[.5,1],[1,1.5]]],[]];function hF(){var e=1,t=1,n=a;function i(u,l){return l.map(c=>r(u,c))}function r(u,l){var c=[],f=[];return s(u,l,d=>{n(d,u,l),jee(d)>0?c.push([d]):f.push(d)}),f.forEach(d=>{for(var h=0,p=c.length,g;h=l,Gs[m<<1].forEach(v);++h=l,Gs[g|m<<1].forEach(v);for(Gs[m<<0].forEach(v);++p=l,y=u[p*e]>=l,Gs[m<<1|y<<2].forEach(v);++h=l,b=y,y=u[p*e+h+1]>=l,Gs[g|m<<1|y<<2|b<<3].forEach(v);Gs[m|y<<3].forEach(v)}for(h=-1,y=u[p*e]>=l,Gs[y<<2].forEach(v);++h=l,Gs[y<<2|b<<3].forEach(v);Gs[y<<3].forEach(v);function v(x){var _=[x[0][0]+h,x[0][1]+p],E=[x[1][0]+h,x[1][1]+p],w=o(_),C=o(E),k,S;(k=d[w])?(S=f[C])?(delete d[k.end],delete f[S.start],k===S?(k.ring.push(E),c(k.ring)):f[k.start]=d[S.end]={start:k.start,end:S.end,ring:k.ring.concat(S.ring)}):(delete d[k.end],k.ring.push(E),d[k.end=C]=k):(k=f[C])?(S=d[w])?(delete f[k.start],delete d[S.end],k===S?(k.ring.push(E),c(k.ring)):f[S.start]=d[k.end]={start:S.start,end:k.end,ring:S.ring.concat(k.ring)}):(delete f[k.start],k.ring.unshift(_),f[k.start=w]=k):f[w]=d[C]={start:w,end:C,ring:[_,E]}}}function o(u){return u[0]*2+u[1]*(e+1)*4}function a(u,l,c){u.forEach(f=>{var d=f[0],h=f[1],p=d|0,g=h|0,m,y=l[g*e+p];d>0&&d0&&h=0&&c>=0||q("invalid size"),e=l,t=c,i},i.smooth=function(u){return arguments.length?(n=u?a:Uee,i):n===a},i}function jee(e){for(var t=0,n=e.length,i=e[n-1][1]*e[0][0]-e[n-1][0]*e[0][1];++ti!=h>i&&n<(d-l)*(i-c)/(h-c)+l&&(r=-r)}return r}function Hee(e,t,n){var i;return Gee(e,t,n)&&Vee(e[i=+(e[0]===t[0])],n[i],t[i])}function Gee(e,t,n){return(t[0]-e[0])*(n[1]-e[1])===(n[0]-e[0])*(t[1]-e[1])}function Vee(e,t,n){return e<=t&&t<=n||n<=t&&t<=e}function pF(e,t,n){return function(i){var r=qr(i),s=n?Math.min(r[0],0):r[0],o=r[1],a=o-s,u=t?Co(s,o,e):a/(e+1);return Ei(s+u,o,u)}}function Wv(e){P.call(this,null,e)}Wv.Definition={type:"Isocontour",metadata:{generates:!0},params:[{name:"field",type:"field"},{name:"thresholds",type:"number",array:!0},{name:"levels",type:"number"},{name:"nice",type:"boolean",default:!1},{name:"resolve",type:"enum",values:["shared","independent"],default:"independent"},{name:"zero",type:"boolean",default:!0},{name:"smooth",type:"boolean",default:!0},{name:"scale",type:"number",expr:!0},{name:"translate",type:"number",array:!0,expr:!0},{name:"as",type:"string",null:!0,default:"contour"}]},ee(Wv,P,{transform(e,t){if(this.value&&!t.changed()&&!e.modified())return t.StopPropagation;var n=t.fork(t.NO_SOURCE|t.NO_FIELDS),i=t.materialize(t.SOURCE).source,r=e.field||En,s=hF().smooth(e.smooth!==!1),o=e.thresholds||Yee(i,r,e),a=e.as===null?null:e.as||"contour",u=[];return i.forEach(l=>{const c=r(l),f=s.size([c.width,c.height])(c.values,W(o)?o:o(c.values));Xee(f,c,l,e),f.forEach(d=>{u.push(m0(l,nt(a!=null?{[a]:d}:d)))})}),this.value&&(n.rem=this.value),this.value=n.source=n.add=u,n}});function Yee(e,t,n){const i=pF(n.levels||10,n.nice,n.zero!==!1);return n.resolve!=="shared"?i:i(e.map(r=>Aa(t(r).values)))}function Xee(e,t,n,i){let r=i.scale||t.scale,s=i.translate||t.translate;if(Ie(r)&&(r=r(n,i)),Ie(s)&&(s=s(n,i)),(r===1||r==null)&&!s)return;const o=(Ze(r)?r:r[0])||1,a=(Ze(r)?r:r[1])||1,u=s&&s[0]||0,l=s&&s[1]||0;e.forEach(gF(t,o,a,u,l))}function gF(e,t,n,i,r){const s=e.x1||0,o=e.y1||0,a=t*n<0;function u(f){f.forEach(l)}function l(f){a&&f.reverse(),f.forEach(c)}function c(f){f[0]=(f[0]-s)*t+i,f[1]=(f[1]-o)*n+r}return function(f){return f.coordinates.forEach(u),f}}function mF(e,t,n){const i=e>=0?e:by(t,n);return Math.round((Math.sqrt(4*i*i+1)-1)/2)}function Hv(e){return Ie(e)?e:kn(+e)}function yF(){var e=u=>u[0],t=u=>u[1],n=tl,i=[-1,-1],r=960,s=500,o=2;function a(u,l){const c=mF(i[0],u,e)>>o,f=mF(i[1],u,t)>>o,d=c?c+2:0,h=f?f+2:0,p=2*d+(r>>o),g=2*h+(s>>o),m=new Float32Array(p*g),y=new Float32Array(p*g);let b=m;u.forEach(x=>{const _=d+(+e(x)>>o),E=h+(+t(x)>>o);_>=0&&_=0&&E0&&f>0?(Hl(p,g,m,y,c),Gl(p,g,y,m,f),Hl(p,g,m,y,c),Gl(p,g,y,m,f),Hl(p,g,m,y,c),Gl(p,g,y,m,f)):c>0?(Hl(p,g,m,y,c),Hl(p,g,y,m,c),Hl(p,g,m,y,c),b=y):f>0&&(Gl(p,g,m,y,f),Gl(p,g,y,m,f),Gl(p,g,m,y,f),b=y);const v=l?Math.pow(2,-2*o):1/g8(b);for(let x=0,_=p*g;x<_;++x)b[x]*=v;return{values:b,scale:1<>o),y2:h+(s>>o)}}return a.x=function(u){return arguments.length?(e=Hv(u),a):e},a.y=function(u){return arguments.length?(t=Hv(u),a):t},a.weight=function(u){return arguments.length?(n=Hv(u),a):n},a.size=function(u){if(!arguments.length)return[r,s];var l=+u[0],c=+u[1];return l>=0&&c>=0||q("invalid size"),r=l,s=c,a},a.cellSize=function(u){return arguments.length?((u=+u)>=1||q("invalid cell size"),o=Math.floor(Math.log(u)/Math.LN2),a):1<=r&&(a>=s&&(u-=n[a-s+o*e]),i[a-r+o*e]=u/Math.min(a+1,e-1+s-a,s))}function Gl(e,t,n,i,r){const s=(r<<1)+1;for(let o=0;o=r&&(a>=s&&(u-=n[o+(a-s)*e]),i[o+(a-r)*e]=u/Math.min(a+1,t-1+s-a,s))}function Gv(e){P.call(this,null,e)}Gv.Definition={type:"KDE2D",metadata:{generates:!0},params:[{name:"size",type:"number",array:!0,length:2,required:!0},{name:"x",type:"field",required:!0},{name:"y",type:"field",required:!0},{name:"weight",type:"field"},{name:"groupby",type:"field",array:!0},{name:"cellSize",type:"number"},{name:"bandwidth",type:"number",array:!0,length:2},{name:"counts",type:"boolean",default:!1},{name:"as",type:"string",default:"grid"}]};const Kee=["x","y","weight","size","cellSize","bandwidth"];function bF(e,t){return Kee.forEach(n=>t[n]!=null?e[n](t[n]):0),e}ee(Gv,P,{transform(e,t){if(this.value&&!t.changed()&&!e.modified())return t.StopPropagation;var n=t.fork(t.NO_SOURCE|t.NO_FIELDS),i=t.materialize(t.SOURCE).source,r=Zee(i,e.groupby),s=(e.groupby||[]).map(Tt),o=bF(yF(),e),a=e.as||"grid",u=[];function l(c,f){for(let d=0;dnt(l({[a]:o(c,e.counts)},c.dims))),this.value&&(n.rem=this.value),this.value=n.source=n.add=u,n}});function Zee(e,t){var n=[],i=c=>c(a),r,s,o,a,u,l;if(t==null)n.push(e);else for(r={},s=0,o=e.length;sn.push(a(c))),s&&o&&(t.visit(u,c=>{var f=s(c),d=o(c);f!=null&&d!=null&&(f=+f)===f&&(d=+d)===d&&i.push([f,d])}),n=n.concat({type:Yv,geometry:{type:Jee,coordinates:i}})),this.value={type:Xv,features:n}}});function Zv(e){P.call(this,null,e)}Zv.Definition={type:"GeoPath",metadata:{modifies:!0},params:[{name:"projection",type:"projection"},{name:"field",type:"field"},{name:"pointRadius",type:"number",expr:!0},{name:"as",type:"string",default:"path"}]},ee(Zv,P,{transform(e,t){var n=t.fork(t.ALL),i=this.value,r=e.field||En,s=e.as||"path",o=n.SOURCE;!i||e.modified()?(this.value=i=dF(e.projection),n.materialize().reflow()):o=r===En||t.modified(r.fields)?n.ADD_MOD:n.ADD;const a=Qee(i,e.pointRadius);return n.visit(o,u=>u[s]=i(r(u))),i.pointRadius(a),n.modifies(s)}});function Qee(e,t){const n=e.pointRadius();return e.context(null),t!=null&&e.pointRadius(t),n}function Jv(e){P.call(this,null,e)}Jv.Definition={type:"GeoPoint",metadata:{modifies:!0},params:[{name:"projection",type:"projection",required:!0},{name:"fields",type:"field",array:!0,required:!0,length:2},{name:"as",type:"string",array:!0,length:2,default:["x","y"]}]},ee(Jv,P,{transform(e,t){var n=e.projection,i=e.fields[0],r=e.fields[1],s=e.as||["x","y"],o=s[0],a=s[1],u;function l(c){const f=n([i(c),r(c)]);f?(c[o]=f[0],c[a]=f[1]):(c[o]=void 0,c[a]=void 0)}return e.modified()?t=t.materialize().reflow(!0).visit(t.SOURCE,l):(u=t.modified(i.fields)||t.modified(r.fields),t.visit(u?t.ADD_MOD:t.ADD,l)),t.modifies(s)}});function Qv(e){P.call(this,null,e)}Qv.Definition={type:"GeoShape",metadata:{modifies:!0,nomod:!0},params:[{name:"projection",type:"projection"},{name:"field",type:"field",default:"datum"},{name:"pointRadius",type:"number",expr:!0},{name:"as",type:"string",default:"shape"}]},ee(Qv,P,{transform(e,t){var n=t.fork(t.ALL),i=this.value,r=e.as||"shape",s=n.ADD;return(!i||e.modified())&&(this.value=i=ete(dF(e.projection),e.field||Bi("datum"),e.pointRadius),n.materialize().reflow(),s=n.SOURCE),n.visit(s,o=>o[r]=i),n.modifies(r)}});function ete(e,t,n){const i=n==null?r=>e(t(r)):r=>{var s=e.pointRadius(),o=e.pointRadius(n)(t(r));return e.pointRadius(s),o};return i.context=r=>(e.context(r),i),i}function ex(e){P.call(this,[],e),this.generator=YQ()}ex.Definition={type:"Graticule",metadata:{changes:!0,generates:!0},params:[{name:"extent",type:"array",array:!0,length:2,content:{type:"number",array:!0,length:2}},{name:"extentMajor",type:"array",array:!0,length:2,content:{type:"number",array:!0,length:2}},{name:"extentMinor",type:"array",array:!0,length:2,content:{type:"number",array:!0,length:2}},{name:"step",type:"number",array:!0,length:2},{name:"stepMajor",type:"number",array:!0,length:2,default:[90,360]},{name:"stepMinor",type:"number",array:!0,length:2,default:[10,10]},{name:"precision",type:"number",default:2.5}]},ee(ex,P,{transform(e,t){var n=this.value,i=this.generator,r;if(!n.length||e.modified())for(const s in e)Ie(i[s])&&i[s](e[s]);return r=i(),n.length?t.mod.push(TE(n[0],r)):t.add.push(nt(r)),n[0]=r,t}});function tx(e){P.call(this,null,e)}tx.Definition={type:"heatmap",metadata:{modifies:!0},params:[{name:"field",type:"field"},{name:"color",type:"string",expr:!0},{name:"opacity",type:"number",expr:!0},{name:"resolve",type:"enum",values:["shared","independent"],default:"independent"},{name:"as",type:"string",default:"image"}]},ee(tx,P,{transform(e,t){if(!t.changed()&&!e.modified())return t.StopPropagation;var n=t.materialize(t.SOURCE).source,i=e.resolve==="shared",r=e.field||En,s=nte(e.opacity,e),o=tte(e.color,e),a=e.as||"image",u={$x:0,$y:0,$value:0,$max:i?Aa(n.map(l=>Aa(r(l).values))):0};return n.forEach(l=>{const c=r(l),f=Oe({},l,u);i||(f.$max=Aa(c.values||[])),l[a]=ite(c,f,o.dep?o:kn(o(f)),s.dep?s:kn(s(f)))}),t.reflow(!0).modifies(a)}});function tte(e,t){let n;return Ie(e)?(n=i=>Oo(e(i,t)),n.dep=vF(e)):n=kn(Oo(e||"#888")),n}function nte(e,t){let n;return Ie(e)?(n=i=>e(i,t),n.dep=vF(e)):e?n=kn(e):(n=i=>i.$value/i.$max||0,n.dep=!0),n}function vF(e){if(!Ie(e))return!1;const t=lr(wn(e));return t.$x||t.$y||t.$value||t.$max}function ite(e,t,n,i){const r=e.width,s=e.height,o=e.x1||0,a=e.y1||0,u=e.x2||r,l=e.y2||s,c=e.values,f=c?m=>c[m]:bo,d=Mo(u-o,l-a),h=d.getContext("2d"),p=h.getImageData(0,0,u-o,l-a),g=p.data;for(let m=a,y=0;m{e[i]!=null&&_F(n,i,e[i])})):jv.forEach(i=>{e.modified(i)&&_F(n,i,e[i])}),e.pointRadius!=null&&n.path.pointRadius(e.pointRadius),e.fit&&rte(n,e),t.fork(t.NO_SOURCE|t.NO_FIELDS)}});function rte(e,t){const n=ote(t.fit);t.extent?e.fitExtent(t.extent,n):t.size&&e.fitSize(t.size,n)}function ste(e){const t=qv((e||"mercator").toLowerCase());return t||q("Unrecognized projection type: "+e),t()}function _F(e,t,n){Ie(e[t])&&e[t](n)}function ote(e){return e=oe(e),e.length===1?e[0]:{type:Xv,features:e.reduce((t,n)=>t.concat(ate(n)),[])}}function ate(e){return e.type===Xv?e.features:oe(e).filter(t=>t!=null).map(t=>t.type===Yv?t:{type:Yv,geometry:t})}const ute=Object.freeze(Object.defineProperty({__proto__:null,contour:Vv,geojson:Kv,geopath:Zv,geopoint:Jv,geoshape:Qv,graticule:ex,heatmap:tx,isocontour:Wv,kde2d:Gv,projection:xF},Symbol.toStringTag,{value:"Module"}));function lte(e,t){var n,i=1;e==null&&(e=0),t==null&&(t=0);function r(){var s,o=n.length,a,u=0,l=0;for(s=0;s=(f=(a+l)/2))?a=f:l=f,(m=n>=(d=(u+c)/2))?u=d:c=d,r=s,!(s=s[y=m<<1|g]))return r[y]=o,e;if(h=+e._x.call(null,s.data),p=+e._y.call(null,s.data),t===h&&n===p)return o.next=s,r?r[y]=o:e._root=o,e;do r=r?r[y]=new Array(4):e._root=new Array(4),(g=t>=(f=(a+l)/2))?a=f:l=f,(m=n>=(d=(u+c)/2))?u=d:c=d;while((y=m<<1|g)===(b=(p>=d)<<1|h>=f));return r[b]=s,r[y]=o,e}function fte(e){var t,n,i=e.length,r,s,o=new Array(i),a=new Array(i),u=1/0,l=1/0,c=-1/0,f=-1/0;for(n=0;nc&&(c=r),sf&&(f=s));if(u>c||l>f)return this;for(this.cover(u,l).cover(c,f),n=0;ne||e>=r||i>t||t>=s;)switch(l=(tc||(a=p.y0)>f||(u=p.x1)=y)<<1|e>=m)&&(p=d[d.length-1],d[d.length-1]=d[d.length-1-g],d[d.length-1-g]=p)}else{var b=e-+this._x.call(null,h.data),v=t-+this._y.call(null,h.data),x=b*b+v*v;if(x=(d=(o+u)/2))?o=d:u=d,(g=f>=(h=(a+l)/2))?a=h:l=h,t=n,!(n=n[m=g<<1|p]))return this;if(!n.length)break;(t[m+1&3]||t[m+2&3]||t[m+3&3])&&(i=t,y=m)}for(;n.data!==e;)if(r=n,!(n=n.next))return this;return(s=n.next)&&delete n.next,r?(s?r.next=s:delete r.next,this):t?(s?t[m]=s:delete t[m],(n=t[0]||t[1]||t[2]||t[3])&&n===(t[3]||t[2]||t[1]||t[0])&&!n.length&&(i?i[y]=n:this._root=n),this):(this._root=s,this)}function yte(e){for(var t=0,n=e.length;td.index){var $=h-C.x-C.vx,O=p-C.y-C.vy,R=$*$+O*O;Rh+S||Ep+S||wl.r&&(l.r=l[c].r)}function u(){if(t){var l,c=t.length,f;for(n=new Array(c),l=0;l[t(_,E,o),_])),x;for(m=0,a=new Array(y);m{}};function kF(){for(var e=0,t=arguments.length,n={},i;e=0&&(i=n.slice(r+1),n=n.slice(0,r)),n&&!t.hasOwnProperty(n))throw new Error("unknown type: "+n);return{type:n,name:i}})}Cg.prototype=kF.prototype={constructor:Cg,on:function(e,t){var n=this._,i=Mte(e+"",n),r,s=-1,o=i.length;if(arguments.length<2){for(;++s0)for(var n=new Array(r),i=0,r,s;i=0&&e._call.call(void 0,t),e=e._next;--Vl}function DF(){Ja=(Ag=pd.now())+$g,Vl=fd=0;try{Ote()}finally{Vl=0,Ite(),Ja=0}}function Lte(){var e=pd.now(),t=e-Ag;t>$F&&($g-=t,Ag=e)}function Ite(){for(var e,t=kg,n,i=1/0;t;)t._call?(i>t._time&&(i=t._time),e=t,t=t._next):(n=t._next,t._next=null,t=e?e._next=n:kg=n);hd=e,sx(i)}function sx(e){if(!Vl){fd&&(fd=clearTimeout(fd));var t=e-Ja;t>24?(e<1/0&&(fd=setTimeout(DF,e-pd.now()-$g)),dd&&(dd=clearInterval(dd))):(dd||(Ag=pd.now(),dd=setInterval(Lte,$F)),Vl=1,SF(DF))}}function Pte(e,t,n){var i=new Sg,r=t;return t==null?(i.restart(e,t,n),i):(i._restart=i.restart,i.restart=function(s,o,a){o=+o,a=a==null?rx():+a,i._restart(function u(l){l+=r,i._restart(u,r+=o,a),s(l)},o,a)},i.restart(e,t,n),i)}const zte=1664525,Bte=1013904223,TF=4294967296;function Ute(){let e=1;return()=>(e=(zte*e+Bte)%TF)/TF}function jte(e){return e.x}function qte(e){return e.y}var Wte=10,Hte=Math.PI*(3-Math.sqrt(5));function Gte(e){var t,n=1,i=.001,r=1-Math.pow(i,1/300),s=0,o=.6,a=new Map,u=FF(f),l=kF("tick","end"),c=Ute();e==null&&(e=[]);function f(){d(),l.call("tick",t),n1?(m==null?a.delete(g):a.set(g,p(m)),t):a.get(g)},find:function(g,m,y){var b=0,v=e.length,x,_,E,w,C;for(y==null?y=1/0:y*=y,b=0;b1?(l.on(g,m),t):l.on(g)}}}function Vte(){var e,t,n,i,r=Xn(-30),s,o=1,a=1/0,u=.81;function l(h){var p,g=e.length,m=nx(e,jte,qte).visitAfter(f);for(i=h,p=0;p=a)return;(h.data!==t||h.next)&&(y===0&&(y=Zo(n),x+=y*y),b===0&&(b=Zo(n),x+=b*b),x=0;)n.tick();else if(n.stopped()&&n.restart(),!i)return t.StopPropagation}return this.finish(e,t)},finish(e,t){const n=t.dataflow;for(let a=this._argops,u=0,l=a.length,c;ue.touch(t).run()}function Jte(e,t){const n=Gte(e),i=n.stop,r=n.restart;let s=!1;return n.stopped=()=>s,n.restart=()=>(s=!1,r()),n.stop=()=>(s=!0,i()),RF(n,t,!0).on("end",()=>s=!0)}function RF(e,t,n,i){var r=oe(t.forces),s,o,a,u;for(s=0,o=ox.length;st(i,n):t)}const nne=Object.freeze(Object.defineProperty({__proto__:null,force:ax},Symbol.toStringTag,{value:"Module"}));function ine(e,t){return e.parent===t.parent?1:2}function rne(e){return e.reduce(sne,0)/e.length}function sne(e,t){return e+t.x}function one(e){return 1+e.reduce(ane,0)}function ane(e,t){return Math.max(e,t.y)}function une(e){for(var t;t=e.children;)e=t[0];return e}function lne(e){for(var t;t=e.children;)e=t[t.length-1];return e}function cne(){var e=ine,t=1,n=1,i=!1;function r(s){var o,a=0;s.eachAfter(function(d){var h=d.children;h?(d.x=rne(h),d.y=one(h)):(d.x=o?a+=e(d,o):0,d.y=0,o=d)});var u=une(s),l=lne(s),c=u.x-e(u,l)/2,f=l.x+e(l,u)/2;return s.eachAfter(i?function(d){d.x=(d.x-s.x)*t,d.y=(s.y-d.y)*n}:function(d){d.x=(d.x-c)/(f-c)*t,d.y=(1-(s.y?d.y/s.y:1))*n})}return r.separation=function(s){return arguments.length?(e=s,r):e},r.size=function(s){return arguments.length?(i=!1,t=+s[0],n=+s[1],r):i?null:[t,n]},r.nodeSize=function(s){return arguments.length?(i=!0,t=+s[0],n=+s[1],r):i?[t,n]:null},r}function fne(e){var t=0,n=e.children,i=n&&n.length;if(!i)t=1;else for(;--i>=0;)t+=n[i].value;e.value=t}function dne(){return this.eachAfter(fne)}function hne(e,t){let n=-1;for(const i of this)e.call(t,i,++n,this);return this}function pne(e,t){for(var n=this,i=[n],r,s,o=-1;n=i.pop();)if(e.call(t,n,++o,this),r=n.children)for(s=r.length-1;s>=0;--s)i.push(r[s]);return this}function gne(e,t){for(var n=this,i=[n],r=[],s,o,a,u=-1;n=i.pop();)if(r.push(n),s=n.children)for(o=0,a=s.length;o=0;)n+=i[r].value;t.value=n})}function bne(e){return this.eachBefore(function(t){t.children&&t.children.sort(e)})}function vne(e){for(var t=this,n=xne(t,e),i=[t];t!==n;)t=t.parent,i.push(t);for(var r=i.length;e!==n;)i.splice(r,0,e),e=e.parent;return i}function xne(e,t){if(e===t)return e;var n=e.ancestors(),i=t.ancestors(),r=null;for(e=n.pop(),t=i.pop();e===t;)r=e,e=n.pop(),t=i.pop();return r}function _ne(){for(var e=this,t=[e];e=e.parent;)t.push(e);return t}function wne(){return Array.from(this)}function Ene(){var e=[];return this.eachBefore(function(t){t.children||e.push(t)}),e}function Cne(){var e=this,t=[];return e.each(function(n){n!==e&&t.push({source:n.parent,target:n})}),t}function*kne(){var e=this,t,n=[e],i,r,s;do for(t=n.reverse(),n=[];e=t.pop();)if(yield e,i=e.children)for(r=0,s=i.length;r=0;--a)r.push(s=o[a]=new Yl(o[a])),s.parent=i,s.depth=i.depth+1;return n.eachBefore(OF)}function Ane(){return ux(this).eachBefore(Fne)}function $ne(e){return e.children}function Sne(e){return Array.isArray(e)?e[1]:null}function Fne(e){e.data.value!==void 0&&(e.value=e.data.value),e.data=e.data.data}function OF(e){var t=0;do e.height=t;while((e=e.parent)&&e.height<++t)}function Yl(e){this.data=e,this.depth=this.height=0,this.parent=null}Yl.prototype=ux.prototype={constructor:Yl,count:dne,each:hne,eachAfter:gne,eachBefore:pne,find:mne,sum:yne,sort:bne,path:vne,ancestors:_ne,descendants:wne,leaves:Ene,links:Cne,copy:Ane,[Symbol.iterator]:kne};function Fg(e){return e==null?null:LF(e)}function LF(e){if(typeof e!="function")throw new Error;return e}function Qa(){return 0}function Xl(e){return function(){return e}}const Dne=1664525,Tne=1013904223,IF=4294967296;function Mne(){let e=1;return()=>(e=(Dne*e+Tne)%IF)/IF}function Nne(e){return typeof e=="object"&&"length"in e?e:Array.from(e)}function Rne(e,t){let n=e.length,i,r;for(;n;)r=t()*n--|0,i=e[n],e[n]=e[r],e[r]=i;return e}function One(e,t){for(var n=0,i=(e=Rne(Array.from(e),t)).length,r=[],s,o;n0&&n*n>i*i+r*r}function lx(e,t){for(var n=0;n1e-6?($+Math.sqrt($*$-4*S*O))/(2*S):O/$);return{x:i+E+w*R,y:r+C+k*R,r:R}}function BF(e,t,n){var i=e.x-t.x,r,s,o=e.y-t.y,a,u,l=i*i+o*o;l?(s=t.r+n.r,s*=s,u=e.r+n.r,u*=u,s>u?(r=(l+u-s)/(2*l),a=Math.sqrt(Math.max(0,u/l-r*r)),n.x=e.x-r*i-a*o,n.y=e.y-r*o+a*i):(r=(l+s-u)/(2*l),a=Math.sqrt(Math.max(0,s/l-r*r)),n.x=t.x+r*i-a*o,n.y=t.y+r*o+a*i)):(n.x=t.x+n.r,n.y=t.y)}function UF(e,t){var n=e.r+t.r-1e-6,i=t.x-e.x,r=t.y-e.y;return n>0&&n*n>i*i+r*r}function jF(e){var t=e._,n=e.next._,i=t.r+n.r,r=(t.x*n.r+n.x*t.r)/i,s=(t.y*n.r+n.y*t.r)/i;return r*r+s*s}function Tg(e){this._=e,this.next=null,this.previous=null}function zne(e,t){if(!(s=(e=Nne(e)).length))return 0;var n,i,r,s,o,a,u,l,c,f,d;if(n=e[0],n.x=0,n.y=0,!(s>1))return n.r;if(i=e[1],n.x=-i.r,i.x=n.r,i.y=0,!(s>2))return n.r+i.r;BF(i,n,r=e[2]),n=new Tg(n),i=new Tg(i),r=new Tg(r),n.next=r.previous=i,i.next=n.previous=r,r.next=i.previous=n;e:for(u=3;uGne(n(x,_,r))),b=y.map(YF),v=new Set(y).add("");for(const x of b)v.has(x)||(v.add(x),y.push(x),b.push(YF(x)),s.push(fx));o=(x,_)=>y[_],a=(x,_)=>b[_]}for(c=0,u=s.length;c=0&&(h=s[y],h.data===fx);--y)h.data=null}if(f.parent=qne,f.eachBefore(function(y){y.depth=y.parent.depth+1,--u}).eachBefore(OF),f.parent=null,u>0)throw new Error("cycle");return f}return i.id=function(r){return arguments.length?(e=Fg(r),i):e},i.parentId=function(r){return arguments.length?(t=Fg(r),i):t},i.path=function(r){return arguments.length?(n=Fg(r),i):n},i}function Gne(e){e=`${e}`;let t=e.length;return dx(e,t-1)&&!dx(e,t-2)&&(e=e.slice(0,-1)),e[0]==="/"?e:`/${e}`}function YF(e){let t=e.length;if(t<2)return"";for(;--t>1&&!dx(e,t););return e.slice(0,t)}function dx(e,t){if(e[t]==="/"){let n=0;for(;t>0&&e[--t]==="\\";)++n;if(!(n&1))return!0}return!1}function Vne(e,t){return e.parent===t.parent?1:2}function hx(e){var t=e.children;return t?t[0]:e.t}function px(e){var t=e.children;return t?t[t.length-1]:e.t}function Yne(e,t,n){var i=n/(t.i-e.i);t.c-=i,t.s+=n,e.c+=i,t.z+=n,t.m+=n}function Xne(e){for(var t=0,n=0,i=e.children,r=i.length,s;--r>=0;)s=i[r],s.z+=t,s.m+=t,t+=s.s+(n+=s.c)}function Kne(e,t,n){return e.a.parent===t.parent?e.a:n}function Mg(e,t){this._=e,this.parent=null,this.children=null,this.A=null,this.a=this,this.z=0,this.m=0,this.c=0,this.s=0,this.t=null,this.i=t}Mg.prototype=Object.create(Yl.prototype);function Zne(e){for(var t=new Mg(e,0),n,i=[t],r,s,o,a;n=i.pop();)if(s=n._.children)for(n.children=new Array(a=s.length),o=a-1;o>=0;--o)i.push(r=n.children[o]=new Mg(s[o],o)),r.parent=n;return(t.parent=new Mg(null,0)).children=[t],t}function Jne(){var e=Vne,t=1,n=1,i=null;function r(l){var c=Zne(l);if(c.eachAfter(s),c.parent.m=-c.z,c.eachBefore(o),i)l.eachBefore(u);else{var f=l,d=l,h=l;l.eachBefore(function(b){b.xd.x&&(d=b),b.depth>h.depth&&(h=b)});var p=f===d?1:e(f,d)/2,g=p-f.x,m=t/(d.x+p+g),y=n/(h.depth||1);l.eachBefore(function(b){b.x=(b.x+g)*m,b.y=b.depth*y})}return l}function s(l){var c=l.children,f=l.parent.children,d=l.i?f[l.i-1]:null;if(c){Xne(l);var h=(c[0].z+c[c.length-1].z)/2;d?(l.z=d.z+e(l._,d._),l.m=l.z-h):l.z=h}else d&&(l.z=d.z+e(l._,d._));l.parent.A=a(l,d,l.parent.A||f[0])}function o(l){l._.x=l.z+l.parent.m,l.m+=l.parent.m}function a(l,c,f){if(c){for(var d=l,h=l,p=c,g=d.parent.children[0],m=d.m,y=h.m,b=p.m,v=g.m,x;p=px(p),d=hx(d),p&&d;)g=hx(g),h=px(h),h.a=l,x=p.z+b-d.z-m+e(p._,d._),x>0&&(Yne(Kne(p,l,f),l,x),m+=x,y+=x),b+=p.m,m+=d.m,v+=g.m,y+=h.m;p&&!px(h)&&(h.t=p,h.m+=b-y),d&&!hx(g)&&(g.t=d,g.m+=m-v,f=l)}return f}function u(l){l.x*=t,l.y=l.depth*n}return r.separation=function(l){return arguments.length?(e=l,r):e},r.size=function(l){return arguments.length?(i=!1,t=+l[0],n=+l[1],r):i?null:[t,n]},r.nodeSize=function(l){return arguments.length?(i=!0,t=+l[0],n=+l[1],r):i?[t,n]:null},r}function Ng(e,t,n,i,r){for(var s=e.children,o,a=-1,u=s.length,l=e.value&&(r-n)/e.value;++ab&&(b=l),E=m*m*_,v=Math.max(b/E,E/y),v>x){m-=l;break}x=v}o.push(u={value:m,dice:h1?i:1)},n}(XF);function Qne(){var e=ZF,t=!1,n=1,i=1,r=[0],s=Qa,o=Qa,a=Qa,u=Qa,l=Qa;function c(d){return d.x0=d.y0=0,d.x1=n,d.y1=i,d.eachBefore(f),r=[0],t&&d.eachBefore(HF),d}function f(d){var h=r[d.depth],p=d.x0+h,g=d.y0+h,m=d.x1-h,y=d.y1-h;m=d-1){var b=s[f];b.x0=p,b.y0=g,b.x1=m,b.y1=y;return}for(var v=l[f],x=h/2+v,_=f+1,E=d-1;_>>1;l[w]y-g){var S=h?(p*k+m*C)/h:m;c(f,_,C,p,g,S,y),c(_,d,k,S,g,m,y)}else{var $=h?(g*k+y*C)/h:y;c(f,_,C,p,g,m,$),c(_,d,k,p,$,m,y)}}}function tie(e,t,n,i,r){(e.depth&1?Ng:yd)(e,t,n,i,r)}const nie=function e(t){function n(i,r,s,o,a){if((u=i._squarify)&&u.ratio===t)for(var u,l,c,f,d=-1,h,p=u.length,g=i.value;++d1?i:1)},n}(XF);function gx(e,t,n){const i={};return e.each(r=>{const s=r.data;n(s)&&(i[t(s)]=r)}),e.lookup=i,e}function mx(e){P.call(this,null,e)}mx.Definition={type:"Nest",metadata:{treesource:!0,changes:!0},params:[{name:"keys",type:"field",array:!0},{name:"generate",type:"boolean"}]};const iie=e=>e.values;ee(mx,P,{transform(e,t){t.source||q("Nest transform requires an upstream data source.");var n=e.generate,i=e.modified(),r=t.clone(),s=this.value;return(!s||i||t.changed())&&(s&&s.each(o=>{o.children&&g0(o.data)&&r.rem.push(o.data)}),this.value=s=ux({values:oe(e.keys).reduce((o,a)=>(o.key(a),o),rie()).entries(r.source)},iie),n&&s.each(o=>{o.children&&(o=nt(o.data),r.add.push(o),r.source.push(o))}),gx(s,_e,_e)),r.source.root=s,r}});function rie(){const e=[],t={entries:r=>i(n(r,0),0),key:r=>(e.push(r),t)};function n(r,s){if(s>=e.length)return r;const o=r.length,a=e[s++],u={},l={};let c=-1,f,d,h;for(;++ce.length)return r;const o=[];for(const a in r)o.push({key:a,values:i(r[a],s)});return o}return t}function Vs(e){P.call(this,null,e)}const sie=(e,t)=>e.parent===t.parent?1:2;ee(Vs,P,{transform(e,t){(!t.source||!t.source.root)&&q(this.constructor.name+" transform requires a backing tree data source.");const n=this.layout(e.method),i=this.fields,r=t.source.root,s=e.as||i;e.field?r.sum(e.field):r.count(),e.sort&&r.sort(Ta(e.sort,o=>o.data)),oie(n,this.params,e),n.separation&&n.separation(e.separation!==!1?sie:tl);try{this.value=n(r)}catch(o){q(o)}return r.each(o=>aie(o,i,s)),t.reflow(e.modified()).modifies(s).modifies("leaf")}});function oie(e,t,n){for(let i,r=0,s=t.length;rs[_e(o)]=1),i.each(o=>{const a=o.data,u=o.parent&&o.parent.data;u&&s[_e(a)]&&s[_e(u)]&&r.add.push(nt({source:u,target:a}))}),this.value=r.add):t.changed(t.MOD)&&(t.visit(t.MOD,o=>s[_e(o)]=1),n.forEach(o=>{(s[_e(o.source)]||s[_e(o.target)])&&r.mod.push(o)})),r}});const QF={binary:eie,dice:yd,slice:Ng,slicedice:tie,squarify:ZF,resquarify:nie},kx=["x0","y0","x1","y1","depth","children"];function Ax(e){Vs.call(this,e)}Ax.Definition={type:"Treemap",metadata:{tree:!0,modifies:!0},params:[{name:"field",type:"field"},{name:"sort",type:"compare"},{name:"method",type:"enum",default:"squarify",values:["squarify","resquarify","binary","dice","slice","slicedice"]},{name:"padding",type:"number",default:0},{name:"paddingInner",type:"number",default:0},{name:"paddingOuter",type:"number",default:0},{name:"paddingTop",type:"number",default:0},{name:"paddingRight",type:"number",default:0},{name:"paddingBottom",type:"number",default:0},{name:"paddingLeft",type:"number",default:0},{name:"ratio",type:"number",default:1.618033988749895},{name:"round",type:"boolean",default:!1},{name:"size",type:"number",array:!0,length:2},{name:"as",type:"string",array:!0,length:kx.length,default:kx}]},ee(Ax,Vs,{layout(){const e=Qne();return e.ratio=t=>{const n=e.tile();n.ratio&&e.tile(n.ratio(t))},e.method=t=>{ue(QF,t)?e.tile(QF[t]):q("Unrecognized Treemap layout method: "+t)},e},params:["method","ratio","size","round","padding","paddingInner","paddingOuter","paddingTop","paddingRight","paddingBottom","paddingLeft"],fields:kx});const uie=Object.freeze(Object.defineProperty({__proto__:null,nest:mx,pack:bx,partition:xx,stratify:_x,tree:Ex,treelinks:Cx,treemap:Ax},Symbol.toStringTag,{value:"Module"})),$x=4278190080;function lie(e,t){const n=e.bitmap();return(t||[]).forEach(i=>n.set(e(i.boundary[0]),e(i.boundary[3]))),[n,void 0]}function cie(e,t,n,i,r){const s=e.width,o=e.height,a=i||r,u=Mo(s,o).getContext("2d"),l=Mo(s,o).getContext("2d"),c=a&&Mo(s,o).getContext("2d");n.forEach(C=>Rg(u,C,!1)),Rg(l,t,!1),a&&Rg(c,t,!0);const f=Sx(u,s,o),d=Sx(l,s,o),h=a&&Sx(c,s,o),p=e.bitmap(),g=a&&e.bitmap();let m,y,b,v,x,_,E,w;for(y=0;y{r.items.forEach(s=>Rg(e,s.items,n))}):$i[i].draw(e,{items:n?t.map(fie):t})}function fie(e){const t=m0(e,{});return t.stroke&&t.strokeOpacity!==0||t.fill&&t.fillOpacity!==0?{...t,strokeOpacity:1,stroke:"#000",fillOpacity:0}:t}const Ys=5,Kn=31,bd=32,Jo=new Uint32Array(bd+1),vr=new Uint32Array(bd+1);vr[0]=0,Jo[0]=~vr[0];for(let e=1;e<=bd;++e)vr[e]=vr[e-1]<<1|1,Jo[e]=~vr[e];function die(e,t){const n=new Uint32Array(~~((e*t+bd)/bd));function i(s,o){n[s]|=o}function r(s,o){n[s]&=o}return{array:n,get:(s,o)=>{const a=o*e+s;return n[a>>>Ys]&1<<(a&Kn)},set:(s,o)=>{const a=o*e+s;i(a>>>Ys,1<<(a&Kn))},clear:(s,o)=>{const a=o*e+s;r(a>>>Ys,~(1<<(a&Kn)))},getRange:(s,o,a,u)=>{let l=u,c,f,d,h;for(;l>=o;--l)if(c=l*e+s,f=l*e+a,d=c>>>Ys,h=f>>>Ys,d===h){if(n[d]&Jo[c&Kn]&vr[(f&Kn)+1])return!0}else{if(n[d]&Jo[c&Kn]||n[h]&vr[(f&Kn)+1])return!0;for(let p=d+1;p{let l,c,f,d,h;for(;o<=u;++o)if(l=o*e+s,c=o*e+a,f=l>>>Ys,d=c>>>Ys,f===d)i(f,Jo[l&Kn]&vr[(c&Kn)+1]);else for(i(f,Jo[l&Kn]),i(d,vr[(c&Kn)+1]),h=f+1;h{let l,c,f,d,h;for(;o<=u;++o)if(l=o*e+s,c=o*e+a,f=l>>>Ys,d=c>>>Ys,f===d)r(f,vr[l&Kn]|Jo[(c&Kn)+1]);else for(r(f,vr[l&Kn]),r(d,Jo[(c&Kn)+1]),h=f+1;hs<0||o<0||u>=t||a>=e}}function hie(e,t,n){const i=Math.max(1,Math.sqrt(e*t/1e6)),r=~~((e+2*n+i)/i),s=~~((t+2*n+i)/i),o=a=>~~((a+n)/i);return o.invert=a=>a*i-n,o.bitmap=()=>die(r,s),o.ratio=i,o.padding=n,o.width=e,o.height=t,o}function pie(e,t,n,i){const r=e.width,s=e.height;return function(o){const a=o.datum.datum.items[i].items,u=a.length,l=o.datum.fontSize,c=Ai.width(o.datum,o.datum.text);let f=0,d,h,p,g,m,y,b;for(let v=0;v=f&&(f=b,o.x=m,o.y=y);return m=c/2,y=l/2,d=o.x-m,h=o.x+m,p=o.y-y,g=o.y+y,o.align="center",d<0&&h<=r?o.align="left":0<=d&&rr||t-(o=i/2)<0||t+o>s}function Qo(e,t,n,i,r,s,o,a){const u=r*s/(i*2),l=e(t-u),c=e(t+u),f=e(n-(s=s/2)),d=e(n+s);return o.outOfBounds(l,f,c,d)||o.getRange(l,f,c,d)||a&&a.getRange(l,f,c,d)}function gie(e,t,n,i){const r=e.width,s=e.height,o=t[0],a=t[1];function u(l,c,f,d,h){const p=e.invert(l),g=e.invert(c);let m=f,y=s,b;if(!Og(p,g,d,h,r,s)&&!Qo(e,p,g,h,d,m,o,a)&&!Qo(e,p,g,h,d,h,o,null)){for(;y-m>=1;)b=(m+y)/2,Qo(e,p,g,h,d,b,o,a)?y=b:m=b;if(m>f)return[p,g,m,!0]}}return function(l){const c=l.datum.datum.items[i].items,f=c.length,d=l.datum.fontSize,h=Ai.width(l.datum,l.datum.text);let p=n?d:0,g=!1,m=!1,y=0,b,v,x,_,E,w,C,k,S,$,O,R,F,A,T,B,H;for(let z=0;zv&&(H=b,b=v,v=H),x>_&&(H=x,x=_,_=H),S=e(b),O=e(v),$=~~((S+O)/2),R=e(x),A=e(_),F=~~((R+A)/2),C=$;C>=S;--C)for(k=F;k>=R;--k)B=u(C,k,p,h,d),B&&([l.x,l.y,p,g]=B);for(C=$;C<=O;++C)for(k=F;k<=A;++k)B=u(C,k,p,h,d),B&&([l.x,l.y,p,g]=B);!g&&!n&&(T=Math.abs(v-b+_-x),E=(b+v)/2,w=(x+_)/2,T>=y&&!Og(E,w,h,d,r,s)&&!Qo(e,E,w,d,h,d,o,null)&&(y=T,l.x=E,l.y=w,m=!0))}return g||m?(E=h/2,w=d/2,o.setRange(e(l.x-E),e(l.y-w),e(l.x+E),e(l.y+w)),l.align="center",l.baseline="middle",!0):!1}}const mie=[-1,-1,1,1],yie=[-1,1,-1,1];function bie(e,t,n,i){const r=e.width,s=e.height,o=t[0],a=t[1],u=e.bitmap();return function(l){const c=l.datum.datum.items[i].items,f=c.length,d=l.datum.fontSize,h=Ai.width(l.datum,l.datum.text),p=[];let g=n?d:0,m=!1,y=!1,b=0,v,x,_,E,w,C,k,S,$,O,R,F;for(let A=0;A=1;)R=($+O)/2,Qo(e,w,C,d,h,R,o,a)?O=R:$=R;$>g&&(l.x=w,l.y=C,g=$,m=!0)}}!m&&!n&&(F=Math.abs(x-v+E-_),w=(v+x)/2,C=(_+E)/2,F>=b&&!Og(w,C,h,d,r,s)&&!Qo(e,w,C,d,h,d,o,null)&&(b=F,l.x=w,l.y=C,y=!0))}return m||y?(w=h/2,C=d/2,o.setRange(e(l.x-w),e(l.y-C),e(l.x+w),e(l.y+C)),l.align="center",l.baseline="middle",!0):!1}}const vie=["right","center","left"],xie=["bottom","middle","top"];function _ie(e,t,n,i){const r=e.width,s=e.height,o=t[0],a=t[1],u=i.length;return function(l){const c=l.boundary,f=l.datum.fontSize;if(c[2]<0||c[5]<0||c[0]>r||c[3]>s)return!1;let d=l.textWidth??0,h,p,g,m,y,b,v,x,_,E,w,C,k,S,$;for(let O=0;O>>2&3)-1,g=h===0&&p===0||i[O]<0,m=h&&p?Math.SQRT1_2:1,y=i[O]<0?-1:1,b=c[1+h]+i[O]*h*m,w=c[4+p]+y*f*p/2+i[O]*p*m,x=w-f/2,_=w+f/2,C=e(b),S=e(x),$=e(_),!d)if(eD(C,C,S,$,o,a,b,b,x,_,c,g))d=Ai.width(l.datum,l.datum.text);else continue;if(E=b+y*d*h/2,b=E-d/2,v=E+d/2,C=e(b),k=e(v),eD(C,k,S,$,o,a,b,v,x,_,c,g))return l.x=h?h*y<0?v:b:E,l.y=p?p*y<0?_:x:w,l.align=vie[h*y+1],l.baseline=xie[p*y+1],o.setRange(C,S,k,$),!0}return!1}}function eD(e,t,n,i,r,s,o,a,u,l,c,f){return!(r.outOfBounds(e,n,t,i)||(f&&s||r).getRange(e,n,t,i))}const Fx=0,Dx=4,Tx=8,Mx=0,Nx=1,Rx=2,wie={"top-left":Fx+Mx,top:Fx+Nx,"top-right":Fx+Rx,left:Dx+Mx,middle:Dx+Nx,right:Dx+Rx,"bottom-left":Tx+Mx,bottom:Tx+Nx,"bottom-right":Tx+Rx},Eie={naive:pie,"reduced-search":gie,floodfill:bie};function Cie(e,t,n,i,r,s,o,a,u,l,c){if(!e.length)return e;const f=Math.max(i.length,r.length),d=kie(i,f),h=Aie(r,f),p=$ie(e[0].datum),g=p==="group"&&e[0].datum.items[u].marktype,m=g==="area",y=Sie(p,g,a,u),b=l===null||l===1/0,v=m&&c==="naive";let x=-1,_=-1;const E=e.map(S=>{const $=b?Ai.width(S,S.text):void 0;return x=Math.max(x,$),_=Math.max(_,S.fontSize),{datum:S,opacity:0,x:void 0,y:void 0,align:void 0,baseline:void 0,boundary:y(S),textWidth:$}});l=l===null||l===1/0?Math.max(x,_)+Math.max(...i):l;const w=hie(t[0],t[1],l);let C;if(!v){n&&E.sort((O,R)=>n(O.datum,R.datum));let S=!1;for(let O=0;OO.datum);C=s.length||$?cie(w,$||[],s,S,m):lie(w,o&&E)}const k=m?Eie[c](w,C,o,u):_ie(w,C,h,d);return E.forEach(S=>S.opacity=+k(S)),E}function kie(e,t){const n=new Float64Array(t),i=e.length;for(let r=0;r[s.x,s.x,s.x,s.y,s.y,s.y];return e?e==="line"||e==="area"?s=>r(s.datum):t==="line"?s=>{const o=s.datum.items[i].items;return r(o.length?o[n==="start"?0:o.length-1]:{x:NaN,y:NaN})}:s=>{const o=s.datum.bounds;return[o.x1,(o.x1+o.x2)/2,o.x2,o.y1,(o.y1+o.y2)/2,o.y2]}:r}const Ox=["x","y","opacity","align","baseline"],tD=["top-left","left","bottom-left","top","bottom","top-right","right","bottom-right"];function Lx(e){P.call(this,null,e)}Lx.Definition={type:"Label",metadata:{modifies:!0},params:[{name:"size",type:"number",array:!0,length:2,required:!0},{name:"sort",type:"compare"},{name:"anchor",type:"string",array:!0,default:tD},{name:"offset",type:"number",array:!0,default:[1]},{name:"padding",type:"number",default:0,null:!0},{name:"lineAnchor",type:"string",values:["start","end"],default:"end"},{name:"markIndex",type:"number",default:0},{name:"avoidBaseMark",type:"boolean",default:!0},{name:"avoidMarks",type:"data",array:!0},{name:"method",type:"string",default:"naive"},{name:"as",type:"string",array:!0,length:Ox.length,default:Ox}]},ee(Lx,P,{transform(e,t){function n(s){const o=e[s];return Ie(o)&&t.modified(o.fields)}const i=e.modified();if(!(i||t.changed(t.ADD_REM)||n("sort")))return;(!e.size||e.size.length!==2)&&q("Size parameter should be specified as a [width, height] array.");const r=e.as||Ox;return Cie(t.materialize(t.SOURCE).source||[],e.size,e.sort,oe(e.offset==null?1:e.offset),oe(e.anchor||tD),e.avoidMarks||[],e.avoidBaseMark!==!1,e.lineAnchor||"end",e.markIndex||0,e.padding===void 0?0:e.padding,e.method||"naive").forEach(s=>{const o=s.datum;o[r[0]]=s.x,o[r[1]]=s.y,o[r[2]]=s.opacity,o[r[3]]=s.align,o[r[4]]=s.baseline}),t.reflow(i).modifies(r)}});const Fie=Object.freeze(Object.defineProperty({__proto__:null,label:Lx},Symbol.toStringTag,{value:"Module"}));function nD(e,t){var n=[],i=function(c){return c(a)},r,s,o,a,u,l;if(t==null)n.push(e);else for(r={},s=0,o=e.length;s{i9(l,e.x,e.y,e.bandwidth||.3).forEach(c=>{const f={};for(let d=0;de==="poly"?t:e==="quad"?2:1;function zx(e){P.call(this,null,e)}zx.Definition={type:"Regression",metadata:{generates:!0},params:[{name:"x",type:"field",required:!0},{name:"y",type:"field",required:!0},{name:"groupby",type:"field",array:!0},{name:"method",type:"string",default:"linear",values:Object.keys(Px)},{name:"order",type:"number",default:3},{name:"extent",type:"number",array:!0,length:2},{name:"params",type:"boolean",default:!1},{name:"as",type:"string",array:!0}]},ee(zx,P,{transform(e,t){const n=t.fork(t.NO_SOURCE|t.NO_FIELDS);if(!this.value||t.changed()||e.modified()){const i=t.materialize(t.SOURCE).source,r=nD(i,e.groupby),s=(e.groupby||[]).map(Tt),o=e.method||"linear",a=e.order==null?3:e.order,u=Die(o,a),l=e.as||[Tt(e.x),Tt(e.y)],c=Px[o],f=[];let d=e.extent;ue(Px,o)||q("Invalid regression method: "+o),d!=null&&o==="log"&&d[0]<=0&&(t.dataflow.warn("Ignoring extent with values <= 0 for log regression."),d=null),r.forEach(h=>{if(h.length<=u){t.dataflow.warn("Skipping regression with more parameters than data points.");return}const g=c(h,e.x,e.y,a);if(e.params){f.push(nt({keys:h.dims,coef:g.coef,rSquared:g.rSquared}));return}const m=d||qr(h,e.x),y=b=>{const v={};for(let x=0;xy([b,g.predict(b)])):A0(g.predict,m,25,200).forEach(y)}),this.value&&(n.rem=this.value),this.value=n.add=n.source=f}return n}});const Tie=Object.freeze(Object.defineProperty({__proto__:null,loess:Ix,regression:zx},Symbol.toStringTag,{value:"Module"})),Xs=11102230246251565e-32,Mn=134217729,Mie=(3+8*Xs)*Xs;function Bx(e,t,n,i,r){let s,o,a,u,l=t[0],c=i[0],f=0,d=0;c>l==c>-l?(s=l,l=t[++f]):(s=c,c=i[++d]);let h=0;if(fl==c>-l?(o=l+s,a=s-(o-l),l=t[++f]):(o=c+s,a=s-(o-c),c=i[++d]),s=o,a!==0&&(r[h++]=a);fl==c>-l?(o=s+l,u=o-s,a=s-(o-u)+(l-u),l=t[++f]):(o=s+c,u=o-s,a=s-(o-u)+(c-u),c=i[++d]),s=o,a!==0&&(r[h++]=a);for(;f=F||-R>=F||(f=e-k,a=e-(k+f)+(f-r),f=n-S,l=n-(S+f)+(f-r),f=t-$,u=t-($+f)+(f-s),f=i-O,c=i-(O+f)+(f-s),a===0&&u===0&&l===0&&c===0)||(F=Lie*o+Mie*Math.abs(R),R+=k*c+O*a-($*l+S*u),R>=F||-R>=F))return R;x=a*O,d=Mn*a,h=d-(d-a),p=a-h,d=Mn*O,g=d-(d-O),m=O-g,_=p*m-(x-h*g-p*g-h*m),E=u*S,d=Mn*u,h=d-(d-u),p=u-h,d=Mn*S,g=d-(d-S),m=S-g,w=p*m-(E-h*g-p*g-h*m),y=_-w,f=_-y,Zn[0]=_-(y+f)+(f-w),b=x+y,f=b-x,v=x-(b-f)+(y-f),y=v-E,f=v-y,Zn[1]=v-(y+f)+(f-E),C=b+y,f=C-b,Zn[2]=b-(C-f)+(y-f),Zn[3]=C;const A=Bx(4,Kl,4,Zn,iD);x=k*c,d=Mn*k,h=d-(d-k),p=k-h,d=Mn*c,g=d-(d-c),m=c-g,_=p*m-(x-h*g-p*g-h*m),E=$*l,d=Mn*$,h=d-(d-$),p=$-h,d=Mn*l,g=d-(d-l),m=l-g,w=p*m-(E-h*g-p*g-h*m),y=_-w,f=_-y,Zn[0]=_-(y+f)+(f-w),b=x+y,f=b-x,v=x-(b-f)+(y-f),y=v-E,f=v-y,Zn[1]=v-(y+f)+(f-E),C=b+y,f=C-b,Zn[2]=b-(C-f)+(y-f),Zn[3]=C;const T=Bx(A,iD,4,Zn,rD);x=a*c,d=Mn*a,h=d-(d-a),p=a-h,d=Mn*c,g=d-(d-c),m=c-g,_=p*m-(x-h*g-p*g-h*m),E=u*l,d=Mn*u,h=d-(d-u),p=u-h,d=Mn*l,g=d-(d-l),m=l-g,w=p*m-(E-h*g-p*g-h*m),y=_-w,f=_-y,Zn[0]=_-(y+f)+(f-w),b=x+y,f=b-x,v=x-(b-f)+(y-f),y=v-E,f=v-y,Zn[1]=v-(y+f)+(f-E),C=b+y,f=C-b,Zn[2]=b-(C-f)+(y-f),Zn[3]=C;const B=Bx(T,rD,4,Zn,sD);return sD[B-1]}function Lg(e,t,n,i,r,s){const o=(t-s)*(n-r),a=(e-r)*(i-s),u=o-a,l=Math.abs(o+a);return Math.abs(u)>=Rie*l?u:-Iie(e,t,n,i,r,s,l)}const oD=Math.pow(2,-52),Ig=new Uint32Array(512);class Pg{static from(t,n=jie,i=qie){const r=t.length,s=new Float64Array(r*2);for(let o=0;o>1;if(n>0&&typeof t[0]!="number")throw new Error("Expected coords to contain numbers.");this.coords=t;const i=Math.max(2*n-5,0);this._triangles=new Uint32Array(i*3),this._halfedges=new Int32Array(i*3),this._hashSize=Math.ceil(Math.sqrt(n)),this._hullPrev=new Uint32Array(n),this._hullNext=new Uint32Array(n),this._hullTri=new Uint32Array(n),this._hullHash=new Int32Array(this._hashSize),this._ids=new Uint32Array(n),this._dists=new Float64Array(n),this.update()}update(){const{coords:t,_hullPrev:n,_hullNext:i,_hullTri:r,_hullHash:s}=this,o=t.length>>1;let a=1/0,u=1/0,l=-1/0,c=-1/0;for(let k=0;kl&&(l=S),$>c&&(c=$),this._ids[k]=k}const f=(a+l)/2,d=(u+c)/2;let h,p,g;for(let k=0,S=1/0;k0&&(p=k,S=$)}let b=t[2*p],v=t[2*p+1],x=1/0;for(let k=0;kO&&(k[S++]=R,O=F)}this.hull=k.subarray(0,S),this.triangles=new Uint32Array(0),this.halfedges=new Uint32Array(0);return}if(Lg(m,y,b,v,_,E)<0){const k=p,S=b,$=v;p=g,b=_,v=E,g=k,_=S,E=$}const w=Uie(m,y,b,v,_,E);this._cx=w.x,this._cy=w.y;for(let k=0;k0&&Math.abs(R-S)<=oD&&Math.abs(F-$)<=oD||(S=R,$=F,O===h||O===p||O===g))continue;let A=0;for(let ne=0,be=this._hashKey(R,F);ne=0;)if(T=B,T===A){T=-1;break}if(T===-1)continue;let H=this._addTriangle(T,O,i[T],-1,-1,r[T]);r[O]=this._legalize(H+2),r[T]=H,C++;let z=i[T];for(;B=i[z],Lg(R,F,t[2*z],t[2*z+1],t[2*B],t[2*B+1])<0;)H=this._addTriangle(z,O,B,r[O],-1,r[z]),r[O]=this._legalize(H+2),i[z]=z,C--,z=B;if(T===A)for(;B=n[T],Lg(R,F,t[2*B],t[2*B+1],t[2*T],t[2*T+1])<0;)H=this._addTriangle(B,O,T,-1,r[T],r[B]),this._legalize(H+2),r[B]=H,i[T]=T,C--,T=B;this._hullStart=n[O]=T,i[T]=n[z]=O,i[O]=z,s[this._hashKey(R,F)]=O,s[this._hashKey(t[2*T],t[2*T+1])]=T}this.hull=new Uint32Array(C);for(let k=0,S=this._hullStart;k0?3-n:1+n)/4}function Ux(e,t,n,i){const r=e-n,s=t-i;return r*r+s*s}function zie(e,t,n,i,r,s,o,a){const u=e-o,l=t-a,c=n-o,f=i-a,d=r-o,h=s-a,p=u*u+l*l,g=c*c+f*f,m=d*d+h*h;return u*(f*m-g*h)-l*(c*m-g*d)+p*(c*h-f*d)<0}function Bie(e,t,n,i,r,s){const o=n-e,a=i-t,u=r-e,l=s-t,c=o*o+a*a,f=u*u+l*l,d=.5/(o*l-a*u),h=(l*c-a*f)*d,p=(o*f-u*c)*d;return h*h+p*p}function Uie(e,t,n,i,r,s){const o=n-e,a=i-t,u=r-e,l=s-t,c=o*o+a*a,f=u*u+l*l,d=.5/(o*l-a*u),h=e+(l*c-a*f)*d,p=t+(o*f-u*c)*d;return{x:h,y:p}}function Zl(e,t,n,i){if(i-n<=20)for(let r=n+1;r<=i;r++){const s=e[r],o=t[s];let a=r-1;for(;a>=n&&t[e[a]]>o;)e[a+1]=e[a--];e[a+1]=s}else{const r=n+i>>1;let s=n+1,o=i;xd(e,r,s),t[e[n]]>t[e[i]]&&xd(e,n,i),t[e[s]]>t[e[i]]&&xd(e,s,i),t[e[n]]>t[e[s]]&&xd(e,n,s);const a=e[s],u=t[a];for(;;){do s++;while(t[e[s]]u);if(o=o-n?(Zl(e,t,s,i),Zl(e,t,n,o-1)):(Zl(e,t,n,o-1),Zl(e,t,s,i))}}function xd(e,t,n){const i=e[t];e[t]=e[n],e[n]=i}function jie(e){return e[0]}function qie(e){return e[1]}const aD=1e-6;class eu{constructor(){this._x0=this._y0=this._x1=this._y1=null,this._=""}moveTo(t,n){this._+=`M${this._x0=this._x1=+t},${this._y0=this._y1=+n}`}closePath(){this._x1!==null&&(this._x1=this._x0,this._y1=this._y0,this._+="Z")}lineTo(t,n){this._+=`L${this._x1=+t},${this._y1=+n}`}arc(t,n,i){t=+t,n=+n,i=+i;const r=t+i,s=n;if(i<0)throw new Error("negative radius");this._x1===null?this._+=`M${r},${s}`:(Math.abs(this._x1-r)>aD||Math.abs(this._y1-s)>aD)&&(this._+="L"+r+","+s),i&&(this._+=`A${i},${i},0,1,1,${t-i},${n}A${i},${i},0,1,1,${this._x1=r},${this._y1=s}`)}rect(t,n,i,r){this._+=`M${this._x0=this._x1=+t},${this._y0=this._y1=+n}h${+i}v${+r}h${-i}Z`}value(){return this._||null}}class jx{constructor(){this._=[]}moveTo(t,n){this._.push([t,n])}closePath(){this._.push(this._[0].slice())}lineTo(t,n){this._.push([t,n])}value(){return this._.length?this._:null}}let Wie=class{constructor(t,[n,i,r,s]=[0,0,960,500]){if(!((r=+r)>=(n=+n))||!((s=+s)>=(i=+i)))throw new Error("invalid bounds");this.delaunay=t,this._circumcenters=new Float64Array(t.points.length*2),this.vectors=new Float64Array(t.points.length*2),this.xmax=r,this.xmin=n,this.ymax=s,this.ymin=i,this._init()}update(){return this.delaunay.update(),this._init(),this}_init(){const{delaunay:{points:t,hull:n,triangles:i},vectors:r}=this;let s,o;const a=this.circumcenters=this._circumcenters.subarray(0,i.length/3*2);for(let g=0,m=0,y=i.length,b,v;g1;)s-=2;for(let o=2;o0){if(n>=this.ymax)return null;(o=(this.ymax-n)/r)0){if(t>=this.xmax)return null;(o=(this.xmax-t)/i)this.xmax?2:0)|(nthis.ymax?8:0)}_simplify(t){if(t&&t.length>4){for(let n=0;n1e-10)return!1}return!0}function Xie(e,t,n){return[e+Math.sin(e+t)*n,t+Math.cos(e-t)*n]}class qx{static from(t,n=Gie,i=Vie,r){return new qx("length"in t?Kie(t,n,i,r):Float64Array.from(Zie(t,n,i,r)))}constructor(t){this._delaunator=new Pg(t),this.inedges=new Int32Array(t.length/2),this._hullIndex=new Int32Array(t.length/2),this.points=this._delaunator.coords,this._init()}update(){return this._delaunator.update(),this._init(),this}_init(){const t=this._delaunator,n=this.points;if(t.hull&&t.hull.length>2&&Yie(t)){this.collinear=Int32Array.from({length:n.length/2},(d,h)=>h).sort((d,h)=>n[2*d]-n[2*h]||n[2*d+1]-n[2*h+1]);const u=this.collinear[0],l=this.collinear[this.collinear.length-1],c=[n[2*u],n[2*u+1],n[2*l],n[2*l+1]],f=1e-8*Math.hypot(c[3]-c[1],c[2]-c[0]);for(let d=0,h=n.length/2;d0&&(this.triangles=new Int32Array(3).fill(-1),this.halfedges=new Int32Array(3).fill(-1),this.triangles[0]=r[0],o[r[0]]=1,r.length===2&&(o[r[1]]=0,this.triangles[1]=r[1],this.triangles[2]=r[1]))}voronoi(t){return new Wie(this,t)}*neighbors(t){const{inedges:n,hull:i,_hullIndex:r,halfedges:s,triangles:o,collinear:a}=this;if(a){const f=a.indexOf(t);f>0&&(yield a[f-1]),f=0&&s!==i&&s!==r;)i=s;return s}_step(t,n,i){const{inedges:r,hull:s,_hullIndex:o,halfedges:a,triangles:u,points:l}=this;if(r[t]===-1||!l.length)return(t+1)%(l.length>>1);let c=t,f=Jl(n-l[t*2],2)+Jl(i-l[t*2+1],2);const d=r[t];let h=d;do{let p=u[h];const g=Jl(n-l[p*2],2)+Jl(i-l[p*2+1],2);if(g>5)*e[1]),m=null,y=l.length,b=-1,v=[],x=l.map(E=>({text:t(E),font:n(E),style:r(E),weight:s(E),rotate:o(E),size:~~(i(E)+1e-14),padding:a(E),xoff:0,yoff:0,x1:0,y1:0,x0:0,y0:0,hasText:!1,sprite:null,datum:E})).sort((E,w)=>w.size-E.size);++b>1,_.y=e[1]*(c()+.5)>>1,ire(p,_,x,b),_.hasText&&h(g,_,m)&&(v.push(_),m?sre(m,_):m=[{x:_.x+_.x0,y:_.y+_.y0},{x:_.x+_.x1,y:_.y+_.y1}],_.x-=e[0]>>1,_.y-=e[1]>>1)}return v};function d(p){p.width=p.height=1;var g=Math.sqrt(p.getContext("2d").getImageData(0,0,1,1).data.length>>2);p.width=(_d<<5)/g,p.height=zg/g;var m=p.getContext("2d");return m.fillStyle=m.strokeStyle="red",m.textAlign="center",{context:m,ratio:g}}function h(p,g,m){for(var y=g.x,b=g.y,v=Math.hypot(e[0],e[1]),x=u(e),_=c()<.5?1:-1,E=-_,w,C,k;(w=x(E+=_))&&(C=~~w[0],k=~~w[1],!(Math.min(Math.abs(C),Math.abs(k))>=v));)if(g.x=y+C,g.y=b+k,!(g.x+g.x0<0||g.y+g.y0<0||g.x+g.x1>e[0]||g.y+g.y1>e[1])&&(!m||!rre(g,p,e[0]))&&(!m||ore(g,m))){for(var S=g.sprite,$=g.width>>5,O=e[0]>>5,R=g.x-($<<4),F=R&127,A=32-F,T=g.y1-g.y0,B=(g.y+g.y0)*O+(R>>5),H,z=0;z>>F:0);B+=O}return g.sprite=null,!0}return!1}return f.words=function(p){return arguments.length?(l=p,f):l},f.size=function(p){return arguments.length?(e=[+p[0],+p[1]],f):e},f.font=function(p){return arguments.length?(n=tu(p),f):n},f.fontStyle=function(p){return arguments.length?(r=tu(p),f):r},f.fontWeight=function(p){return arguments.length?(s=tu(p),f):s},f.rotate=function(p){return arguments.length?(o=tu(p),f):o},f.text=function(p){return arguments.length?(t=tu(p),f):t},f.spiral=function(p){return arguments.length?(u=lre[p]||p,f):u},f.fontSize=function(p){return arguments.length?(i=tu(p),f):i},f.padding=function(p){return arguments.length?(a=tu(p),f):a},f.random=function(p){return arguments.length?(c=p,f):c},f}function ire(e,t,n,i){if(!t.sprite){var r=e.context,s=e.ratio;r.clearRect(0,0,(_d<<5)/s,zg/s);var o=0,a=0,u=0,l=n.length,c,f,d,h,p;for(--i;++i>5<<5,d=~~Math.max(Math.abs(b+v),Math.abs(b-v))}else c=c+31>>5<<5;if(d>u&&(u=d),o+c>=_d<<5&&(o=0,a+=u,u=0),a+d>=zg)break;r.translate((o+(c>>1))/s,(a+(d>>1))/s),t.rotate&&r.rotate(t.rotate*Hx),r.fillText(t.text,0,0),t.padding&&(r.lineWidth=2*t.padding,r.strokeText(t.text,0,0)),r.restore(),t.width=c,t.height=d,t.xoff=o,t.yoff=a,t.x1=c>>1,t.y1=d>>1,t.x0=-t.x1,t.y0=-t.y1,t.hasText=!0,o+=c}for(var _=r.getImageData(0,0,(_d<<5)/s,zg/s).data,E=[];--i>=0;)if(t=n[i],!!t.hasText){for(c=t.width,f=c>>5,d=t.y1-t.y0,h=0;h>5),S=_[(a+p)*(_d<<5)+(o+h)<<2]?1<<31-h%32:0;E[k]|=S,w|=S}w?C=p:(t.y0++,d--,p--,a++)}t.y1=t.y0+C,t.sprite=E.slice(0,(t.y1-t.y0)*f)}}}function rre(e,t,n){n>>=5;for(var i=e.sprite,r=e.width>>5,s=e.x-(r<<4),o=s&127,a=32-o,u=e.y1-e.y0,l=(e.y+e.y0)*n+(s>>5),c,f=0;f>>o:0))&t[l+d])return!0;l+=n}return!1}function sre(e,t){var n=e[0],i=e[1];t.x+t.x0i.x&&(i.x=t.x+t.x1),t.y+t.y1>i.y&&(i.y=t.y+t.y1)}function ore(e,t){return e.x+e.x1>t[0].x&&e.x+e.x0t[0].y&&e.y+e.y0g(p(m))}r.forEach(p=>{p[o[0]]=NaN,p[o[1]]=NaN,p[o[3]]=0});const l=s.words(r).text(e.text).size(e.size||[500,500]).padding(e.padding||1).spiral(e.spiral||"archimedean").rotate(e.rotate||0).font(e.font||"sans-serif").fontStyle(e.fontStyle||"normal").fontWeight(e.fontWeight||"normal").fontSize(a).random(Wi).layout(),c=s.size(),f=c[0]>>1,d=c[1]>>1,h=l.length;for(let p=0,g,m;pnew Uint8Array(e),hre=e=>new Uint16Array(e),wd=e=>new Uint32Array(e);function pre(){let e=8,t=[],n=wd(0),i=Bg(0,e),r=Bg(0,e);return{data:()=>t,seen:()=>n=gre(n,t.length),add(s){for(let o=0,a=t.length,u=s.length,l;ot.length,curr:()=>i,prev:()=>r,reset:s=>r[s]=i[s],all:()=>e<257?255:e<65537?65535:4294967295,set(s,o){i[s]|=o},clear(s,o){i[s]&=~o},resize(s,o){const a=i.length;(s>a||o>e)&&(e=Math.max(o,e),i=Bg(s,e,i),r=Bg(s,e))}}}function gre(e,t,n){return e.length>=t?e:(n=n||new e.constructor(t),n.set(e),n)}function Bg(e,t,n){const i=(t<257?dre:t<65537?hre:wd)(e);return n&&i.set(n),i}function cD(e,t,n){const i=1<0)for(m=0;me,size:()=>n}}function mre(e,t){return e.sort.call(t,(n,i)=>{const r=e[n],s=e[i];return rs?1:0}),Tq(e,t)}function yre(e,t,n,i,r,s,o,a,u){let l=0,c=0,f;for(f=0;lt.modified(i.fields));return n?this.reinit(e,t):this.eval(e,t)}else return this.init(e,t)},init(e,t){const n=e.fields,i=e.query,r=this._indices={},s=this._dims=[],o=i.length;let a=0,u,l;for(;a{const s=r.remove(t,n);for(const o in i)i[o].reindex(s)})},update(e,t,n){const i=this._dims,r=e.query,s=t.stamp,o=i.length;let a=0,u,l;for(n.filters=0,l=0;lh)for(m=h,y=Math.min(f,p);mp)for(m=Math.max(f,p),y=d;mf)for(p=f,g=Math.min(l,d);pd)for(p=Math.max(l,d),g=c;pa[c]&n?null:o[c];return s.filter(s.MOD,l),r&r-1?(s.filter(s.ADD,c=>{const f=a[c]&n;return!f&&f^u[c]&n?o[c]:null}),s.filter(s.REM,c=>{const f=a[c]&n;return f&&!(f^(f^u[c]&n))?o[c]:null})):(s.filter(s.ADD,l),s.filter(s.REM,c=>(a[c]&n)===r?o[c]:null)),s.filter(s.SOURCE,c=>l(c._index))}});const bre=Object.freeze(Object.defineProperty({__proto__:null,crossfilter:Vx,resolvefilter:Yx},Symbol.toStringTag,{value:"Module"})),vre="RawCode",nu="Literal",xre="Property",_re="Identifier",wre="ArrayExpression",Ere="BinaryExpression",dD="CallExpression",Cre="ConditionalExpression",kre="LogicalExpression",Are="MemberExpression",$re="ObjectExpression",Sre="UnaryExpression";function xr(e){this.type=e}xr.prototype.visit=function(e){let t,n,i;if(e(this))return 1;for(t=Fre(this),n=0,i=t.length;n",as[iu]="Identifier",as[ea]="Keyword",as[jg]="Null",as[ru]="Numeric",as[ci]="Punctuator",as[Cd]="String",as[Dre]="RegularExpression";var Tre="ArrayExpression",Mre="BinaryExpression",Nre="CallExpression",Rre="ConditionalExpression",hD="Identifier",Ore="Literal",Lre="LogicalExpression",Ire="MemberExpression",Pre="ObjectExpression",zre="Property",Bre="UnaryExpression",en="Unexpected token %0",Ure="Unexpected number",jre="Unexpected string",qre="Unexpected identifier",Wre="Unexpected reserved word",Hre="Unexpected end of input",Xx="Invalid regular expression",Kx="Invalid regular expression: missing /",pD="Octal literals are not allowed in strict mode.",Gre="Duplicate data property in object literal not allowed in strict mode",hn="ILLEGAL",kd="Disabled.",Vre=new RegExp("[\\xAA\\xB5\\xBA\\xC0-\\xD6\\xD8-\\xF6\\xF8-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0370-\\u0374\\u0376\\u0377\\u037A-\\u037D\\u037F\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u048A-\\u052F\\u0531-\\u0556\\u0559\\u0561-\\u0587\\u05D0-\\u05EA\\u05F0-\\u05F2\\u0620-\\u064A\\u066E\\u066F\\u0671-\\u06D3\\u06D5\\u06E5\\u06E6\\u06EE\\u06EF\\u06FA-\\u06FC\\u06FF\\u0710\\u0712-\\u072F\\u074D-\\u07A5\\u07B1\\u07CA-\\u07EA\\u07F4\\u07F5\\u07FA\\u0800-\\u0815\\u081A\\u0824\\u0828\\u0840-\\u0858\\u08A0-\\u08B2\\u0904-\\u0939\\u093D\\u0950\\u0958-\\u0961\\u0971-\\u0980\\u0985-\\u098C\\u098F\\u0990\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BD\\u09CE\\u09DC\\u09DD\\u09DF-\\u09E1\\u09F0\\u09F1\\u0A05-\\u0A0A\\u0A0F\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32\\u0A33\\u0A35\\u0A36\\u0A38\\u0A39\\u0A59-\\u0A5C\\u0A5E\\u0A72-\\u0A74\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2\\u0AB3\\u0AB5-\\u0AB9\\u0ABD\\u0AD0\\u0AE0\\u0AE1\\u0B05-\\u0B0C\\u0B0F\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32\\u0B33\\u0B35-\\u0B39\\u0B3D\\u0B5C\\u0B5D\\u0B5F-\\u0B61\\u0B71\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99\\u0B9A\\u0B9C\\u0B9E\\u0B9F\\u0BA3\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BD0\\u0C05-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C39\\u0C3D\\u0C58\\u0C59\\u0C60\\u0C61\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBD\\u0CDE\\u0CE0\\u0CE1\\u0CF1\\u0CF2\\u0D05-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D3A\\u0D3D\\u0D4E\\u0D60\\u0D61\\u0D7A-\\u0D7F\\u0D85-\\u0D96\\u0D9A-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD\\u0DC0-\\u0DC6\\u0E01-\\u0E30\\u0E32\\u0E33\\u0E40-\\u0E46\\u0E81\\u0E82\\u0E84\\u0E87\\u0E88\\u0E8A\\u0E8D\\u0E94-\\u0E97\\u0E99-\\u0E9F\\u0EA1-\\u0EA3\\u0EA5\\u0EA7\\u0EAA\\u0EAB\\u0EAD-\\u0EB0\\u0EB2\\u0EB3\\u0EBD\\u0EC0-\\u0EC4\\u0EC6\\u0EDC-\\u0EDF\\u0F00\\u0F40-\\u0F47\\u0F49-\\u0F6C\\u0F88-\\u0F8C\\u1000-\\u102A\\u103F\\u1050-\\u1055\\u105A-\\u105D\\u1061\\u1065\\u1066\\u106E-\\u1070\\u1075-\\u1081\\u108E\\u10A0-\\u10C5\\u10C7\\u10CD\\u10D0-\\u10FA\\u10FC-\\u1248\\u124A-\\u124D\\u1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u1380-\\u138F\\u13A0-\\u13F4\\u1401-\\u166C\\u166F-\\u167F\\u1681-\\u169A\\u16A0-\\u16EA\\u16EE-\\u16F8\\u1700-\\u170C\\u170E-\\u1711\\u1720-\\u1731\\u1740-\\u1751\\u1760-\\u176C\\u176E-\\u1770\\u1780-\\u17B3\\u17D7\\u17DC\\u1820-\\u1877\\u1880-\\u18A8\\u18AA\\u18B0-\\u18F5\\u1900-\\u191E\\u1950-\\u196D\\u1970-\\u1974\\u1980-\\u19AB\\u19C1-\\u19C7\\u1A00-\\u1A16\\u1A20-\\u1A54\\u1AA7\\u1B05-\\u1B33\\u1B45-\\u1B4B\\u1B83-\\u1BA0\\u1BAE\\u1BAF\\u1BBA-\\u1BE5\\u1C00-\\u1C23\\u1C4D-\\u1C4F\\u1C5A-\\u1C7D\\u1CE9-\\u1CEC\\u1CEE-\\u1CF1\\u1CF5\\u1CF6\\u1D00-\\u1DBF\\u1E00-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FBC\\u1FBE\\u1FC2-\\u1FC4\\u1FC6-\\u1FCC\\u1FD0-\\u1FD3\\u1FD6-\\u1FDB\\u1FE0-\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FFC\\u2071\\u207F\\u2090-\\u209C\\u2102\\u2107\\u210A-\\u2113\\u2115\\u2119-\\u211D\\u2124\\u2126\\u2128\\u212A-\\u212D\\u212F-\\u2139\\u213C-\\u213F\\u2145-\\u2149\\u214E\\u2160-\\u2188\\u2C00-\\u2C2E\\u2C30-\\u2C5E\\u2C60-\\u2CE4\\u2CEB-\\u2CEE\\u2CF2\\u2CF3\\u2D00-\\u2D25\\u2D27\\u2D2D\\u2D30-\\u2D67\\u2D6F\\u2D80-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2E2F\\u3005-\\u3007\\u3021-\\u3029\\u3031-\\u3035\\u3038-\\u303C\\u3041-\\u3096\\u309D-\\u309F\\u30A1-\\u30FA\\u30FC-\\u30FF\\u3105-\\u312D\\u3131-\\u318E\\u31A0-\\u31BA\\u31F0-\\u31FF\\u3400-\\u4DB5\\u4E00-\\u9FCC\\uA000-\\uA48C\\uA4D0-\\uA4FD\\uA500-\\uA60C\\uA610-\\uA61F\\uA62A\\uA62B\\uA640-\\uA66E\\uA67F-\\uA69D\\uA6A0-\\uA6EF\\uA717-\\uA71F\\uA722-\\uA788\\uA78B-\\uA78E\\uA790-\\uA7AD\\uA7B0\\uA7B1\\uA7F7-\\uA801\\uA803-\\uA805\\uA807-\\uA80A\\uA80C-\\uA822\\uA840-\\uA873\\uA882-\\uA8B3\\uA8F2-\\uA8F7\\uA8FB\\uA90A-\\uA925\\uA930-\\uA946\\uA960-\\uA97C\\uA984-\\uA9B2\\uA9CF\\uA9E0-\\uA9E4\\uA9E6-\\uA9EF\\uA9FA-\\uA9FE\\uAA00-\\uAA28\\uAA40-\\uAA42\\uAA44-\\uAA4B\\uAA60-\\uAA76\\uAA7A\\uAA7E-\\uAAAF\\uAAB1\\uAAB5\\uAAB6\\uAAB9-\\uAABD\\uAAC0\\uAAC2\\uAADB-\\uAADD\\uAAE0-\\uAAEA\\uAAF2-\\uAAF4\\uAB01-\\uAB06\\uAB09-\\uAB0E\\uAB11-\\uAB16\\uAB20-\\uAB26\\uAB28-\\uAB2E\\uAB30-\\uAB5A\\uAB5C-\\uAB5F\\uAB64\\uAB65\\uABC0-\\uABE2\\uAC00-\\uD7A3\\uD7B0-\\uD7C6\\uD7CB-\\uD7FB\\uF900-\\uFA6D\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\uFB17\\uFB1D\\uFB1F-\\uFB28\\uFB2A-\\uFB36\\uFB38-\\uFB3C\\uFB3E\\uFB40\\uFB41\\uFB43\\uFB44\\uFB46-\\uFBB1\\uFBD3-\\uFD3D\\uFD50-\\uFD8F\\uFD92-\\uFDC7\\uFDF0-\\uFDFB\\uFE70-\\uFE74\\uFE76-\\uFEFC\\uFF21-\\uFF3A\\uFF41-\\uFF5A\\uFF66-\\uFFBE\\uFFC2-\\uFFC7\\uFFCA-\\uFFCF\\uFFD2-\\uFFD7\\uFFDA-\\uFFDC]"),Yre=new RegExp("[\\xAA\\xB5\\xBA\\xC0-\\xD6\\xD8-\\xF6\\xF8-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0300-\\u0374\\u0376\\u0377\\u037A-\\u037D\\u037F\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u0483-\\u0487\\u048A-\\u052F\\u0531-\\u0556\\u0559\\u0561-\\u0587\\u0591-\\u05BD\\u05BF\\u05C1\\u05C2\\u05C4\\u05C5\\u05C7\\u05D0-\\u05EA\\u05F0-\\u05F2\\u0610-\\u061A\\u0620-\\u0669\\u066E-\\u06D3\\u06D5-\\u06DC\\u06DF-\\u06E8\\u06EA-\\u06FC\\u06FF\\u0710-\\u074A\\u074D-\\u07B1\\u07C0-\\u07F5\\u07FA\\u0800-\\u082D\\u0840-\\u085B\\u08A0-\\u08B2\\u08E4-\\u0963\\u0966-\\u096F\\u0971-\\u0983\\u0985-\\u098C\\u098F\\u0990\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BC-\\u09C4\\u09C7\\u09C8\\u09CB-\\u09CE\\u09D7\\u09DC\\u09DD\\u09DF-\\u09E3\\u09E6-\\u09F1\\u0A01-\\u0A03\\u0A05-\\u0A0A\\u0A0F\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32\\u0A33\\u0A35\\u0A36\\u0A38\\u0A39\\u0A3C\\u0A3E-\\u0A42\\u0A47\\u0A48\\u0A4B-\\u0A4D\\u0A51\\u0A59-\\u0A5C\\u0A5E\\u0A66-\\u0A75\\u0A81-\\u0A83\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2\\u0AB3\\u0AB5-\\u0AB9\\u0ABC-\\u0AC5\\u0AC7-\\u0AC9\\u0ACB-\\u0ACD\\u0AD0\\u0AE0-\\u0AE3\\u0AE6-\\u0AEF\\u0B01-\\u0B03\\u0B05-\\u0B0C\\u0B0F\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32\\u0B33\\u0B35-\\u0B39\\u0B3C-\\u0B44\\u0B47\\u0B48\\u0B4B-\\u0B4D\\u0B56\\u0B57\\u0B5C\\u0B5D\\u0B5F-\\u0B63\\u0B66-\\u0B6F\\u0B71\\u0B82\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99\\u0B9A\\u0B9C\\u0B9E\\u0B9F\\u0BA3\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BBE-\\u0BC2\\u0BC6-\\u0BC8\\u0BCA-\\u0BCD\\u0BD0\\u0BD7\\u0BE6-\\u0BEF\\u0C00-\\u0C03\\u0C05-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C39\\u0C3D-\\u0C44\\u0C46-\\u0C48\\u0C4A-\\u0C4D\\u0C55\\u0C56\\u0C58\\u0C59\\u0C60-\\u0C63\\u0C66-\\u0C6F\\u0C81-\\u0C83\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBC-\\u0CC4\\u0CC6-\\u0CC8\\u0CCA-\\u0CCD\\u0CD5\\u0CD6\\u0CDE\\u0CE0-\\u0CE3\\u0CE6-\\u0CEF\\u0CF1\\u0CF2\\u0D01-\\u0D03\\u0D05-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D3A\\u0D3D-\\u0D44\\u0D46-\\u0D48\\u0D4A-\\u0D4E\\u0D57\\u0D60-\\u0D63\\u0D66-\\u0D6F\\u0D7A-\\u0D7F\\u0D82\\u0D83\\u0D85-\\u0D96\\u0D9A-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD\\u0DC0-\\u0DC6\\u0DCA\\u0DCF-\\u0DD4\\u0DD6\\u0DD8-\\u0DDF\\u0DE6-\\u0DEF\\u0DF2\\u0DF3\\u0E01-\\u0E3A\\u0E40-\\u0E4E\\u0E50-\\u0E59\\u0E81\\u0E82\\u0E84\\u0E87\\u0E88\\u0E8A\\u0E8D\\u0E94-\\u0E97\\u0E99-\\u0E9F\\u0EA1-\\u0EA3\\u0EA5\\u0EA7\\u0EAA\\u0EAB\\u0EAD-\\u0EB9\\u0EBB-\\u0EBD\\u0EC0-\\u0EC4\\u0EC6\\u0EC8-\\u0ECD\\u0ED0-\\u0ED9\\u0EDC-\\u0EDF\\u0F00\\u0F18\\u0F19\\u0F20-\\u0F29\\u0F35\\u0F37\\u0F39\\u0F3E-\\u0F47\\u0F49-\\u0F6C\\u0F71-\\u0F84\\u0F86-\\u0F97\\u0F99-\\u0FBC\\u0FC6\\u1000-\\u1049\\u1050-\\u109D\\u10A0-\\u10C5\\u10C7\\u10CD\\u10D0-\\u10FA\\u10FC-\\u1248\\u124A-\\u124D\\u1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u135D-\\u135F\\u1380-\\u138F\\u13A0-\\u13F4\\u1401-\\u166C\\u166F-\\u167F\\u1681-\\u169A\\u16A0-\\u16EA\\u16EE-\\u16F8\\u1700-\\u170C\\u170E-\\u1714\\u1720-\\u1734\\u1740-\\u1753\\u1760-\\u176C\\u176E-\\u1770\\u1772\\u1773\\u1780-\\u17D3\\u17D7\\u17DC\\u17DD\\u17E0-\\u17E9\\u180B-\\u180D\\u1810-\\u1819\\u1820-\\u1877\\u1880-\\u18AA\\u18B0-\\u18F5\\u1900-\\u191E\\u1920-\\u192B\\u1930-\\u193B\\u1946-\\u196D\\u1970-\\u1974\\u1980-\\u19AB\\u19B0-\\u19C9\\u19D0-\\u19D9\\u1A00-\\u1A1B\\u1A20-\\u1A5E\\u1A60-\\u1A7C\\u1A7F-\\u1A89\\u1A90-\\u1A99\\u1AA7\\u1AB0-\\u1ABD\\u1B00-\\u1B4B\\u1B50-\\u1B59\\u1B6B-\\u1B73\\u1B80-\\u1BF3\\u1C00-\\u1C37\\u1C40-\\u1C49\\u1C4D-\\u1C7D\\u1CD0-\\u1CD2\\u1CD4-\\u1CF6\\u1CF8\\u1CF9\\u1D00-\\u1DF5\\u1DFC-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FBC\\u1FBE\\u1FC2-\\u1FC4\\u1FC6-\\u1FCC\\u1FD0-\\u1FD3\\u1FD6-\\u1FDB\\u1FE0-\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FFC\\u200C\\u200D\\u203F\\u2040\\u2054\\u2071\\u207F\\u2090-\\u209C\\u20D0-\\u20DC\\u20E1\\u20E5-\\u20F0\\u2102\\u2107\\u210A-\\u2113\\u2115\\u2119-\\u211D\\u2124\\u2126\\u2128\\u212A-\\u212D\\u212F-\\u2139\\u213C-\\u213F\\u2145-\\u2149\\u214E\\u2160-\\u2188\\u2C00-\\u2C2E\\u2C30-\\u2C5E\\u2C60-\\u2CE4\\u2CEB-\\u2CF3\\u2D00-\\u2D25\\u2D27\\u2D2D\\u2D30-\\u2D67\\u2D6F\\u2D7F-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2DE0-\\u2DFF\\u2E2F\\u3005-\\u3007\\u3021-\\u302F\\u3031-\\u3035\\u3038-\\u303C\\u3041-\\u3096\\u3099\\u309A\\u309D-\\u309F\\u30A1-\\u30FA\\u30FC-\\u30FF\\u3105-\\u312D\\u3131-\\u318E\\u31A0-\\u31BA\\u31F0-\\u31FF\\u3400-\\u4DB5\\u4E00-\\u9FCC\\uA000-\\uA48C\\uA4D0-\\uA4FD\\uA500-\\uA60C\\uA610-\\uA62B\\uA640-\\uA66F\\uA674-\\uA67D\\uA67F-\\uA69D\\uA69F-\\uA6F1\\uA717-\\uA71F\\uA722-\\uA788\\uA78B-\\uA78E\\uA790-\\uA7AD\\uA7B0\\uA7B1\\uA7F7-\\uA827\\uA840-\\uA873\\uA880-\\uA8C4\\uA8D0-\\uA8D9\\uA8E0-\\uA8F7\\uA8FB\\uA900-\\uA92D\\uA930-\\uA953\\uA960-\\uA97C\\uA980-\\uA9C0\\uA9CF-\\uA9D9\\uA9E0-\\uA9FE\\uAA00-\\uAA36\\uAA40-\\uAA4D\\uAA50-\\uAA59\\uAA60-\\uAA76\\uAA7A-\\uAAC2\\uAADB-\\uAADD\\uAAE0-\\uAAEF\\uAAF2-\\uAAF6\\uAB01-\\uAB06\\uAB09-\\uAB0E\\uAB11-\\uAB16\\uAB20-\\uAB26\\uAB28-\\uAB2E\\uAB30-\\uAB5A\\uAB5C-\\uAB5F\\uAB64\\uAB65\\uABC0-\\uABEA\\uABEC\\uABED\\uABF0-\\uABF9\\uAC00-\\uD7A3\\uD7B0-\\uD7C6\\uD7CB-\\uD7FB\\uF900-\\uFA6D\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\uFB17\\uFB1D-\\uFB28\\uFB2A-\\uFB36\\uFB38-\\uFB3C\\uFB3E\\uFB40\\uFB41\\uFB43\\uFB44\\uFB46-\\uFBB1\\uFBD3-\\uFD3D\\uFD50-\\uFD8F\\uFD92-\\uFDC7\\uFDF0-\\uFDFB\\uFE00-\\uFE0F\\uFE20-\\uFE2D\\uFE33\\uFE34\\uFE4D-\\uFE4F\\uFE70-\\uFE74\\uFE76-\\uFEFC\\uFF10-\\uFF19\\uFF21-\\uFF3A\\uFF3F\\uFF41-\\uFF5A\\uFF66-\\uFFBE\\uFFC2-\\uFFC7\\uFFCA-\\uFFCF\\uFFD2-\\uFFD7\\uFFDA-\\uFFDC]");function qg(e,t){if(!e)throw new Error("ASSERT: "+t)}function Ks(e){return e>=48&&e<=57}function Zx(e){return"0123456789abcdefABCDEF".includes(e)}function Ad(e){return"01234567".includes(e)}function Xre(e){return e===32||e===9||e===11||e===12||e===160||e>=5760&&[5760,6158,8192,8193,8194,8195,8196,8197,8198,8199,8200,8201,8202,8239,8287,12288,65279].includes(e)}function $d(e){return e===10||e===13||e===8232||e===8233}function Sd(e){return e===36||e===95||e>=65&&e<=90||e>=97&&e<=122||e===92||e>=128&&Vre.test(String.fromCharCode(e))}function Wg(e){return e===36||e===95||e>=65&&e<=90||e>=97&&e<=122||e>=48&&e<=57||e===92||e>=128&&Yre.test(String.fromCharCode(e))}const Kre={if:1,in:1,do:1,var:1,for:1,new:1,try:1,let:1,this:1,else:1,case:1,void:1,with:1,enum:1,while:1,break:1,catch:1,throw:1,const:1,yield:1,class:1,super:1,return:1,typeof:1,delete:1,switch:1,export:1,import:1,public:1,static:1,default:1,finally:1,extends:1,package:1,private:1,function:1,continue:1,debugger:1,interface:1,protected:1,instanceof:1,implements:1};function gD(){for(;U1114111||e!=="}")&&et({},en,hn),t<=65535?String.fromCharCode(t):(n=(t-65536>>10)+55296,i=(t-65536&1023)+56320,String.fromCharCode(n,i))}function mD(){var e,t;for(e=pe.charCodeAt(U++),t=String.fromCharCode(e),e===92&&(pe.charCodeAt(U)!==117&&et({},en,hn),++U,e=Jx("u"),(!e||e==="\\"||!Sd(e.charCodeAt(0)))&&et({},en,hn),t=e);U>>=")return U+=4,{type:ci,value:o,start:e,end:U};if(s=o.substr(0,3),s===">>>"||s==="<<="||s===">>=")return U+=3,{type:ci,value:s,start:e,end:U};if(r=s.substr(0,2),i===r[1]&&"+-<>&|".includes(i)||r==="=>")return U+=2,{type:ci,value:r,start:e,end:U};if(r==="//"&&et({},en,hn),"<>=!+-*%&|^/".includes(i))return++U,{type:ci,value:i,start:e,end:U};et({},en,hn)}function ese(e){let t="";for(;U{if(parseInt(r,16)<=1114111)return"x";et({},Xx)}).replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g,"x"));try{new RegExp(n)}catch{et({},Xx)}try{return new RegExp(e,t)}catch{return null}}function rse(){var e,t,n,i,r;for(e=pe[U],qg(e==="/","Regular expression literal must start with a slash"),t=pe[U++],n=!1,i=!1;U=0&&et({},Xx,n),{value:n,literal:t}}function ose(){var e,t,n,i;return ot=null,gD(),e=U,t=rse(),n=sse(),i=ise(t.value,n.value),{literal:t.literal+n.literal,value:i,regex:{pattern:t.value,flags:n.value},start:e,end:U}}function ase(e){return e.type===iu||e.type===ea||e.type===Ug||e.type===jg}function bD(){if(gD(),U>=Nn)return{type:Ed,start:U,end:U};const e=pe.charCodeAt(U);return Sd(e)?Qre():e===40||e===41||e===59?Qx():e===39||e===34?nse():e===46?Ks(pe.charCodeAt(U+1))?yD():Qx():Ks(e)?yD():Qx()}function fi(){const e=ot;return U=e.end,ot=bD(),U=e.end,e}function vD(){const e=U;ot=bD(),U=e}function use(e){const t=new xr(Tre);return t.elements=e,t}function xD(e,t,n){const i=new xr(e==="||"||e==="&&"?Lre:Mre);return i.operator=e,i.left=t,i.right=n,i}function lse(e,t){const n=new xr(Nre);return n.callee=e,n.arguments=t,n}function cse(e,t,n){const i=new xr(Rre);return i.test=e,i.consequent=t,i.alternate=n,i}function e7(e){const t=new xr(hD);return t.name=e,t}function Fd(e){const t=new xr(Ore);return t.value=e.value,t.raw=pe.slice(e.start,e.end),e.regex&&(t.raw==="//"&&(t.raw="/(?:)/"),t.regex=e.regex),t}function _D(e,t,n){const i=new xr(Ire);return i.computed=e==="[",i.object=t,i.property=n,i.computed||(n.member=!0),i}function fse(e){const t=new xr(Pre);return t.properties=e,t}function wD(e,t,n){const i=new xr(zre);return i.key=t,i.value=n,i.kind=e,i}function dse(e,t){const n=new xr(Bre);return n.operator=e,n.argument=t,n.prefix=!0,n}function et(e,t){var n,i=Array.prototype.slice.call(arguments,2),r=t.replace(/%(\d)/g,(s,o)=>(qg(o":case"<=":case">=":case"instanceof":case"in":t=7;break;case"<<":case">>":case">>>":t=8;break;case"+":case"-":t=9;break;case"*":case"/":case"%":t=11;break}return t}function Cse(){var e,t,n,i,r,s,o,a,u,l;if(e=ot,u=Gg(),i=ot,r=kD(i),r===0)return u;for(i.prec=r,fi(),t=[e,ot],o=Gg(),s=[u,i,o];(r=kD(ot))>0;){for(;s.length>2&&r<=s[s.length-2].prec;)o=s.pop(),a=s.pop().value,u=s.pop(),t.pop(),n=xD(a,u,o),s.push(n);i=fi(),i.prec=r,s.push(i),t.push(ot),n=Gg(),s.push(n)}for(l=s.length-1,n=s[l],t.pop();l>1;)t.pop(),n=xD(s[l-1].value,s[l-2],n),l-=2;return n}function su(){var e,t,n;return e=Cse(),wt("?")&&(fi(),t=su(),Rn(":"),n=su(),e=cse(e,t,n)),e}function n7(){const e=su();if(wt(","))throw new Error(kd);return e}function AD(e){pe=e,U=0,Nn=pe.length,ot=null,vD();const t=n7();if(ot.type!==Ed)throw new Error("Unexpect token after expression.");return t}var $D={NaN:"NaN",E:"Math.E",LN2:"Math.LN2",LN10:"Math.LN10",LOG2E:"Math.LOG2E",LOG10E:"Math.LOG10E",PI:"Math.PI",SQRT1_2:"Math.SQRT1_2",SQRT2:"Math.SQRT2",MIN_VALUE:"Number.MIN_VALUE",MAX_VALUE:"Number.MAX_VALUE"};function SD(e){function t(o,a,u,l){let c=e(a[0]);return u&&(c=u+"("+c+")",u.lastIndexOf("new ",0)===0&&(c="("+c+")")),c+"."+o+(l<0?"":l===0?"()":"("+a.slice(1).map(e).join(",")+")")}function n(o,a,u){return l=>t(o,l,a,u)}const i="new Date",r="String",s="RegExp";return{isNaN:"Number.isNaN",isFinite:"Number.isFinite",abs:"Math.abs",acos:"Math.acos",asin:"Math.asin",atan:"Math.atan",atan2:"Math.atan2",ceil:"Math.ceil",cos:"Math.cos",exp:"Math.exp",floor:"Math.floor",hypot:"Math.hypot",log:"Math.log",max:"Math.max",min:"Math.min",pow:"Math.pow",random:"Math.random",round:"Math.round",sin:"Math.sin",sqrt:"Math.sqrt",tan:"Math.tan",clamp:function(o){o.length<3&&q("Missing arguments to clamp function."),o.length>3&&q("Too many arguments to clamp function.");const a=o.map(e);return"Math.max("+a[1]+", Math.min("+a[2]+","+a[0]+"))"},now:"Date.now",utc:"Date.UTC",datetime:i,date:n("getDate",i,0),day:n("getDay",i,0),year:n("getFullYear",i,0),month:n("getMonth",i,0),hours:n("getHours",i,0),minutes:n("getMinutes",i,0),seconds:n("getSeconds",i,0),milliseconds:n("getMilliseconds",i,0),time:n("getTime",i,0),timezoneoffset:n("getTimezoneOffset",i,0),utcdate:n("getUTCDate",i,0),utcday:n("getUTCDay",i,0),utcyear:n("getUTCFullYear",i,0),utcmonth:n("getUTCMonth",i,0),utchours:n("getUTCHours",i,0),utcminutes:n("getUTCMinutes",i,0),utcseconds:n("getUTCSeconds",i,0),utcmilliseconds:n("getUTCMilliseconds",i,0),length:n("length",null,-1),parseFloat:"parseFloat",parseInt:"parseInt",upper:n("toUpperCase",r,0),lower:n("toLowerCase",r,0),substring:n("substring",r),split:n("split",r),trim:n("trim",r,0),btoa:"btoa",atob:"atob",regexp:s,test:n("test",s),if:function(o){o.length<3&&q("Missing arguments to if function."),o.length>3&&q("Too many arguments to if function.");const a=o.map(e);return"("+a[0]+"?"+a[1]+":"+a[2]+")"}}}function kse(e){const t=e&&e.length-1;return t&&(e[0]==='"'&&e[t]==='"'||e[0]==="'"&&e[t]==="'")?e.slice(1,-1):e}function FD(e){e=e||{};const t=e.allowed?lr(e.allowed):{},n=e.forbidden?lr(e.forbidden):{},i=e.constants||$D,r=(e.functions||SD)(f),s=e.globalvar,o=e.fieldvar,a=Ie(s)?s:p=>`${s}["${p}"]`;let u={},l={},c=0;function f(p){if(se(p))return p;const g=d[p.type];return g==null&&q("Unsupported type: "+p.type),g(p)}const d={Literal:p=>p.raw,Identifier:p=>{const g=p.name;return c>0?g:ue(n,g)?q("Illegal identifier: "+g):ue(i,g)?i[g]:ue(t,g)?g:(u[g]=1,a(g))},MemberExpression:p=>{const g=!p.computed,m=f(p.object);g&&(c+=1);const y=f(p.property);return m===o&&(l[kse(y)]=1),g&&(c-=1),m+(g?"."+y:"["+y+"]")},CallExpression:p=>{p.callee.type!=="Identifier"&&q("Illegal callee type: "+p.callee.type);const g=p.callee.name,m=p.arguments,y=ue(r,g)&&r[g];return y||q("Unrecognized function: "+g),Ie(y)?y(m):y+"("+m.map(f).join(",")+")"},ArrayExpression:p=>"["+p.elements.map(f).join(",")+"]",BinaryExpression:p=>"("+f(p.left)+" "+p.operator+" "+f(p.right)+")",UnaryExpression:p=>"("+p.operator+f(p.argument)+")",ConditionalExpression:p=>"("+f(p.test)+"?"+f(p.consequent)+":"+f(p.alternate)+")",LogicalExpression:p=>"("+f(p.left)+p.operator+f(p.right)+")",ObjectExpression:p=>"{"+p.properties.map(f).join(",")+"}",Property:p=>{c+=1;const g=f(p.key);return c-=1,g+":"+f(p.value)}};function h(p){const g={code:f(p),globals:Object.keys(u),fields:Object.keys(l)};return u={},l={},g}return h.functions=r,h.constants=i,h}const DD=Symbol("vega_selection_getter");function TD(e){return(!e.getter||!e.getter[DD])&&(e.getter=Bi(e.field),e.getter[DD]=!0),e.getter}const i7="intersect",MD="union",Ase="vlMulti",$se="vlPoint",ND="or",Sse="and",us="_vgsid_",Dd=Bi(us),Fse="E",Dse="R",Tse="R-E",Mse="R-LE",Nse="R-RE",Rse="E-LT",Ose="E-LTE",Lse="E-GT",Ise="E-GTE",Pse="E-VALID",zse="E-ONE",Vg="index:unit";function RD(e,t){for(var n=t.fields,i=t.values,r=n.length,s=0,o,a;s=i[s])return!1}else if(a.type===Ose){if(o>i[s])return!1}else if(a.type===Lse){if(o<=i[s])return!1}else if(a.type===Ise){if(oOe(t.fields?{values:t.fields.map(i=>TD(i)(n.datum))}:{[us]:Dd(n.datum)},t))}function Hse(e,t,n,i){for(var r=this.context.data[e],s=r?r.values.value:[],o={},a={},u={},l,c,f,d,h,p,g,m,y,b,v=s.length,x=0,_,E;x(w[c[k].field]=C,w),{})))}else h=us,p=Dd(l),g=o[h]||(o[h]={}),m=g[d]||(g[d]=[]),m.push(p),n&&(m=a[d]||(a[d]=[]),m.push({[us]:p}));if(t=t||MD,o[us]?o[us]=r7[`${us}_${t}`](...Object.values(o[us])):Object.keys(o).forEach(w=>{o[w]=Object.keys(o[w]).map(C=>o[w][C]).reduce((C,k)=>C===void 0?k:r7[`${u[w]}_${t}`](C,k))}),s=Object.keys(a),n&&s.length){const w=i?$se:Ase;o[w]=t===MD?{[ND]:s.reduce((C,k)=>(C.push(...a[k]),C),[])}:{[Sse]:s.map(C=>({[ND]:a[C]}))}}return o}var r7={[`${us}_union`]:Bq,[`${us}_intersect`]:Pq,E_union:function(e,t){if(!e.length)return t;for(var n=0,i=t.length;nt.includes(n)):t},R_union:function(e,t){var n=Cn(t[0]),i=Cn(t[1]);return n>i&&(n=t[1],i=t[0]),e.length?(e[0]>n&&(e[0]=n),e[1]i&&(n=t[1],i=t[0]),e.length?ii&&(e[1]=i),e):[n,i]}};const Gse=":",Vse="@";function s7(e,t,n,i){t[0].type!==nu&&q("First argument to selection functions must be a string literal.");const r=t[0].value,s=t.length>=2&&Ge(t).value,o="unit",a=Vse+o,u=Gse+r;s===i7&&!ue(i,a)&&(i[a]=n.getData(r).indataRef(n,o)),ue(i,u)||(i[u]=n.getData(r).tuplesRef())}function LD(e){const t=this.context.data[e];return t?t.values.value:[]}function Yse(e,t,n){const i=this.context.data[e]["index:"+t],r=i?i.value.get(n):void 0;return r&&r.count}function Xse(e,t){const n=this.context.dataflow,i=this.context.data[e],r=i.input;return n.pulse(r,n.changeset().remove(Ui).insert(t)),1}function Kse(e,t,n){if(e){const i=this.context.dataflow,r=e.mark.source;i.pulse(r,i.changeset().encode(e,t))}return n!==void 0?n:e}const Td=e=>function(t,n){const i=this.context.dataflow.locale();return t===null?"null":i[e](n)(t)},Zse=Td("format"),ID=Td("timeFormat"),Jse=Td("utcFormat"),Qse=Td("timeParse"),eoe=Td("utcParse"),Yg=new Date(2e3,0,1);function Xg(e,t,n){return!Number.isInteger(e)||!Number.isInteger(t)?"":(Yg.setYear(2e3),Yg.setMonth(e),Yg.setDate(t),ID.call(this,Yg,n))}function toe(e){return Xg.call(this,e,1,"%B")}function noe(e){return Xg.call(this,e,1,"%b")}function ioe(e){return Xg.call(this,0,2+e,"%A")}function roe(e){return Xg.call(this,0,2+e,"%a")}const soe=":",ooe="@",o7="%",PD="$";function a7(e,t,n,i){t[0].type!==nu&&q("First argument to data functions must be a string literal.");const r=t[0].value,s=soe+r;if(!ue(s,i))try{i[s]=n.getData(r).tuplesRef()}catch{}}function aoe(e,t,n,i){t[0].type!==nu&&q("First argument to indata must be a string literal."),t[1].type!==nu&&q("Second argument to indata must be a string literal.");const r=t[0].value,s=t[1].value,o=ooe+s;ue(o,i)||(i[o]=n.getData(r).indataRef(n,s))}function Jn(e,t,n,i){if(t[0].type===nu)zD(n,i,t[0].value);else for(e in n.scales)zD(n,i,e)}function zD(e,t,n){const i=o7+n;if(!ue(t,i))try{t[i]=e.scaleRef(n)}catch{}}function ls(e,t){if(se(e)){const n=t.scales[e];return n&&vX(n.value)?n.value:void 0}}function uoe(e,t,n){t.__bandwidth=r=>r&&r.bandwidth?r.bandwidth():0,n._bandwidth=Jn,n._range=Jn,n._scale=Jn;const i=r=>"_["+(r.type===nu?Q(o7+r.value):Q(o7)+"+"+e(r))+"]";return{_bandwidth:r=>`this.__bandwidth(${i(r[0])})`,_range:r=>`${i(r[0])}.range()`,_scale:r=>`${i(r[0])}(${e(r[1])})`}}function u7(e,t){return function(n,i,r){if(n){const s=ls(n,(r||this).context);return s&&s.path[e](i)}else return t(i)}}const loe=u7("area",$Q),coe=u7("bounds",TQ),foe=u7("centroid",IQ);function doe(e,t){const n=ls(e,(t||this).context);return n&&n.scale()}function hoe(e){const t=this.context.group;let n=!1;if(t)for(;e;){if(e===t){n=!0;break}e=e.mark.group}return n}function l7(e,t,n){try{e[t].apply(e,["EXPRESSION"].concat([].slice.call(n)))}catch(i){e.warn(i)}return n[n.length-1]}function poe(){return l7(this.context.dataflow,"warn",arguments)}function goe(){return l7(this.context.dataflow,"info",arguments)}function moe(){return l7(this.context.dataflow,"debug",arguments)}function c7(e){const t=e/255;return t<=.03928?t/12.92:Math.pow((t+.055)/1.055,2.4)}function f7(e){const t=Oo(e),n=c7(t.r),i=c7(t.g),r=c7(t.b);return .2126*n+.7152*i+.0722*r}function yoe(e,t){const n=f7(e),i=f7(t),r=Math.max(n,i),s=Math.min(n,i);return(r+.05)/(s+.05)}function boe(){const e=[].slice.call(arguments);return e.unshift({}),Oe(...e)}function BD(e,t){return e===t||e!==e&&t!==t?!0:W(e)?W(t)&&e.length===t.length?voe(e,t):!1:re(e)&&re(t)?UD(e,t):!1}function voe(e,t){for(let n=0,i=e.length;nUD(e,t)}function xoe(e,t,n,i,r,s){const o=this.context.dataflow,a=this.context.data[e],u=a.input,l=o.stamp();let c=a.changes,f,d;if(o._trigger===!1||!(u.value.length||t||i))return 0;if((!c||c.stamp{a.modified=!0,o.pulse(u,c).run()},!0,1)),n&&(f=n===!0?Ui:W(n)||g0(n)?n:jD(n),c.remove(f)),t&&c.insert(t),i&&(f=jD(i),u.value.some(f)?c.remove(f):c.insert(i)),r)for(d in s)c.modify(r,d,s[d]);return 1}function _oe(e){const t=e.touches,n=t[0].clientX-t[1].clientX,i=t[0].clientY-t[1].clientY;return Math.hypot(n,i)}function woe(e){const t=e.touches;return Math.atan2(t[0].clientY-t[1].clientY,t[0].clientX-t[1].clientX)}const qD={};function Eoe(e,t){const n=qD[t]||(qD[t]=Bi(t));return W(e)?e.map(n):n(e)}function Kg(e){return W(e)||ArrayBuffer.isView(e)?e:null}function d7(e){return Kg(e)||(se(e)?e:null)}function Coe(e){for(var t=arguments.length,n=new Array(t>1?t-1:0),i=1;i1?t-1:0),i=1;i1?t-1:0),i=1;i1?t-1:0),i=1;io.stop(c(f),s(f))),o}function zoe(e,t,n){const i=ls(e,(n||this).context);return function(r){return i?i.path.context(r)(t):""}}function Boe(e){let t=null;return function(n){return n?Tf(n,t=t||Tl(e)):e}}const WD=e=>e.data;function HD(e,t){const n=LD.call(t,e);return n.root&&n.root.lookup||{}}function Uoe(e,t,n){const i=HD(e,this),r=i[t],s=i[n];return r&&s?r.path(s).map(WD):void 0}function joe(e,t){const n=HD(e,this)[t];return n?n.ancestors().map(WD):void 0}const GD=()=>typeof window<"u"&&window||null;function qoe(){const e=GD();return e?e.screen:{}}function Woe(){const e=GD();return e?[e.innerWidth,e.innerHeight]:[void 0,void 0]}function Hoe(){const e=this.context.dataflow,t=e.container&&e.container();return t?[t.clientWidth,t.clientHeight]:[void 0,void 0]}function VD(e,t,n){if(!e)return[];const[i,r]=e,s=new Ut().set(i[0],i[1],r[0],r[1]),o=n||this.context.dataflow.scenegraph().root;return i$(o,s,Goe(t))}function Goe(e){let t=null;if(e){const n=oe(e.marktype),i=oe(e.markname);t=r=>(!n.length||n.some(s=>r.marktype===s))&&(!i.length||i.some(s=>r.name===s))}return t}function Voe(e,t,n){let i=arguments.length>3&&arguments[3]!==void 0?arguments[3]:5;e=oe(e);const r=e[e.length-1];return r===void 0||Math.hypot(r[0]-t,r[1]-n)>i?[...e,[t,n]]:e}function Yoe(e){return oe(e).reduce((t,n,i)=>{let[r,s]=n;return t+=i==0?`M ${r},${s} `:i===e.length-1?" Z":`L ${r},${s} `},"")}function Xoe(e,t,n){const{x:i,y:r,mark:s}=n,o=new Ut().set(Number.MAX_SAFE_INTEGER,Number.MAX_SAFE_INTEGER,Number.MIN_SAFE_INTEGER,Number.MIN_SAFE_INTEGER);for(const[u,l]of t)uo.x2&&(o.x2=u),lo.y2&&(o.y2=l);return o.translate(i,r),VD([[o.x1,o.y1],[o.x2,o.y2]],e,s).filter(u=>Koe(u.x,u.y,t))}function Koe(e,t,n){let i=0;for(let r=0,s=n.length-1;rt!=a>t&&e<(o-u)*(t-l)/(a-l)+u&&i++}return i&1}const Md={random(){return Wi()},cumulativeNormal:E0,cumulativeLogNormal:Cy,cumulativeUniform:Sy,densityNormal:vy,densityLogNormal:Ey,densityUniform:$y,quantileNormal:C0,quantileLogNormal:ky,quantileUniform:Fy,sampleNormal:w0,sampleLogNormal:wy,sampleUniform:Ay,isArray:W,isBoolean:xo,isDate:_o,isDefined(e){return e!==void 0},isNumber:Ze,isObject:re,isRegExp:A2,isString:se,isTuple:g0,isValid(e){return e!=null&&e===e},toBoolean:S2,toDate(e){return F2(e)},toNumber:Cn,toString:D2,indexof:koe,join:Coe,lastindexof:Aoe,replace:Soe,reverse:Foe,sort:Doe,slice:$oe,flush:G4,lerp:Y4,merge:boe,pad:Z4,peek:Ge,pluck:Eoe,span:Xc,inrange:ol,truncate:J4,rgb:Oo,lab:q0,hcl:W0,hsl:B0,luminance:f7,contrast:yoe,sequence:Ei,format:Zse,utcFormat:Jse,utcParse:eoe,utcOffset:U8,utcSequence:W8,timeFormat:ID,timeParse:Qse,timeOffset:B8,timeSequence:q8,timeUnitSpecifier:F8,monthFormat:toe,monthAbbrevFormat:noe,dayFormat:ioe,dayAbbrevFormat:roe,quarter:j4,utcquarter:q4,week:T8,utcweek:R8,dayofyear:D8,utcdayofyear:N8,warn:poe,info:goe,debug:moe,extent(e){return qr(e)},inScope:hoe,intersect:VD,clampRange:W4,pinchDistance:_oe,pinchAngle:woe,screen:qoe,containerSize:Hoe,windowSize:Woe,bandspace:Toe,setdata:Xse,pathShape:Boe,panLinear:P4,panLog:z4,panPow:B4,panSymlog:U4,zoomLinear:_2,zoomLog:w2,zoomPow:Xh,zoomSymlog:E2,encode:Kse,modify:xoe,lassoAppend:Voe,lassoPath:Yoe,intersectLasso:Xoe},Zoe=["view","item","group","xy","x","y"],Joe="event.vega.",YD="this.",h7={},XD={forbidden:["_"],allowed:["datum","event","item"],fieldvar:"datum",globalvar:e=>`_[${Q(PD+e)}]`,functions:Qoe,constants:$D,visitors:h7},p7=FD(XD);function Qoe(e){const t=SD(e);Zoe.forEach(n=>t[n]=Joe+n);for(const n in Md)t[n]=YD+n;return Oe(t,uoe(e,Md,h7)),t}function Pt(e,t,n){return arguments.length===1?Md[e]:(Md[e]=t,n&&(h7[e]=n),p7&&(p7.functions[e]=YD+e),this)}Pt("bandwidth",Moe,Jn),Pt("copy",Noe,Jn),Pt("domain",Roe,Jn),Pt("range",Loe,Jn),Pt("invert",Ooe,Jn),Pt("scale",Ioe,Jn),Pt("gradient",Poe,Jn),Pt("geoArea",loe,Jn),Pt("geoBounds",coe,Jn),Pt("geoCentroid",foe,Jn),Pt("geoShape",zoe,Jn),Pt("geoScale",doe,Jn),Pt("indata",Yse,aoe),Pt("data",LD,a7),Pt("treePath",Uoe,a7),Pt("treeAncestors",joe,a7),Pt("vlSelectionTest",Bse,s7),Pt("vlSelectionIdTest",qse,s7),Pt("vlSelectionResolve",Hse,s7),Pt("vlSelectionTuples",Wse);function cs(e,t){const n={};let i;try{e=se(e)?e:Q(e)+"",i=AD(e)}catch{q("Expression parse error: "+e)}i.visit(s=>{if(s.type!==dD)return;const o=s.callee.name,a=XD.visitors[o];a&&a(o,s.arguments,t,n)});const r=p7(i);return r.globals.forEach(s=>{const o=PD+s;!ue(n,o)&&t.getSignal(s)&&(n[o]=t.signalRef(s))}),{$expr:Oe({code:r.code},t.options.ast?{ast:i}:null),$fields:r.fields,$params:n}}function eae(e){const t=this,n=e.operators||[];return e.background&&(t.background=e.background),e.eventConfig&&(t.eventConfig=e.eventConfig),e.locale&&(t.locale=e.locale),n.forEach(i=>t.parseOperator(i)),n.forEach(i=>t.parseOperatorParameters(i)),(e.streams||[]).forEach(i=>t.parseStream(i)),(e.updates||[]).forEach(i=>t.parseUpdate(i)),t.resolve()}const tae=lr(["rule"]),KD=lr(["group","image","rect"]);function nae(e,t){let n="";return tae[t]||(e.x2&&(e.x?(KD[t]&&(n+="if(o.x>o.x2)$=o.x,o.x=o.x2,o.x2=$;"),n+="o.width=o.x2-o.x;"):n+="o.x=o.x2-(o.width||0);"),e.xc&&(n+="o.x=o.xc-(o.width||0)/2;"),e.y2&&(e.y?(KD[t]&&(n+="if(o.y>o.y2)$=o.y,o.y=o.y2,o.y2=$;"),n+="o.height=o.y2-o.y;"):n+="o.y=o.y2-(o.height||0);"),e.yc&&(n+="o.y=o.yc-(o.height||0)/2;")),n}function g7(e){return(e+"").toLowerCase()}function iae(e){return g7(e)==="operator"}function rae(e){return g7(e)==="collect"}function Nd(e,t,n){n.endsWith(";")||(n="return("+n+");");const i=Function(...t.concat(n));return e&&e.functions?i.bind(e.functions):i}function sae(e,t,n,i){return`((u = ${e}) < (v = ${t}) || u == null) && v != null ? ${n} + : (u > v || v == null) && u != null ? ${i} + : ((v = v instanceof Date ? +v : v), (u = u instanceof Date ? +u : u)) !== u && v === v ? ${n} + : v !== v && u === u ? ${i} : `}var oae={operator:(e,t)=>Nd(e,["_"],t.code),parameter:(e,t)=>Nd(e,["datum","_"],t.code),event:(e,t)=>Nd(e,["event"],t.code),handler:(e,t)=>{const n=`var datum=event.item&&event.item.datum;return ${t.code};`;return Nd(e,["_","event"],n)},encode:(e,t)=>{const{marktype:n,channels:i}=t;let r="var o=item,datum=o.datum,m=0,$;";for(const s in i){const o="o["+Q(s)+"]";r+=`$=${i[s].code};if(${o}!==$)${o}=$,m=1;`}return r+=nae(i,n),r+="return m;",Nd(e,["item","_"],r)},codegen:{get(e){const t=`[${e.map(Q).join("][")}]`,n=Function("_",`return _${t};`);return n.path=t,n},comparator(e,t){let n;const i=(s,o)=>{const a=t[o];let u,l;return s.path?(u=`a${s.path}`,l=`b${s.path}`):((n=n||{})["f"+o]=s,u=`this.f${o}(a)`,l=`this.f${o}(b)`),sae(u,l,-a,a)},r=Function("a","b","var u, v; return "+e.map(i).join("")+"0;");return n?r.bind(n):r}}};function aae(e){const t=this;iae(e.type)||!e.type?t.operator(e,e.update?t.operatorExpression(e.update):null):t.transform(e,e.type)}function uae(e){const t=this;if(e.params){const n=t.get(e.id);n||q("Invalid operator id: "+e.id),t.dataflow.connect(n,n.parameters(t.parseParameters(e.params),e.react,e.initonly))}}function lae(e,t){t=t||{};const n=this;for(const i in e){const r=e[i];t[i]=W(r)?r.map(s=>ZD(s,n,t)):ZD(r,n,t)}return t}function ZD(e,t,n){if(!e||!re(e))return e;for(let i=0,r=JD.length,s;ir&&r.$tupleid?_e:r);return t.fn[n]||(t.fn[n]=C2(i,e.$order,t.expr.codegen))}function gae(e,t){const n=e.$encode,i={};for(const r in n){const s=n[r];i[r]=ii(t.encodeExpression(s.$expr),s.$fields),i[r].output=s.$output}return i}function mae(e,t){return t}function yae(e,t){const n=e.$subflow;return function(i,r,s){const o=t.fork().parse(n),a=o.get(n.operators[0].id),u=o.signals.parent;return u&&u.set(s),a.detachSubflow=()=>t.detach(o),a}}function bae(){return _e}function vae(e){var t=this,n=e.filter!=null?t.eventExpression(e.filter):void 0,i=e.stream!=null?t.get(e.stream):void 0,r;e.source?i=t.events(e.source,e.type,n):e.merge&&(r=e.merge.map(s=>t.get(s)),i=r[0].merge.apply(r[0],r.slice(1))),e.between&&(r=e.between.map(s=>t.get(s)),i=i.between(r[0],r[1])),e.filter&&(i=i.filter(n)),e.throttle!=null&&(i=i.throttle(+e.throttle)),e.debounce!=null&&(i=i.debounce(+e.debounce)),i==null&&q("Invalid stream definition: "+JSON.stringify(e)),e.consume&&i.consume(!0),t.stream(e,i)}function xae(e){var t=this,n=re(n=e.source)?n.$ref:n,i=t.get(n),r=null,s=e.update,o=void 0;i||q("Source not defined: "+e.source),r=e.target&&e.target.$expr?t.eventExpression(e.target.$expr):t.get(e.target),s&&s.$expr&&(s.$params&&(o=t.parseParameters(s.$params)),s=t.handlerExpression(s.$expr)),t.update(e,i,r,s,o)}const _ae={skip:!0};function wae(e){var t=this,n={};if(e.signals){var i=n.signals={};Object.keys(t.signals).forEach(s=>{const o=t.signals[s];e.signals(s,o)&&(i[s]=o.value)})}if(e.data){var r=n.data={};Object.keys(t.data).forEach(s=>{const o=t.data[s];e.data(s,o)&&(r[s]=o.input.value)})}return t.subcontext&&e.recurse!==!1&&(n.subcontext=t.subcontext.map(s=>s.getState(e))),n}function Eae(e){var t=this,n=t.dataflow,i=e.data,r=e.signals;Object.keys(r||{}).forEach(s=>{n.update(t.signals[s],r[s],_ae)}),Object.keys(i||{}).forEach(s=>{n.pulse(t.data[s].input,n.changeset().remove(Ui).insert(i[s]))}),(e.subcontext||[]).forEach((s,o)=>{const a=t.subcontext[o];a&&a.setState(s)})}function QD(e,t,n,i){return new eT(e,t,n,i)}function eT(e,t,n,i){this.dataflow=e,this.transforms=t,this.events=e.events.bind(e),this.expr=i||oae,this.signals={},this.scales={},this.nodes={},this.data={},this.fn={},n&&(this.functions=Object.create(n),this.functions.context=this)}function tT(e){this.dataflow=e.dataflow,this.transforms=e.transforms,this.events=e.events,this.expr=e.expr,this.signals=Object.create(e.signals),this.scales=Object.create(e.scales),this.nodes=Object.create(e.nodes),this.data=Object.create(e.data),this.fn=Object.create(e.fn),e.functions&&(this.functions=Object.create(e.functions),this.functions.context=this)}eT.prototype=tT.prototype={fork(){const e=new tT(this);return(this.subcontext||(this.subcontext=[])).push(e),e},detach(e){this.subcontext=this.subcontext.filter(n=>n!==e);const t=Object.keys(e.nodes);for(const n of t)e.nodes[n]._targets=null;for(const n of t)e.nodes[n].detach();e.nodes=null},get(e){return this.nodes[e]},set(e,t){return this.nodes[e]=t},add(e,t){const n=this,i=n.dataflow,r=e.value;if(n.set(e.id,t),rae(e.type)&&r&&(r.$ingest?i.ingest(t,r.$ingest,r.$format):r.$request?i.preload(t,r.$request,r.$format):i.pulse(t,i.changeset().insert(r))),e.root&&(n.root=t),e.parent){let s=n.get(e.parent.$ref);s?(i.connect(s,[t]),t.targets().add(s)):(n.unresolved=n.unresolved||[]).push(()=>{s=n.get(e.parent.$ref),i.connect(s,[t]),t.targets().add(s)})}if(e.signal&&(n.signals[e.signal]=t),e.scale&&(n.scales[e.scale]=t),e.data)for(const s in e.data){const o=n.data[s]||(n.data[s]={});e.data[s].forEach(a=>o[a]=t)}},resolve(){return(this.unresolved||[]).forEach(e=>e()),delete this.unresolved,this},operator(e,t){this.add(e,this.dataflow.add(e.value,t))},transform(e,t){this.add(e,this.dataflow.add(this.transforms[g7(t)]))},stream(e,t){this.set(e.id,t)},update(e,t,n,i,r){this.dataflow.on(t,n,i,r,e.options)},operatorExpression(e){return this.expr.operator(this,e)},parameterExpression(e){return this.expr.parameter(this,e)},eventExpression(e){return this.expr.event(this,e)},handlerExpression(e){return this.expr.handler(this,e)},encodeExpression(e){return this.expr.encode(this,e)},parse:eae,parseOperator:aae,parseOperatorParameters:uae,parseParameters:lae,parseStream:vae,parseUpdate:xae,getState:wae,setState:Eae};function Cae(e){const t=e.container();t&&(t.setAttribute("role","graphics-document"),t.setAttribute("aria-roleDescription","visualization"),nT(t,e.description()))}function nT(e,t){e&&(t==null?e.removeAttribute("aria-label"):e.setAttribute("aria-label",t))}function kae(e){e.add(null,t=>(e._background=t.bg,e._resize=1,t.bg),{bg:e._signals.background})}const m7="default";function Aae(e){const t=e._signals.cursor||(e._signals.cursor=e.add({user:m7,item:null}));e.on(e.events("view","pointermove"),t,(n,i)=>{const r=t.value,s=r?se(r)?r:r.user:m7,o=i.item&&i.item.cursor||null;return r&&s===r.user&&o==r.item?r:{user:s,item:o}}),e.add(null,function(n){let i=n.cursor,r=this.value;return se(i)||(r=i.item,i=i.user),y7(e,i&&i!==m7?i:r||i),r},{cursor:t})}function y7(e,t){const n=e.globalCursor()?typeof document<"u"&&document.body:e.container();if(n)return t==null?n.style.removeProperty("cursor"):n.style.cursor=t}function Zg(e,t){var n=e._runtime.data;return ue(n,t)||q("Unrecognized data set: "+t),n[t]}function $ae(e,t){return arguments.length<2?Zg(this,e).values.value:Jg.call(this,e,Ao().remove(Ui).insert(t))}function Jg(e,t){ME(t)||q("Second argument to changes must be a changeset.");const n=Zg(this,e);return n.modified=!0,this.pulse(n.input,t)}function Sae(e,t){return Jg.call(this,e,Ao().insert(t))}function Fae(e,t){return Jg.call(this,e,Ao().remove(t))}function iT(e){var t=e.padding();return Math.max(0,e._viewWidth+t.left+t.right)}function rT(e){var t=e.padding();return Math.max(0,e._viewHeight+t.top+t.bottom)}function Qg(e){var t=e.padding(),n=e._origin;return[t.left+n[0],t.top+n[1]]}function Dae(e){var t=Qg(e),n=iT(e),i=rT(e);e._renderer.background(e.background()),e._renderer.resize(n,i,t),e._handler.origin(t),e._resizeListeners.forEach(r=>{try{r(n,i)}catch(s){e.error(s)}})}function Tae(e,t,n){var i=e._renderer,r=i&&i.canvas(),s,o,a;return r&&(a=Qg(e),o=t.changedTouches?t.changedTouches[0]:t,s=Ep(o,r),s[0]-=a[0],s[1]-=a[1]),t.dataflow=e,t.item=n,t.vega=Mae(e,n,s),t}function Mae(e,t,n){const i=t?t.mark.marktype==="group"?t:t.mark.group:null;function r(o){var a=i,u;if(o){for(u=t;u;u=u.mark.group)if(u.mark.name===o){a=u;break}}return a&&a.mark&&a.mark.interactive?a:{}}function s(o){if(!o)return n;se(o)&&(o=r(o));const a=n.slice();for(;o;)a[0]-=o.x||0,a[1]-=o.y||0,o=o.mark&&o.mark.group;return a}return{view:kn(e),item:kn(t||{}),group:r,xy:s,x:o=>s(o)[0],y:o=>s(o)[1]}}const sT="view",Nae="timer",Rae="window",Oae={trap:!1};function Lae(e){const t=Oe({defaults:{}},e),n=(i,r)=>{r.forEach(s=>{W(i[s])&&(i[s]=lr(i[s]))})};return n(t.defaults,["prevent","allow"]),n(t,["view","window","selector"]),t}function oT(e,t,n,i){e._eventListeners.push({type:n,sources:oe(t),handler:i})}function Iae(e,t){var n=e._eventConfig.defaults,i=n.prevent,r=n.allow;return i===!1||r===!0?!1:i===!0||r===!1?!0:i?i[t]:r?!r[t]:e.preventDefault()}function e1(e,t,n){const i=e._eventConfig&&e._eventConfig[t];return i===!1||re(i)&&!i[n]?(e.warn(`Blocked ${t} ${n} event listener.`),!1):!0}function Pae(e,t,n){var i=this,r=new v0(n),s=function(l,c){i.runAsync(null,()=>{e===sT&&Iae(i,t)&&l.preventDefault(),r.receive(Tae(i,l,c))})},o;if(e===Nae)e1(i,"timer",t)&&i.timer(s,t);else if(e===sT)e1(i,"view",t)&&i.addEventListener(t,s,Oae);else if(e===Rae?e1(i,"window",t)&&typeof window<"u"&&(o=[window]):typeof document<"u"&&e1(i,"selector",t)&&(o=Array.from(document.querySelectorAll(e))),!o)i.warn("Can not resolve event source: "+e);else{for(var a=0,u=o.length;a=0;)t[r].stop();for(r=i.length;--r>=0;)for(o=i[r],s=o.sources.length;--s>=0;)o.sources[s].removeEventListener(o.type,o.handler);for(e&&e.call(this,this._handler,null,null,null),r=n.length;--r>=0;)u=n[r].type,a=n[r].handler,this._handler.off(u,a);return this}function Di(e,t,n){const i=document.createElement(e);for(const r in t)i.setAttribute(r,t[r]);return n!=null&&(i.textContent=n),i}const Uae="vega-bind",jae="vega-bind-name",qae="vega-bind-radio";function Wae(e,t,n){if(!t)return;const i=n.param;let r=n.state;return r||(r=n.state={elements:null,active:!1,set:null,update:o=>{o!=e.signal(i.signal)&&e.runAsync(null,()=>{r.source=!0,e.signal(i.signal,o)})}},i.debounce&&(r.update=k2(i.debounce,r.update))),(i.input==null&&i.element?Hae:Vae)(r,t,i,e),r.active||(e.on(e._signals[i.signal],null,()=>{r.source?r.source=!1:r.set(e.signal(i.signal))}),r.active=!0),r}function Hae(e,t,n,i){const r=n.event||"input",s=()=>e.update(t.value);i.signal(n.signal,t.value),t.addEventListener(r,s),oT(i,t,r,s),e.set=o=>{t.value=o,t.dispatchEvent(Gae(r))}}function Gae(e){return typeof Event<"u"?new Event(e):{type:e}}function Vae(e,t,n,i){const r=i.signal(n.signal),s=Di("div",{class:Uae}),o=n.input==="radio"?s:s.appendChild(Di("label"));o.appendChild(Di("span",{class:jae},n.name||n.signal)),t.appendChild(s);let a=Yae;switch(n.input){case"checkbox":a=Xae;break;case"select":a=Kae;break;case"radio":a=Zae;break;case"range":a=Jae;break}a(e,o,n,r)}function Yae(e,t,n,i){const r=Di("input");for(const s in n)s!=="signal"&&s!=="element"&&r.setAttribute(s==="input"?"type":s,n[s]);r.setAttribute("name",n.signal),r.value=i,t.appendChild(r),r.addEventListener("input",()=>e.update(r.value)),e.elements=[r],e.set=s=>r.value=s}function Xae(e,t,n,i){const r={type:"checkbox",name:n.signal};i&&(r.checked=!0);const s=Di("input",r);t.appendChild(s),s.addEventListener("change",()=>e.update(s.checked)),e.elements=[s],e.set=o=>s.checked=!!o||null}function Kae(e,t,n,i){const r=Di("select",{name:n.signal}),s=n.labels||[];n.options.forEach((o,a)=>{const u={value:o};t1(o,i)&&(u.selected=!0),r.appendChild(Di("option",u,(s[a]||o)+""))}),t.appendChild(r),r.addEventListener("change",()=>{e.update(n.options[r.selectedIndex])}),e.elements=[r],e.set=o=>{for(let a=0,u=n.options.length;a{const u={type:"radio",name:n.signal,value:o};t1(o,i)&&(u.checked=!0);const l=Di("input",u);l.addEventListener("change",()=>e.update(o));const c=Di("label",{},(s[a]||o)+"");return c.prepend(l),r.appendChild(c),l}),e.set=o=>{const a=e.elements,u=a.length;for(let l=0;l{u.textContent=a.value,e.update(+a.value)};a.addEventListener("input",l),a.addEventListener("change",l),e.elements=[a],e.set=c=>{a.value=c,u.textContent=c}}function t1(e,t){return e===t||e+""==t+""}function cT(e,t,n,i,r,s){return t=t||new i(e.loader()),t.initialize(n,iT(e),rT(e),Qg(e),r,s).background(e.background())}function b7(e,t){return t?function(){try{t.apply(this,arguments)}catch(n){e.error(n)}}:null}function Qae(e,t,n,i){const r=new i(e.loader(),b7(e,e.tooltip())).scene(e.scenegraph().root).initialize(n,Qg(e),e);return t&&t.handlers().forEach(s=>{r.on(s.type,s.handler)}),r}function eue(e,t){const n=this,i=n._renderType,r=n._eventConfig.bind,s=Lp(i);e=n._el=e?v7(n,e,!0):null,Cae(n),s||n.error("Unrecognized renderer type: "+i);const o=s.handler||Hf,a=e?s.renderer:s.headless;return n._renderer=a?cT(n,n._renderer,e,a):null,n._handler=Qae(n,n._handler,e,o),n._redraw=!0,e&&r!=="none"&&(t=t?n._elBind=v7(n,t,!0):e.appendChild(Di("form",{class:"vega-bindings"})),n._bind.forEach(u=>{u.param.element&&r!=="container"&&(u.element=v7(n,u.param.element,!!u.param.input))}),n._bind.forEach(u=>{Wae(n,u.element||t,u)})),n}function v7(e,t,n){if(typeof t=="string")if(typeof document<"u"){if(t=document.querySelector(t),!t)return e.error("Signal bind element not found: "+t),null}else return e.error("DOM document instance not found."),null;if(t&&n)try{t.textContent=""}catch(i){t=null,e.error(i)}return t}const Rd=e=>+e||0,tue=e=>({top:e,bottom:e,left:e,right:e});function fT(e){return re(e)?{top:Rd(e.top),bottom:Rd(e.bottom),left:Rd(e.left),right:Rd(e.right)}:tue(Rd(e))}async function x7(e,t,n,i){const r=Lp(t),s=r&&r.headless;return s||q("Unrecognized renderer type: "+t),await e.runAsync(),cT(e,null,null,s,n,i).renderAsync(e._scenegraph.root)}async function nue(e,t){e!==Go.Canvas&&e!==Go.SVG&&e!==Go.PNG&&q("Unrecognized image type: "+e);const n=await x7(this,e,t);return e===Go.SVG?iue(n.svg(),"image/svg+xml"):n.canvas().toDataURL("image/png")}function iue(e,t){const n=new Blob([e],{type:t});return window.URL.createObjectURL(n)}async function rue(e,t){return(await x7(this,Go.Canvas,e,t)).canvas()}async function sue(e){return(await x7(this,Go.SVG,e)).svg()}function oue(e,t,n){return QD(e,xl,Md,n).parse(t)}function aue(e){var t=this._runtime.scales;return ue(t,e)||q("Unrecognized scale or projection: "+e),t[e].value}var dT="width",hT="height",_7="padding",pT={skip:!0};function gT(e,t){var n=e.autosize(),i=e.padding();return t-(n&&n.contains===_7?i.left+i.right:0)}function mT(e,t){var n=e.autosize(),i=e.padding();return t-(n&&n.contains===_7?i.top+i.bottom:0)}function uue(e){var t=e._signals,n=t[dT],i=t[hT],r=t[_7];function s(){e._autosize=e._resize=1}e._resizeWidth=e.add(null,a=>{e._width=a.size,e._viewWidth=gT(e,a.size),s()},{size:n}),e._resizeHeight=e.add(null,a=>{e._height=a.size,e._viewHeight=mT(e,a.size),s()},{size:i});const o=e.add(null,s,{pad:r});e._resizeWidth.rank=n.rank+1,e._resizeHeight.rank=i.rank+1,o.rank=r.rank+1}function lue(e,t,n,i,r,s){this.runAfter(o=>{let a=0;o._autosize=0,o.width()!==n&&(a=1,o.signal(dT,n,pT),o._resizeWidth.skip(!0)),o.height()!==i&&(a=1,o.signal(hT,i,pT),o._resizeHeight.skip(!0)),o._viewWidth!==e&&(o._resize=1,o._viewWidth=e),o._viewHeight!==t&&(o._resize=1,o._viewHeight=t),(o._origin[0]!==r[0]||o._origin[1]!==r[1])&&(o._resize=1,o._origin=r),a&&o.run("enter"),s&&o.runAfter(u=>u.resize())},!1,1)}function cue(e){return this._runtime.getState(e||{data:fue,signals:due,recurse:!0})}function fue(e,t){return t.modified&&W(t.input.value)&&!e.startsWith("_:vega:_")}function due(e,t){return!(e==="parent"||t instanceof xl.proxy)}function hue(e){return this.runAsync(null,t=>{t._trigger=!1,t._runtime.setState(e)},t=>{t._trigger=!0}),this}function pue(e,t){function n(i){e({timestamp:Date.now(),elapsed:i})}this._timers.push(Pte(n,t))}function gue(e,t,n,i){const r=e.element();r&&r.setAttribute("title",mue(i))}function mue(e){return e==null?"":W(e)?yT(e):re(e)&&!_o(e)?yue(e):e+""}function yue(e){return Object.keys(e).map(t=>{const n=e[t];return t+": "+(W(n)?yT(n):bT(n))}).join(` +`)}function yT(e){return"["+e.map(bT).join(", ")+"]"}function bT(e){return W(e)?"[…]":re(e)&&!_o(e)?"{…}":e}function bue(){if(this.renderer()==="canvas"&&this._renderer._canvas){let e=null;const t=()=>{e!=null&&e();const n=matchMedia(`(resolution: ${window.devicePixelRatio}dppx)`);n.addEventListener("change",t),e=()=>{n.removeEventListener("change",t)},this._renderer._canvas.getContext("2d").pixelRatio=window.devicePixelRatio||1,this._redraw=!0,this._resize=1,this.resize().runAsync()};t()}}function vT(e,t){const n=this;if(t=t||{},vl.call(n),t.loader&&n.loader(t.loader),t.logger&&n.logger(t.logger),t.logLevel!=null&&n.logLevel(t.logLevel),t.locale||e.locale){const s=Oe({},e.locale,t.locale);n.locale(xE(s.number,s.time))}n._el=null,n._elBind=null,n._renderType=t.renderer||Go.Canvas,n._scenegraph=new xA;const i=n._scenegraph.root;n._renderer=null,n._tooltip=t.tooltip||gue,n._redraw=!0,n._handler=new Hf().scene(i),n._globalCursor=!1,n._preventDefault=!1,n._timers=[],n._eventListeners=[],n._resizeListeners=[],n._eventConfig=Lae(e.eventConfig),n.globalCursor(n._eventConfig.globalCursor);const r=oue(n,e,t.expr);n._runtime=r,n._signals=r.signals,n._bind=(e.bindings||[]).map(s=>({state:null,param:Oe({},s)})),r.root&&r.root.set(i),i.source=r.data.root.input,n.pulse(r.data.root.input,n.changeset().insert(i.items)),n._width=n.width(),n._height=n.height(),n._viewWidth=gT(n,n._width),n._viewHeight=mT(n,n._height),n._origin=[0,0],n._resize=0,n._autosize=1,uue(n),kae(n),Aae(n),n.description(e.description),t.hover&&n.hover(),t.container&&n.initialize(t.container,t.bind),t.watchPixelRatio&&n._watchPixelRatio()}function n1(e,t){return ue(e._signals,t)?e._signals[t]:q("Unrecognized signal name: "+Q(t))}function xT(e,t){const n=(e._targets||[]).filter(i=>i._update&&i._update.handler===t);return n.length?n[0]:null}function _T(e,t,n,i){let r=xT(n,i);return r||(r=b7(e,()=>i(t,n.value)),r.handler=i,e.on(n,null,r)),e}function wT(e,t,n){const i=xT(t,n);return i&&t._targets.remove(i),e}ee(vT,vl,{async evaluate(e,t,n){if(await vl.prototype.evaluate.call(this,e,t),this._redraw||this._resize)try{this._renderer&&(this._resize&&(this._resize=0,Dae(this)),await this._renderer.renderAsync(this._scenegraph.root)),this._redraw=!1}catch(i){this.error(i)}return n&&p0(this,n),this},dirty(e){this._redraw=!0,this._renderer&&this._renderer.dirty(e)},description(e){if(arguments.length){const t=e!=null?e+"":null;return t!==this._desc&&nT(this._el,this._desc=t),this}return this._desc},container(){return this._el},scenegraph(){return this._scenegraph},origin(){return this._origin.slice()},signal(e,t,n){const i=n1(this,e);return arguments.length===1?i.value:this.update(i,t,n)},width(e){return arguments.length?this.signal("width",e):this.signal("width")},height(e){return arguments.length?this.signal("height",e):this.signal("height")},padding(e){return arguments.length?this.signal("padding",fT(e)):fT(this.signal("padding"))},autosize(e){return arguments.length?this.signal("autosize",e):this.signal("autosize")},background(e){return arguments.length?this.signal("background",e):this.signal("background")},renderer(e){return arguments.length?(Lp(e)||q("Unrecognized renderer type: "+e),e!==this._renderType&&(this._renderType=e,this._resetRenderer()),this):this._renderType},tooltip(e){return arguments.length?(e!==this._tooltip&&(this._tooltip=e,this._resetRenderer()),this):this._tooltip},loader(e){return arguments.length?(e!==this._loader&&(vl.prototype.loader.call(this,e),this._resetRenderer()),this):this._loader},resize(){return this._autosize=1,this.touch(n1(this,"autosize"))},_resetRenderer(){this._renderer&&(this._renderer=null,this.initialize(this._el,this._elBind))},_resizeView:lue,addEventListener(e,t,n){let i=t;return n&&n.trap===!1||(i=b7(this,t),i.raw=t),this._handler.on(e,i),this},removeEventListener(e,t){for(var n=this._handler.handlers(e),i=n.length,r,s;--i>=0;)if(s=n[i].type,r=n[i].handler,e===s&&(t===r||t===r.raw)){this._handler.off(s,r);break}return this},addResizeListener(e){const t=this._resizeListeners;return t.includes(e)||t.push(e),this},removeResizeListener(e){var t=this._resizeListeners,n=t.indexOf(e);return n>=0&&t.splice(n,1),this},addSignalListener(e,t){return _T(this,e,n1(this,e),t)},removeSignalListener(e,t){return wT(this,n1(this,e),t)},addDataListener(e,t){return _T(this,e,Zg(this,e).values,t)},removeDataListener(e,t){return wT(this,Zg(this,e).values,t)},globalCursor(e){if(arguments.length){if(this._globalCursor!==!!e){const t=y7(this,null);this._globalCursor=!!e,t&&y7(this,t)}return this}else return this._globalCursor},preventDefault(e){return arguments.length?(this._preventDefault=e,this):this._preventDefault},timer:pue,events:Pae,finalize:Bae,hover:zae,data:$ae,change:Jg,insert:Sae,remove:Fae,scale:aue,initialize:eue,toImageURL:nue,toCanvas:rue,toSVG:sue,getState:cue,setState:hue,_watchPixelRatio:bue});const vue="view",i1="[",r1="]",ET="{",CT="}",xue=":",kT=",",_ue="@",wue=">",Eue=/[[\]{}]/,Cue={"*":1,arc:1,area:1,group:1,image:1,line:1,path:1,rect:1,rule:1,shape:1,symbol:1,text:1,trail:1};let AT,$T;function ta(e,t,n){return AT=t||vue,$T=n||Cue,ST(e.trim()).map(w7)}function kue(e){return $T[e]}function Od(e,t,n,i,r){const s=e.length;let o=0,a;for(;t=0?--o:i&&i.indexOf(a)>=0&&++o}return t}function ST(e){const t=[],n=e.length;let i=0,r=0;for(;r' after between selector: "+e;i=i.map(w7);const r=w7(e.slice(1).trim());return r.between?{between:i,stream:r}:(r.between=i,r)}function $ue(e){const t={source:AT},n=[];let i=[0,0],r=0,s=0,o=e.length,a=0,u,l;if(e[o-1]===CT){if(a=e.lastIndexOf(ET),a>=0){try{i=Sue(e.substring(a+1,o-1))}catch{throw"Invalid throttle specification: "+e}e=e.slice(0,a).trim(),o=e.length}else throw"Unmatched right brace: "+e;a=0}if(!o)throw e;if(e[0]===_ue&&(r=++a),u=Od(e,a,xue),u1?(t.type=n[1],r?t.markname=n[0].slice(1):kue(n[0])?t.marktype=n[0]:t.source=n[0]):t.type=n[0],t.type.slice(-1)==="!"&&(t.consume=!0,t.type=t.type.slice(0,-1)),l!=null&&(t.filter=l),i[0]&&(t.throttle=i[0]),i[1]&&(t.debounce=i[1]),t}function Sue(e){const t=e.split(kT);if(!e.length||t.length>2)throw e;return t.map(n=>{const i=+n;if(i!==i)throw e;return i})}function Fue(e){return re(e)?e:{type:e||"pad"}}const Ld=e=>+e||0,Due=e=>({top:e,bottom:e,left:e,right:e});function Tue(e){return re(e)?e.signal?e:{top:Ld(e.top),bottom:Ld(e.bottom),left:Ld(e.left),right:Ld(e.right)}:Due(Ld(e))}const tn=e=>re(e)&&!W(e)?Oe({},e):{value:e};function FT(e,t,n,i){return n!=null?(re(n)&&!W(n)||W(n)&&n.length&&re(n[0])?e.update[t]=n:e[i||"enter"][t]={value:n},1):0}function pn(e,t,n){for(const i in t)FT(e,i,t[i]);for(const i in n)FT(e,i,n[i],"update")}function Ql(e,t,n){for(const i in t)n&&ue(n,i)||(e[i]=Oe(e[i]||{},t[i]));return e}function ec(e,t){return t&&(t.enter&&t.enter[e]||t.update&&t.update[e])}const E7="mark",C7="frame",k7="scope",Mue="axis",Nue="axis-domain",Rue="axis-grid",Oue="axis-label",Lue="axis-tick",Iue="axis-title",Pue="legend",zue="legend-band",Bue="legend-entry",Uue="legend-gradient",DT="legend-label",jue="legend-symbol",que="legend-title",Wue="title",Hue="title-text",Gue="title-subtitle";function Vue(e,t,n,i,r){const s={},o={};let a,u,l,c;u="lineBreak",t==="text"&&r[u]!=null&&!ec(u,e)&&A7(s,u,r[u]),(n=="legend"||String(n).startsWith("axis"))&&(n=null),c=n===C7?r.group:n===E7?Oe({},r.mark,r[t]):null;for(u in c)l=ec(u,e)||(u==="fill"||u==="stroke")&&(ec("fill",e)||ec("stroke",e)),l||A7(s,u,c[u]);oe(i).forEach(f=>{const d=r.style&&r.style[f];for(const h in d)ec(h,e)||A7(s,h,d[h])}),e=Oe({},e);for(u in s)c=s[u],c.signal?(a=a||{})[u]=c:o[u]=c;return e.enter=Oe(o,e.enter),a&&(e.update=Oe(a,e.update)),e}function A7(e,t,n){e[t]=n&&n.signal?{signal:n.signal}:{value:n}}const TT=e=>se(e)?Q(e):e.signal?`(${e.signal})`:MT(e);function s1(e){if(e.gradient!=null)return Xue(e);let t=e.signal?`(${e.signal})`:e.color?Yue(e.color):e.field!=null?MT(e.field):e.value!==void 0?Q(e.value):void 0;return e.scale!=null&&(t=Kue(e,t)),t===void 0&&(t=null),e.exponent!=null&&(t=`pow(${t},${a1(e.exponent)})`),e.mult!=null&&(t+=`*${a1(e.mult)}`),e.offset!=null&&(t+=`+${a1(e.offset)}`),e.round&&(t=`round(${t})`),t}const o1=(e,t,n,i)=>`(${e}(${[t,n,i].map(s1).join(",")})+'')`;function Yue(e){return e.c?o1("hcl",e.h,e.c,e.l):e.h||e.s?o1("hsl",e.h,e.s,e.l):e.l||e.a?o1("lab",e.l,e.a,e.b):e.r||e.g||e.b?o1("rgb",e.r,e.g,e.b):null}function Xue(e){const t=[e.start,e.stop,e.count].map(n=>n==null?null:Q(n));for(;t.length&&Ge(t)==null;)t.pop();return t.unshift(TT(e.gradient)),`gradient(${t.join(",")})`}function a1(e){return re(e)?"("+s1(e)+")":e}function MT(e){return NT(re(e)?e:{datum:e})}function NT(e){let t,n,i;if(e.signal)t="datum",i=e.signal;else if(e.group||e.parent){for(n=Math.max(1,e.level||1),t="item";n-- >0;)t+=".mark.group";e.parent?(i=e.parent,t+=".datum"):i=e.group}else e.datum?(t="datum",i=e.datum):q("Invalid field reference: "+Q(e));return e.signal||(i=se(i)?jr(i).map(Q).join("]["):NT(i)),t+"["+i+"]"}function Kue(e,t){const n=TT(e.scale);return e.range!=null?t=`lerp(_range(${n}), ${+e.range})`:(t!==void 0&&(t=`_scale(${n}, ${t})`),e.band&&(t=(t?t+"+":"")+`_bandwidth(${n})`+(+e.band==1?"":"*"+a1(e.band)),e.extra&&(t=`(datum.extra ? _scale(${n}, datum.extra.value) : ${t})`)),t==null&&(t="0")),t}function Zue(e){let t="";return e.forEach(n=>{const i=s1(n);t+=n.test?`(${n.test})?${i}:`:i}),Ge(t)===":"&&(t+="null"),t}function RT(e,t,n,i,r,s){const o={};s=s||{},s.encoders={$encode:o},e=Vue(e,t,n,i,r.config);for(const a in e)o[a]=Jue(e[a],t,s,r);return s}function Jue(e,t,n,i){const r={},s={};for(const o in e)e[o]!=null&&(r[o]=ele(Que(e[o]),i,n,s));return{$expr:{marktype:t,channels:r},$fields:Object.keys(s),$output:Object.keys(e)}}function Que(e){return W(e)?Zue(e):s1(e)}function ele(e,t,n,i){const r=cs(e,t);return r.$fields.forEach(s=>i[s]=1),Oe(n,r.$params),r.$expr}const tle="outer",nle=["value","update","init","react","bind"];function OT(e,t){q(e+' for "outer" push: '+Q(t))}function LT(e,t){const n=e.name;if(e.push===tle)t.signals[n]||OT("No prior signal definition",n),nle.forEach(i=>{e[i]!==void 0&&OT("Invalid property ",i)});else{const i=t.addSignal(n,e.value);e.react===!1&&(i.react=!1),e.bind&&t.addBinding(n,e.bind)}}function $7(e,t,n,i){this.id=-1,this.type=e,this.value=t,this.params=n,i&&(this.parent=i)}function u1(e,t,n,i){return new $7(e,t,n,i)}function l1(e,t){return u1("operator",e,t)}function Ee(e){const t={$ref:e.id};return e.id<0&&(e.refs=e.refs||[]).push(t),t}function Id(e,t){return t?{$field:e,$name:t}:{$field:e}}const S7=Id("key");function IT(e,t){return{$compare:e,$order:t}}function ile(e,t){const n={$key:e};return t&&(n.$flat=!0),n}const rle="ascending",sle="descending";function ole(e){return re(e)?(e.order===sle?"-":"+")+c1(e.op,e.field):""}function c1(e,t){return(e&&e.signal?"$"+e.signal:e||"")+(e&&t?"_":"")+(t&&t.signal?"$"+t.signal:t||"")}const F7="scope",D7="view";function Yt(e){return e&&e.signal}function ale(e){return e&&e.expr}function f1(e){if(Yt(e))return!0;if(re(e)){for(const t in e)if(f1(e[t]))return!0}return!1}function _r(e,t){return e??t}function ou(e){return e&&e.signal||e}const PT="timer";function Pd(e,t){return(e.merge?lle:e.stream?cle:e.type?fle:q("Invalid stream specification: "+Q(e)))(e,t)}function ule(e){return e===F7?D7:e||D7}function lle(e,t){const n=e.merge.map(r=>Pd(r,t)),i=T7({merge:n},e,t);return t.addStream(i).id}function cle(e,t){const n=Pd(e.stream,t),i=T7({stream:n},e,t);return t.addStream(i).id}function fle(e,t){let n;e.type===PT?(n=t.event(PT,e.throttle),e={between:e.between,filter:e.filter}):n=t.event(ule(e.source),e.type);const i=T7({stream:n},e,t);return Object.keys(i).length===1?n:t.addStream(i).id}function T7(e,t,n){let i=t.between;return i&&(i.length!==2&&q('Stream "between" parameter must have 2 entries: '+Q(t)),e.between=[Pd(i[0],n),Pd(i[1],n)]),i=t.filter?[].concat(t.filter):[],(t.marktype||t.markname||t.markrole)&&i.push(dle(t.marktype,t.markname,t.markrole)),t.source===F7&&i.push("inScope(event.item)"),i.length&&(e.filter=cs("("+i.join(")&&(")+")",n).$expr),(i=t.throttle)!=null&&(e.throttle=+i),(i=t.debounce)!=null&&(e.debounce=+i),t.consume&&(e.consume=!0),e}function dle(e,t,n){const i="event.item";return i+(e&&e!=="*"?"&&"+i+".mark.marktype==='"+e+"'":"")+(n?"&&"+i+".mark.role==='"+n+"'":"")+(t?"&&"+i+".mark.name==='"+t+"'":"")}const hle={code:"_.$value",ast:{type:"Identifier",value:"value"}};function ple(e,t,n){const i=e.encode,r={target:n};let s=e.events,o=e.update,a=[];s||q("Signal update missing events specification."),se(s)&&(s=ta(s,t.isSubscope()?F7:D7)),s=oe(s).filter(u=>u.signal||u.scale?(a.push(u),0):1),a.length>1&&(a=[mle(a)]),s.length&&a.push(s.length>1?{merge:s}:s[0]),i!=null&&(o&&q("Signal encode and update are mutually exclusive."),o="encode(item(),"+Q(i)+")"),r.update=se(o)?cs(o,t):o.expr!=null?cs(o.expr,t):o.value!=null?o.value:o.signal!=null?{$expr:hle,$params:{$value:t.signalRef(o.signal)}}:q("Invalid signal update specification."),e.force&&(r.options={force:!0}),a.forEach(u=>t.addUpdate(Oe(gle(u,t),r)))}function gle(e,t){return{source:e.signal?t.signalRef(e.signal):e.scale?t.scaleRef(e.scale):Pd(e,t)}}function mle(e){return{signal:"["+e.map(t=>t.scale?'scale("'+t.scale+'")':t.signal)+"]"}}function yle(e,t){const n=t.getSignal(e.name);let i=e.update;e.init&&(i?q("Signals can not include both init and update expressions."):(i=e.init,n.initonly=!0)),i&&(i=cs(i,t),n.update=i.$expr,n.params=i.$params),e.on&&e.on.forEach(r=>ple(r,t,n.id))}const dt=e=>(t,n,i)=>u1(e,n,t||void 0,i),zT=dt("aggregate"),ble=dt("axisticks"),BT=dt("bound"),wr=dt("collect"),UT=dt("compare"),vle=dt("datajoin"),jT=dt("encode"),xle=dt("expression"),_le=dt("facet"),wle=dt("field"),Ele=dt("key"),Cle=dt("legendentries"),kle=dt("load"),Ale=dt("mark"),$le=dt("multiextent"),Sle=dt("multivalues"),Fle=dt("overlap"),Dle=dt("params"),qT=dt("prefacet"),Tle=dt("projection"),Mle=dt("proxy"),Nle=dt("relay"),WT=dt("render"),Rle=dt("scale"),au=dt("sieve"),Ole=dt("sortitems"),HT=dt("viewlayout"),Lle=dt("values");let Ile=0;const GT={min:"min",max:"max",count:"sum"};function Ple(e,t){const n=e.type||"linear";gk(n)||q("Unrecognized scale type: "+Q(n)),t.addScale(e.name,{type:n,domain:void 0})}function zle(e,t){const n=t.getScale(e.name).params;let i;n.domain=VT(e.domain,e,t),e.range!=null&&(n.range=XT(e,t,n)),e.interpolate!=null&&Xle(e.interpolate,n),e.nice!=null&&(n.nice=Yle(e.nice,t)),e.bins!=null&&(n.bins=Vle(e.bins,t));for(i in e)ue(n,i)||i==="name"||(n[i]=Zi(e[i],t))}function Zi(e,t){return re(e)?e.signal?t.signalRef(e.signal):q("Unsupported object: "+Q(e)):e}function d1(e,t){return e.signal?t.signalRef(e.signal):e.map(n=>Zi(n,t))}function h1(e){q("Can not find data set: "+Q(e))}function VT(e,t,n){if(!e){(t.domainMin!=null||t.domainMax!=null)&&q("No scale domain defined for domainMin/domainMax to override.");return}return e.signal?n.signalRef(e.signal):(W(e)?Ble:e.fields?jle:Ule)(e,t,n)}function Ble(e,t,n){return e.map(i=>Zi(i,n))}function Ule(e,t,n){const i=n.getData(e.data);return i||h1(e.data),Dl(t.type)?i.valuesRef(n,e.field,YT(e.sort,!1)):bk(t.type)?i.domainRef(n,e.field):i.extentRef(n,e.field)}function jle(e,t,n){const i=e.data,r=e.fields.reduce((s,o)=>(o=se(o)?{data:i,field:o}:W(o)||o.signal?qle(o,n):o,s.push(o),s),[]);return(Dl(t.type)?Wle:bk(t.type)?Hle:Gle)(e,n,r)}function qle(e,t){const n="_:vega:_"+Ile++,i=wr({});if(W(e))i.value={$ingest:e};else if(e.signal){const r="setdata("+Q(n)+","+e.signal+")";i.params.input=t.signalRef(r)}return t.addDataPipeline(n,[i,au({})]),{data:n,field:"data"}}function Wle(e,t,n){const i=YT(e.sort,!0);let r,s;const o=n.map(l=>{const c=t.getData(l.data);return c||h1(l.data),c.countsRef(t,l.field,i)}),a={groupby:S7,pulse:o};i&&(r=i.op||"count",s=i.field?c1(r,i.field):"count",a.ops=[GT[r]],a.fields=[t.fieldRef(s)],a.as=[s]),r=t.add(zT(a));const u=t.add(wr({pulse:Ee(r)}));return s=t.add(Lle({field:S7,sort:t.sortRef(i),pulse:Ee(u)})),Ee(s)}function YT(e,t){return e&&(!e.field&&!e.op?re(e)?e.field="key":e={field:"key"}:!e.field&&e.op!=="count"?q("No field provided for sort aggregate op: "+e.op):t&&e.field&&e.op&&!GT[e.op]&&q("Multiple domain scales can not be sorted using "+e.op)),e}function Hle(e,t,n){const i=n.map(r=>{const s=t.getData(r.data);return s||h1(r.data),s.domainRef(t,r.field)});return Ee(t.add(Sle({values:i})))}function Gle(e,t,n){const i=n.map(r=>{const s=t.getData(r.data);return s||h1(r.data),s.extentRef(t,r.field)});return Ee(t.add($le({extents:i})))}function Vle(e,t){return e.signal||W(e)?d1(e,t):t.objectProperty(e)}function Yle(e,t){return e.signal?t.signalRef(e.signal):re(e)?{interval:Zi(e.interval),step:Zi(e.step)}:Zi(e)}function Xle(e,t){t.interpolate=Zi(e.type||e),e.gamma!=null&&(t.interpolateGamma=Zi(e.gamma))}function XT(e,t,n){const i=t.config.range;let r=e.range;if(r.signal)return t.signalRef(r.signal);if(se(r)){if(i&&ue(i,r))return e=Oe({},e,{range:i[r]}),XT(e,t,n);r==="width"?r=[0,{signal:"width"}]:r==="height"?r=Dl(e.type)?[0,{signal:"height"}]:[{signal:"height"},0]:q("Unrecognized scale range value: "+Q(r))}else if(r.scheme){n.scheme=W(r.scheme)?d1(r.scheme,t):Zi(r.scheme,t),r.extent&&(n.schemeExtent=d1(r.extent,t)),r.count&&(n.schemeCount=Zi(r.count,t));return}else if(r.step){n.rangeStep=Zi(r.step,t);return}else{if(Dl(e.type)&&!W(r))return VT(r,e,t);W(r)||q("Unsupported range type: "+Q(r))}return r.map(s=>(W(s)?d1:Zi)(s,t))}function Kle(e,t){const n=t.config.projection||{},i={};for(const r in e)r!=="name"&&(i[r]=M7(e[r],r,t));for(const r in n)i[r]==null&&(i[r]=M7(n[r],r,t));t.addProjection(e.name,i)}function M7(e,t,n){return W(e)?e.map(i=>M7(i,t,n)):re(e)?e.signal?n.signalRef(e.signal):t==="fit"?e:q("Unsupported parameter object: "+Q(e)):e}const Er="top",tc="left",nc="right",na="bottom",KT="center",Zle="vertical",Jle="start",Qle="middle",ece="end",N7="index",R7="label",tce="offset",ic="perc",nce="perc2",Ji="value",zd="guide-label",O7="guide-title",ice="group-title",rce="group-subtitle",ZT="symbol",p1="gradient",L7="discrete",I7="size",P7=[I7,"shape","fill","stroke","strokeWidth","strokeDash","opacity"],Bd={name:1,style:1,interactive:1},Ye={value:0},Qi={value:1},g1="group",JT="rect",z7="rule",sce="symbol",uu="text";function Ud(e){return e.type=g1,e.interactive=e.interactive||!1,e}function di(e,t){const n=(i,r)=>_r(e[i],_r(t[i],r));return n.isVertical=i=>Zle===_r(e.direction,t.direction||(i?t.symbolDirection:t.gradientDirection)),n.gradientLength=()=>_r(e.gradientLength,t.gradientLength||t.gradientWidth),n.gradientThickness=()=>_r(e.gradientThickness,t.gradientThickness||t.gradientHeight),n.entryColumns=()=>_r(e.columns,_r(t.columns,+n.isVertical(!0))),n}function QT(e,t){const n=t&&(t.update&&t.update[e]||t.enter&&t.enter[e]);return n&&n.signal?n:n?n.value:null}function oce(e,t,n){const i=t.config.style[n];return i&&i[e]}function m1(e,t,n){return`item.anchor === '${Jle}' ? ${e} : item.anchor === '${ece}' ? ${t} : ${n}`}const B7=m1(Q(tc),Q(nc),Q(KT));function ace(e){const t=e("tickBand");let n=e("tickOffset"),i,r;return t?t.signal?(i={signal:`(${t.signal}) === 'extent' ? 1 : 0.5`},r={signal:`(${t.signal}) === 'extent'`},re(n)||(n={signal:`(${t.signal}) === 'extent' ? 0 : ${n}`})):t==="extent"?(i=1,r=!0,n=0):(i=.5,r=!1):(i=e("bandPosition"),r=e("tickExtra")),{extra:r,band:i,offset:n}}function eM(e,t){return t?e?re(e)?Object.assign({},e,{offset:eM(e.offset,t)}):{value:e,offset:t}:t:e}function Ti(e,t){return t?(e.name=t.name,e.style=t.style||e.style,e.interactive=!!t.interactive,e.encode=Ql(e.encode,t,Bd)):e.interactive=!1,e}function uce(e,t,n,i){const r=di(e,n),s=r.isVertical(),o=r.gradientThickness(),a=r.gradientLength();let u,l,c,f,d;s?(l=[0,1],c=[0,0],f=o,d=a):(l=[0,0],c=[1,0],f=a,d=o);const h={enter:u={opacity:Ye,x:Ye,y:Ye,width:tn(f),height:tn(d)},update:Oe({},u,{opacity:Qi,fill:{gradient:t,start:l,stop:c}}),exit:{opacity:Ye}};return pn(h,{stroke:r("gradientStrokeColor"),strokeWidth:r("gradientStrokeWidth")},{opacity:r("gradientOpacity")}),Ti({type:JT,role:Uue,encode:h},i)}function lce(e,t,n,i,r){const s=di(e,n),o=s.isVertical(),a=s.gradientThickness(),u=s.gradientLength();let l,c,f,d,h="";o?(l="y",f="y2",c="x",d="width",h="1-"):(l="x",f="x2",c="y",d="height");const p={opacity:Ye,fill:{scale:t,field:Ji}};p[l]={signal:h+"datum."+ic,mult:u},p[c]=Ye,p[f]={signal:h+"datum."+nce,mult:u},p[d]=tn(a);const g={enter:p,update:Oe({},p,{opacity:Qi}),exit:{opacity:Ye}};return pn(g,{stroke:s("gradientStrokeColor"),strokeWidth:s("gradientStrokeWidth")},{opacity:s("gradientOpacity")}),Ti({type:JT,role:zue,key:Ji,from:r,encode:g},i)}const cce=`datum.${ic}<=0?"${tc}":datum.${ic}>=1?"${nc}":"${KT}"`,fce=`datum.${ic}<=0?"${na}":datum.${ic}>=1?"${Er}":"${Qle}"`;function tM(e,t,n,i){const r=di(e,t),s=r.isVertical(),o=tn(r.gradientThickness()),a=r.gradientLength();let u=r("labelOverlap"),l,c,f,d,h="";const p={enter:l={opacity:Ye},update:c={opacity:Qi,text:{field:R7}},exit:{opacity:Ye}};return pn(p,{fill:r("labelColor"),fillOpacity:r("labelOpacity"),font:r("labelFont"),fontSize:r("labelFontSize"),fontStyle:r("labelFontStyle"),fontWeight:r("labelFontWeight"),limit:_r(e.labelLimit,t.gradientLabelLimit)}),s?(l.align={value:"left"},l.baseline=c.baseline={signal:fce},f="y",d="x",h="1-"):(l.align=c.align={signal:cce},l.baseline={value:"top"},f="x",d="y"),l[f]=c[f]={signal:h+"datum."+ic,mult:a},l[d]=c[d]=o,o.offset=_r(e.labelOffset,t.gradientLabelOffset)||0,u=u?{separation:r("labelSeparation"),method:u,order:"datum."+N7}:void 0,Ti({type:uu,role:DT,style:zd,key:Ji,from:i,encode:p,overlap:u},n)}function dce(e,t,n,i,r){const s=di(e,t),o=n.entries,a=!!(o&&o.interactive),u=o?o.name:void 0,l=s("clipHeight"),c=s("symbolOffset"),f={data:"value"},d=`(${r}) ? datum.${tce} : datum.${I7}`,h=l?tn(l):{field:I7},p=`datum.${N7}`,g=`max(1, ${r})`;let m,y,b,v,x;h.mult=.5,m={enter:y={opacity:Ye,x:{signal:d,mult:.5,offset:c},y:h},update:b={opacity:Qi,x:y.x,y:y.y},exit:{opacity:Ye}};let _=null,E=null;e.fill||(_=t.symbolBaseFillColor,E=t.symbolBaseStrokeColor),pn(m,{fill:s("symbolFillColor",_),shape:s("symbolType"),size:s("symbolSize"),stroke:s("symbolStrokeColor",E),strokeDash:s("symbolDash"),strokeDashOffset:s("symbolDashOffset"),strokeWidth:s("symbolStrokeWidth")},{opacity:s("symbolOpacity")}),P7.forEach(S=>{e[S]&&(b[S]=y[S]={scale:e[S],field:Ji})});const w=Ti({type:sce,role:jue,key:Ji,from:f,clip:l?!0:void 0,encode:m},n.symbols),C=tn(c);C.offset=s("labelOffset"),m={enter:y={opacity:Ye,x:{signal:d,offset:C},y:h},update:b={opacity:Qi,text:{field:R7},x:y.x,y:y.y},exit:{opacity:Ye}},pn(m,{align:s("labelAlign"),baseline:s("labelBaseline"),fill:s("labelColor"),fillOpacity:s("labelOpacity"),font:s("labelFont"),fontSize:s("labelFontSize"),fontStyle:s("labelFontStyle"),fontWeight:s("labelFontWeight"),limit:s("labelLimit")});const k=Ti({type:uu,role:DT,style:zd,key:Ji,from:f,encode:m},n.labels);return m={enter:{noBound:{value:!l},width:Ye,height:l?tn(l):Ye,opacity:Ye},exit:{opacity:Ye},update:b={opacity:Qi,row:{signal:null},column:{signal:null}}},s.isVertical(!0)?(v=`ceil(item.mark.items.length / ${g})`,b.row.signal=`${p}%${v}`,b.column.signal=`floor(${p} / ${v})`,x={field:["row",p]}):(b.row.signal=`floor(${p} / ${g})`,b.column.signal=`${p} % ${g}`,x={field:p}),b.column.signal=`(${r})?${b.column.signal}:${p}`,i={facet:{data:i,name:"value",groupby:N7}},Ud({role:k7,from:i,encode:Ql(m,o,Bd),marks:[w,k],name:u,interactive:a,sort:x})}function hce(e,t){const n=di(e,t);return{align:n("gridAlign"),columns:n.entryColumns(),center:{row:!0,column:!1},padding:{row:n("rowPadding"),column:n("columnPadding")}}}const U7='item.orient === "left"',j7='item.orient === "right"',y1=`(${U7} || ${j7})`,pce=`datum.vgrad && ${y1}`,gce=m1('"top"','"bottom"','"middle"'),mce=m1('"right"','"left"','"center"'),yce=`datum.vgrad && ${j7} ? (${mce}) : (${y1} && !(datum.vgrad && ${U7})) ? "left" : ${B7}`,bce=`item._anchor || (${y1} ? "middle" : "start")`,vce=`${pce} ? (${U7} ? -90 : 90) : 0`,xce=`${y1} ? (datum.vgrad ? (${j7} ? "bottom" : "top") : ${gce}) : "top"`;function _ce(e,t,n,i){const r=di(e,t),s={enter:{opacity:Ye},update:{opacity:Qi,x:{field:{group:"padding"}},y:{field:{group:"padding"}}},exit:{opacity:Ye}};return pn(s,{orient:r("titleOrient"),_anchor:r("titleAnchor"),anchor:{signal:bce},angle:{signal:vce},align:{signal:yce},baseline:{signal:xce},text:e.title,fill:r("titleColor"),fillOpacity:r("titleOpacity"),font:r("titleFont"),fontSize:r("titleFontSize"),fontStyle:r("titleFontStyle"),fontWeight:r("titleFontWeight"),limit:r("titleLimit"),lineHeight:r("titleLineHeight")},{align:r("titleAlign"),baseline:r("titleBaseline")}),Ti({type:uu,role:que,style:O7,from:i,encode:s},n)}function wce(e,t){let n;return re(e)&&(e.signal?n=e.signal:e.path?n="pathShape("+nM(e.path)+")":e.sphere&&(n="geoShape("+nM(e.sphere)+', {type: "Sphere"})')),n?t.signalRef(n):!!e}function nM(e){return re(e)&&e.signal?e.signal:Q(e)}function iM(e){const t=e.role||"";return t.startsWith("axis")||t.startsWith("legend")||t.startsWith("title")?t:e.type===g1?k7:t||E7}function Ece(e){return{marktype:e.type,name:e.name||void 0,role:e.role||iM(e),zindex:+e.zindex||void 0,aria:e.aria,description:e.description}}function Cce(e,t){return e&&e.signal?t.signalRef(e.signal):e!==!1}function q7(e,t){const n=UE(e.type);n||q("Unrecognized transform type: "+Q(e.type));const i=u1(n.type.toLowerCase(),null,rM(n,e,t));return e.signal&&t.addSignal(e.signal,t.proxy(i)),i.metadata=n.metadata||{},i}function rM(e,t,n){const i={},r=e.params.length;for(let s=0;ssM(e,s,n)):sM(e,r,n)}function sM(e,t,n){const i=e.type;if(Yt(t))return aM(i)?q("Expression references can not be signals."):W7(i)?n.fieldRef(t):uM(i)?n.compareRef(t):n.signalRef(t.signal);{const r=e.expr||W7(i);return r&&Sce(t)?n.exprRef(t.expr,t.as):r&&Fce(t)?Id(t.field,t.as):aM(i)?cs(t,n):Dce(i)?Ee(n.getData(t).values):W7(i)?Id(t):uM(i)?n.compareRef(t):t}}function Ace(e,t,n){return se(t.from)||q('Lookup "from" parameter must be a string literal.'),n.getData(t.from).lookupRef(n,t.key)}function $ce(e,t,n){const i=t[e.name];return e.array?(W(i)||q("Expected an array of sub-parameters. Instead: "+Q(i)),i.map(r=>oM(e,r,n))):oM(e,i,n)}function oM(e,t,n){const i=e.params.length;let r;for(let o=0;oe&&e.expr,Fce=e=>e&&e.field,Dce=e=>e==="data",aM=e=>e==="expr",W7=e=>e==="field",uM=e=>e==="compare";function Tce(e,t,n){let i,r,s,o,a;return e?(i=e.facet)&&(t||q("Only group marks can be faceted."),i.field!=null?o=a=b1(i,n):(e.data?a=Ee(n.getData(e.data).aggregate):(s=q7(Oe({type:"aggregate",groupby:oe(i.groupby)},i.aggregate),n),s.params.key=n.keyRef(i.groupby),s.params.pulse=b1(i,n),o=a=Ee(n.add(s))),r=n.keyRef(i.groupby,!0))):o=Ee(n.add(wr(null,[{}]))),o||(o=b1(e,n)),{key:r,pulse:o,parent:a}}function b1(e,t){return e.$ref?e:e.data&&e.data.$ref?e.data:Ee(t.getData(e.data).output)}function lu(e,t,n,i,r){this.scope=e,this.input=t,this.output=n,this.values=i,this.aggregate=r,this.index={}}lu.fromEntries=function(e,t){const n=t.length,i=t[n-1],r=t[n-2];let s=t[0],o=null,a=1;for(s&&s.type==="load"&&(s=t[1]),e.add(t[0]);af??"null").join(",")+"),0)",c=cs(l,t);u.update=c.$expr,u.params=c.$params}function v1(e,t){const n=iM(e),i=e.type===g1,r=e.from&&e.from.facet,s=e.overlap;let o=e.layout||n===k7||n===C7,a,u,l,c,f,d,h;const p=n===E7||o||r,g=Tce(e.from,i,t);u=t.add(vle({key:g.key||(e.key?Id(e.key):void 0),pulse:g.pulse,clean:!i}));const m=Ee(u);u=l=t.add(wr({pulse:m})),u=t.add(Ale({markdef:Ece(e),interactive:Cce(e.interactive,t),clip:wce(e.clip,t),context:{$context:!0},groups:t.lookup(),parent:t.signals.parent?t.signalRef("parent"):null,index:t.markpath(),pulse:Ee(u)}));const y=Ee(u);u=c=t.add(jT(RT(e.encode,e.type,n,e.style,t,{mod:!1,pulse:y}))),u.params.parent=t.encode(),e.transform&&e.transform.forEach(E=>{const w=q7(E,t),C=w.metadata;(C.generates||C.changes)&&q("Mark transforms should not generate new data."),C.nomod||(c.params.mod=!0),w.params.pulse=Ee(u),t.add(u=w)}),e.sort&&(u=t.add(Ole({sort:t.compareRef(e.sort),pulse:Ee(u)})));const b=Ee(u);(r||o)&&(o=t.add(HT({layout:t.objectProperty(e.layout),legends:t.legends,mark:y,pulse:b})),d=Ee(o));const v=t.add(BT({mark:y,pulse:d||b}));h=Ee(v),i&&(p&&(a=t.operators,a.pop(),o&&a.pop()),t.pushState(b,d||h,m),r?Mce(e,t,g):p?Nce(e,t,g):t.parse(e),t.popState(),p&&(o&&a.push(o),a.push(v))),s&&(h=Rce(s,h,t));const x=t.add(WT({pulse:h})),_=t.add(au({pulse:Ee(x)},void 0,t.parent()));e.name!=null&&(f=e.name,t.addData(f,new lu(t,l,x,_)),e.on&&e.on.forEach(E=>{(E.insert||E.remove||E.toggle)&&q("Marks only support modify triggers."),fM(E,t,f)}))}function Rce(e,t,n){const i=e.method,r=e.bound,s=e.separation,o={separation:Yt(s)?n.signalRef(s.signal):s,method:Yt(i)?n.signalRef(i.signal):i,pulse:t};if(e.order&&(o.sort=n.compareRef({field:e.order})),r){const a=r.tolerance;o.boundTolerance=Yt(a)?n.signalRef(a.signal):+a,o.boundScale=n.scaleRef(r.scale),o.boundOrient=r.orient}return Ee(n.add(Fle(o)))}function Oce(e,t){const n=t.config.legend,i=e.encode||{},r=di(e,n),s=i.legend||{},o=s.name||void 0,a=s.interactive,u=s.style,l={};let c=0,f,d,h;P7.forEach(v=>e[v]?(l[v]=e[v],c=c||e[v]):0),c||q("Missing valid scale for legend.");const p=Lce(e,t.scaleType(c)),g={title:e.title!=null,scales:l,type:p,vgrad:p!=="symbol"&&r.isVertical()},m=Ee(t.add(wr(null,[g]))),y={enter:{x:{value:0},y:{value:0}}},b=Ee(t.add(Cle(d={type:p,scale:t.scaleRef(c),count:t.objectProperty(r("tickCount")),limit:t.property(r("symbolLimit")),values:t.objectProperty(e.values),minstep:t.property(e.tickMinStep),formatType:t.property(e.formatType),formatSpecifier:t.property(e.format)})));return p===p1?(h=[uce(e,c,n,i.gradient),tM(e,n,i.labels,b)],d.count=d.count||t.signalRef(`max(2,2*floor((${ou(r.gradientLength())})/100))`)):p===L7?h=[lce(e,c,n,i.gradient,b),tM(e,n,i.labels,b)]:(f=hce(e,n),h=[dce(e,n,i,b,ou(f.columns))],d.size=zce(e,t,h[0].marks)),h=[Ud({role:Bue,from:m,encode:y,marks:h,layout:f,interactive:a})],g.title&&h.push(_ce(e,n,i.title,m)),v1(Ud({role:Pue,from:m,encode:Ql(Pce(r,e,n),s,Bd),marks:h,aria:r("aria"),description:r("description"),zindex:r("zindex"),name:o,interactive:a,style:u}),t)}function Lce(e,t){let n=e.type||ZT;return!e.type&&Ice(e)===1&&(e.fill||e.stroke)&&(n=Kb(t)?p1:Zb(t)?L7:ZT),n!==p1?n:Zb(t)?L7:p1}function Ice(e){return P7.reduce((t,n)=>t+(e[n]?1:0),0)}function Pce(e,t,n){const i={enter:{},update:{}};return pn(i,{orient:e("orient"),offset:e("offset"),padding:e("padding"),titlePadding:e("titlePadding"),cornerRadius:e("cornerRadius"),fill:e("fillColor"),stroke:e("strokeColor"),strokeWidth:n.strokeWidth,strokeDash:n.strokeDash,x:e("legendX"),y:e("legendY"),format:t.format,formatType:t.formatType}),i}function zce(e,t,n){const i=ou(dM("size",e,n)),r=ou(dM("strokeWidth",e,n)),s=ou(Bce(n[1].encode,t,zd));return cs(`max(ceil(sqrt(${i})+${r}),${s})`,t)}function dM(e,t,n){return t[e]?`scale("${t[e]}",datum)`:QT(e,n[0].encode)}function Bce(e,t,n){return QT("fontSize",e)||oce("fontSize",t,n)}const Uce=`item.orient==="${tc}"?-90:item.orient==="${nc}"?90:0`;function jce(e,t){e=se(e)?{text:e}:e;const n=di(e,t.config.title),i=e.encode||{},r=i.group||{},s=r.name||void 0,o=r.interactive,a=r.style,u=[],l={},c=Ee(t.add(wr(null,[l])));return u.push(Hce(e,n,qce(e),c)),e.subtitle&&u.push(Gce(e,n,i.subtitle,c)),v1(Ud({role:Wue,from:c,encode:Wce(n,r),marks:u,aria:n("aria"),description:n("description"),zindex:n("zindex"),name:s,interactive:o,style:a}),t)}function qce(e){const t=e.encode;return t&&t.title||Oe({name:e.name,interactive:e.interactive,style:e.style},t)}function Wce(e,t){const n={enter:{},update:{}};return pn(n,{orient:e("orient"),anchor:e("anchor"),align:{signal:B7},angle:{signal:Uce},limit:e("limit"),frame:e("frame"),offset:e("offset")||0,padding:e("subtitlePadding")}),Ql(n,t,Bd)}function Hce(e,t,n,i){const r={value:0},s=e.text,o={enter:{opacity:r},update:{opacity:{value:1}},exit:{opacity:r}};return pn(o,{text:s,align:{signal:"item.mark.group.align"},angle:{signal:"item.mark.group.angle"},limit:{signal:"item.mark.group.limit"},baseline:"top",dx:t("dx"),dy:t("dy"),fill:t("color"),font:t("font"),fontSize:t("fontSize"),fontStyle:t("fontStyle"),fontWeight:t("fontWeight"),lineHeight:t("lineHeight")},{align:t("align"),angle:t("angle"),baseline:t("baseline")}),Ti({type:uu,role:Hue,style:ice,from:i,encode:o},n)}function Gce(e,t,n,i){const r={value:0},s=e.subtitle,o={enter:{opacity:r},update:{opacity:{value:1}},exit:{opacity:r}};return pn(o,{text:s,align:{signal:"item.mark.group.align"},angle:{signal:"item.mark.group.angle"},limit:{signal:"item.mark.group.limit"},baseline:"top",dx:t("dx"),dy:t("dy"),fill:t("subtitleColor"),font:t("subtitleFont"),fontSize:t("subtitleFontSize"),fontStyle:t("subtitleFontStyle"),fontWeight:t("subtitleFontWeight"),lineHeight:t("subtitleLineHeight")},{align:t("align"),angle:t("angle"),baseline:t("baseline")}),Ti({type:uu,role:Gue,style:rce,from:i,encode:o},n)}function Vce(e,t){const n=[];e.transform&&e.transform.forEach(i=>{n.push(q7(i,t))}),e.on&&e.on.forEach(i=>{fM(i,t,e.name)}),t.addDataPipeline(e.name,Yce(e,t,n))}function Yce(e,t,n){const i=[];let r=null,s=!1,o=!1,a,u,l,c,f;for(e.values?Yt(e.values)||f1(e.format)?(i.push(hM(t,e)),i.push(r=cu())):i.push(r=cu({$ingest:e.values,$format:e.format})):e.url?f1(e.url)||f1(e.format)?(i.push(hM(t,e)),i.push(r=cu())):i.push(r=cu({$request:e.url,$format:e.format})):e.source&&(r=a=oe(e.source).map(d=>Ee(t.getData(d).output)),i.push(null)),u=0,l=n.length;ue===na||e===Er,x1=(e,t,n)=>Yt(e)?Jce(e.signal,t,n):e===tc||e===Er?t:n,nn=(e,t,n)=>Yt(e)?Kce(e.signal,t,n):pM(e)?t:n,Cr=(e,t,n)=>Yt(e)?Zce(e.signal,t,n):pM(e)?n:t,gM=(e,t,n)=>Yt(e)?Qce(e.signal,t,n):e===Er?{value:t}:{value:n},Xce=(e,t,n)=>Yt(e)?efe(e.signal,t,n):e===nc?{value:t}:{value:n},Kce=(e,t,n)=>mM(`${e} === '${Er}' || ${e} === '${na}'`,t,n),Zce=(e,t,n)=>mM(`${e} !== '${Er}' && ${e} !== '${na}'`,t,n),Jce=(e,t,n)=>H7(`${e} === '${tc}' || ${e} === '${Er}'`,t,n),Qce=(e,t,n)=>H7(`${e} === '${Er}'`,t,n),efe=(e,t,n)=>H7(`${e} === '${nc}'`,t,n),mM=(e,t,n)=>(t=t!=null?tn(t):t,n=n!=null?tn(n):n,yM(t)&&yM(n)?(t=t?t.signal||Q(t.value):null,n=n?n.signal||Q(n.value):null,{signal:`${e} ? (${t}) : (${n})`}):[Oe({test:e},t)].concat(n||[])),yM=e=>e==null||Object.keys(e).length===1,H7=(e,t,n)=>({signal:`${e} ? (${rc(t)}) : (${rc(n)})`}),tfe=(e,t,n,i,r)=>({signal:(i!=null?`${e} === '${tc}' ? (${rc(i)}) : `:"")+(n!=null?`${e} === '${na}' ? (${rc(n)}) : `:"")+(r!=null?`${e} === '${nc}' ? (${rc(r)}) : `:"")+(t!=null?`${e} === '${Er}' ? (${rc(t)}) : `:"")+"(null)"}),rc=e=>Yt(e)?e.signal:e==null?null:Q(e),nfe=(e,t)=>t===0?0:Yt(e)?{signal:`(${e.signal}) * ${t}`}:{value:e*t},sc=(e,t)=>{const n=e.signal;return n&&n.endsWith("(null)")?{signal:n.slice(0,-6)+t.signal}:e};function oc(e,t,n,i){let r;if(t&&ue(t,e))return t[e];if(ue(n,e))return n[e];if(e.startsWith("title")){switch(e){case"titleColor":r="fill";break;case"titleFont":case"titleFontSize":case"titleFontWeight":r=e[5].toLowerCase()+e.slice(6)}return i[O7][r]}else if(e.startsWith("label")){switch(e){case"labelColor":r="fill";break;case"labelFont":case"labelFontSize":r=e[5].toLowerCase()+e.slice(6)}return i[zd][r]}return null}function bM(e){const t={};for(const n of e)if(n)for(const i in n)t[i]=1;return Object.keys(t)}function ife(e,t){var n=t.config,i=n.style,r=n.axis,s=t.scaleType(e.scale)==="band"&&n.axisBand,o=e.orient,a,u,l;if(Yt(o)){const f=bM([n.axisX,n.axisY]),d=bM([n.axisTop,n.axisBottom,n.axisLeft,n.axisRight]);a={};for(l of f)a[l]=nn(o,oc(l,n.axisX,r,i),oc(l,n.axisY,r,i));u={};for(l of d)u[l]=tfe(o.signal,oc(l,n.axisTop,r,i),oc(l,n.axisBottom,r,i),oc(l,n.axisLeft,r,i),oc(l,n.axisRight,r,i))}else a=o===Er||o===na?n.axisX:n.axisY,u=n["axis"+o[0].toUpperCase()+o.slice(1)];return a||u||s?Oe({},r,a,u,s):r}function rfe(e,t,n,i){const r=di(e,t),s=e.orient;let o,a;const u={enter:o={opacity:Ye},update:a={opacity:Qi},exit:{opacity:Ye}};pn(u,{stroke:r("domainColor"),strokeCap:r("domainCap"),strokeDash:r("domainDash"),strokeDashOffset:r("domainDashOffset"),strokeWidth:r("domainWidth"),strokeOpacity:r("domainOpacity")});const l=vM(e,0),c=vM(e,1);return o.x=a.x=nn(s,l,Ye),o.x2=a.x2=nn(s,c),o.y=a.y=Cr(s,l,Ye),o.y2=a.y2=Cr(s,c),Ti({type:z7,role:Nue,from:i,encode:u},n)}function vM(e,t){return{scale:e.scale,range:t}}function sfe(e,t,n,i,r){const s=di(e,t),o=e.orient,a=e.gridScale,u=x1(o,1,-1),l=ofe(e.offset,u);let c,f,d;const h={enter:c={opacity:Ye},update:d={opacity:Qi},exit:f={opacity:Ye}};pn(h,{stroke:s("gridColor"),strokeCap:s("gridCap"),strokeDash:s("gridDash"),strokeDashOffset:s("gridDashOffset"),strokeOpacity:s("gridOpacity"),strokeWidth:s("gridWidth")});const p={scale:e.scale,field:Ji,band:r.band,extra:r.extra,offset:r.offset,round:s("tickRound")},g=nn(o,{signal:"height"},{signal:"width"}),m=a?{scale:a,range:0,mult:u,offset:l}:{value:0,offset:l},y=a?{scale:a,range:1,mult:u,offset:l}:Oe(g,{mult:u,offset:l});return c.x=d.x=nn(o,p,m),c.y=d.y=Cr(o,p,m),c.x2=d.x2=Cr(o,y),c.y2=d.y2=nn(o,y),f.x=nn(o,p),f.y=Cr(o,p),Ti({type:z7,role:Rue,key:Ji,from:i,encode:h},n)}function ofe(e,t){if(t!==1)if(!re(e))e=Yt(t)?{signal:`(${t.signal}) * (${e||0})`}:t*(e||0);else{let n=e=Oe({},e);for(;n.mult!=null;)if(re(n.mult))n=n.mult=Oe({},n.mult);else return n.mult=Yt(t)?{signal:`(${n.mult}) * (${t.signal})`}:n.mult*t,e;n.mult=t}return e}function afe(e,t,n,i,r,s){const o=di(e,t),a=e.orient,u=x1(a,-1,1);let l,c,f;const d={enter:l={opacity:Ye},update:f={opacity:Qi},exit:c={opacity:Ye}};pn(d,{stroke:o("tickColor"),strokeCap:o("tickCap"),strokeDash:o("tickDash"),strokeDashOffset:o("tickDashOffset"),strokeOpacity:o("tickOpacity"),strokeWidth:o("tickWidth")});const h=tn(r);h.mult=u;const p={scale:e.scale,field:Ji,band:s.band,extra:s.extra,offset:s.offset,round:o("tickRound")};return f.y=l.y=nn(a,Ye,p),f.y2=l.y2=nn(a,h),c.x=nn(a,p),f.x=l.x=Cr(a,Ye,p),f.x2=l.x2=Cr(a,h),c.y=Cr(a,p),Ti({type:z7,role:Lue,key:Ji,from:i,encode:d},n)}function G7(e,t,n,i,r){return{signal:'flush(range("'+e+'"), scale("'+e+'", datum.value), '+t+","+n+","+i+","+r+")"}}function ufe(e,t,n,i,r,s){const o=di(e,t),a=e.orient,u=e.scale,l=x1(a,-1,1),c=ou(o("labelFlush")),f=ou(o("labelFlushOffset")),d=o("labelAlign"),h=o("labelBaseline");let p=c===0||!!c,g;const m=tn(r);m.mult=l,m.offset=tn(o("labelPadding")||0),m.offset.mult=l;const y={scale:u,field:Ji,band:.5,offset:eM(s.offset,o("labelOffset"))},b=nn(a,p?G7(u,c,'"left"','"right"','"center"'):{value:"center"},Xce(a,"left","right")),v=nn(a,gM(a,"bottom","top"),p?G7(u,c,'"top"','"bottom"','"middle"'):{value:"middle"}),x=G7(u,c,`-(${f})`,f,0);p=p&&f;const _={opacity:Ye,x:nn(a,y,m),y:Cr(a,y,m)},E={enter:_,update:g={opacity:Qi,text:{field:R7},x:_.x,y:_.y,align:b,baseline:v},exit:{opacity:Ye,x:_.x,y:_.y}};pn(E,{dx:!d&&p?nn(a,x):null,dy:!h&&p?Cr(a,x):null}),pn(E,{angle:o("labelAngle"),fill:o("labelColor"),fillOpacity:o("labelOpacity"),font:o("labelFont"),fontSize:o("labelFontSize"),fontWeight:o("labelFontWeight"),fontStyle:o("labelFontStyle"),limit:o("labelLimit"),lineHeight:o("labelLineHeight")},{align:d,baseline:h});const w=o("labelBound");let C=o("labelOverlap");return C=C||w?{separation:o("labelSeparation"),method:C,order:"datum.index",bound:w?{scale:u,orient:a,tolerance:w}:null}:void 0,g.align!==b&&(g.align=sc(g.align,b)),g.baseline!==v&&(g.baseline=sc(g.baseline,v)),Ti({type:uu,role:Oue,style:zd,key:Ji,from:i,encode:E,overlap:C},n)}function lfe(e,t,n,i){const r=di(e,t),s=e.orient,o=x1(s,-1,1);let a,u;const l={enter:a={opacity:Ye,anchor:tn(r("titleAnchor",null)),align:{signal:B7}},update:u=Oe({},a,{opacity:Qi,text:tn(e.title)}),exit:{opacity:Ye}},c={signal:`lerp(range("${e.scale}"), ${m1(0,1,.5)})`};return u.x=nn(s,c),u.y=Cr(s,c),a.angle=nn(s,Ye,nfe(o,90)),a.baseline=nn(s,gM(s,na,Er),{value:na}),u.angle=a.angle,u.baseline=a.baseline,pn(l,{fill:r("titleColor"),fillOpacity:r("titleOpacity"),font:r("titleFont"),fontSize:r("titleFontSize"),fontStyle:r("titleFontStyle"),fontWeight:r("titleFontWeight"),limit:r("titleLimit"),lineHeight:r("titleLineHeight")},{align:r("titleAlign"),angle:r("titleAngle"),baseline:r("titleBaseline")}),cfe(r,s,l,n),l.update.align=sc(l.update.align,a.align),l.update.angle=sc(l.update.angle,a.angle),l.update.baseline=sc(l.update.baseline,a.baseline),Ti({type:uu,role:Iue,style:O7,from:i,encode:l},n)}function cfe(e,t,n,i){const r=(a,u)=>a!=null?(n.update[u]=sc(tn(a),n.update[u]),!1):!ec(u,i),s=r(e("titleX"),"x"),o=r(e("titleY"),"y");n.enter.auto=o===s?tn(o):nn(t,tn(o),tn(s))}function ffe(e,t){const n=ife(e,t),i=e.encode||{},r=i.axis||{},s=r.name||void 0,o=r.interactive,a=r.style,u=di(e,n),l=ace(u),c={scale:e.scale,ticks:!!u("ticks"),labels:!!u("labels"),grid:!!u("grid"),domain:!!u("domain"),title:e.title!=null},f=Ee(t.add(wr({},[c]))),d=Ee(t.add(ble({scale:t.scaleRef(e.scale),extra:t.property(l.extra),count:t.objectProperty(e.tickCount),values:t.objectProperty(e.values),minstep:t.property(e.tickMinStep),formatType:t.property(e.formatType),formatSpecifier:t.property(e.format)}))),h=[];let p;return c.grid&&h.push(sfe(e,n,i.grid,d,l)),c.ticks&&(p=u("tickSize"),h.push(afe(e,n,i.ticks,d,p,l))),c.labels&&(p=c.ticks?p:0,h.push(ufe(e,n,i.labels,d,p,l))),c.domain&&h.push(rfe(e,n,i.domain,f)),c.title&&h.push(lfe(e,n,i.title,f)),v1(Ud({role:Mue,from:f,encode:Ql(dfe(u,e),r,Bd),marks:h,aria:u("aria"),description:u("description"),zindex:u("zindex"),name:s,interactive:o,style:a}),t)}function dfe(e,t){const n={enter:{},update:{}};return pn(n,{orient:e("orient"),offset:e("offset")||0,position:_r(t.position,0),titlePadding:e("titlePadding"),minExtent:e("minExtent"),maxExtent:e("maxExtent"),range:{signal:`abs(span(range("${t.scale}")))`},translate:e("translate"),format:t.format,formatType:t.formatType}),n}function xM(e,t,n){const i=oe(e.signals),r=oe(e.scales);return n||i.forEach(s=>LT(s,t)),oe(e.projections).forEach(s=>Kle(s,t)),r.forEach(s=>Ple(s,t)),oe(e.data).forEach(s=>Vce(s,t)),r.forEach(s=>zle(s,t)),(n||i).forEach(s=>yle(s,t)),oe(e.axes).forEach(s=>ffe(s,t)),oe(e.marks).forEach(s=>v1(s,t)),oe(e.legends).forEach(s=>Oce(s,t)),e.title&&jce(e.title,t),t.parseLambdas(),t}const hfe=e=>Ql({enter:{x:{value:0},y:{value:0}},update:{width:{signal:"width"},height:{signal:"height"}}},e);function pfe(e,t){const n=t.config,i=Ee(t.root=t.add(l1())),r=gfe(e,n);r.forEach(l=>LT(l,t)),t.description=e.description||n.description,t.eventConfig=n.events,t.legends=t.objectProperty(n.legend&&n.legend.layout),t.locale=n.locale;const s=t.add(wr()),o=t.add(jT(RT(hfe(e.encode),g1,C7,e.style,t,{pulse:Ee(s)}))),a=t.add(HT({layout:t.objectProperty(e.layout),legends:t.legends,autosize:t.signalRef("autosize"),mark:i,pulse:Ee(o)}));t.operators.pop(),t.pushState(Ee(o),Ee(a),null),xM(e,t,r),t.operators.push(a);let u=t.add(BT({mark:i,pulse:Ee(a)}));return u=t.add(WT({pulse:Ee(u)})),u=t.add(au({pulse:Ee(u)})),t.addData("root",new lu(t,s,s,u)),t}function qd(e,t){return t&&t.signal?{name:e,update:t.signal}:{name:e,value:t}}function gfe(e,t){const n=o=>_r(e[o],t[o]),i=[qd("background",n("background")),qd("autosize",Fue(n("autosize"))),qd("padding",Tue(n("padding"))),qd("width",n("width")||0),qd("height",n("height")||0)],r=i.reduce((o,a)=>(o[a.name]=a,o),{}),s={};return oe(e.signals).forEach(o=>{ue(r,o.name)?o=Oe(r[o.name],o):i.push(o),s[o.name]=o}),oe(t.signals).forEach(o=>{!ue(s,o.name)&&!ue(r,o.name)&&i.push(o)}),i}function _M(e,t){this.config=e||{},this.options=t||{},this.bindings=[],this.field={},this.signals={},this.lambdas={},this.scales={},this.events={},this.data={},this.streams=[],this.updates=[],this.operators=[],this.eventConfig=null,this.locale=null,this._id=0,this._subid=0,this._nextsub=[0],this._parent=[],this._encode=[],this._lookup=[],this._markpath=[]}function wM(e){this.config=e.config,this.options=e.options,this.legends=e.legends,this.field=Object.create(e.field),this.signals=Object.create(e.signals),this.lambdas=Object.create(e.lambdas),this.scales=Object.create(e.scales),this.events=Object.create(e.events),this.data=Object.create(e.data),this.streams=[],this.updates=[],this.operators=[],this._id=0,this._subid=++e._nextsub[0],this._nextsub=e._nextsub,this._parent=e._parent.slice(),this._encode=e._encode.slice(),this._lookup=e._lookup.slice(),this._markpath=e._markpath}_M.prototype=wM.prototype={parse(e){return xM(e,this)},fork(){return new wM(this)},isSubscope(){return this._subid>0},toRuntime(){return this.finish(),{description:this.description,operators:this.operators,streams:this.streams,updates:this.updates,bindings:this.bindings,eventConfig:this.eventConfig,locale:this.locale}},id(){return(this._subid?this._subid+":":0)+this._id++},add(e){return this.operators.push(e),e.id=this.id(),e.refs&&(e.refs.forEach(t=>{t.$ref=e.id}),e.refs=null),e},proxy(e){const t=e instanceof $7?Ee(e):e;return this.add(Mle({value:t}))},addStream(e){return this.streams.push(e),e.id=this.id(),e},addUpdate(e){return this.updates.push(e),e},finish(){let e,t;this.root&&(this.root.root=!0);for(e in this.signals)this.signals[e].signal=e;for(e in this.scales)this.scales[e].scale=e;function n(i,r,s){let o,a;i&&(o=i.data||(i.data={}),a=o[r]||(o[r]=[]),a.push(s))}for(e in this.data){t=this.data[e],n(t.input,e,"input"),n(t.output,e,"output"),n(t.values,e,"values");for(const i in t.index)n(t.index[i],e,"index:"+i)}return this},pushState(e,t,n){this._encode.push(Ee(this.add(au({pulse:e})))),this._parent.push(t),this._lookup.push(n?Ee(this.proxy(n)):null),this._markpath.push(-1)},popState(){this._encode.pop(),this._parent.pop(),this._lookup.pop(),this._markpath.pop()},parent(){return Ge(this._parent)},encode(){return Ge(this._encode)},lookup(){return Ge(this._lookup)},markpath(){const e=this._markpath;return++e[e.length-1]},fieldRef(e,t){if(se(e))return Id(e,t);e.signal||q("Unsupported field reference: "+Q(e));const n=e.signal;let i=this.field[n];if(!i){const r={name:this.signalRef(n)};t&&(r.as=t),this.field[n]=i=Ee(this.add(wle(r)))}return i},compareRef(e){let t=!1;const n=s=>Yt(s)?(t=!0,this.signalRef(s.signal)):ale(s)?(t=!0,this.exprRef(s.expr)):s,i=oe(e.field).map(n),r=oe(e.order).map(n);return t?Ee(this.add(UT({fields:i,orders:r}))):IT(i,r)},keyRef(e,t){let n=!1;const i=s=>Yt(s)?(n=!0,Ee(r[s.signal])):s,r=this.signals;return e=oe(e).map(i),n?Ee(this.add(Ele({fields:e,flat:t}))):ile(e,t)},sortRef(e){if(!e)return e;const t=c1(e.op,e.field),n=e.order||rle;return n.signal?Ee(this.add(UT({fields:t,orders:this.signalRef(n.signal)}))):IT(t,n)},event(e,t){const n=e+":"+t;if(!this.events[n]){const i=this.id();this.streams.push({id:i,source:e,type:t}),this.events[n]=i}return this.events[n]},hasOwnSignal(e){return ue(this.signals,e)},addSignal(e,t){this.hasOwnSignal(e)&&q("Duplicate signal name: "+Q(e));const n=t instanceof $7?t:this.add(l1(t));return this.signals[e]=n},getSignal(e){return this.signals[e]||q("Unrecognized signal name: "+Q(e)),this.signals[e]},signalRef(e){return this.signals[e]?Ee(this.signals[e]):(ue(this.lambdas,e)||(this.lambdas[e]=this.add(l1(null))),Ee(this.lambdas[e]))},parseLambdas(){const e=Object.keys(this.lambdas);for(let t=0,n=e.length;t0?",":"")+(re(r)?r.signal||V7(r):Q(r))}return n+"]"}function yfe(e){let t="{",n=0,i,r;for(i in e)r=e[i],t+=(++n>1?",":"")+Q(i)+":"+(re(r)?r.signal||V7(r):Q(r));return t+"}"}function bfe(){const e="sans-serif",i="#4c78a8",r="#000",s="#888",o="#ddd";return{description:"Vega visualization",padding:0,autosize:"pad",background:null,events:{defaults:{allow:["wheel"]}},group:null,mark:null,arc:{fill:i},area:{fill:i},image:null,line:{stroke:i,strokeWidth:2},path:{stroke:i},rect:{fill:i},rule:{stroke:r},shape:{stroke:i},symbol:{fill:i,size:64},text:{fill:r,font:e,fontSize:11},trail:{fill:i,size:2},style:{"guide-label":{fill:r,font:e,fontSize:10},"guide-title":{fill:r,font:e,fontSize:11,fontWeight:"bold"},"group-title":{fill:r,font:e,fontSize:13,fontWeight:"bold"},"group-subtitle":{fill:r,font:e,fontSize:12},point:{size:30,strokeWidth:2,shape:"circle"},circle:{size:30,strokeWidth:2},square:{size:30,strokeWidth:2,shape:"square"},cell:{fill:"transparent",stroke:o},view:{fill:"transparent"}},title:{orient:"top",anchor:"middle",offset:4,subtitlePadding:3},axis:{minExtent:0,maxExtent:200,bandPosition:.5,domain:!0,domainWidth:1,domainColor:s,grid:!1,gridWidth:1,gridColor:o,labels:!0,labelAngle:0,labelLimit:180,labelOffset:0,labelPadding:2,ticks:!0,tickColor:s,tickOffset:0,tickRound:!0,tickSize:5,tickWidth:1,titlePadding:4},axisBand:{tickOffset:-.5},projection:{type:"mercator"},legend:{orient:"right",padding:0,gridAlign:"each",columnPadding:10,rowPadding:2,symbolDirection:"vertical",gradientDirection:"vertical",gradientLength:200,gradientThickness:16,gradientStrokeColor:o,gradientStrokeWidth:0,gradientLabelOffset:2,labelAlign:"left",labelBaseline:"middle",labelLimit:160,labelOffset:4,labelOverlap:!0,symbolLimit:30,symbolType:"circle",symbolSize:100,symbolOffset:0,symbolStrokeWidth:1.5,symbolBaseFillColor:"transparent",symbolBaseStrokeColor:s,titleLimit:180,titleOrient:"top",titlePadding:5,layout:{offset:18,direction:"horizontal",left:{direction:"vertical"},right:{direction:"vertical"}}},range:{category:{scheme:"tableau10"},ordinal:{scheme:"blues"},heatmap:{scheme:"yellowgreenblue"},ramp:{scheme:"blues"},diverging:{scheme:"blueorange",extent:[1,0]},symbol:["circle","square","triangle-up","cross","diamond","triangle-right","triangle-down","triangle-left"]}}}function vfe(e,t,n){return re(e)||q("Input Vega specification must be an object."),t=nl(bfe(),t,e.config),pfe(e,new _M(t,n)).toRuntime()}var xfe="5.32.0";Oe(xl,_V,JJ,EQ,ute,nne,Fie,uie,Tie,tre,fre,bre);const _fe=Object.freeze(Object.defineProperty({__proto__:null,Bounds:Ut,CanvasHandler:Hf,CanvasRenderer:Mp,DATE:oi,DAY:$n,DAYOFYEAR:Gr,Dataflow:vl,Debug:M4,Error:b2,EventStream:v0,Gradient:Lk,GroupItem:up,HOURS:Ci,Handler:N3,HybridHandler:ZA,HybridRenderer:H3,Info:T4,Item:ap,MILLISECONDS:cr,MINUTES:ki,MONTH:An,Marks:$i,MultiPulse:py,None:D4,Operator:xt,Parameters:b0,Pulse:Fo,QUARTER:si,RenderType:Go,Renderer:qf,ResourceLoader:Vk,SECONDS:qi,SVGHandler:MA,SVGRenderer:W3,SVGStringRenderer:KA,Scenegraph:xA,TIME_UNITS:H2,Transform:P,View:vT,WEEK:Wt,Warn:v2,YEAR:cn,accessor:ii,accessorFields:wn,accessorName:Tt,array:oe,ascending:rl,bandwidthNRD:by,bin:WE,bootstrapCI:HE,boundClip:a$,boundContext:Lf,boundItem:T3,boundMark:mA,boundStroke:Bs,changeset:Ao,clampRange:W4,codegenExpression:FD,compare:C2,constant:kn,cumulativeLogNormal:Cy,cumulativeNormal:E0,cumulativeUniform:Sy,dayofyear:D8,debounce:k2,defaultLocale:ay,definition:UE,densityLogNormal:Ey,densityNormal:vy,densityUniform:$y,domChild:Gt,domClear:Vi,domCreate:Wo,domFind:M3,dotbin:GE,error:q,expressionFunction:Pt,extend:Oe,extent:qr,extentIndex:H4,falsy:vo,fastmap:sl,field:Bi,flush:G4,font:_p,fontFamily:Uf,fontSize:ns,format:f0,formatLocale:l0,formats:fy,hasOwnProperty:ue,id:Vc,identity:En,inferType:EE,inferTypes:CE,ingest:nt,inherits:ee,inrange:ol,interpolate:Jb,interpolateColors:np,interpolateRange:vk,intersect:i$,intersectBoxLine:Rl,intersectPath:m3,intersectPoint:y3,intersectRule:tA,isArray:W,isBoolean:xo,isDate:_o,isFunction:Ie,isIterable:V4,isNumber:Ze,isObject:re,isRegExp:A2,isString:se,isTuple:g0,key:$2,lerp:Y4,lineHeight:jo,loader:d0,locale:xE,logger:x2,lruCache:X4,markup:q3,merge:K4,mergeConfig:nl,multiLineOffset:S3,one:tl,pad:Z4,panLinear:P4,panLog:z4,panPow:B4,panSymlog:U4,parse:vfe,parseExpression:AD,parseSelector:ta,path:D0,pathCurves:r3,pathEqual:u$,pathParse:Tl,pathRectangle:qk,pathRender:Tf,pathSymbols:jk,pathTrail:Wk,peek:Ge,point:Ep,projection:qv,quantileLogNormal:ky,quantileNormal:C0,quantileUniform:Fy,quantiles:my,quantizeInterpolator:xk,quarter:j4,quartiles:yy,get random(){return Wi},randomInteger:kG,randomKDE:_y,randomLCG:CG,randomLogNormal:YE,randomMixture:XE,randomNormal:xy,randomUniform:KE,read:SE,regressionConstant:Dy,regressionExp:JE,regressionLinear:Ty,regressionLoess:i9,regressionLog:ZE,regressionPoly:e9,regressionPow:QE,regressionQuad:My,renderModule:Lp,repeat:Yc,resetDefaultLocale:_H,resetSVGClipId:Gk,resetSVGDefIds:mJ,responseType:$E,runtimeContext:QD,sampleCurve:A0,sampleLogNormal:wy,sampleNormal:w0,sampleUniform:Ay,scale:Qe,sceneEqual:V3,sceneFromJSON:bA,scenePickVisit:gp,sceneToJSON:yA,sceneVisit:pr,sceneZOrder:b3,scheme:Qb,serializeXML:qA,setHybridRendererOptions:dJ,setRandom:wG,span:Xc,splitAccessPath:jr,stringValue:Q,textMetrics:Ai,timeBin:X8,timeFloor:I8,timeFormatLocale:df,timeInterval:gl,timeOffset:B8,timeSequence:q8,timeUnitSpecifier:F8,timeUnits:V2,toBoolean:S2,toDate:F2,toNumber:Cn,toSet:lr,toString:D2,transform:jE,transforms:xl,truncate:J4,truthy:Ui,tupleid:_e,typeParsers:uy,utcFloor:P8,utcInterval:ml,utcOffset:U8,utcSequence:W8,utcdayofyear:N8,utcquarter:q4,utcweek:R8,version:xfe,visitArray:wo,week:T8,writeConfig:il,zero:bo,zoomLinear:_2,zoomLog:w2,zoomPow:Xh,zoomSymlog:E2},Symbol.toStringTag,{value:"Module"}));function wfe(e,t,n){let i;t.x2&&(t.x?(n&&e.x>e.x2&&(i=e.x,e.x=e.x2,e.x2=i),e.width=e.x2-e.x):e.x=e.x2-(e.width||0)),t.xc&&(e.x=e.xc-(e.width||0)/2),t.y2&&(t.y?(n&&e.y>e.y2&&(i=e.y,e.y=e.y2,e.y2=i),e.height=e.y2-e.y):e.y=e.y2-(e.height||0)),t.yc&&(e.y=e.yc-(e.height||0)/2)}var Efe={NaN:NaN,E:Math.E,LN2:Math.LN2,LN10:Math.LN10,LOG2E:Math.LOG2E,LOG10E:Math.LOG10E,PI:Math.PI,SQRT1_2:Math.SQRT1_2,SQRT2:Math.SQRT2,MIN_VALUE:Number.MIN_VALUE,MAX_VALUE:Number.MAX_VALUE},Cfe={"*":(e,t)=>e*t,"+":(e,t)=>e+t,"-":(e,t)=>e-t,"/":(e,t)=>e/t,"%":(e,t)=>e%t,">":(e,t)=>e>t,"<":(e,t)=>ee<=t,">=":(e,t)=>e>=t,"==":(e,t)=>e==t,"!=":(e,t)=>e!=t,"===":(e,t)=>e===t,"!==":(e,t)=>e!==t,"&":(e,t)=>e&t,"|":(e,t)=>e|t,"^":(e,t)=>e^t,"<<":(e,t)=>e<>":(e,t)=>e>>t,">>>":(e,t)=>e>>>t},kfe={"+":e=>+e,"-":e=>-e,"~":e=>~e,"!":e=>!e};const Afe=Array.prototype.slice,fu=(e,t,n)=>{const i=n?n(t[0]):t[0];return i[e].apply(i,Afe.call(t,1))},$fe=(e,t,n,i,r,s,o)=>new Date(e,t||0,n??1,i||0,r||0,s||0,o||0);var Sfe={isNaN:Number.isNaN,isFinite:Number.isFinite,abs:Math.abs,acos:Math.acos,asin:Math.asin,atan:Math.atan,atan2:Math.atan2,ceil:Math.ceil,cos:Math.cos,exp:Math.exp,floor:Math.floor,log:Math.log,max:Math.max,min:Math.min,pow:Math.pow,random:Math.random,round:Math.round,sin:Math.sin,sqrt:Math.sqrt,tan:Math.tan,clamp:(e,t,n)=>Math.max(t,Math.min(n,e)),now:Date.now,utc:Date.UTC,datetime:$fe,date:e=>new Date(e).getDate(),day:e=>new Date(e).getDay(),year:e=>new Date(e).getFullYear(),month:e=>new Date(e).getMonth(),hours:e=>new Date(e).getHours(),minutes:e=>new Date(e).getMinutes(),seconds:e=>new Date(e).getSeconds(),milliseconds:e=>new Date(e).getMilliseconds(),time:e=>new Date(e).getTime(),timezoneoffset:e=>new Date(e).getTimezoneOffset(),utcdate:e=>new Date(e).getUTCDate(),utcday:e=>new Date(e).getUTCDay(),utcyear:e=>new Date(e).getUTCFullYear(),utcmonth:e=>new Date(e).getUTCMonth(),utchours:e=>new Date(e).getUTCHours(),utcminutes:e=>new Date(e).getUTCMinutes(),utcseconds:e=>new Date(e).getUTCSeconds(),utcmilliseconds:e=>new Date(e).getUTCMilliseconds(),length:e=>e.length,join:function(){return fu("join",arguments)},indexof:function(){return fu("indexOf",arguments)},lastindexof:function(){return fu("lastIndexOf",arguments)},slice:function(){return fu("slice",arguments)},reverse:e=>e.slice().reverse(),sort:e=>e.slice().sort(rl),parseFloat,parseInt,upper:e=>String(e).toUpperCase(),lower:e=>String(e).toLowerCase(),substring:function(){return fu("substring",arguments,String)},split:function(){return fu("split",arguments,String)},replace:function(){return fu("replace",arguments,String)},trim:e=>String(e).trim(),btoa:e=>btoa(e),atob:e=>atob(e),regexp:RegExp,test:(e,t)=>RegExp(e).test(t)};const Ffe=["view","item","group","xy","x","y"],Y7=new Set([Function,eval,setTimeout,setInterval]);typeof setImmediate=="function"&&Y7.add(setImmediate);const Dfe={Literal:(e,t)=>t.value,Identifier:(e,t)=>{const n=t.name;return e.memberDepth>0?n:n==="datum"?e.datum:n==="event"?e.event:n==="item"?e.item:Efe[n]||e.params["$"+n]},MemberExpression:(e,t)=>{const n=!t.computed,i=e(t.object);n&&(e.memberDepth+=1);const r=e(t.property);if(n&&(e.memberDepth-=1),Y7.has(i[r])){console.error(`Prevented interpretation of member "${r}" which could lead to insecure code execution`);return}return i[r]},CallExpression:(e,t)=>{const n=t.arguments;let i=t.callee.name;return i.startsWith("_")&&(i=i.slice(1)),i==="if"?e(n[0])?e(n[1]):e(n[2]):(e.fn[i]||Sfe[i]).apply(e.fn,n.map(e))},ArrayExpression:(e,t)=>t.elements.map(e),BinaryExpression:(e,t)=>Cfe[t.operator](e(t.left),e(t.right)),UnaryExpression:(e,t)=>kfe[t.operator](e(t.argument)),ConditionalExpression:(e,t)=>e(t.test)?e(t.consequent):e(t.alternate),LogicalExpression:(e,t)=>t.operator==="&&"?e(t.left)&&e(t.right):e(t.left)||e(t.right),ObjectExpression:(e,t)=>t.properties.reduce((n,i)=>{e.memberDepth+=1;const r=e(i.key);return e.memberDepth-=1,Y7.has(e(i.value))?console.error(`Prevented interpretation of property "${r}" which could lead to insecure code execution`):n[r]=e(i.value),n},{})};function Wd(e,t,n,i,r,s){const o=a=>Dfe[a.type](o,a);return o.memberDepth=0,o.fn=Object.create(t),o.params=n,o.datum=i,o.event=r,o.item=s,Ffe.forEach(a=>o.fn[a]=function(){return r.vega[a](...arguments)}),o(e)}var Tfe={operator(e,t){const n=t.ast,i=e.functions;return r=>Wd(n,i,r)},parameter(e,t){const n=t.ast,i=e.functions;return(r,s)=>Wd(n,i,s,r)},event(e,t){const n=t.ast,i=e.functions;return r=>Wd(n,i,void 0,void 0,r)},handler(e,t){const n=t.ast,i=e.functions;return(r,s)=>{const o=s.item&&s.item.datum;return Wd(n,i,r,o,s)}},encode(e,t){const{marktype:n,channels:i}=t,r=e.functions,s=n==="group"||n==="image"||n==="rect";return(o,a)=>{const u=o.datum;let l=0,c;for(const f in i)c=Wd(i[f].ast,r,a,u,void 0,o),o[f]!==c&&(o[f]=c,l=1);return n!=="rule"&&wfe(o,i,s),l}}};const Mfe={version:"5.23.0"};function X7(e){return X(e,"or")}function K7(e){return X(e,"and")}function Z7(e){return X(e,"not")}function _1(e,t){if(Z7(e))_1(e.not,t);else if(K7(e))for(const n of e.and)_1(n,t);else if(X7(e))for(const n of e.or)_1(n,t);else t(e)}function ac(e,t){return Z7(e)?{not:ac(e.not,t)}:K7(e)?{and:e.and.map(n=>ac(n,t))}:X7(e)?{or:e.or.map(n=>ac(n,t))}:t(e)}const De=structuredClone;function EM(e){throw new Error(e)}function uc(e,t){const n={};for(const i of t)ue(e,i)&&(n[i]=e[i]);return n}function hi(e,t){const n={...e};for(const i of t)delete n[i];return n}Set.prototype.toJSON=function(){return`Set(${[...this].map(e=>pt(e)).join(",")})`};function We(e){if(Ze(e))return e;const t=se(e)?e:pt(e);if(t.length<250)return t;let n=0;for(let i=0;ia===0?o:`[${o}]`),s=r.map((o,a)=>r.slice(0,a+1).join(""));for(const o of s)t.add(o)}return t}function n_(e,t){return e===void 0||t===void 0?!0:e_(t_(e),t_(t))}function ht(e){return Y(e).length===0}const Y=Object.keys,gn=Object.values,ia=Object.entries;function Hd(e){return e===!0||e===!1}function At(e){const t=e.replace(/\W/g,"_");return(e.match(/^\d+/)?"_":"")+t}function Gd(e,t){return Z7(e)?`!(${Gd(e.not,t)})`:K7(e)?`(${e.and.map(n=>Gd(n,t)).join(") && (")})`:X7(e)?`(${e.or.map(n=>Gd(n,t)).join(") || (")})`:t(e)}function w1(e,t){if(t.length===0)return!0;const n=t.shift();return n in e&&w1(e[n],t)&&delete e[n],ht(e)}function Vd(e){return e.charAt(0).toUpperCase()+e.substr(1)}function i_(e,t="datum"){const n=jr(e),i=[];for(let r=1;r<=n.length;r++){const s=`[${n.slice(0,r).map(Q).join("][")}]`;i.push(`${t}${s}`)}return i.join(" && ")}function AM(e,t="datum"){return`${t}[${Q(jr(e).join("."))}]`}function at(e){return`datum['${e.replaceAll("'","\\'")}']`}function Ofe(e){return e.replace(/(\[|\]|\.|'|")/g,"\\$1")}function er(e){return`${jr(e).map(Ofe).join("\\.")}`}function du(e,t,n){return e.replace(new RegExp(t.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"),"g"),n)}function cc(e){return`${jr(e).join(".")}`}function fc(e){return e?jr(e).length:0}function zt(...e){return e.find(t=>t!==void 0)}let $M=42;function SM(e){const t=++$M;return e?String(e)+t:t}function Lfe(){$M=42}function FM(e){return DM(e)?e:`__${e}`}function DM(e){return e.startsWith("__")}function Yd(e){if(e!==void 0)return(e%360+360)%360}function E1(e){return Ze(e)?!0:!isNaN(e)&&!isNaN(parseFloat(e))}const TM=Object.getPrototypeOf(structuredClone({}));function Mi(e,t){if(e===t)return!0;if(e&&t&&typeof e=="object"&&typeof t=="object"){if(e.constructor.name!==t.constructor.name)return!1;let n,i;if(Array.isArray(e)){if(n=e.length,n!=t.length)return!1;for(i=n;i--!==0;)if(!Mi(e[i],t[i]))return!1;return!0}if(e instanceof Map&&t instanceof Map){if(e.size!==t.size)return!1;for(const s of e.entries())if(!t.has(s[0]))return!1;for(const s of e.entries())if(!Mi(s[1],t.get(s[0])))return!1;return!0}if(e instanceof Set&&t instanceof Set){if(e.size!==t.size)return!1;for(const s of e.entries())if(!t.has(s[0]))return!1;return!0}if(ArrayBuffer.isView(e)&&ArrayBuffer.isView(t)){if(n=e.length,n!=t.length)return!1;for(i=n;i--!==0;)if(e[i]!==t[i])return!1;return!0}if(e.constructor===RegExp)return e.source===t.source&&e.flags===t.flags;if(e.valueOf!==Object.prototype.valueOf&&e.valueOf!==TM.valueOf)return e.valueOf()===t.valueOf();if(e.toString!==Object.prototype.toString&&e.toString!==TM.toString)return e.toString()===t.toString();const r=Object.keys(e);if(n=r.length,n!==Object.keys(t).length)return!1;for(i=n;i--!==0;)if(!Object.prototype.hasOwnProperty.call(t,r[i]))return!1;for(i=n;i--!==0;){const s=r[i];if(!Mi(e[s],t[s]))return!1}return!0}return e!==e&&t!==t}function pt(e){const t=[];return function n(i){if(i&&i.toJSON&&typeof i.toJSON=="function"&&(i=i.toJSON()),i===void 0)return;if(typeof i=="number")return isFinite(i)?""+i:"null";if(typeof i!="object")return JSON.stringify(i);let r,s;if(Array.isArray(i)){for(s="[",r=0;rT1(e[t])?At(`_${t}_${ia(e[t])}`):At(`_${t}_${e[t]}`)).join("")}function _t(e){return e===!0||mu(e)&&!e.binned}function mn(e){return e==="binned"||mu(e)&&e.binned===!0}function mu(e){return re(e)}function T1(e){return X(e,"param")}function HM(e){switch(e){case Zs:case Js:case to:case pi:case hs:case ps:case ua:case no:case oa:case aa:case gi:return 6;case la:return 4;default:return 10}}function Jd(e){return X(e,"expr")}function yn(e,{level:t}={level:0}){const n=Y(e||{}),i={};for(const r of n)i[r]=t===0?Ni(e[r]):yn(e[r],{level:t-1});return i}function GM(e){const{anchor:t,frame:n,offset:i,orient:r,angle:s,limit:o,color:a,subtitleColor:u,subtitleFont:l,subtitleFontSize:c,subtitleFontStyle:f,subtitleFontWeight:d,subtitleLineHeight:h,subtitlePadding:p,...g}=e,m={...g,...a?{fill:a}:{}},y={...t?{anchor:t}:{},...n?{frame:n}:{},...i?{offset:i}:{},...r?{orient:r}:{},...s!==void 0?{angle:s}:{},...o!==void 0?{limit:o}:{}},b={...u?{subtitleColor:u}:{},...l?{subtitleFont:l}:{},...c?{subtitleFontSize:c}:{},...f?{subtitleFontStyle:f}:{},...d?{subtitleFontWeight:d}:{},...h?{subtitleLineHeight:h}:{},...p?{subtitlePadding:p}:{}},v=uc(e,["align","baseline","dx","dy","limit"]);return{titleMarkConfig:m,subtitleMarkConfig:v,nonMarkTitleProperties:y,subtitle:b}}function da(e){return se(e)||W(e)&&se(e[0])}function me(e){return X(e,"signal")}function yu(e){return X(e,"step")}function ade(e){return W(e)?!1:X(e,"fields")&&!X(e,"data")}function ude(e){return W(e)?!1:X(e,"fields")&&X(e,"data")}function so(e){return W(e)?!1:X(e,"field")&&X(e,"data")}const lde=Y({aria:1,description:1,ariaRole:1,ariaRoleDescription:1,blend:1,opacity:1,fill:1,fillOpacity:1,stroke:1,strokeCap:1,strokeWidth:1,strokeOpacity:1,strokeDash:1,strokeDashOffset:1,strokeJoin:1,strokeOffset:1,strokeMiterLimit:1,startAngle:1,endAngle:1,padAngle:1,innerRadius:1,outerRadius:1,size:1,shape:1,interpolate:1,tension:1,orient:1,align:1,baseline:1,text:1,dir:1,dx:1,dy:1,ellipsis:1,limit:1,radius:1,theta:1,angle:1,font:1,fontSize:1,fontWeight:1,fontStyle:1,lineBreak:1,lineHeight:1,cursor:1,href:1,tooltip:1,cornerRadius:1,cornerRadiusTopLeft:1,cornerRadiusTopRight:1,cornerRadiusBottomLeft:1,cornerRadiusBottomRight:1,aspect:1,width:1,height:1,url:1,smooth:1}),cde={arc:1,area:1,group:1,image:1,line:1,path:1,rect:1,rule:1,shape:1,symbol:1,text:1,trail:1},g_=["cornerRadius","cornerRadiusTopLeft","cornerRadiusTopRight","cornerRadiusBottomLeft","cornerRadiusBottomRight"];function VM(e){const t=W(e.condition)?e.condition.map(YM):YM(e.condition);return{...Ni(e),condition:t}}function Ni(e){if(Jd(e)){const{expr:t,...n}=e;return{signal:t,...n}}return e}function YM(e){if(Jd(e)){const{expr:t,...n}=e;return{signal:t,...n}}return e}function Et(e){if(Jd(e)){const{expr:t,...n}=e;return{signal:t,...n}}return me(e)?e:e!==void 0?{value:e}:void 0}function fde(e){return me(e)?e.signal:Q(e)}function XM(e){return me(e)?e.signal:Q(e.value)}function Dr(e){return me(e)?e.signal:e==null?null:Q(e)}function dde(e,t,n){for(const i of n){const r=ys(i,t.markDef,t.config);r!==void 0&&(e[i]=Et(r))}return e}function KM(e){return[].concat(e.type,e.style??[])}function gt(e,t,n,i={}){const{vgChannel:r,ignoreVgConfig:s}=i;return r&&X(t,r)?t[r]:t[e]!==void 0?t[e]:s&&(!r||r===e)?void 0:ys(e,t,n,i)}function ys(e,t,n,{vgChannel:i}={}){const r=m_(e,t,n.style);return zt(i?r:void 0,r,i?n[t.type][i]:void 0,n[t.type][e],i?n.mark[i]:n.mark[e])}function m_(e,t,n){return ZM(e,KM(t),n)}function ZM(e,t,n){t=oe(t);let i;for(const r of t){const s=n[r];X(s,e)&&(i=s[e])}return i}function JM(e,t){return oe(e).reduce((n,i)=>(n.field.push(te(i,t)),n.order.push(i.sort??"ascending"),n),{field:[],order:[]})}function QM(e,t){const n=[...e];return t.forEach(i=>{for(const r of n)if(Mi(r,i))return;n.push(i)}),n}function eN(e,t){return Mi(e,t)||!t?e:e?[...oe(e),...oe(t)].join(", "):t}function tN(e,t){const n=e.value,i=t.value;if(n==null||i===null)return{explicit:e.explicit,value:null};if((da(n)||me(n))&&(da(i)||me(i)))return{explicit:e.explicit,value:eN(n,i)};if(da(n)||me(n))return{explicit:e.explicit,value:n};if(da(i)||me(i))return{explicit:e.explicit,value:i};if(!da(n)&&!me(n)&&!da(i)&&!me(i))return{explicit:e.explicit,value:QM(n,i)};throw new Error("It should never reach here")}function y_(e){return`Invalid specification ${pt(e)}. Make sure the specification includes at least one of the following properties: "mark", "layer", "facet", "hconcat", "vconcat", "concat", or "repeat".`}const hde='Autosize "fit" only works for single views and layered views.';function nN(e){return`${e=="width"?"Width":"Height"} "container" only works for single views and layered views.`}function iN(e){const t=e=="width"?"Width":"Height",n=e=="width"?"x":"y";return`${t} "container" only works well with autosize "fit" or "fit-${n}".`}function rN(e){return e?`Dropping "fit-${e}" because spec has discrete ${mi(e)}.`:'Dropping "fit" because spec has discrete size.'}function b_(e){return`Unknown field for ${e}. Cannot calculate view size.`}function sN(e){return`Cannot project a selection on encoding channel "${e}", which has no field.`}function pde(e,t){return`Cannot project a selection on encoding channel "${e}" as it uses an aggregate function ("${t}").`}function gde(e){return`The "nearest" transform is not supported for ${e} marks.`}function oN(e){return`Selection not supported for ${e} yet.`}function mde(e){return`Cannot find a selection named "${e}".`}const yde="Scale bindings are currently only supported for scales with unbinned, continuous domains.",bde="Sequntial scales are deprecated. The available quantitative scale type values are linear, log, pow, sqrt, symlog, time and utc",vde="Legend bindings are only supported for selections over an individual field or encoding channel.";function xde(e){return`Lookups can only be performed on selection parameters. "${e}" is a variable parameter.`}function _de(e){return`Cannot define and lookup the "${e}" selection in the same view. Try moving the lookup into a second, layered view?`}const wde="The same selection must be used to override scale domains in a layered view.",Ede='Interval selections should be initialized using "x", "y", "longitude", or "latitude" keys.';function Cde(e){return`Unknown repeated value "${e}".`}function aN(e){return`The "columns" property cannot be used when "${e}" has nested row/column.`}const kde="Multiple timer selections in one unit spec are not supported. Ignoring all but the first.",v_="Animation involving facet, layer, or concat is currently unsupported.";function Ade(e){return`A "field" or "encoding" must be specified when using a selection as a scale domain. Using "field": ${Q(e)}.`}function $de(e,t,n,i){return(e.length?"Multiple ":"No ")+`matching ${Q(t)} encoding found for selection ${Q(n.param)}. Using "field": ${Q(i)}.`}const Sde="Axes cannot be shared in concatenated or repeated views yet (https://github.com/vega/vega-lite/issues/2415).";function Fde(e){return`Unrecognized parse "${e}".`}function uN(e,t,n){return`An ancestor parsed field "${e}" as ${n} but a child wants to parse the field as ${t}.`}const Dde="Attempt to add the same child twice.";function Tde(e){return`Ignoring an invalid transform: ${pt(e)}.`}const Mde='If "from.fields" is not specified, "as" has to be a string that specifies the key to be used for the data from the secondary source.';function lN(e){return`Config.customFormatTypes is not true, thus custom format type and format for channel ${e} are dropped.`}function Nde(e){const{parentProjection:t,projection:n}=e;return`Layer's shared projection ${pt(t)} is overridden by a child projection ${pt(n)}.`}const Rde="Arc marks uses theta channel rather than angle, replacing angle with theta.";function Ode(e){return`${e}Offset dropped because ${e} is continuous`}function Lde(e,t,n){return`Channel ${e} is a ${t}. Converted to {value: ${pt(n)}}.`}function cN(e){return`Invalid field type "${e}".`}function Ide(e,t){return`Invalid field type "${e}" for aggregate: "${t}", using "quantitative" instead.`}function Pde(e){return`Invalid aggregation operator "${e}".`}function fN(e,t){const{fill:n,stroke:i}=t;return`Dropping color ${e} as the plot also has ${n&&i?"fill and stroke":n?"fill":"stroke"}.`}function zde(e){return`Position range does not support relative band size for ${e}.`}function x_(e,t){return`Dropping ${pt(e)} from channel "${t}" since it does not contain any data field, datum, value, or signal.`}const Bde="Line marks cannot encode size with a non-groupby field. You may want to use trail marks instead.";function M1(e,t,n){return`${e} dropped as it is incompatible with "${t}".`}function Ude(e){return`${e}-encoding is dropped as ${e} is not a valid encoding channel.`}function jde(e){return`${e} encoding should be discrete (ordinal / nominal / binned).`}function qde(e){return`${e} encoding should be discrete (ordinal / nominal / binned) or use a discretizing scale (e.g. threshold).`}function Wde(e){return`Facet encoding dropped as ${e.join(" and ")} ${e.length>1?"are":"is"} also specified.`}function __(e,t){return`Using discrete channel "${e}" to encode "${t}" field can be misleading as it does not encode ${t==="ordinal"?"order":"magnitude"}.`}function Hde(e){return`The ${e} for range marks cannot be an expression`}function Gde(e,t){return`Line mark is for continuous lines and thus cannot be used with ${e&&t?"x2 and y2":e?"x2":"y2"}. We will use the rule mark (line segments) instead.`}function Vde(e,t){return`Specified orient "${e}" overridden with "${t}".`}function Yde(e){return`Cannot use the scale property "${e}" with non-color channel.`}function Xde(e){return`Cannot use the relative band size with ${e} scale.`}function Kde(e){return`Using unaggregated domain with raw field has no effect (${pt(e)}).`}function Zde(e){return`Unaggregated domain not applicable for "${e}" since it produces values outside the origin domain of the source data.`}function Jde(e){return`Unaggregated domain is currently unsupported for log scale (${pt(e)}).`}function Qde(e){return`Cannot apply size to non-oriented mark "${e}".`}function ehe(e,t,n){return`Channel "${e}" does not work with "${t}" scale. We are using "${n}" scale instead.`}function the(e,t){return`FieldDef does not work with "${e}" scale. We are using "${t}" scale instead.`}function dN(e,t,n){return`${n}-scale's "${t}" is dropped as it does not work with ${e} scale.`}function hN(e){return`The step for "${e}" is dropped because the ${e==="width"?"x":"y"} is continuous.`}function nhe(e,t,n,i){return`Conflicting ${t.toString()} property "${e.toString()}" (${pt(n)} and ${pt(i)}). Using ${pt(n)}.`}function ihe(e,t,n,i){return`Conflicting ${t.toString()} property "${e.toString()}" (${pt(n)} and ${pt(i)}). Using the union of the two domains.`}function rhe(e){return`Setting the scale to be independent for "${e}" means we also have to set the guide (axis or legend) to be independent.`}function she(e){return`Dropping sort property ${pt(e)} as unioned domains only support boolean or op "count", "min", and "max".`}const pN="Domains that should be unioned has conflicting sort properties. Sort will be set to true.",ohe="Detected faceted independent scales that union domain of multiple fields from different data sources. We will use the first field. The result view size may be incorrect.",ahe="Detected faceted independent scales that union domain of the same fields from different source. We will assume that this is the same field from a different fork of the same data source. However, if this is not the case, the result view size may be incorrect.",uhe="Detected faceted independent scales that union domain of multiple fields from the same data source. We will use the first field. The result view size may be incorrect.";function lhe(e){return`Cannot stack "${e}" if there is already "${e}2".`}function che(e){return`Stack is applied to a non-linear scale (${e}).`}function fhe(e){return`Stacking is applied even though the aggregate function is non-summative ("${e}").`}function N1(e,t){return`Invalid ${e}: ${pt(t)}.`}function dhe(e){return`Dropping day from datetime ${pt(e)} as day cannot be combined with other units.`}function hhe(e,t){return`${t?"extent ":""}${t&&e?"and ":""}${e?"center ":""}${t&&e?"are ":"is "}not needed when data are aggregated.`}function phe(e,t,n){return`${e} is not usually used with ${t} for ${n}.`}function ghe(e,t){return`Continuous axis should not have customized aggregation function ${e}; ${t} already agregates the axis.`}function gN(e){return`1D error band does not support ${e}.`}function mN(e){return`Channel ${e} is required for "binned" bin.`}function mhe(e){return`Channel ${e} should not be used with "binned" bin.`}function yhe(e){return`Domain for ${e} is required for threshold scale.`}const yN=x2(v2);let bu=yN;function bhe(e){return bu=e,bu}function vhe(){return bu=yN,bu}function w_(...e){bu.error(...e)}function Z(...e){bu.warn(...e)}function xhe(...e){bu.debug(...e)}function vu(e){if(e&&re(e)){for(const t of C_)if(X(e,t))return!0}return!1}const bN=["january","february","march","april","may","june","july","august","september","october","november","december"],_he=bN.map(e=>e.substr(0,3)),vN=["sunday","monday","tuesday","wednesday","thursday","friday","saturday"],whe=vN.map(e=>e.substr(0,3));function Ehe(e){if(E1(e)&&(e=+e),Ze(e))return e>4&&Z(N1("quarter",e)),e-1;throw new Error(N1("quarter",e))}function Che(e){if(E1(e)&&(e=+e),Ze(e))return e-1;{const t=e.toLowerCase(),n=bN.indexOf(t);if(n!==-1)return n;const i=t.substr(0,3),r=_he.indexOf(i);if(r!==-1)return r;throw new Error(N1("month",e))}}function khe(e){if(E1(e)&&(e=+e),Ze(e))return e%7;{const t=e.toLowerCase(),n=vN.indexOf(t);if(n!==-1)return n;const i=t.substr(0,3),r=whe.indexOf(i);if(r!==-1)return r;throw new Error(N1("day",e))}}function E_(e,t){const n=[];if(t&&e.day!==void 0&&Y(e).length>1&&(Z(dhe(e)),e=De(e),delete e.day),e.year!==void 0?n.push(e.year):n.push(2012),e.month!==void 0){const i=t?Che(e.month):e.month;n.push(i)}else if(e.quarter!==void 0){const i=t?Ehe(e.quarter):e.quarter;n.push(Ze(i)?i*3:`${i}*3`)}else n.push(0);if(e.date!==void 0)n.push(e.date);else if(e.day!==void 0){const i=t?khe(e.day):e.day;n.push(Ze(i)?i+1:`${i}+1`)}else n.push(1);for(const i of["hours","minutes","seconds","milliseconds"]){const r=e[i];n.push(typeof r>"u"?0:r)}return n}function xu(e){const n=E_(e,!0).join(", ");return e.utc?`utc(${n})`:`datetime(${n})`}function Ahe(e){const n=E_(e,!1).join(", ");return e.utc?`utc(${n})`:`datetime(${n})`}function $he(e){const t=E_(e,!0);return e.utc?+new Date(Date.UTC(...t)):+new Date(...t)}const xN={year:1,quarter:1,month:1,week:1,day:1,dayofyear:1,date:1,hours:1,minutes:1,seconds:1,milliseconds:1},C_=Y(xN);function She(e){return ue(xN,e)}function _u(e){return re(e)?e.binned:_N(e)}function _N(e){return e&&e.startsWith("binned")}function k_(e){return e.startsWith("utc")}function Fhe(e){return e.substring(3)}const Dhe={"year-month":"%b %Y ","year-month-date":"%b %d, %Y "};function R1(e){return C_.filter(t=>EN(e,t))}function wN(e){const t=R1(e);return t[t.length-1]}function EN(e,t){const n=e.indexOf(t);return!(n<0||n>0&&t==="seconds"&&e.charAt(n-1)==="i"||e.length>n+3&&t==="day"&&e.charAt(n+3)==="o"||n>0&&t==="year"&&e.charAt(n-1)==="f")}function The(e,t,{end:n}={end:!1}){const i=i_(t),r=k_(e)?"utc":"";function s(u){return u==="quarter"?`(${r}quarter(${i})-1)`:`${r}${u}(${i})`}let o;const a={};for(const u of C_)EN(e,u)&&(a[u]=s(u),o=u);return n&&(a[o]+="+1"),Ahe(a)}function CN(e){if(!e)return;const t=R1(e);return`timeUnitSpecifier(${pt(t)}, ${pt(Dhe)})`}function Mhe(e,t,n){if(!e)return;const i=CN(e);return`${n||k_(e)?"utc":"time"}Format(${t}, ${i})`}function sn(e){if(!e)return;let t;return se(e)?_N(e)?t={unit:e.substring(6),binned:!0}:t={unit:e}:re(e)&&(t={...e,...e.unit?{unit:e.unit}:{}}),k_(t.unit)&&(t.utc=!0,t.unit=Fhe(t.unit)),t}function Nhe(e){const{utc:t,...n}=sn(e);return n.unit?(t?"utc":"")+Y(n).map(i=>At(`${i==="unit"?"":`_${i}_`}${n[i]}`)).join(""):(t?"utc":"")+"timeunit"+Y(n).map(i=>At(`_${i}_${n[i]}`)).join("")}function kN(e,t=n=>n){const n=sn(e),i=wN(n.unit);if(i&&i!=="day"){const r={year:2001,month:1,date:1,hours:0,minutes:0,seconds:0,milliseconds:0},{step:s,part:o}=AN(i,n.step),a={...r,[o]:+r[o]+s};return`${t(xu(a))} - ${t(xu(r))}`}}const Rhe={year:1,month:1,date:1,hours:1,minutes:1,seconds:1,milliseconds:1};function Ohe(e){return ue(Rhe,e)}function AN(e,t=1){if(Ohe(e))return{part:e,step:t};switch(e){case"day":case"dayofyear":return{part:"date",step:t};case"quarter":return{part:"month",step:t*3};case"week":return{part:"date",step:t*7}}}function Lhe(e){return X(e,"param")}function A_(e){return!!(e!=null&&e.field)&&e.equal!==void 0}function $_(e){return!!(e!=null&&e.field)&&e.lt!==void 0}function S_(e){return!!(e!=null&&e.field)&&e.lte!==void 0}function F_(e){return!!(e!=null&&e.field)&&e.gt!==void 0}function D_(e){return!!(e!=null&&e.field)&&e.gte!==void 0}function T_(e){if(e!=null&&e.field){if(W(e.range)&&e.range.length===2)return!0;if(me(e.range))return!0}return!1}function M_(e){return!!(e!=null&&e.field)&&(W(e.oneOf)||W(e.in))}function Ihe(e){return!!(e!=null&&e.field)&&e.valid!==void 0}function $N(e){return M_(e)||A_(e)||T_(e)||$_(e)||F_(e)||S_(e)||D_(e)}function bs(e,t){return K1(e,{timeUnit:t,wrapTime:!0})}function Phe(e,t){return e.map(n=>bs(n,t))}function SN(e,t=!0){const{field:n}=e,i=sn(e.timeUnit),{unit:r,binned:s}=i||{},o=te(e,{expr:"datum"}),a=r?`time(${s?o:The(r,n)})`:o;if(A_(e))return`${a}===${bs(e.equal,r)}`;if($_(e)){const u=e.lt;return`${a}<${bs(u,r)}`}else if(F_(e)){const u=e.gt;return`${a}>${bs(u,r)}`}else if(S_(e)){const u=e.lte;return`${a}<=${bs(u,r)}`}else if(D_(e)){const u=e.gte;return`${a}>=${bs(u,r)}`}else{if(M_(e))return`indexof([${Phe(e.oneOf,r).join(",")}], ${a}) !== -1`;if(Ihe(e))return O1(a,e.valid);if(T_(e)){const{range:u}=yn(e),l=me(u)?{signal:`${u.signal}[0]`}:u[0],c=me(u)?{signal:`${u.signal}[1]`}:u[1];if(l!==null&&c!==null&&t)return"inrange("+a+", ["+bs(l,r)+", "+bs(c,r)+"])";const f=[];return l!==null&&f.push(`${a} >= ${bs(l,r)}`),c!==null&&f.push(`${a} <= ${bs(c,r)}`),f.length>0?f.join(" && "):"true"}}throw new Error(`Invalid field predicate: ${pt(e)}`)}function O1(e,t=!0){return t?`isValid(${e}) && isFinite(+${e})`:`!isValid(${e}) || !isFinite(+${e})`}function zhe(e){return $N(e)&&e.timeUnit?{...e,timeUnit:sn(e.timeUnit)}:e}const Qd={quantitative:"quantitative",ordinal:"ordinal",temporal:"temporal",nominal:"nominal",geojson:"geojson"};function Bhe(e){return e==="quantitative"||e==="temporal"}function FN(e){return e==="ordinal"||e==="nominal"}const wu=Qd.quantitative,N_=Qd.ordinal,gc=Qd.temporal,R_=Qd.nominal,mc=Qd.geojson;function Uhe(e){if(e)switch(e=e.toLowerCase(),e){case"q":case wu:return"quantitative";case"t":case gc:return"temporal";case"o":case N_:return"ordinal";case"n":case R_:return"nominal";case mc:return"geojson"}}const bn={LINEAR:"linear",LOG:"log",POW:"pow",SQRT:"sqrt",TIME:"time",UTC:"utc",POINT:"point",BAND:"band"},O_={linear:"numeric",log:"numeric",pow:"numeric",sqrt:"numeric",symlog:"numeric",identity:"numeric",sequential:"numeric",time:"time",utc:"time",ordinal:"ordinal","bin-ordinal":"bin-ordinal",point:"ordinal-position",band:"ordinal-position",quantile:"discretizing",quantize:"discretizing",threshold:"discretizing"};function jhe(e,t){const n=O_[e],i=O_[t];return n===i||n==="ordinal-position"&&i==="time"||i==="ordinal-position"&&n==="time"}const qhe={linear:0,log:1,pow:1,sqrt:1,symlog:1,identity:1,sequential:1,time:0,utc:0,point:10,band:11,ordinal:0,"bin-ordinal":0,quantile:0,quantize:0,threshold:0};function DN(e){return qhe[e]}const TN=new Set(["linear","log","pow","sqrt","symlog"]),MN=new Set([...TN,"time","utc"]);function NN(e){return TN.has(e)}const RN=new Set(["quantile","quantize","threshold"]),Whe=new Set([...MN,...RN,"sequential","identity"]),Hhe=new Set(["ordinal","bin-ordinal","point","band"]);function on(e){return Hhe.has(e)}function Tr(e){return Whe.has(e)}function vs(e){return MN.has(e)}function yc(e){return RN.has(e)}const Ghe={pointPadding:.5,barBandPaddingInner:.1,rectBandPaddingInner:0,tickBandPaddingInner:.25,bandWithNestedOffsetPaddingInner:.2,bandWithNestedOffsetPaddingOuter:.2,minBandSize:2,minFontSize:8,maxFontSize:40,minOpacity:.3,maxOpacity:.8,minSize:4,minStrokeWidth:1,maxStrokeWidth:4,quantileCount:4,quantizeCount:4,zero:!0,framesPerSecond:2,animationDuration:5};function Vhe(e){return!se(e)&&X(e,"name")}function ON(e){return X(e,"param")}function Yhe(e){return X(e,"unionWith")}function Xhe(e){return re(e)&&"field"in e}const Khe={type:1,domain:1,domainMax:1,domainMin:1,domainMid:1,domainRaw:1,align:1,range:1,rangeMax:1,rangeMin:1,scheme:1,bins:1,reverse:1,round:1,clamp:1,nice:1,base:1,exponent:1,constant:1,interpolate:1,zero:1,padding:1,paddingInner:1,paddingOuter:1},{type:J7e,domain:Q7e,range:e_e,rangeMax:t_e,rangeMin:n_e,scheme:i_e,...Zhe}=Khe,Jhe=Y(Zhe);function L_(e,t){switch(t){case"type":case"domain":case"reverse":case"range":return!0;case"scheme":case"interpolate":return!["point","band","identity"].includes(e);case"bins":return!["point","band","identity","ordinal"].includes(e);case"round":return vs(e)||e==="band"||e==="point";case"padding":case"rangeMin":case"rangeMax":return vs(e)||["point","band"].includes(e);case"paddingOuter":case"align":return["point","band"].includes(e);case"paddingInner":return e==="band";case"domainMax":case"domainMid":case"domainMin":case"domainRaw":case"clamp":return vs(e);case"nice":return vs(e)||e==="quantize"||e==="threshold";case"exponent":return e==="pow";case"base":return e==="log";case"constant":return e==="symlog";case"zero":return Tr(e)&&!qe(["log","time","utc","threshold","quantile"],e)}}function LN(e,t){switch(t){case"interpolate":case"scheme":case"domainMid":return pc(e)?void 0:Yde(t);case"align":case"type":case"bins":case"domain":case"domainMax":case"domainMin":case"domainRaw":case"range":case"base":case"exponent":case"constant":case"nice":case"padding":case"paddingInner":case"paddingOuter":case"rangeMax":case"rangeMin":case"reverse":case"round":case"clamp":case"zero":return}}function Qhe(e,t){return qe([N_,R_],t)?e===void 0||on(e):t===gc?qe([bn.TIME,bn.UTC,void 0],e):t===wu?NN(e)||yc(e)||e===void 0:!0}function e0e(e,t,n=!1){if(!ms(e))return!1;switch(e){case $t:case rn:case ra:case dc:case tr:case Ar:return vs(t)||t==="band"?!0:t==="point"?!n:!1;case sa:return qe(["linear","band"],t);case to:case ua:case no:case oa:case aa:case hu:return vs(t)||yc(t)||qe(["band","point","ordinal"],t);case pi:case hs:case ps:return t!=="band";case la:case gi:return t==="ordinal"||yc(t)}}function t0e(e){return re(e)&&"value"in e}const Qn={arc:"arc",area:"area",bar:"bar",image:"image",line:"line",point:"point",rect:"rect",rule:"rule",text:"text",tick:"tick",trail:"trail",circle:"circle",square:"square",geoshape:"geoshape"},IN=Qn.arc,L1=Qn.area,I1=Qn.bar,n0e=Qn.image,P1=Qn.line,z1=Qn.point,i0e=Qn.rect,B1=Qn.rule,PN=Qn.text,I_=Qn.tick,r0e=Qn.trail,P_=Qn.circle,z_=Qn.square,zN=Qn.geoshape;function ha(e){return["line","area","trail"].includes(e)}function eh(e){return["rect","bar","image","arc","tick"].includes(e)}const s0e=new Set(Y(Qn));function xs(e){return X(e,"type")}const o0e=["stroke","strokeWidth","strokeDash","strokeDashOffset","strokeOpacity","strokeJoin","strokeMiterLimit"],a0e=["fill","fillOpacity"],u0e=[...o0e,...a0e],BN=Y({color:1,filled:1,invalid:1,order:1,radius2:1,theta2:1,timeUnitBandSize:1,timeUnitBandPosition:1}),B_=["binSpacing","continuousBandSize","discreteBandSize","minBandSize"],l0e={area:["line","point"],bar:B_,rect:B_,line:["point"],tick:["bandSize","thickness",...B_]},c0e={color:"#4c78a8",invalid:"break-paths-show-path-domains",timeUnitBandSize:1},UN=Y({mark:1,arc:1,area:1,bar:1,circle:1,image:1,line:1,point:1,rect:1,rule:1,square:1,text:1,tick:1,trail:1,geoshape:1});function Eu(e){return X(e,"band")}const f0e={horizontal:["cornerRadiusTopRight","cornerRadiusBottomRight"],vertical:["cornerRadiusTopLeft","cornerRadiusTopRight"]},U_={binSpacing:0,continuousBandSize:5,minBandSize:.25,timeUnitBandPosition:.5},d0e={...U_,binSpacing:1},h0e={...U_,thickness:1};function p0e(e){return xs(e)?e.type:e}function jN(e,{isPath:t}){return e===void 0||e==="break-paths-show-path-domains"?t?"break-paths-show-domains":"filter":e===null?"show":e}function j_({markDef:e,config:t,scaleChannel:n,scaleType:i,isCountAggregate:r}){var a,u;if(!i||!Tr(i)||r)return"always-valid";const s=jN(gt("invalid",e,t),{isPath:ha(e.type)});return((u=(a=t.scale)==null?void 0:a.invalid)==null?void 0:u[n])!==void 0?"show":s}function g0e(e){return e==="break-paths-filter-domains"||e==="break-paths-show-domains"}function qN({scaleName:e,scale:t,mode:n}){const i=`domain('${e}')`;if(!t||!e)return;const r=`${i}[0]`,s=`peek(${i})`,o=t.domainHasZero();return o==="definitely"?{scale:e,value:0}:o==="maybe"?{signal:`scale('${e}', inrange(0, ${i}) ? 0 : ${n==="zeroOrMin"?r:s})`}:{signal:`scale('${e}', ${n==="zeroOrMin"?r:s})`}}function WN({scaleChannel:e,channelDef:t,scale:n,scaleName:i,markDef:r,config:s}){var c;const o=n==null?void 0:n.get("type"),a=Rr(t),u=D1(a==null?void 0:a.aggregate),l=j_({scaleChannel:e,markDef:r,config:s,scaleType:o,isCountAggregate:u});if(a&&l==="show"){const f=((c=s.scale.invalid)==null?void 0:c[e])??"zero-or-min";return{test:O1(te(a,{expr:"datum"}),!1),...m0e(f,n,i)}}}function m0e(e,t,n){if(t0e(e)){const{value:i}=e;return me(i)?{signal:i.signal}:{value:i}}return qN({scale:t,scaleName:n,mode:"zeroOrMin"})}function q_(e){const{channel:t,channelDef:n,markDef:i,scale:r,scaleName:s,config:o}=e,a=gu(t),u=W_(e),l=WN({scaleChannel:a,channelDef:n,scale:r,scaleName:s,markDef:i,config:o});return l!==void 0?[l,u]:u}function y0e(e){const{datum:t}=e;return vu(t)?xu(t):`${pt(t)}`}function Cu(e,t,n,i){const r={};if(t&&(r.scale=t),_s(e)){const{datum:s}=e;vu(s)?r.signal=xu(s):me(s)?r.signal=s.signal:Jd(s)?r.signal=s.expr:r.value=s}else r.field=te(e,n);if(i){const{offset:s,band:o}=i;s&&(r.offset=s),o&&(r.band=o)}return r}function U1({scaleName:e,fieldOrDatumDef:t,fieldOrDatumDef2:n,offset:i,startSuffix:r,endSuffix:s="end",bandPosition:o=.5}){const a=!me(o)&&0{switch(t.fieldTitle){case"plain":return e.field;case"functional":return M0e(e);default:return T0e(e,t)}};let aR=oR;function uR(e){aR=e}function N0e(){uR(oR)}function xc(e,t,{allowDisabling:n,includeDefault:i=!0}){var a;const r=(a=X_(e))==null?void 0:a.title;if(!J(e))return r??e.title;const s=e,o=i?K_(s,t):void 0;return n?zt(r,s.title,o):r??s.title??o}function X_(e){if(vc(e)&&e.axis)return e.axis;if(rR(e)&&e.legend)return e.legend;if(V_(e)&&e.header)return e.header}function K_(e,t){return aR(e,t)}function V1(e){if(sR(e)){const{format:t,formatType:n}=e;return{format:t,formatType:n}}else{const t=X_(e)??{},{format:n,formatType:i}=t;return{format:n,formatType:i}}}function R0e(e,t){var s;switch(t){case"latitude":case"longitude":return"quantitative";case"row":case"column":case"facet":case"shape":case"strokeDash":return"nominal";case"order":return"ordinal"}if(Y_(e)&&W(e.sort))return"ordinal";const{aggregate:n,bin:i,timeUnit:r}=e;if(r)return"temporal";if(i||n&&!fa(n)&&!ro(n))return"quantitative";if(Au(e)&&((s=e.scale)!=null&&s.type))switch(O_[e.scale.type]){case"numeric":case"discretizing":return"quantitative";case"time":return"temporal"}return"nominal"}function Rr(e){if(J(e))return e;if(W1(e))return e.condition}function Xt(e){if(Me(e))return e;if(sh(e))return e.condition}function lR(e,t,n,i={}){if(se(e)||Ze(e)||xo(e)){const r=se(e)?"string":Ze(e)?"number":"boolean";return Z(Lde(t,r,e)),{value:e}}return Me(e)?Y1(e,t,n,i):sh(e)?{...e,condition:Y1(e.condition,t,n,i)}:e}function Y1(e,t,n,i){if(sR(e)){const{format:r,formatType:s,...o}=e;if(ku(s)&&!n.customFormatTypes)return Z(lN(t)),Y1(o,t,n,i)}else{const r=vc(e)?"axis":rR(e)?"legend":V_(e)?"header":null;if(r&&e[r]){const{format:s,formatType:o,...a}=e[r];if(ku(o)&&!n.customFormatTypes)return Z(lN(t)),Y1({...e,[r]:a},t,n,i)}}return J(e)?Z_(e,t,i):O0e(e)}function O0e(e){let t=e.type;if(t)return e;const{datum:n}=e;return t=Ze(n)?"quantitative":se(n)?"nominal":vu(n)?"temporal":void 0,{...e,type:t}}function Z_(e,t,{compositeMark:n=!1}={}){const{aggregate:i,timeUnit:r,bin:s,field:o}=e,a={...e};if(!n&&i&&!p_(i)&&!fa(i)&&!ro(i)&&(Z(Pde(i)),delete a.aggregate),r&&(a.timeUnit=sn(r)),o&&(a.field=`${o}`),_t(s)&&(a.bin=X1(s,t)),mn(s)&&!Bt(t)&&Z(mhe(t)),ei(a)){const{type:u}=a,l=Uhe(u);u!==l&&(a.type=l),u!=="quantitative"&&D1(i)&&(Z(Ide(u,i)),a.type="quantitative")}else if(!PM(t)){const u=R0e(a,t);a.type=u}if(ei(a)){const{compatible:u,warning:l}=L0e(a,t)||{};u===!1&&Z(l)}if(Y_(a)&&se(a.sort)){const{sort:u}=a;if(ZN(u))return{...a,sort:{encoding:u}};const l=u.substring(1);if(u.charAt(0)==="-"&&ZN(l))return{...a,sort:{encoding:l,order:"descending"}}}if(V_(a)){const{header:u}=a;if(u){const{orient:l,...c}=u;if(l)return{...a,header:{...c,labelOrient:u.labelOrient||l,titleOrient:u.titleOrient||l}}}}return a}function X1(e,t){return xo(e)?{maxbins:HM(t)}:e==="binned"?{binned:!0}:!e.maxbins&&!e.step?{...e,maxbins:HM(t)}:e}const _c={compatible:!0};function L0e(e,t){const n=e.type;if(n==="geojson"&&t!=="shape")return{compatible:!1,warning:`Channel ${t} should not be used with a geojson data.`};switch(t){case Zs:case Js:case C1:return G1(e)?_c:{compatible:!1,warning:jde(t)};case $t:case rn:case ra:case dc:case pi:case hs:case ps:case Xd:case Kd:case k1:case pu:case A1:case $1:case hu:case tr:case Ar:case S1:return _c;case Sr:case nr:case $r:case Fr:return n!==wu?{compatible:!1,warning:`Channel ${t} should be used with a quantitative field only, not ${e.type} field.`}:_c;case no:case oa:case aa:case ua:case to:case eo:case Qs:case kr:case ds:case sa:return n==="nominal"&&!e.sort?{compatible:!1,warning:`Channel ${t} should not be used with an unsorted discrete field.`}:_c;case gi:case la:return!G1(e)&&!F0e(e)?{compatible:!1,warning:qde(t)}:_c;case hc:return e.type==="nominal"&&!("sort"in e)?{compatible:!1,warning:"Channel order is inappropriate for nominal field, which has no inherent order."}:_c}}function wc(e){const{formatType:t}=V1(e);return t==="time"||!t&&I0e(e)}function I0e(e){return e&&(e.type==="temporal"||J(e)&&!!e.timeUnit)}function K1(e,{timeUnit:t,type:n,wrapTime:i,undefinedIfExprNotRequired:r}){var u;const s=t&&((u=sn(t))==null?void 0:u.unit);let o=s||n==="temporal",a;return Jd(e)?a=e.expr:me(e)?a=e.signal:vu(e)?(o=!0,a=xu(e)):(se(e)||Ze(e))&&o&&(a=`datetime(${pt(e)})`,She(s)&&(Ze(e)&&e<1e4||se(e)&&isNaN(Date.parse(e)))&&(a=xu({[s]:e}))),a?i&&o?`time(${a})`:a:r?void 0:pt(e)}function cR(e,t){const{type:n}=e;return t.map(i=>{const r=J(e)&&!_u(e.timeUnit)?e.timeUnit:void 0,s=K1(i,{timeUnit:r,type:n,undefinedIfExprNotRequired:!0});return s!==void 0?{signal:s}:i})}function oh(e,t){return _t(e.bin)?ms(t)&&["ordinal","nominal"].includes(e.type):(console.warn("Only call this method for binned field defs."),!1)}const fR={labelAlign:{part:"labels",vgProp:"align"},labelBaseline:{part:"labels",vgProp:"baseline"},labelColor:{part:"labels",vgProp:"fill"},labelFont:{part:"labels",vgProp:"font"},labelFontSize:{part:"labels",vgProp:"fontSize"},labelFontStyle:{part:"labels",vgProp:"fontStyle"},labelFontWeight:{part:"labels",vgProp:"fontWeight"},labelOpacity:{part:"labels",vgProp:"opacity"},labelOffset:null,labelPadding:null,gridColor:{part:"grid",vgProp:"stroke"},gridDash:{part:"grid",vgProp:"strokeDash"},gridDashOffset:{part:"grid",vgProp:"strokeDashOffset"},gridOpacity:{part:"grid",vgProp:"opacity"},gridWidth:{part:"grid",vgProp:"strokeWidth"},tickColor:{part:"ticks",vgProp:"stroke"},tickDash:{part:"ticks",vgProp:"strokeDash"},tickDashOffset:{part:"ticks",vgProp:"strokeDashOffset"},tickOpacity:{part:"ticks",vgProp:"opacity"},tickSize:null,tickWidth:{part:"ticks",vgProp:"strokeWidth"}};function ah(e){return e==null?void 0:e.condition}const dR=["domain","grid","labels","ticks","title"],P0e={grid:"grid",gridCap:"grid",gridColor:"grid",gridDash:"grid",gridDashOffset:"grid",gridOpacity:"grid",gridScale:"grid",gridWidth:"grid",orient:"main",bandPosition:"both",aria:"main",description:"main",domain:"main",domainCap:"main",domainColor:"main",domainDash:"main",domainDashOffset:"main",domainOpacity:"main",domainWidth:"main",format:"main",formatType:"main",labelAlign:"main",labelAngle:"main",labelBaseline:"main",labelBound:"main",labelColor:"main",labelFlush:"main",labelFlushOffset:"main",labelFont:"main",labelFontSize:"main",labelFontStyle:"main",labelFontWeight:"main",labelLimit:"main",labelLineHeight:"main",labelOffset:"main",labelOpacity:"main",labelOverlap:"main",labelPadding:"main",labels:"main",labelSeparation:"main",maxExtent:"main",minExtent:"main",offset:"both",position:"main",tickCap:"main",tickColor:"main",tickDash:"main",tickDashOffset:"main",tickMinStep:"both",tickOffset:"both",tickOpacity:"main",tickRound:"both",ticks:"main",tickSize:"main",tickWidth:"both",title:"main",titleAlign:"main",titleAnchor:"main",titleAngle:"main",titleBaseline:"main",titleColor:"main",titleFont:"main",titleFontSize:"main",titleFontStyle:"main",titleFontWeight:"main",titleLimit:"main",titleLineHeight:"main",titleOpacity:"main",titlePadding:"main",titleX:"main",titleY:"main",encode:"both",scale:"both",tickBand:"both",tickCount:"both",tickExtra:"both",translate:"both",values:"both",zindex:"both"},hR={orient:1,aria:1,bandPosition:1,description:1,domain:1,domainCap:1,domainColor:1,domainDash:1,domainDashOffset:1,domainOpacity:1,domainWidth:1,format:1,formatType:1,grid:1,gridCap:1,gridColor:1,gridDash:1,gridDashOffset:1,gridOpacity:1,gridWidth:1,labelAlign:1,labelAngle:1,labelBaseline:1,labelBound:1,labelColor:1,labelFlush:1,labelFlushOffset:1,labelFont:1,labelFontSize:1,labelFontStyle:1,labelFontWeight:1,labelLimit:1,labelLineHeight:1,labelOffset:1,labelOpacity:1,labelOverlap:1,labelPadding:1,labels:1,labelSeparation:1,maxExtent:1,minExtent:1,offset:1,position:1,tickBand:1,tickCap:1,tickColor:1,tickCount:1,tickDash:1,tickDashOffset:1,tickExtra:1,tickMinStep:1,tickOffset:1,tickOpacity:1,tickRound:1,ticks:1,tickSize:1,tickWidth:1,title:1,titleAlign:1,titleAnchor:1,titleAngle:1,titleBaseline:1,titleColor:1,titleFont:1,titleFontSize:1,titleFontStyle:1,titleFontWeight:1,titleLimit:1,titleLineHeight:1,titleOpacity:1,titlePadding:1,titleX:1,titleY:1,translate:1,values:1,zindex:1},z0e={...hR,style:1,labelExpr:1,encoding:1};function pR(e){return ue(z0e,e)}const gR=Y({axis:1,axisBand:1,axisBottom:1,axisDiscrete:1,axisLeft:1,axisPoint:1,axisQuantitative:1,axisRight:1,axisTemporal:1,axisTop:1,axisX:1,axisXBand:1,axisXDiscrete:1,axisXPoint:1,axisXQuantitative:1,axisXTemporal:1,axisY:1,axisYBand:1,axisYDiscrete:1,axisYPoint:1,axisYQuantitative:1,axisYTemporal:1});function ao(e){return X(e,"mark")}class Z1{constructor(t,n){this.name=t,this.run=n}hasMatchingType(t){return ao(t)?p0e(t.mark)===this.name:!1}}function $u(e,t){const n=e&&e[t];return n?W(n)?lc(n,i=>!!i.field):J(n)||W1(n):!1}function mR(e,t){const n=e&&e[t];return n?W(n)?lc(n,i=>!!i.field):J(n)||_s(n)||sh(n):!1}function yR(e,t){if(Bt(t)){const n=e[t];if((J(n)||_s(n))&&(FN(n.type)||J(n)&&n.timeUnit)){const i=a_(t);return mR(e,i)}}return!1}function bR(e){return lc(zfe,t=>{if($u(e,t)){const n=e[t];if(W(n))return lc(n,i=>!!i.aggregate);{const i=Rr(n);return i&&!!i.aggregate}}return!1})}function vR(e,t){const n=[],i=[],r=[],s=[],o={};return J_(e,(a,u)=>{if(J(a)){const{field:l,aggregate:c,bin:f,timeUnit:d,...h}=a;if(c||d||f){const p=X_(a),g=p==null?void 0:p.title;let m=te(a,{forAs:!0});const y={...g?[]:{title:xc(a,t,{allowDisabling:!0})},...h,field:m};if(c){let b;if(fa(c)?(b="argmax",m=te({op:"argmax",field:c.argmax},{forAs:!0}),y.field=`${m}.${l}`):ro(c)?(b="argmin",m=te({op:"argmin",field:c.argmin},{forAs:!0}),y.field=`${m}.${l}`):c!=="boxplot"&&c!=="errorbar"&&c!=="errorband"&&(b=c),b){const v={op:b,as:m};l&&(v.field=l),s.push(v)}}else if(n.push(m),ei(a)&&_t(f)){if(i.push({bin:f,field:l,as:m}),n.push(te(a,{binSuffix:"end"})),oh(a,u)&&n.push(te(a,{binSuffix:"range"})),Bt(u)){const b={field:`${m}_end`};o[`${u}2`]=b}y.bin="binned",PM(u)||(y.type=wu)}else if(d&&!_u(d)){r.push({timeUnit:d,field:l,as:m});const b=ei(a)&&a.type!==gc&&"time";b&&(u===Xd||u===pu?y.formatType=b:Kfe(u)?y.legend={formatType:b,...y.legend}:Bt(u)&&(y.axis={formatType:b,...y.axis}))}o[u]=y}else n.push(l),o[u]=e[u]}else o[u]=e[u]}),{bins:i,timeUnits:r,aggregate:s,groupby:n,encoding:o}}function B0e(e,t,n){const i=Jfe(t,n);if(i){if(i==="binned"){const r=e[t===kr?$t:rn];return!!(J(r)&&J(e[t])&&mn(r.bin))}}else return!1;return!0}function U0e(e,t,n,i){const r={};for(const s of Y(e))IM(s)||Z(Ude(s));for(let s of Hfe){if(!e[s])continue;const o=e[s];if(Zd(s)){const a=Wfe(s),u=r[a];if(J(u)&&Bhe(u.type)&&J(o)&&!u.timeUnit){Z(Ode(a));continue}}if(s==="angle"&&t==="arc"&&!e.theta&&(Z(Rde),s=tr),!B0e(e,s,t)){Z(M1(s,t));continue}if(s===to&&t==="line"){const a=Rr(e[s]);if(a!=null&&a.aggregate){Z(Bde);continue}}if(s===pi&&(n?"fill"in e:"stroke"in e)){Z(fN("encoding",{fill:"fill"in e,stroke:"stroke"in e}));continue}if(s===Kd||s===hc&&!W(o)&&!Nr(o)||s===pu&&W(o)){if(o){if(s===hc){const a=e[s];if(nR(a)){r[s]=a;continue}}r[s]=oe(o).reduce((a,u)=>(J(u)?a.push(Z_(u,s)):Z(x_(u,s)),a),[])}}else{if(s===pu&&o===null)r[s]=null;else if(!J(o)&&!_s(o)&&!Nr(o)&&!rh(o)&&!me(o)){Z(x_(o,s));continue}r[s]=lR(o,s,i)}}return r}function J1(e,t){const n={};for(const i of Y(e)){const r=lR(e[i],i,t,{compositeMark:!0});n[i]=r}return n}function j0e(e){const t=[];for(const n of Y(e))if($u(e,n)){const i=e[n],r=oe(i);for(const s of r)J(s)?t.push(s):W1(s)&&t.push(s.condition)}return t}function J_(e,t,n){if(e)for(const i of Y(e)){const r=e[i];if(W(r))for(const s of r)t.call(n,s,i);else t.call(n,r,i)}}function q0e(e,t,n,i){return e?Y(e).reduce((r,s)=>{const o=e[s];return W(o)?o.reduce((a,u)=>t.call(i,a,u,s),r):t.call(i,r,o,s)},n):n}function xR(e,t){return Y(t).reduce((n,i)=>{switch(i){case $t:case rn:case A1:case S1:case $1:case kr:case ds:case ra:case dc:case tr:case eo:case Ar:case Qs:case sa:case $r:case Sr:case Fr:case nr:case Xd:case gi:case hu:case pu:return n;case hc:if(e==="line"||e==="trail")return n;case Kd:case k1:{const r=t[i];if(W(r)||J(r))for(const s of oe(r))s.aggregate||n.push(te(s,{}));return n}case to:if(e==="trail")return n;case pi:case hs:case ps:case no:case oa:case aa:case la:case ua:{const r=Rr(t[i]);return r&&!r.aggregate&&n.push(te(r,{})),n}}},[])}function W0e(e){const{tooltip:t,...n}=e;if(!t)return{filteredEncoding:n};let i,r;if(W(t)){for(const s of t)s.aggregate?(i||(i=[]),i.push(s)):(r||(r=[]),r.push(s));i&&(n.tooltip=i)}else t.aggregate?n.tooltip=t:r=t;return W(r)&&r.length===1&&(r=r[0]),{customTooltipWithoutAggregatedField:r,filteredEncoding:n}}function Q_(e,t,n,i=!0){if("tooltip"in n)return{tooltip:n.tooltip};const r=e.map(({fieldPrefix:o,titlePrefix:a})=>{const u=i?` of ${ew(t)}`:"";return{field:o+t.field,type:t.type,title:me(a)?{signal:`${a}"${escape(u)}"`}:a+u}}),s=j0e(n).map($0e);return{tooltip:[...r,...fs(s,We)]}}function ew(e){const{title:t,field:n}=e;return zt(t,n)}function tw(e,t,n,i,r){const{scale:s,axis:o}=n;return({partName:a,mark:u,positionPrefix:l,endPositionPrefix:c=void 0,extraEncoding:f={}})=>{const d=ew(n);return _R(e,a,r,{mark:u,encoding:{[t]:{field:`${l}_${n.field}`,type:n.type,...d!==void 0?{title:d}:{},...s!==void 0?{scale:s}:{},...o!==void 0?{axis:o}:{}},...se(c)?{[`${t}2`]:{field:`${c}_${n.field}`}}:{},...i,...f}})}}function _R(e,t,n,i){const{clip:r,color:s,opacity:o}=e,a=e.type;return e[t]||e[t]===void 0&&n[t]?[{...i,mark:{...n[t],...r?{clip:r}:{},...s?{color:s}:{},...o?{opacity:o}:{},...xs(i.mark)?i.mark:{type:i.mark},style:`${a}-${String(t)}`,...xo(e[t])?{}:e[t]}}]:[]}function wR(e,t,n){const{encoding:i}=e,r=t==="vertical"?"y":"x",s=i[r],o=i[`${r}2`],a=i[`${r}Error`],u=i[`${r}Error2`];return{continuousAxisChannelDef:Q1(s,n),continuousAxisChannelDef2:Q1(o,n),continuousAxisChannelDefError:Q1(a,n),continuousAxisChannelDefError2:Q1(u,n),continuousAxis:r}}function Q1(e,t){if(e!=null&&e.aggregate){const{aggregate:n,...i}=e;return n!==t&&Z(ghe(n,t)),i}else return e}function ER(e,t){const{mark:n,encoding:i}=e,{x:r,y:s}=i;if(xs(n)&&n.orient)return n.orient;if(ga(r)){if(ga(s)){const o=J(r)&&r.aggregate,a=J(s)&&s.aggregate;if(!o&&a===t)return"vertical";if(!a&&o===t)return"horizontal";if(o===t&&a===t)throw new Error("Both x and y cannot have aggregate");return wc(s)&&!wc(r)?"horizontal":"vertical"}return"horizontal"}else{if(ga(s))return"vertical";throw new Error(`Need a valid continuous axis for ${t}s`)}}const em="boxplot",H0e=["box","median","outliers","rule","ticks"],G0e=new Z1(em,kR);function CR(e){return Ze(e)?"tukey":e}function kR(e,{config:t}){e={...e,encoding:J1(e.encoding,t)};const{mark:n,encoding:i,params:r,projection:s,...o}=e,a=xs(n)?n:{type:n};r&&Z(oN("boxplot"));const u=a.extent??t.boxplot.extent,l=gt("size",a,t),c=a.invalid,f=CR(u),{bins:d,timeUnits:h,transform:p,continuousAxisChannelDef:g,continuousAxis:m,groupby:y,aggregate:b,encodingWithoutContinuousAxis:v,ticksOrient:x,boxOrient:_,customTooltipWithoutAggregatedField:E}=V0e(e,u,t),w=cc(g.field),{color:C,size:k,...S}=v,$=r2=>tw(a,m,g,r2,t.boxplot),O=$(S),R=$(v),F=(re(t.boxplot.box)?t.boxplot.box.color:t.mark.color)||"#4c78a8",A=$({...S,...k?{size:k}:{},color:{condition:{test:`${at(`lower_box_${g.field}`)} >= ${at(`upper_box_${g.field}`)}`,...C||{value:F}}}}),T=Q_([{fieldPrefix:f==="min-max"?"upper_whisker_":"max_",titlePrefix:"Max"},{fieldPrefix:"upper_box_",titlePrefix:"Q3"},{fieldPrefix:"mid_box_",titlePrefix:"Median"},{fieldPrefix:"lower_box_",titlePrefix:"Q1"},{fieldPrefix:f==="min-max"?"lower_whisker_":"min_",titlePrefix:"Min"}],g,v),B={type:"tick",color:"black",opacity:1,orient:x,invalid:c,aria:!1},H=f==="min-max"?T:Q_([{fieldPrefix:"upper_whisker_",titlePrefix:"Upper Whisker"},{fieldPrefix:"lower_whisker_",titlePrefix:"Lower Whisker"}],g,v),z=[...O({partName:"rule",mark:{type:"rule",invalid:c,aria:!1},positionPrefix:"lower_whisker",endPositionPrefix:"lower_box",extraEncoding:H}),...O({partName:"rule",mark:{type:"rule",invalid:c,aria:!1},positionPrefix:"upper_box",endPositionPrefix:"upper_whisker",extraEncoding:H}),...O({partName:"ticks",mark:B,positionPrefix:"lower_whisker",extraEncoding:H}),...O({partName:"ticks",mark:B,positionPrefix:"upper_whisker",extraEncoding:H})],ne=[...f!=="tukey"?z:[],...R({partName:"box",mark:{type:"bar",...l?{size:l}:{},orient:_,invalid:c,ariaRoleDescription:"box"},positionPrefix:"lower_box",endPositionPrefix:"upper_box",extraEncoding:T}),...A({partName:"median",mark:{type:"tick",invalid:c,...re(t.boxplot.median)&&t.boxplot.median.color?{color:t.boxplot.median.color}:{},...l?{size:l}:{},orient:x,aria:!1},positionPrefix:"mid_box",extraEncoding:T})];if(f==="min-max")return{...o,transform:(o.transform??[]).concat(p),layer:ne};const be=at(`lower_box_${g.field}`),de=at(`upper_box_${g.field}`),Te=`(${de} - ${be})`,ct=`${be} - ${u} * ${Te}`,$e=`${de} + ${u} * ${Te}`,Ot=at(g.field),or={joinaggregate:AR(g.field),groupby:y},Ii={transform:[{filter:`(${ct} <= ${Ot}) && (${Ot} <= ${$e})`},{aggregate:[{op:"min",field:g.field,as:`lower_whisker_${w}`},{op:"max",field:g.field,as:`upper_whisker_${w}`},{op:"min",field:`lower_box_${g.field}`,as:`lower_box_${w}`},{op:"max",field:`upper_box_${g.field}`,as:`upper_box_${w}`},...b],groupby:y}],layer:z},{tooltip:le,...Re}=S,{scale:Le,axis:K}=g,Kt=ew(g),Xe=hi(K,["title"]),Pn=_R(a,"outliers",t.boxplot,{transform:[{filter:`(${Ot} < ${ct}) || (${Ot} > ${$e})`}],mark:"point",encoding:{[m]:{field:g.field,type:g.type,...Kt!==void 0?{title:Kt}:{},...Le!==void 0?{scale:Le}:{},...ht(Xe)?{}:{axis:Xe}},...Re,...C?{color:C}:{},...E?{tooltip:E}:{}}})[0];let ln;const Fs=[...d,...h,or];return Pn?ln={transform:Fs,layer:[Pn,Ii]}:(ln=Ii,ln.transform.unshift(...Fs)),{...o,layer:[ln,{transform:p,layer:ne}]}}function AR(e){const t=cc(e);return[{op:"q1",field:e,as:`lower_box_${t}`},{op:"q3",field:e,as:`upper_box_${t}`}]}function V0e(e,t,n){const i=ER(e,em),{continuousAxisChannelDef:r,continuousAxis:s}=wR(e,i,em),o=r.field,a=cc(o),u=CR(t),l=[...AR(o),{op:"median",field:o,as:`mid_box_${a}`},{op:"min",field:o,as:(u==="min-max"?"lower_whisker_":"min_")+a},{op:"max",field:o,as:(u==="min-max"?"upper_whisker_":"max_")+a}],c=u==="min-max"||u==="tukey"?[]:[{calculate:`${at(`upper_box_${a}`)} - ${at(`lower_box_${a}`)}`,as:`iqr_${a}`},{calculate:`min(${at(`upper_box_${a}`)} + ${at(`iqr_${a}`)} * ${t}, ${at(`max_${a}`)})`,as:`upper_whisker_${a}`},{calculate:`max(${at(`lower_box_${a}`)} - ${at(`iqr_${a}`)} * ${t}, ${at(`min_${a}`)})`,as:`lower_whisker_${a}`}],{[s]:f,...d}=e.encoding,{customTooltipWithoutAggregatedField:h,filteredEncoding:p}=W0e(d),{bins:g,timeUnits:m,aggregate:y,groupby:b,encoding:v}=vR(p,n),x=i==="vertical"?"horizontal":"vertical",_=i,E=[...g,...m,{aggregate:[...y,...l],groupby:b},...c];return{bins:g,timeUnits:m,transform:E,groupby:b,aggregate:y,continuousAxisChannelDef:r,continuousAxis:s,encodingWithoutContinuousAxis:v,ticksOrient:x,boxOrient:_,customTooltipWithoutAggregatedField:h}}const nw="errorbar",Y0e=["ticks","rule"],X0e=new Z1(nw,$R);function $R(e,{config:t}){e={...e,encoding:J1(e.encoding,t)};const{transform:n,continuousAxisChannelDef:i,continuousAxis:r,encodingWithoutContinuousAxis:s,ticksOrient:o,markDef:a,outerSpec:u,tooltipEncoding:l}=SR(e,nw,t);delete s.size;const c=tw(a,r,i,s,t.errorbar),f=a.thickness,d=a.size,h={type:"tick",orient:o,aria:!1,...f!==void 0?{thickness:f}:{},...d!==void 0?{size:d}:{}},p=[...c({partName:"ticks",mark:h,positionPrefix:"lower",extraEncoding:l}),...c({partName:"ticks",mark:h,positionPrefix:"upper",extraEncoding:l}),...c({partName:"rule",mark:{type:"rule",ariaRoleDescription:"errorbar",...f!==void 0?{size:f}:{}},positionPrefix:"lower",endPositionPrefix:"upper",extraEncoding:l})];return{...u,transform:n,...p.length>1?{layer:p}:{...p[0]}}}function K0e(e,t){const{encoding:n}=e;if(Z0e(n))return{orient:ER(e,t),inputType:"raw"};const i=J0e(n),r=Q0e(n),s=n.x,o=n.y;if(i){if(r)throw new Error(`${t} cannot be both type aggregated-upper-lower and aggregated-error`);const a=n.x2,u=n.y2;if(Me(a)&&Me(u))throw new Error(`${t} cannot have both x2 and y2`);if(Me(a)){if(ga(s))return{orient:"horizontal",inputType:"aggregated-upper-lower"};throw new Error(`Both x and x2 have to be quantitative in ${t}`)}else if(Me(u)){if(ga(o))return{orient:"vertical",inputType:"aggregated-upper-lower"};throw new Error(`Both y and y2 have to be quantitative in ${t}`)}throw new Error("No ranged axis")}else{const a=n.xError,u=n.xError2,l=n.yError,c=n.yError2;if(Me(u)&&!Me(a))throw new Error(`${t} cannot have xError2 without xError`);if(Me(c)&&!Me(l))throw new Error(`${t} cannot have yError2 without yError`);if(Me(a)&&Me(l))throw new Error(`${t} cannot have both xError and yError with both are quantiative`);if(Me(a)){if(ga(s))return{orient:"horizontal",inputType:"aggregated-error"};throw new Error("All x, xError, and xError2 (if exist) have to be quantitative")}else if(Me(l)){if(ga(o))return{orient:"vertical",inputType:"aggregated-error"};throw new Error("All y, yError, and yError2 (if exist) have to be quantitative")}throw new Error("No ranged axis")}}function Z0e(e){return(Me(e.x)||Me(e.y))&&!Me(e.x2)&&!Me(e.y2)&&!Me(e.xError)&&!Me(e.xError2)&&!Me(e.yError)&&!Me(e.yError2)}function J0e(e){return Me(e.x2)||Me(e.y2)}function Q0e(e){return Me(e.xError)||Me(e.xError2)||Me(e.yError)||Me(e.yError2)}function SR(e,t,n){const{mark:i,encoding:r,params:s,projection:o,...a}=e,u=xs(i)?i:{type:i};s&&Z(oN(t));const{orient:l,inputType:c}=K0e(e,t),{continuousAxisChannelDef:f,continuousAxisChannelDef2:d,continuousAxisChannelDefError:h,continuousAxisChannelDefError2:p,continuousAxis:g}=wR(e,l,t),{errorBarSpecificAggregate:m,postAggregateCalculates:y,tooltipSummary:b,tooltipTitleWithFieldName:v}=epe(u,f,d,h,p,c,t,n),{[g]:x,[g==="x"?"x2":"y2"]:_,[g==="x"?"xError":"yError"]:E,[g==="x"?"xError2":"yError2"]:w,...C}=r,{bins:k,timeUnits:S,aggregate:$,groupby:O,encoding:R}=vR(C,n),F=[...$,...m],A=c!=="raw"?[]:O,T=Q_(b,f,R,v);return{transform:[...a.transform??[],...k,...S,...F.length===0?[]:[{aggregate:F,groupby:A}],...y],groupby:A,continuousAxisChannelDef:f,continuousAxis:g,encodingWithoutContinuousAxis:R,ticksOrient:l==="vertical"?"horizontal":"vertical",markDef:u,outerSpec:a,tooltipEncoding:T}}function epe(e,t,n,i,r,s,o,a){let u=[],l=[];const c=t.field;let f,d=!1;if(s==="raw"){const h=e.center?e.center:e.extent?e.extent==="iqr"?"median":"mean":a.errorbar.center,p=e.extent?e.extent:h==="mean"?"stderr":"iqr";if(h==="median"!=(p==="iqr")&&Z(phe(h,p,o)),p==="stderr"||p==="stdev")u=[{op:p,field:c,as:`extent_${c}`},{op:h,field:c,as:`center_${c}`}],l=[{calculate:`${at(`center_${c}`)} + ${at(`extent_${c}`)}`,as:`upper_${c}`},{calculate:`${at(`center_${c}`)} - ${at(`extent_${c}`)}`,as:`lower_${c}`}],f=[{fieldPrefix:"center_",titlePrefix:Vd(h)},{fieldPrefix:"upper_",titlePrefix:FR(h,p,"+")},{fieldPrefix:"lower_",titlePrefix:FR(h,p,"-")}],d=!0;else{let g,m,y;p==="ci"?(g="mean",m="ci0",y="ci1"):(g="median",m="q1",y="q3"),u=[{op:m,field:c,as:`lower_${c}`},{op:y,field:c,as:`upper_${c}`},{op:g,field:c,as:`center_${c}`}],f=[{fieldPrefix:"upper_",titlePrefix:xc({field:c,aggregate:y,type:"quantitative"},a,{allowDisabling:!1})},{fieldPrefix:"lower_",titlePrefix:xc({field:c,aggregate:m,type:"quantitative"},a,{allowDisabling:!1})},{fieldPrefix:"center_",titlePrefix:xc({field:c,aggregate:g,type:"quantitative"},a,{allowDisabling:!1})}]}}else{(e.center||e.extent)&&Z(hhe(e.center,e.extent)),s==="aggregated-upper-lower"?(f=[],l=[{calculate:at(n.field),as:`upper_${c}`},{calculate:at(c),as:`lower_${c}`}]):s==="aggregated-error"&&(f=[{fieldPrefix:"",titlePrefix:c}],l=[{calculate:`${at(c)} + ${at(i.field)}`,as:`upper_${c}`}],r?l.push({calculate:`${at(c)} + ${at(r.field)}`,as:`lower_${c}`}):l.push({calculate:`${at(c)} - ${at(i.field)}`,as:`lower_${c}`}));for(const h of l)f.push({fieldPrefix:h.as.substring(0,6),titlePrefix:du(du(h.calculate,"datum['",""),"']","")})}return{postAggregateCalculates:l,errorBarSpecificAggregate:u,tooltipSummary:f,tooltipTitleWithFieldName:d}}function FR(e,t,n){return`${Vd(e)} ${n} ${t}`}const iw="errorband",tpe=["band","borders"],npe=new Z1(iw,DR);function DR(e,{config:t}){e={...e,encoding:J1(e.encoding,t)};const{transform:n,continuousAxisChannelDef:i,continuousAxis:r,encodingWithoutContinuousAxis:s,markDef:o,outerSpec:a,tooltipEncoding:u}=SR(e,iw,t),l=o,c=tw(l,r,i,s,t.errorband),f=e.encoding.x!==void 0&&e.encoding.y!==void 0;let d={type:f?"area":"rect"},h={type:f?"line":"rule"};const p={...l.interpolate?{interpolate:l.interpolate}:{},...l.tension&&l.interpolate?{tension:l.tension}:{}};return f?(d={...d,...p,ariaRoleDescription:"errorband"},h={...h,...p,aria:!1}):l.interpolate?Z(gN("interpolate")):l.tension&&Z(gN("tension")),{...a,transform:n,layer:[...c({partName:"band",mark:d,positionPrefix:"lower",endPositionPrefix:"upper",extraEncoding:u}),...c({partName:"borders",mark:h,positionPrefix:"lower",extraEncoding:u}),...c({partName:"borders",mark:h,positionPrefix:"upper",extraEncoding:u})]}}const TR={};function rw(e,t,n){const i=new Z1(e,t);TR[e]={normalizer:i,parts:n}}function ipe(){return Y(TR)}rw(em,kR,H0e),rw(nw,$R,Y0e),rw(iw,DR,tpe);const rpe=["gradientHorizontalMaxLength","gradientHorizontalMinLength","gradientVerticalMaxLength","gradientVerticalMinLength","unselectedOpacity"],MR={titleAlign:"align",titleAnchor:"anchor",titleAngle:"angle",titleBaseline:"baseline",titleColor:"color",titleFont:"font",titleFontSize:"fontSize",titleFontStyle:"fontStyle",titleFontWeight:"fontWeight",titleLimit:"limit",titleLineHeight:"lineHeight",titleOrient:"orient",titlePadding:"offset"},NR={labelAlign:"align",labelAnchor:"anchor",labelAngle:"angle",labelBaseline:"baseline",labelColor:"color",labelFont:"font",labelFontSize:"fontSize",labelFontStyle:"fontStyle",labelFontWeight:"fontWeight",labelLimit:"limit",labelLineHeight:"lineHeight",labelOrient:"orient",labelPadding:"offset"},spe=Y(MR),ope=Y(NR),RR=Y({header:1,headerRow:1,headerColumn:1,headerFacet:1}),OR=["size","shape","fill","stroke","strokeDash","strokeWidth","opacity"],ape={gradientHorizontalMaxLength:200,gradientHorizontalMinLength:100,gradientVerticalMaxLength:200,gradientVerticalMinLength:64,unselectedOpacity:.35},upe={aria:1,clipHeight:1,columnPadding:1,columns:1,cornerRadius:1,description:1,direction:1,fillColor:1,format:1,formatType:1,gradientLength:1,gradientOpacity:1,gradientStrokeColor:1,gradientStrokeWidth:1,gradientThickness:1,gridAlign:1,labelAlign:1,labelBaseline:1,labelColor:1,labelFont:1,labelFontSize:1,labelFontStyle:1,labelFontWeight:1,labelLimit:1,labelOffset:1,labelOpacity:1,labelOverlap:1,labelPadding:1,labelSeparation:1,legendX:1,legendY:1,offset:1,orient:1,padding:1,rowPadding:1,strokeColor:1,symbolDash:1,symbolDashOffset:1,symbolFillColor:1,symbolLimit:1,symbolOffset:1,symbolOpacity:1,symbolSize:1,symbolStrokeColor:1,symbolStrokeWidth:1,symbolType:1,tickCount:1,tickMinStep:1,title:1,titleAlign:1,titleAnchor:1,titleBaseline:1,titleColor:1,titleFont:1,titleFontSize:1,titleFontStyle:1,titleFontWeight:1,titleLimit:1,titleLineHeight:1,titleOpacity:1,titleOrient:1,titlePadding:1,type:1,values:1,zindex:1},Or="_vgsid_",lpe={point:{on:"click",fields:[Or],toggle:"event.shiftKey",resolve:"global",clear:"dblclick"},interval:{on:"[pointerdown, window:pointerup] > window:pointermove!",encodings:["x","y"],translate:"[pointerdown, window:pointerup] > window:pointermove!",zoom:"wheel!",mark:{fill:"#333",fillOpacity:.125,stroke:"white"},resolve:"global",clear:"dblclick"}};function sw(e){return e==="legend"||!!(e!=null&&e.legend)}function ow(e){return sw(e)&&re(e)}function aw(e){return!!(e!=null&&e.select)}function LR(e){const t=[];for(const n of e||[]){if(aw(n))continue;const{expr:i,bind:r,...s}=n;if(r&&i){const o={...s,bind:r,init:i};t.push(o)}else{const o={...s,...i?{update:i}:{},...r?{bind:r}:{}};t.push(o)}}return t}function cpe(e){return tm(e)||lw(e)||uw(e)}function uw(e){return X(e,"concat")}function tm(e){return X(e,"vconcat")}function lw(e){return X(e,"hconcat")}function IR({step:e,offsetIsDiscrete:t}){return t?e.for??"offset":"position"}function ws(e){return X(e,"step")}function PR(e){return X(e,"view")||X(e,"width")||X(e,"height")}const zR=20,fpe=Y({align:1,bounds:1,center:1,columns:1,spacing:1});function dpe(e,t,n){const i=n[t],r={},{spacing:s,columns:o}=i;s!==void 0&&(r.spacing=s),o!==void 0&&(q1(e)&&!ih(e.facet)||uw(e))&&(r.columns=o),tm(e)&&(r.columns=1);for(const a of fpe)if(e[a]!==void 0)if(a==="spacing"){const u=e[a];r[a]=Ze(u)?u:{row:u.row??s,column:u.column??s}}else r[a]=e[a];return r}function cw(e,t){return e[t]??e[t==="width"?"continuousWidth":"continuousHeight"]}function fw(e,t){const n=nm(e,t);return ws(n)?n.step:BR}function nm(e,t){const n=e[t]??e[t==="width"?"discreteWidth":"discreteHeight"];return zt(n,{step:e.step})}const BR=20,hpe={background:"white",padding:5,timeFormat:"%b %d, %Y",countTitle:"Count of Records",view:{continuousWidth:200,continuousHeight:200,step:BR},mark:c0e,arc:{},area:{},bar:d0e,circle:{},geoshape:{},image:{},line:{},point:{},rect:U_,rule:{color:"black"},square:{},text:{color:"black"},tick:h0e,trail:{},boxplot:{size:14,extent:1.5,box:{},median:{color:"white"},outliers:{},rule:{},ticks:null},errorbar:{center:"mean",rule:!0,ticks:!1},errorband:{band:{opacity:.3},borders:!1},scale:Ghe,projection:{},legend:ape,header:{titlePadding:10,labelPadding:10},headerColumn:{},headerRow:{},headerFacet:{},selection:lpe,style:{},title:{},facet:{spacing:zR},concat:{spacing:zR},normalizedNumberFormat:".0%"},uo=["#4c78a8","#f58518","#e45756","#72b7b2","#54a24b","#eeca3b","#b279a2","#ff9da6","#9d755d","#bab0ac"],UR={text:11,guideLabel:10,guideTitle:11,groupTitle:13,groupSubtitle:12},jR={blue:uo[0],orange:uo[1],red:uo[2],teal:uo[3],green:uo[4],yellow:uo[5],purple:uo[6],pink:uo[7],brown:uo[8],gray0:"#000",gray1:"#111",gray2:"#222",gray3:"#333",gray4:"#444",gray5:"#555",gray6:"#666",gray7:"#777",gray8:"#888",gray9:"#999",gray10:"#aaa",gray11:"#bbb",gray12:"#ccc",gray13:"#ddd",gray14:"#eee",gray15:"#fff"};function ppe(e={}){return{signals:[{name:"color",value:re(e)?{...jR,...e}:jR}],mark:{color:{signal:"color.blue"}},rule:{color:{signal:"color.gray0"}},text:{color:{signal:"color.gray0"}},style:{"guide-label":{fill:{signal:"color.gray0"}},"guide-title":{fill:{signal:"color.gray0"}},"group-title":{fill:{signal:"color.gray0"}},"group-subtitle":{fill:{signal:"color.gray0"}},cell:{stroke:{signal:"color.gray8"}}},axis:{domainColor:{signal:"color.gray13"},gridColor:{signal:"color.gray8"},tickColor:{signal:"color.gray13"}},range:{category:[{signal:"color.blue"},{signal:"color.orange"},{signal:"color.red"},{signal:"color.teal"},{signal:"color.green"},{signal:"color.yellow"},{signal:"color.purple"},{signal:"color.pink"},{signal:"color.brown"},{signal:"color.grey8"}]}}}function gpe(e){return{signals:[{name:"fontSize",value:re(e)?{...UR,...e}:UR}],text:{fontSize:{signal:"fontSize.text"}},style:{"guide-label":{fontSize:{signal:"fontSize.guideLabel"}},"guide-title":{fontSize:{signal:"fontSize.guideTitle"}},"group-title":{fontSize:{signal:"fontSize.groupTitle"}},"group-subtitle":{fontSize:{signal:"fontSize.groupSubtitle"}}}}}function mpe(e){return{text:{font:e},style:{"guide-label":{font:e},"guide-title":{font:e},"group-title":{font:e},"group-subtitle":{font:e}}}}function qR(e){const t=Y(e||{}),n={};for(const i of t){const r=e[i];n[i]=ah(r)?VM(r):Ni(r)}return n}function ype(e){const t=Y(e),n={};for(const i of t)n[i]=qR(e[i]);return n}const bpe=[...UN,...gR,...RR,"background","padding","legend","lineBreak","scale","style","title","view"];function WR(e={}){const{color:t,font:n,fontSize:i,selection:r,...s}=e,o=nl({},De(hpe),n?mpe(n):{},t?ppe(t):{},i?gpe(i):{},s||{});r&&il(o,"selection",r,!0);const a=hi(o,bpe);for(const u of["background","lineBreak","padding"])o[u]&&(a[u]=Ni(o[u]));for(const u of UN)o[u]&&(a[u]=yn(o[u]));for(const u of gR)o[u]&&(a[u]=qR(o[u]));for(const u of RR)o[u]&&(a[u]=yn(o[u]));if(o.legend&&(a.legend=yn(o.legend)),o.scale){const{invalid:u,...l}=o.scale,c=yn(u,{level:1});a.scale={...yn(l),...Y(c).length>0?{invalid:c}:{}}}return o.style&&(a.style=ype(o.style)),o.title&&(a.title=yn(o.title)),o.view&&(a.view=yn(o.view)),a}const vpe=new Set(["view",...s0e]),xpe=["color","fontSize","background","padding","facet","concat","numberFormat","numberFormatType","normalizedNumberFormat","normalizedNumberFormatType","timeFormat","countTitle","header","axisQuantitative","axisTemporal","axisDiscrete","axisPoint","axisXBand","axisXPoint","axisXDiscrete","axisXQuantitative","axisXTemporal","axisYBand","axisYPoint","axisYDiscrete","axisYQuantitative","axisYTemporal","scale","selection","overlay"],_pe={view:["continuousWidth","continuousHeight","discreteWidth","discreteHeight","step"],...l0e};function wpe(e){e=De(e);for(const t of xpe)delete e[t];if(e.axis)for(const t in e.axis)ah(e.axis[t])&&delete e.axis[t];if(e.legend)for(const t of rpe)delete e.legend[t];if(e.mark){for(const t of BN)delete e.mark[t];e.mark.tooltip&&re(e.mark.tooltip)&&delete e.mark.tooltip}e.params&&(e.signals=(e.signals||[]).concat(LR(e.params)),delete e.params);for(const t of vpe){for(const i of BN)delete e[t][i];const n=_pe[t];if(n)for(const i of n)delete e[t][i];Cpe(e,t)}for(const t of ipe())delete e[t];Epe(e);for(const t in e)re(e[t])&&ht(e[t])&&delete e[t];return ht(e)?void 0:e}function Epe(e){const{titleMarkConfig:t,subtitleMarkConfig:n,subtitle:i}=GM(e.title);ht(t)||(e.style["group-title"]={...e.style["group-title"],...t}),ht(n)||(e.style["group-subtitle"]={...e.style["group-subtitle"],...n}),ht(i)?delete e.title:e.title=i}function Cpe(e,t,n,i){const r=e[t];t==="view"&&(n="cell");const s={...r,...e.style[n??t]};ht(s)||(e.style[n??t]=s),delete e[t]}function im(e){return X(e,"layer")}function kpe(e){return X(e,"repeat")}function Ape(e){return!W(e.repeat)&&X(e.repeat,"layer")}class dw{map(t,n){return q1(t)?this.mapFacet(t,n):kpe(t)?this.mapRepeat(t,n):lw(t)?this.mapHConcat(t,n):tm(t)?this.mapVConcat(t,n):uw(t)?this.mapConcat(t,n):this.mapLayerOrUnit(t,n)}mapLayerOrUnit(t,n){if(im(t))return this.mapLayer(t,n);if(ao(t))return this.mapUnit(t,n);throw new Error(y_(t))}mapLayer(t,n){return{...t,layer:t.layer.map(i=>this.mapLayerOrUnit(i,n))}}mapHConcat(t,n){return{...t,hconcat:t.hconcat.map(i=>this.map(i,n))}}mapVConcat(t,n){return{...t,vconcat:t.vconcat.map(i=>this.map(i,n))}}mapConcat(t,n){const{concat:i,...r}=t;return{...r,concat:i.map(s=>this.map(s,n))}}mapFacet(t,n){return{...t,spec:this.map(t.spec,n)}}mapRepeat(t,n){return{...t,spec:this.map(t.spec,n)}}}const $pe={zero:1,center:1,normalize:1};function Spe(e){return ue($pe,e)}const Fpe=new Set([IN,I1,L1,B1,z1,P_,z_,P1,PN,I_]),Dpe=new Set([I1,L1,IN]);function Ec(e){return J(e)&&bc(e)==="quantitative"&&!e.bin}function HR(e,t,{orient:n,type:i}){const r=t==="x"?"y":"radius",s=t==="x"&&["bar","area"].includes(i),o=e[t],a=e[r];if(J(o)&&J(a))if(Ec(o)&&Ec(a)){if(o.stack)return t;if(a.stack)return r;const u=J(o)&&!!o.aggregate,l=J(a)&&!!a.aggregate;if(u!==l)return u?t:r;if(s){if(n==="vertical")return r;if(n==="horizontal")return t}}else{if(Ec(o))return t;if(Ec(a))return r}else{if(Ec(o))return s&&n==="vertical"?void 0:t;if(Ec(a))return s&&n==="horizontal"?void 0:r}}function Tpe(e){switch(e){case"x":return"y";case"y":return"x";case"theta":return"radius";case"radius":return"theta"}}function GR(e,t){var g,m;const n=xs(e)?e:{type:e},i=n.type;if(!Fpe.has(i))return null;const r=HR(t,"x",n)||HR(t,"theta",n);if(!r)return null;const s=t[r],o=J(s)?te(s,{}):void 0,a=Tpe(r),u=[],l=new Set;if(t[a]){const y=t[a],b=J(y)?te(y,{}):void 0;b&&b!==o&&(u.push(a),l.add(b))}const c=a==="x"?"xOffset":"yOffset",f=t[c],d=J(f)?te(f,{}):void 0;d&&d!==o&&(u.push(c),l.add(d));const h=Gfe.reduce((y,b)=>{if(b!=="tooltip"&&$u(t,b)){const v=t[b];for(const x of oe(v)){const _=Rr(x);if(_.aggregate)continue;const E=te(_,{});(!E||!l.has(E))&&y.push({channel:b,fieldDef:_})}}return y},[]);let p;return s.stack!==void 0?xo(s.stack)?p=s.stack?"zero":null:p=s.stack:Dpe.has(i)&&(p="zero"),!p||!Spe(p)||bR(t)&&h.length===0?null:((g=s==null?void 0:s.scale)!=null&&g.type&&((m=s==null?void 0:s.scale)==null?void 0:m.type)!==bn.LINEAR&&s!=null&&s.stack&&Z(che(s.scale.type)),Me(t[gs(r)])?(s.stack!==void 0&&Z(lhe(r)),null):(J(s)&&s.aggregate&&!sde.has(s.aggregate)&&Z(fhe(s.aggregate)),{groupbyChannels:u,groupbyFields:l,fieldChannel:r,impute:s.impute===null?!1:ha(i),stackBy:h,offset:p}))}function VR(e,t,n){const i=yn(e),r=gt("orient",i,n);if(i.orient=Ope(i.type,t,r),r!==void 0&&r!==i.orient&&Z(Vde(i.orient,r)),i.type==="bar"&&i.orient){const u=gt("cornerRadiusEnd",i,n);if(u!==void 0){const l=i.orient==="horizontal"&&t.x2||i.orient==="vertical"&&t.y2?["cornerRadius"]:f0e[i.orient];for(const c of l)i[c]=u;i.cornerRadiusEnd!==void 0&&delete i.cornerRadiusEnd}}const s=gt("opacity",i,n),o=gt("fillOpacity",i,n);return s===void 0&&o===void 0&&(i.opacity=Npe(i.type,t)),gt("cursor",i,n)===void 0&&(i.cursor=Mpe(i,t,n)),i}function Mpe(e,t,n){return t.href||e.href||gt("href",e,n)?"pointer":e.cursor}function Npe(e,t){if(qe([z1,I_,P_,z_],e)&&!bR(t))return .7}function Rpe(e,t,{graticule:n}){if(n)return!1;const i=ys("filled",e,t),r=e.type;return zt(i,r!==z1&&r!==P1&&r!==B1)}function Ope(e,t,n){switch(e){case z1:case P_:case z_:case PN:case i0e:case n0e:return}const{x:i,y:r,x2:s,y2:o}=t;switch(e){case I1:if(J(i)&&(mn(i.bin)||J(r)&&r.aggregate&&!i.aggregate))return"vertical";if(J(r)&&(mn(r.bin)||J(i)&&i.aggregate&&!r.aggregate))return"horizontal";if(o||s){if(n)return n;if(!s)return(J(i)&&i.type===wu&&!_t(i.bin)||H1(i))&&J(r)&&mn(r.bin)?"horizontal":"vertical";if(!o)return(J(r)&&r.type===wu&&!_t(r.bin)||H1(r))&&J(i)&&mn(i.bin)?"vertical":"horizontal"}case B1:if(s&&!(J(i)&&mn(i.bin))&&o&&!(J(r)&&mn(r.bin)))return;case L1:if(o)return J(r)&&mn(r.bin)?"horizontal":"vertical";if(s)return J(i)&&mn(i.bin)?"vertical":"horizontal";if(e===B1){if(i&&!r)return"vertical";if(r&&!i)return"horizontal"}case P1:case I_:{const a=iR(i),u=iR(r);if(n)return n;if(a&&!u)return e!=="tick"?"horizontal":"vertical";if(!a&&u)return e!=="tick"?"vertical":"horizontal";if(a&&u)return"vertical";{const l=ei(i)&&i.type===gc,c=ei(r)&&r.type===gc;if(l&&!c)return"vertical";if(!l&&c)return"horizontal"}return}}return"vertical"}function Lpe(e){const{point:t,line:n,...i}=e;return Y(i).length>1?i:i.type}function Ipe(e){for(const t of["line","area","rule","trail"])e[t]&&(e={...e,[t]:hi(e[t],["point","line"])});return e}function hw(e,t={},n){return e.point==="transparent"?{opacity:0}:e.point?re(e.point)?e.point:{}:e.point!==void 0?null:t.point||n.shape?re(t.point)?t.point:{}:void 0}function YR(e,t={}){return e.line?e.line===!0?{}:e.line:e.line!==void 0?null:t.line?t.line===!0?{}:t.line:void 0}class Ppe{constructor(){this.name="path-overlay"}hasMatchingType(t,n){if(ao(t)){const{mark:i,encoding:r}=t,s=xs(i)?i:{type:i};switch(s.type){case"line":case"rule":case"trail":return!!hw(s,n[s.type],r);case"area":return!!hw(s,n[s.type],r)||!!YR(s,n[s.type])}}return!1}run(t,n,i){const{config:r}=n,{params:s,projection:o,mark:a,name:u,encoding:l,...c}=t,f=J1(l,r),d=xs(a)?a:{type:a},h=hw(d,r[d.type],f),p=d.type==="area"&&YR(d,r[d.type]),g=[{name:u,...s?{params:s}:{},mark:Lpe({...d.type==="area"&&d.opacity===void 0&&d.fillOpacity===void 0?{opacity:.7}:{},...d}),encoding:hi(f,["shape"])}],m=GR(VR(d,f,r),f);let y=f;if(m){const{fieldChannel:b,offset:v}=m;y={...f,[b]:{...f[b],...v?{stack:v}:{}}}}return y=hi(y,["y2","x2"]),p&&g.push({...o?{projection:o}:{},mark:{type:"line",...uc(d,["clip","interpolate","tension","tooltip"]),...p},encoding:y}),h&&g.push({...o?{projection:o}:{},mark:{type:"point",opacity:1,filled:!0,...uc(d,["clip","tooltip"]),...h},encoding:y}),i({...c,layer:g},{...n,config:Ipe(r)})}}function zpe(e,t){return t?ih(e)?JR(e,t):XR(e,t):e}function pw(e,t){return t?JR(e,t):e}function gw(e,t,n){const i=t[e];if(k0e(i)){if(i.repeat in n)return{...t,[e]:n[i.repeat]};Z(Cde(i.repeat));return}return t}function XR(e,t){if(e=gw("field",e,t),e!==void 0){if(e===null)return null;if(Y_(e)&&oo(e.sort)){const n=gw("field",e.sort,t);e={...e,...n?{sort:n}:{}}}return e}}function KR(e,t){if(J(e))return XR(e,t);{const n=gw("datum",e,t);return n!==e&&!n.type&&(n.type="nominal"),n}}function ZR(e,t){if(Me(e)){const n=KR(e,t);if(n)return n;if(rh(e))return{condition:e.condition}}else{if(sh(e)){const n=KR(e.condition,t);if(n)return{...e,condition:n};{const{condition:i,...r}=e;return r}}return e}}function JR(e,t){const n={};for(const i in e)if(X(e,i)){const r=e[i];if(W(r))n[i]=r.map(s=>ZR(s,t)).filter(s=>s);else{const s=ZR(r,t);s!==void 0&&(n[i]=s)}}return n}class Bpe{constructor(){this.name="RuleForRangedLine"}hasMatchingType(t){if(ao(t)){const{encoding:n,mark:i}=t;if(i==="line"||xs(i)&&i.type==="line")for(const r of qfe){const s=gu(r),o=n[s];if(n[r]&&(J(o)&&!mn(o.bin)||_s(o)))return!0}}return!1}run(t,n,i){const{encoding:r,mark:s}=t;return Z(Gde(!!r.x2,!!r.y2)),i({...t,mark:re(s)?{...s,type:"rule"}:"rule"},n)}}class Upe extends dw{constructor(){super(...arguments),this.nonFacetUnitNormalizers=[G0e,X0e,npe,new Ppe,new Bpe]}map(t,n){if(ao(t)){const i=$u(t.encoding,Zs),r=$u(t.encoding,Js),s=$u(t.encoding,C1);if(i||r||s)return this.mapFacetedUnit(t,n)}return super.map(t,n)}mapUnit(t,n){const{parentEncoding:i,parentProjection:r}=n,s=pw(t.encoding,n.repeater),o={...t,...t.name?{name:[n.repeaterPrefix,t.name].filter(u=>u).join("_")}:{},...s?{encoding:s}:{}};if(i||r)return this.mapUnitWithParentEncodingOrProjection(o,n);const a=this.mapLayerOrUnit.bind(this);for(const u of this.nonFacetUnitNormalizers)if(u.hasMatchingType(o,n.config))return u.run(o,n,a);return o}mapRepeat(t,n){return Ape(t)?this.mapLayerRepeat(t,n):this.mapNonLayerRepeat(t,n)}mapLayerRepeat(t,n){const{repeat:i,spec:r,...s}=t,{row:o,column:a,layer:u}=i,{repeater:l={},repeaterPrefix:c=""}=n;return o||a?this.mapRepeat({...t,repeat:{...o?{row:o}:{},...a?{column:a}:{}},spec:{repeat:{layer:u},spec:r}},n):{...s,layer:u.map(f=>{const d={...l,layer:f},h=`${(r.name?`${r.name}_`:"")+c}child__layer_${At(f)}`,p=this.mapLayerOrUnit(r,{...n,repeater:d,repeaterPrefix:h});return p.name=h,p})}}mapNonLayerRepeat(t,n){const{repeat:i,spec:r,data:s,...o}=t;!W(i)&&t.columns&&(t=hi(t,["columns"]),Z(aN("repeat")));const a=[],{repeater:u={},repeaterPrefix:l=""}=n,c=!W(i)&&i.row||[u?u.row:null],f=!W(i)&&i.column||[u?u.column:null],d=W(i)&&i||[u?u.repeat:null];for(const p of d)for(const g of c)for(const m of f){const y={repeat:p,row:g,column:m,layer:u.layer},b=(r.name?`${r.name}_`:"")+l+"child__"+(W(i)?`${At(p)}`:(i.row?`row_${At(g)}`:"")+(i.column?`column_${At(m)}`:"")),v=this.map(r,{...n,repeater:y,repeaterPrefix:b});v.name=b,a.push(hi(v,["data"]))}const h=W(i)?t.columns:i.column?i.column.length:1;return{data:r.data??s,align:"all",...o,columns:h,concat:a}}mapFacet(t,n){const{facet:i}=t;return ih(i)&&t.columns&&(t=hi(t,["columns"]),Z(aN("facet"))),super.mapFacet(t,n)}mapUnitWithParentEncodingOrProjection(t,n){const{encoding:i,projection:r}=t,{parentEncoding:s,parentProjection:o,config:a}=n,u=eO({parentProjection:o,projection:r}),l=QR({parentEncoding:s,encoding:pw(i,n.repeater)});return this.mapUnit({...t,...u?{projection:u}:{},...l?{encoding:l}:{}},{config:a})}mapFacetedUnit(t,n){const{row:i,column:r,facet:s,...o}=t.encoding,{mark:a,width:u,projection:l,height:c,view:f,params:d,encoding:h,...p}=t,{facetMapping:g,layout:m}=this.getFacetMappingAndLayout({row:i,column:r,facet:s},n),y=pw(o,n.repeater);return this.mapFacet({...p,...m,facet:g,spec:{...u?{width:u}:{},...c?{height:c}:{},...f?{view:f}:{},...l?{projection:l}:{},mark:a,encoding:y,...d?{params:d}:{}}},n)}getFacetMappingAndLayout(t,n){const{row:i,column:r,facet:s}=t;if(i||r){s&&Z(Wde([...i?[Zs]:[],...r?[Js]:[]]));const o={},a={};for(const u of[Zs,Js]){const l=t[u];if(l){const{align:c,center:f,spacing:d,columns:h,...p}=l;o[u]=p;for(const g of["align","center","spacing"])l[g]!==void 0&&(a[g]??(a[g]={}),a[g][u]=l[g])}}return{facetMapping:o,layout:a}}else{const{align:o,center:a,spacing:u,columns:l,...c}=s;return{facetMapping:zpe(c,n.repeater),layout:{...o?{align:o}:{},...a?{center:a}:{},...u?{spacing:u}:{},...l?{columns:l}:{}}}}}mapLayer(t,{parentEncoding:n,parentProjection:i,...r}){const{encoding:s,projection:o,...a}=t,u={...r,parentEncoding:QR({parentEncoding:n,encoding:s,layer:!0}),parentProjection:eO({parentProjection:i,projection:o})};return super.mapLayer({...a,...t.name?{name:[u.repeaterPrefix,t.name].filter(l=>l).join("_")}:{}},u)}}function QR({parentEncoding:e,encoding:t={},layer:n}){let i={};if(e){const r=new Set([...Y(e),...Y(t)]);for(const s of r){const o=t[s],a=e[s];if(Me(o)){const u={...a,...o};i[s]=u}else sh(o)?i[s]={...o,condition:{...a,...o.condition}}:o||o===null?i[s]=o:(n||Nr(a)||me(a)||Me(a)||W(a))&&(i[s]=a)}}else i=t;return!i||ht(i)?void 0:i}function eO(e){const{parentProjection:t,projection:n}=e;return t&&n&&Z(Nde({parentProjection:t,projection:n})),n??t}function mw(e){return X(e,"filter")}function jpe(e){return X(e,"stop")}function tO(e){return X(e,"lookup")}function qpe(e){return X(e,"data")}function Wpe(e){return X(e,"param")}function Hpe(e){return X(e,"pivot")}function Gpe(e){return X(e,"density")}function Vpe(e){return X(e,"quantile")}function Ype(e){return X(e,"regression")}function Xpe(e){return X(e,"loess")}function Kpe(e){return X(e,"sample")}function Zpe(e){return X(e,"window")}function Jpe(e){return X(e,"joinaggregate")}function Qpe(e){return X(e,"flatten")}function ege(e){return X(e,"calculate")}function nO(e){return X(e,"bin")}function tge(e){return X(e,"impute")}function nge(e){return X(e,"timeUnit")}function ige(e){return X(e,"aggregate")}function rge(e){return X(e,"stack")}function sge(e){return X(e,"fold")}function oge(e){return X(e,"extent")&&!X(e,"density")&&!X(e,"regression")}function age(e){return e.map(t=>mw(t)?{filter:ac(t.filter,zhe)}:t)}class uge extends dw{map(t,n){return n.emptySelections??(n.emptySelections={}),n.selectionPredicates??(n.selectionPredicates={}),t=iO(t,n),super.map(t,n)}mapLayerOrUnit(t,n){if(t=iO(t,n),t.encoding){const i={};for(const[r,s]of ia(t.encoding))i[r]=rO(s,n);t={...t,encoding:i}}return super.mapLayerOrUnit(t,n)}mapUnit(t,n){const{selection:i,...r}=t;return i?{...r,params:ia(i).map(([s,o])=>{const{init:a,bind:u,empty:l,...c}=o;c.type==="single"?(c.type="point",c.toggle=!1):c.type==="multi"&&(c.type="point"),n.emptySelections[s]=l!=="none";for(const f of gn(n.selectionPredicates[s]??{}))f.empty=l!=="none";return{name:s,value:a,select:c,bind:u}})}:t}}function iO(e,t){const{transform:n,...i}=e;if(n){const r=n.map(s=>{if(mw(s))return{filter:yw(s,t)};if(nO(s)&&mu(s.bin))return{...s,bin:sO(s.bin)};if(tO(s)){const{selection:o,...a}=s.from;return o?{...s,from:{param:o,...a}}:s}return s});return{...i,transform:r}}return e}function rO(e,t){var i,r;const n=De(e);if(J(n)&&mu(n.bin)&&(n.bin=sO(n.bin)),Au(n)&&((r=(i=n.scale)==null?void 0:i.domain)!=null&&r.selection)){const{selection:s,...o}=n.scale.domain;n.scale.domain={...o,...s?{param:s}:{}}}if(rh(n))if(W(n.condition))n.condition=n.condition.map(s=>{const{selection:o,param:a,test:u,...l}=s;return a?s:{...l,test:yw(s,t)}});else{const{selection:s,param:o,test:a,...u}=rO(n.condition,t);n.condition=o?n.condition:{...u,test:yw(n.condition,t)}}return n}function sO(e){const t=e.extent;if(t!=null&&t.selection){const{selection:n,...i}=t;return{...e,extent:{...i,param:n}}}return e}function yw(e,t){const n=i=>ac(i,r=>{var s;const o=t.emptySelections[r]??!0,a={param:r,empty:o};return(s=t.selectionPredicates)[r]??(s[r]=[]),t.selectionPredicates[r].push(a),a});return e.selection?n(e.selection):ac(e.test||e.filter,i=>i.selection?n(i.selection):i)}class bw extends dw{map(t,n){const i=n.selections??[];if(t.params&&!ao(t)){const r=[];for(const s of t.params)aw(s)?i.push(s):r.push(s);t.params=r}return n.selections=i,super.map(t,n)}mapUnit(t,n){const i=n.selections;if(!i||!i.length)return t;const r=(n.path??[]).concat(t.name),s=[];for(const o of i)if(!o.views||!o.views.length)s.push(o);else for(const a of o.views)(se(a)&&(a===t.name||r.includes(a))||W(a)&&a.map(u=>r.indexOf(u)).every((u,l,c)=>u!==-1&&(l===0||u>c[l-1])))&&s.push(o);return s.length&&(t.params=s),t}}for(const e of["mapFacet","mapRepeat","mapHConcat","mapVConcat","mapLayer"]){const t=bw.prototype[e];bw.prototype[e]=function(n,i){return t.call(this,n,lge(n,i))}}function lge(e,t){return e.name?{...t,path:(t.path??[]).concat(e.name)}:t}function oO(e,t){t===void 0&&(t=WR(e.config));const n=hge(e,t),{width:i,height:r}=e,s=pge(n,{width:i,height:r,autosize:e.autosize},t);return{...n,...s?{autosize:s}:{}}}const cge=new Upe,fge=new uge,dge=new bw;function hge(e,t={}){const n={config:t};return dge.map(cge.map(fge.map(e,n),n),n)}function aO(e){return se(e)?{type:e}:e??{}}function pge(e,t,n){let{width:i,height:r}=t;const s=ao(e)||im(e),o={};s?i=="container"&&r=="container"?(o.type="fit",o.contains="padding"):i=="container"?(o.type="fit-x",o.contains="padding"):r=="container"&&(o.type="fit-y",o.contains="padding"):(i=="container"&&(Z(nN("width")),i=void 0),r=="container"&&(Z(nN("height")),r=void 0));const a={type:"pad",...o,...n?aO(n.autosize):{},...aO(e.autosize)};if(a.type==="fit"&&!s&&(Z(hde),a.type="pad"),i=="container"&&!(a.type=="fit"||a.type=="fit-x")&&Z(iN("width")),r=="container"&&!(a.type=="fit"||a.type=="fit-y")&&Z(iN("height")),!Mi(a,{type:"pad"}))return a}function gge(e){return["fit","fit-x","fit-y"].includes(e)}function mge(e){return e?`fit-${F1(e)}`:"fit"}const yge=["background","padding"];function uO(e,t){const n={};for(const i of yge)e&&e[i]!==void 0&&(n[i]=Ni(e[i]));return t&&(n.params=e.params),n}class lo{constructor(t={},n={}){this.explicit=t,this.implicit=n}clone(){return new lo(De(this.explicit),De(this.implicit))}combine(){return{...this.explicit,...this.implicit}}get(t){return zt(this.explicit[t],this.implicit[t])}getWithExplicit(t){return this.explicit[t]!==void 0?{explicit:!0,value:this.explicit[t]}:this.implicit[t]!==void 0?{explicit:!1,value:this.implicit[t]}:{explicit:!1,value:void 0}}setWithExplicit(t,{value:n,explicit:i}){n!==void 0&&this.set(t,n,i)}set(t,n,i){return delete this[i?"implicit":"explicit"][t],this[i?"explicit":"implicit"][t]=n,this}copyKeyFromSplit(t,{explicit:n,implicit:i}){n[t]!==void 0?this.set(t,n[t],!0):i[t]!==void 0&&this.set(t,i[t],!1)}copyKeyFromObject(t,n){n[t]!==void 0&&this.set(t,n[t],!0)}copyAll(t){for(const n of Y(t.combine())){const i=t.getWithExplicit(n);this.setWithExplicit(n,i)}}}function Es(e){return{explicit:!0,value:e}}function Ri(e){return{explicit:!1,value:e}}function lO(e){return(t,n,i,r)=>{const s=e(t.value,n.value);return s>0?t:s<0?n:rm(t,n,i,r)}}function rm(e,t,n,i){return e.explicit&&t.explicit&&Z(nhe(n,i,e.value,t.value)),e}function ma(e,t,n,i,r=rm){return e===void 0||e.value===void 0?t:e.explicit&&!t.explicit?e:t.explicit&&!e.explicit?t:Mi(e.value,t.value)?e:r(e,t,n,i)}class bge extends lo{constructor(t={},n={},i=!1){super(t,n),this.explicit=t,this.implicit=n,this.parseNothing=i}clone(){const t=super.clone();return t.parseNothing=this.parseNothing,t}}function Cc(e){return X(e,"url")}function uh(e){return X(e,"values")}function cO(e){return X(e,"name")&&!Cc(e)&&!uh(e)&&!ya(e)}function ya(e){return e&&(fO(e)||dO(e)||vw(e))}function fO(e){return X(e,"sequence")}function dO(e){return X(e,"sphere")}function vw(e){return X(e,"graticule")}var Ft;(function(e){e[e.Raw=0]="Raw",e[e.Main=1]="Main",e[e.Row=2]="Row",e[e.Column=3]="Column",e[e.Lookup=4]="Lookup",e[e.PreFilterInvalid=5]="PreFilterInvalid",e[e.PostFilterInvalid=6]="PostFilterInvalid"})(Ft||(Ft={}));function hO({invalid:e,isPath:t}){switch(jN(e,{isPath:t})){case"filter":return{marks:"exclude-invalid-values",scales:"exclude-invalid-values"};case"break-paths-show-domains":return{marks:t?"include-invalid-values":"exclude-invalid-values",scales:"include-invalid-values"};case"break-paths-filter-domains":return{marks:t?"include-invalid-values":"exclude-invalid-values",scales:"exclude-invalid-values"};case"show":return{marks:"include-invalid-values",scales:"include-invalid-values"}}}function vge(e){const{marks:t,scales:n}=hO(e);return t===n?Ft.Main:n==="include-invalid-values"?Ft.PreFilterInvalid:Ft.PostFilterInvalid}class ut{constructor(t,n){this.debugName=n,this._children=[],this._parent=null,t&&(this.parent=t)}clone(){throw new Error("Cannot clone node")}get parent(){return this._parent}set parent(t){this._parent=t,t&&t.addChild(this)}get children(){return this._children}numChildren(){return this._children.length}addChild(t,n){if(this._children.includes(t)){Z(Dde);return}n!==void 0?this._children.splice(n,0,t):this._children.push(t)}removeChild(t){const n=this._children.indexOf(t);return this._children.splice(n,1),n}remove(){let t=this._parent.removeChild(this);for(const n of this._children)n._parent=this._parent,this._parent.addChild(n,t++)}insertAsParentOf(t){const n=t.parent;n.removeChild(this),this.parent=n,t.parent=this}swapWithParent(){const t=this._parent,n=t.parent;for(const r of this._children)r.parent=t;this._children=[],t.removeChild(this);const i=t.parent.removeChild(t);this._parent=n,n.addChild(this,i),t.parent=this}}class yi extends ut{clone(){const t=new this.constructor;return t.debugName=`clone_${this.debugName}`,t._source=this._source,t._name=`clone_${this._name}`,t.type=this.type,t.refCounts=this.refCounts,t.refCounts[t._name]=0,t}constructor(t,n,i,r){super(t,n),this.type=i,this.refCounts=r,this._source=this._name=n,this.refCounts&&!(this._name in this.refCounts)&&(this.refCounts[this._name]=0)}dependentFields(){return new Set}producedFields(){return new Set}hash(){return this._hash===void 0&&(this._hash=`Output ${SM()}`),this._hash}getSource(){return this.refCounts[this._name]++,this._source}isRequired(){return!!this.refCounts[this._name]}setSource(t){this._source=t}}function xw(e){return e.as!==void 0}function pO(e){return`${e}_end`}class Cs extends ut{clone(){return new Cs(null,De(this.timeUnits))}constructor(t,n){super(t),this.timeUnits=n}static makeFromEncoding(t,n){const i=n.reduceFieldDef((r,s,o)=>{const{field:a,timeUnit:u}=s;if(u){let l;if(_u(u)){if(Dt(n)){const{mark:c,markDef:f,config:d}=n,h=pa({fieldDef:s,markDef:f,config:d});(eh(c)||h)&&(l={timeUnit:sn(u),field:a})}}else l={as:te(s,{forAs:!0}),field:a,timeUnit:u};if(Dt(n)){const{mark:c,markDef:f,config:d}=n,h=pa({fieldDef:s,markDef:f,config:d});eh(c)&&Bt(o)&&h!==.5&&(l.rectBandPosition=h)}l&&(r[We(l)]=l)}return r},{});return ht(i)?null:new Cs(t,i)}static makeFromTransform(t,n){const{timeUnit:i,...r}={...n},s=sn(i),o={...r,timeUnit:s};return new Cs(t,{[We(o)]:o})}merge(t){this.timeUnits={...this.timeUnits};for(const n in t.timeUnits)this.timeUnits[n]||(this.timeUnits[n]=t.timeUnits[n]);for(const n of t.children)t.removeChild(n),n.parent=this;t.remove()}removeFormulas(t){const n={};for(const[i,r]of ia(this.timeUnits)){const s=xw(r)?r.as:`${r.field}_end`;t.has(s)||(n[i]=r)}this.timeUnits=n}producedFields(){return new Set(gn(this.timeUnits).map(t=>xw(t)?t.as:pO(t.field)))}dependentFields(){return new Set(gn(this.timeUnits).map(t=>t.field))}hash(){return`TimeUnit ${We(this.timeUnits)}`}assemble(){const t=[];for(const n of gn(this.timeUnits)){const{rectBandPosition:i}=n,r=sn(n.timeUnit);if(xw(n)){const{field:s,as:o}=n,{unit:a,utc:u,...l}=r,c=[o,`${o}_end`];t.push({field:er(s),type:"timeunit",...a?{units:R1(a)}:{},...u?{timezone:"utc"}:{},...l,as:c}),t.push(...mO(c,i,r))}else if(n){const{field:s}=n,o=s.replaceAll("\\.","."),a=gO({timeUnit:r,field:o}),u=pO(o);t.push({type:"formula",expr:a,as:u}),t.push(...mO([o,u],i,r))}}return t}}const sm="offsetted_rect_start",om="offsetted_rect_end";function gO({timeUnit:e,field:t,reverse:n}){const{unit:i,utc:r}=e,s=wN(i),{part:o,step:a}=AN(s,e.step);return`${r?"utcOffset":"timeOffset"}('${o}', ${at(t)}, ${n?-a:a})`}function mO([e,t],n,i){if(n!==void 0&&n!==.5){const r=at(e),s=at(t);return[{type:"formula",expr:yO([gO({timeUnit:i,field:e,reverse:!0}),r],n+.5),as:`${e}_${sm}`},{type:"formula",expr:yO([r,s],n+.5),as:`${e}_${om}`}]}return[]}function yO([e,t],n){return`${1-n} * ${e} + ${n} * ${t}`}const lh="_tuple_fields";class xge{constructor(...t){this.items=t,this.hasChannel={},this.hasField={},this.hasSelectionId=!1}}const _ge={defined:()=>!0,parse:(e,t,n)=>{const i=t.name,r=t.project??(t.project=new xge),s={},o={},a=new Set,u=(p,g)=>{const m=g==="visual"?p.channel:p.field;let y=At(`${i}_${m}`);for(let b=1;a.has(y);b++)y=At(`${i}_${m}_${b}`);return a.add(y),{[g]:y}},l=t.type,c=e.config.selection[l],f=n.value!==void 0?oe(n.value):null;let{fields:d,encodings:h}=re(n.select)?n.select:{};if(!d&&!h&&f){for(const p of f)if(re(p))for(const g of Y(p))jfe(g)?(h||(h=[])).push(g):l==="interval"?(Z(Ede),h=c.encodings):(d??(d=[])).push(g)}!d&&!h&&(h=c.encodings,"fields"in c&&(d=c.fields));for(const p of h??[]){const g=e.fieldDef(p);if(g){let m=g.field;if(g.aggregate){Z(pde(p,g.aggregate));continue}else if(!m){Z(sN(p));continue}if(g.timeUnit&&!_u(g.timeUnit)){m=e.vgField(p);const y={timeUnit:g.timeUnit,as:m,field:g.field};o[We(y)]=y}if(!s[m]){const y=l==="interval"&&ms(p)&&Tr(e.getScaleComponent(p).get("type"))?"R":g.bin?"R-RE":"E",b={field:m,channel:p,type:y,index:r.items.length};b.signals={...u(b,"data"),...u(b,"visual")},r.items.push(s[m]=b),r.hasField[m]=s[m],r.hasSelectionId=r.hasSelectionId||m===Or,OM(p)?(b.geoChannel=p,b.channel=RM(p),r.hasChannel[b.channel]=s[m]):r.hasChannel[p]=s[m]}}else Z(sN(p))}for(const p of d??[]){if(r.hasField[p])continue;const g={type:"E",field:p,index:r.items.length};g.signals={...u(g,"data")},r.items.push(g),r.hasField[p]=g,r.hasSelectionId=r.hasSelectionId||p===Or}f&&(t.init=f.map(p=>r.items.map(g=>re(p)?p[g.geoChannel||g.channel]!==void 0?p[g.geoChannel||g.channel]:p[g.field]:p))),ht(o)||(r.timeUnit=new Cs(null,o))},signals:(e,t,n)=>{const i=t.name+lh;return n.filter(s=>s.name===i).length>0||t.project.hasSelectionId?n:n.concat({name:i,value:t.project.items.map(_O)})}},bO="_curr",am="anim_value",kc="anim_clock",_w="eased_anim_clock",vO="min_extent",xO="max_range_extent",ww="last_tick_at",Ew="is_playing",wge=1/60*1e3,Ege=(e,t)=>[{name:_w,update:kc},{name:`${e}_domain`,init:`domain('${t}')`},{name:vO,init:`extent(${e}_domain)[0]`},{name:xO,init:`extent(range('${t}'))[1]`},{name:am,update:`invert('${t}', ${_w})`}],Cge={defined:e=>e.type==="point",topLevelSignals:(e,t,n)=>(ks(t)&&(n=n.concat([{name:kc,init:"0",on:[{events:{type:"timer",throttle:wge},update:`${Ew} ? (${kc} + (now() - ${ww}) > ${xO} ? 0 : ${kc} + (now() - ${ww})) : ${kc}`}]},{name:ww,init:"now()",on:[{events:[{signal:kc},{signal:Ew}],update:"now()"}]},{name:Ew,init:"true"}])),n),signals:(e,t,n)=>{const i=t.name,r=i+lh,s=t.project,o="(item().isVoronoi ? datum.datum : datum)",a=gn(e.component.selection??{}).reduce((c,f)=>f.type==="interval"?c.concat(f.name+Ac):c,[]).map(c=>`indexof(item().mark.name, '${c}') < 0`).join(" && "),u=`datum && item().mark.marktype !== 'group' && indexof(item().mark.role, 'legend') < 0${a?` && ${a}`:""}`;let l=`unit: ${Du(e)}, `;if(t.project.hasSelectionId)l+=`${Or}: ${o}[${Q(Or)}]`;else if(ks(t))l+=`fields: ${r}, values: [${am} ? ${am} : ${vO}]`;else{const c=s.items.map(f=>{const d=e.fieldDef(f.channel);return d!=null&&d.bin?`[${o}[${Q(e.vgField(f.channel,{}))}], ${o}[${Q(e.vgField(f.channel,{binSuffix:"end"}))}]]`:`${o}[${Q(f.field)}]`}).join(", ");l+=`fields: ${r}, values: [${c}]`}if(ks(t))return n.concat(Ege(t.name,e.scaleName(sa)),[{name:i+ho,on:[{events:[{signal:_w},{signal:am}],update:`{${l}}`,force:!0}]}]);{const c=t.events;return n.concat([{name:i+ho,on:c?[{events:c,update:`${u} ? {${l}} : null`,force:!0}]:[]}])}}};function _O(e){const{signals:t,hasLegend:n,index:i,...r}=e;return r.field=er(r.field),r}function Su(e,t=!0,n=En){if(W(e)){const i=e.map(r=>Su(r,t,n));return t?`[${i.join(", ")}]`:i}else if(vu(e))return n(t?xu(e):$he(e));return t?n(pt(e)):e}function kge(e,t){for(const n of gn(e.component.selection??{})){const i=n.name;let r=`${i}${ho}, ${n.resolve==="global"?"true":`{unit: ${Du(e)}}`}`;for(const s of dm)s.defined(n)&&(s.signals&&(t=s.signals(e,n,t)),s.modifyExpr&&(r=s.modifyExpr(e,n,r)));t.push({name:i+Jge,on:[{events:{signal:n.name+ho},update:`modify(${Q(n.name+Fu)}, ${r})`}]})}return Cw(t)}function Age(e,t){if(e.component.selection&&Y(e.component.selection).length){const n=Q(e.getName("cell"));t.unshift({name:"facet",value:{},on:[{events:ta("pointermove","scope"),update:`isTuple(facet) ? facet : group(${n}).datum`}]})}return Cw(t)}function $ge(e,t){let n=!1;for(const i of gn(e.component.selection??{})){const r=i.name,s=Q(r+Fu);if(t.filter(a=>a.name===r).length===0){const a=i.resolve==="global"?"union":i.resolve,u=i.type==="point"?", true, true)":")";t.push({name:i.name,update:`${GO}(${s}, ${Q(a)}${u}`})}n=!0;for(const a of dm)a.defined(i)&&a.topLevelSignals&&(t=a.topLevelSignals(e,i,t))}return n&&t.filter(r=>r.name==="unit").length===0&&t.unshift({name:"unit",value:{},on:[{events:"pointermove",update:"isTuple(group()) ? group() : unit"}]}),Cw(t)}function Sge(e,t){const n=[],i=[],r=Du(e,{escape:!1});for(const s of gn(e.component.selection??{})){const o={name:s.name+Fu};if(s.project.hasSelectionId&&(o.transform=[{type:"collect",sort:{field:Or}}]),s.init){const u=s.project.items.map(_O);o.values=s.project.hasSelectionId?s.init.map(l=>({unit:r,[Or]:Su(l,!1)[0]})):s.init.map(l=>({unit:r,fields:u,values:Su(l,!1)}))}if([...n,...t].filter(u=>u.name===s.name+Fu).length||n.push(o),ks(s)&&t.length){const u=e.lookupDataSource(e.getDataName(Ft.Main)),l=t.find(f=>f.name===u),c=l.transform.find(f=>f.type==="filter"&&f.expr.includes("vlSelectionTest"));if(c){l.transform=l.transform.filter(d=>d!==c);const f={name:l.name+bO,source:l.name,transform:[c]};i.push(f)}}}return n.concat(t,i)}function wO(e,t){for(const n of gn(e.component.selection??{}))for(const i of dm)i.defined(n)&&i.marks&&(t=i.marks(e,n,t));return t}function Fge(e,t){for(const n of e.children)Dt(n)&&(t=wO(n,t));return t}function Dge(e,t,n,i){const r=cL(e,t.param,t);return{signal:Tr(n.get("type"))&&W(i)&&i[0]>i[1]?`isValid(${r}) && reverse(${r})`:r}}function Cw(e){return e.map(t=>(t.on&&!t.on.length&&delete t.on,t))}const co={defined:e=>e.type==="interval"&&e.resolve==="global"&&e.bind&&e.bind==="scales",parse:(e,t)=>{const n=t.scales=[];for(const i of t.project.items){const r=i.channel;if(!ms(r))continue;const s=e.getScaleComponent(r),o=s?s.get("type"):void 0;if(o=="sequential"&&Z(bde),!s||!Tr(o)){Z(yde);continue}s.set("selectionExtent",{param:t.name,field:i.field},!0),n.push(i)}},topLevelSignals:(e,t,n)=>{const i=t.scales.filter(o=>n.filter(a=>a.name===o.signals.data).length===0);if(!e.parent||Aw(e)||i.length===0)return n;const r=n.find(o=>o.name===t.name);let s=r.update;if(s.includes(GO))r.update=`{${i.map(o=>`${Q(er(o.field))}: ${o.signals.data}`).join(", ")}}`;else{for(const o of i){const a=`${Q(er(o.field))}: ${o.signals.data}`;s.includes(a)||(s=`${s.substring(0,s.length-1)}, ${a}}`)}r.update=s}return n.concat(i.map(o=>({name:o.signals.data})))},signals:(e,t,n)=>{if(e.parent&&!Aw(e))for(const i of t.scales){const r=n.find(s=>s.name===i.signals.data);r.push="outer",delete r.value,delete r.update}return n}};function kw(e,t){return`domain(${Q(e.scaleName(t))})`}function Aw(e){return e.parent&&Lc(e.parent)&&(!e.parent.parent||Aw(e.parent.parent))}const Ac="_brush",EO="_scale_trigger",ch="geo_interval_init_tick",CO="_init",Tge="_center",Mge={defined:e=>e.type==="interval",parse:(e,t,n)=>{var i;if(e.hasProjection){const r={...re(n.select)?n.select:{}};r.fields=[Or],r.encodings||(r.encodings=n.value?Y(n.value):[Sr,$r]),n.select={type:"interval",...r}}if(t.translate&&!co.defined(t)){const r=`!event.item || event.item.mark.name !== ${Q(t.name+Ac)}`;for(const s of t.events){if(!s.between){Z(`${s} is not an ordered event stream for interval selections.`);continue}const o=oe((i=s.between[0]).filter??(i.filter=[]));o.includes(r)||o.push(r)}}},signals:(e,t,n)=>{const i=t.name,r=i+ho,s=gn(t.project.hasChannel).filter(a=>a.channel===$t||a.channel===rn),o=t.init?t.init[0]:null;if(n.push(...s.reduce((a,u)=>a.concat(Nge(e,t,u,o&&o[u.index])),[])),e.hasProjection){const a=Q(e.projectionName()),u=e.projectionName()+Tge,{x:l,y:c}=t.project.hasChannel,f=l&&l.signals.visual,d=c&&c.signals.visual,h=l?o&&o[l.index]:`${u}[0]`,p=c?o&&o[c.index]:`${u}[1]`,g=_=>e.getSizeSignalRef(_).signal,m=`[[${f?f+"[0]":"0"}, ${d?d+"[0]":"0"}],[${f?f+"[1]":g("width")}, ${d?d+"[1]":g("height")}]]`;o&&(n.unshift({name:i+CO,init:`[scale(${a}, [${l?h[0]:h}, ${c?p[0]:p}]), scale(${a}, [${l?h[1]:h}, ${c?p[1]:p}])]`}),(!l||!c)&&(n.find(E=>E.name===u)||n.unshift({name:u,update:`invert(${a}, [${g("width")}/2, ${g("height")}/2])`})));const y=`intersect(${m}, {markname: ${Q(e.getName("marks"))}}, unit.mark)`,b=`{unit: ${Du(e)}}`,v=`vlSelectionTuples(${y}, ${b})`,x=s.map(_=>_.signals.visual);return n.concat({name:r,on:[{events:[...x.length?[{signal:x.join(" || ")}]:[],...o?[{signal:ch}]:[]],update:v}]})}else{if(!co.defined(t)){const l=i+EO,c=s.map(f=>{const d=f.channel,{data:h,visual:p}=f.signals,g=Q(e.scaleName(d)),m=e.getScaleComponent(d).get("type"),y=Tr(m)?"+":"";return`(!isArray(${h}) || (${y}invert(${g}, ${p})[0] === ${y}${h}[0] && ${y}invert(${g}, ${p})[1] === ${y}${h}[1]))`});c.length&&n.push({name:l,value:{},on:[{events:s.map(f=>({scale:e.scaleName(f.channel)})),update:c.join(" && ")+` ? ${l} : {}`}]})}const a=s.map(l=>l.signals.data),u=`unit: ${Du(e)}, fields: ${i+lh}, values`;return n.concat({name:r,...o?{init:`{${u}: ${Su(o)}}`}:{},...a.length?{on:[{events:[{signal:a.join(" || ")}],update:`${a.join(" && ")} ? {${u}: [${a}]} : null`}]}:{}})}},topLevelSignals:(e,t,n)=>(Dt(e)&&e.hasProjection&&t.init&&(n.filter(r=>r.name===ch).length||n.unshift({name:ch,value:null,on:[{events:"timer{1}",update:`${ch} === null ? {} : ${ch}`}]})),n),marks:(e,t,n)=>{const i=t.name,{x:r,y:s}=t.project.hasChannel,o=r==null?void 0:r.signals.visual,a=s==null?void 0:s.signals.visual,u=`data(${Q(t.name+Fu)})`;if(co.defined(t)||!r&&!s)return n;const l={x:r!==void 0?{signal:`${o}[0]`}:{value:0},y:s!==void 0?{signal:`${a}[0]`}:{value:0},x2:r!==void 0?{signal:`${o}[1]`}:{field:{group:"width"}},y2:s!==void 0?{signal:`${a}[1]`}:{field:{group:"height"}}};if(t.resolve==="global")for(const m of Y(l))l[m]=[{test:`${u}.length && ${u}[0].unit === ${Du(e)}`,...l[m]},{value:0}];const{fill:c,fillOpacity:f,cursor:d,...h}=t.mark,p=Y(h).reduce((m,y)=>(m[y]=[{test:[r!==void 0&&`${o}[0] !== ${o}[1]`,s!==void 0&&`${a}[0] !== ${a}[1]`].filter(b=>b).join(" && "),value:h[y]},{value:null}],m),{}),g=d??(t.translate?"move":null);return[{name:`${i+Ac}_bg`,type:"rect",clip:!0,encode:{enter:{fill:{value:c},fillOpacity:{value:f}},update:l}},...n,{name:i+Ac,type:"rect",clip:!0,encode:{enter:{...g?{cursor:{value:g}}:{},fill:{value:"transparent"}},update:{...l,...p}}}]}};function Nge(e,t,n,i){const r=!e.hasProjection,s=n.channel,o=n.signals.visual,a=Q(r?e.scaleName(s):e.projectionName()),u=d=>`scale(${a}, ${d})`,l=e.getSizeSignalRef(s===$t?"width":"height").signal,c=`${s}(unit)`,f=t.events.reduce((d,h)=>[...d,{events:h.between[0],update:`[${c}, ${c}]`},{events:h,update:`[${o}[0], clamp(${c}, 0, ${l})]`}],[]);if(r){const d=n.signals.data,h=co.defined(t),p=e.getScaleComponent(s),g=p?p.get("type"):void 0,m=i?{init:Su(i,!0,u)}:{value:[]};return f.push({events:{signal:t.name+EO},update:Tr(g)?`[${u(`${d}[0]`)}, ${u(`${d}[1]`)}]`:"[0, 0]"}),h?[{name:d,on:[]}]:[{name:o,...m,on:f},{name:d,...i?{init:Su(i)}:{},on:[{events:{signal:o},update:`${o}[0] === ${o}[1] ? null : invert(${a}, ${o})`}]}]}else{const d=s===$t?0:1,h=t.name+CO,p=i?{init:`[${h}[0][${d}], ${h}[1][${d}]]`}:{value:[]};return[{name:o,...p,on:f}]}}function $c({model:e,channelDef:t,vgChannel:n,invalidValueRef:i,mainRefFn:r}){const s=rh(t)&&t.condition;let o=[];s&&(o=oe(s).map(l=>{const c=r(l);if(C0e(l)){const{param:f,empty:d}=l;return{test:lL(e,{param:f,empty:d}),...c}}else return{test:vm(e,l.test),...c}})),i!==void 0&&o.push(i);const a=r(t);return a!==void 0&&o.push(a),o.length>1||o.length===1&&o[0].test?{[n]:o}:o.length===1?{[n]:o[0]}:{}}function $w(e,t="text"){const n=e.encoding[t];return $c({model:e,channelDef:n,vgChannel:t,mainRefFn:i=>um(i,e.config),invalidValueRef:void 0})}function um(e,t,n="datum"){if(e){if(Nr(e))return Et(e.value);if(Me(e)){const{format:i,formatType:r}=V1(e);return H_({fieldOrDatumDef:e,format:i,formatType:r,expr:n,config:t})}}}function kO(e,t={}){const{encoding:n,markDef:i,config:r,stack:s}=e,o=n.tooltip;if(W(o))return{tooltip:$O({tooltip:o},s,r,t)};{const a=t.reactiveGeom?"datum.datum":"datum";return $c({model:e,channelDef:o,vgChannel:"tooltip",mainRefFn:l=>{const c=um(l,r,a);if(c)return c;if(l===null)return;let f=gt("tooltip",i,r);if(f===!0&&(f={content:"encoding"}),se(f))return{value:f};if(re(f))return me(f)?f:f.content==="encoding"?$O(n,s,r,t):{signal:a}},invalidValueRef:void 0})}}function AO(e,t,n,{reactiveGeom:i}={}){const r={...n,...n.tooltipFormat},s=new Set,o=i?"datum.datum":"datum",a=[];function u(c,f){const d=gu(f),h=ei(c)?c:{...c,type:e[d].type},p=h.title||K_(h,r),g=oe(p).join(", ").replaceAll(/"/g,'\\"');let m;if(Bt(f)){const y=f==="x"?"x2":"y2",b=Rr(e[y]);if(mn(h.bin)&&b){const v=te(h,{expr:o}),x=te(b,{expr:o}),{format:_,formatType:E}=V1(h);m=nh(v,x,_,E,r),s.add(y)}}if((Bt(f)||f===tr||f===Ar)&&t&&t.fieldChannel===f&&t.offset==="normalize"){const{format:y,formatType:b}=V1(h);m=H_({fieldOrDatumDef:h,format:y,formatType:b,expr:o,config:r,normalizeStack:!0}).signal}m??(m=um(h,r,o).signal),a.push({channel:f,key:g,value:m})}J_(e,(c,f)=>{J(c)?u(c,f):W1(c)&&u(c.condition,f)});const l={};for(const{channel:c,key:f,value:d}of a)!s.has(c)&&!l[f]&&(l[f]=d);return l}function $O(e,t,n,{reactiveGeom:i}={}){const r=AO(e,t,n,{reactiveGeom:i}),s=ia(r).map(([o,a])=>`"${o}": ${a}`);return s.length>0?{signal:`{${s.join(", ")}}`}:void 0}function Rge(e){const{markDef:t,config:n}=e,i=gt("aria",t,n);return i===!1?{}:{...i?{aria:i}:{},...Oge(e),...Lge(e)}}function Oge(e){const{mark:t,markDef:n,config:i}=e;if(i.aria===!1)return{};const r=gt("ariaRoleDescription",n,i);return r!=null?{ariaRoleDescription:{value:r}}:ue(cde,t)?{}:{ariaRoleDescription:{value:t}}}function Lge(e){const{encoding:t,markDef:n,config:i,stack:r}=e,s=t.description;if(s)return $c({model:e,channelDef:s,vgChannel:"description",mainRefFn:u=>um(u,e.config),invalidValueRef:void 0});const o=gt("description",n,i);if(o!=null)return{description:Et(o)};if(i.aria===!1)return{};const a=AO(t,r,i);if(!ht(a))return{description:{signal:ia(a).map(([u,l],c)=>`"${c>0?"; ":""}${u}: " + (${l})`).join(" + ")}}}function vn(e,t,n={}){const{markDef:i,encoding:r,config:s}=t,{vgChannel:o}=n;let{defaultRef:a,defaultValue:u}=n;const l=r[e];a===void 0&&(u??(u=gt(e,i,s,{vgChannel:o,ignoreVgConfig:!rh(l)})),u!==void 0&&(a=Et(u)));const c={markDef:i,config:s,scaleName:t.scaleName(e),scale:t.getScaleComponent(e)},f=WN({...c,scaleChannel:e,channelDef:l});return $c({model:t,channelDef:l,vgChannel:o??e,invalidValueRef:f,mainRefFn:h=>W_({...c,channel:e,channelDef:h,stack:null,defaultRef:a})})}function SO(e,t={filled:void 0}){const{markDef:n,encoding:i,config:r}=e,{type:s}=n,o=t.filled??gt("filled",n,r),a=qe(["bar","point","circle","square","geoshape"],s)?"transparent":void 0,u=gt(o===!0?"color":void 0,n,r,{vgChannel:"fill"})??r.mark[o===!0&&"color"]??a,l=gt(o===!1?"color":void 0,n,r,{vgChannel:"stroke"})??r.mark[o===!1&&"color"],c=o?"fill":"stroke",f={...u?{fill:Et(u)}:{},...l?{stroke:Et(l)}:{}};return n.color&&(o?n.fill:n.stroke)&&Z(fN("property",{fill:"fill"in n,stroke:"stroke"in n})),{...f,...vn("color",e,{vgChannel:c,defaultValue:o?u:l}),...vn("fill",e,{defaultValue:i.fill?u:void 0}),...vn("stroke",e,{defaultValue:i.stroke?l:void 0})}}function Ige(e){const{encoding:t,mark:n}=e,i=t.order;return!ha(n)&&Nr(i)?$c({model:e,channelDef:i,vgChannel:"zindex",mainRefFn:r=>Et(r.value),invalidValueRef:void 0}):{}}function Sc({channel:e,markDef:t,encoding:n={},model:i,bandPosition:r}){const s=`${e}Offset`,o=t[s],a=n[s];if((s==="xOffset"||s==="yOffset")&&a)return{offsetType:"encoding",offset:W_({channel:s,channelDef:a,markDef:t,config:i==null?void 0:i.config,scaleName:i.scaleName(s),scale:i.getScaleComponent(s),stack:null,defaultRef:Et(o),bandPosition:r})};const u=t[s];return u?{offsetType:"visual",offset:u}:{}}function ti(e,t,{defaultPos:n,vgChannel:i}){const{encoding:r,markDef:s,config:o,stack:a}=t,u=r[e],l=r[gs(e)],c=t.scaleName(e),f=t.getScaleComponent(e),{offset:d,offsetType:h}=Sc({channel:e,markDef:s,encoding:r,model:t,bandPosition:.5}),p=Sw({model:t,defaultPos:n,channel:e,scaleName:c,scale:f}),g=!u&&Bt(e)&&(r.latitude||r.longitude)?{field:t.getName(e)}:Pge({channel:e,channelDef:u,channel2Def:l,markDef:s,config:o,scaleName:c,scale:f,stack:a,offset:d,defaultRef:p,bandPosition:h==="encoding"?0:void 0});return g?{[i||e]:g}:void 0}function Pge(e){const{channel:t,channelDef:n,scaleName:i,stack:r,offset:s,markDef:o}=e;if(Me(n)&&r&&t===r.fieldChannel){if(J(n)){let a=n.bandPosition;if(a===void 0&&o.type==="text"&&(t==="radius"||t==="theta")&&(a=.5),a!==void 0)return U1({scaleName:i,fieldOrDatumDef:n,startSuffix:"start",bandPosition:a,offset:s})}return Cu(n,i,{suffix:"end"},{offset:s})}return q_(e)}function Sw({model:e,defaultPos:t,channel:n,scaleName:i,scale:r}){const{markDef:s,config:o}=e;return()=>{const a=gu(n),u=ca(n),l=gt(n,s,o,{vgChannel:u});if(l!==void 0)return th(n,l);switch(t){case"zeroOrMin":return FO({scaleName:i,scale:r,mode:"zeroOrMin",mainChannel:a,config:o});case"zeroOrMax":return FO({scaleName:i,scale:r,mode:{zeroOrMax:{widthSignal:e.width.signal,heightSignal:e.height.signal}},mainChannel:a,config:o});case"mid":return{...e[mi(n)],mult:.5}}}}function FO({mainChannel:e,config:t,...n}){const i=qN(n),{mode:r}=n;if(i)return i;switch(e){case"radius":{if(r==="zeroOrMin")return{value:0};const{widthSignal:s,heightSignal:o}=r.zeroOrMax;return{signal:`min(${s},${o})/2`}}case"theta":return r==="zeroOrMin"?{value:0}:{signal:"2*PI"};case"x":return r==="zeroOrMin"?{value:0}:{field:{group:"width"}};case"y":return r==="zeroOrMin"?{field:{group:"height"}}:{value:0}}}const zge={left:"x",center:"xc",right:"x2"},Bge={top:"y",middle:"yc",bottom:"y2"};function DO(e,t,n,i="middle"){if(e==="radius"||e==="theta")return ca(e);const r=e==="x"?"align":"baseline",s=gt(r,t,n);let o;return me(s)?(Z(Hde(r)),o=void 0):o=s,e==="x"?zge[o||(i==="top"?"left":"center")]:Bge[o||i]}function lm(e,t,{defaultPos:n,defaultPos2:i,range:r}){return r?TO(e,t,{defaultPos:n,defaultPos2:i}):ti(e,t,{defaultPos:n})}function TO(e,t,{defaultPos:n,defaultPos2:i}){const{markDef:r,config:s}=t,o=gs(e),a=mi(e),u=Uge(t,i,o),l=u[a]?DO(e,r,s):ca(e);return{...ti(e,t,{defaultPos:n,vgChannel:l}),...u}}function Uge(e,t,n){const{encoding:i,mark:r,markDef:s,stack:o,config:a}=e,u=gu(n),l=mi(n),c=ca(n),f=i[u],d=e.scaleName(u),h=e.getScaleComponent(u),{offset:p}=n in i||n in s?Sc({channel:n,markDef:s,encoding:i,model:e}):Sc({channel:u,markDef:s,encoding:i,model:e});if(!f&&(n==="x2"||n==="y2")&&(i.latitude||i.longitude)){const m=mi(n),y=e.markDef[m];return y!=null?{[m]:{value:y}}:{[c]:{field:e.getName(n)}}}const g=jge({channel:n,channelDef:f,channel2Def:i[n],markDef:s,config:a,scaleName:d,scale:h,stack:o,offset:p,defaultRef:void 0});return g!==void 0?{[c]:g}:cm(n,s)||cm(n,{[n]:m_(n,s,a.style),[l]:m_(l,s,a.style)})||cm(n,a[r])||cm(n,a.mark)||{[c]:Sw({model:e,defaultPos:t,channel:n,scaleName:d,scale:h})()}}function jge({channel:e,channelDef:t,channel2Def:n,markDef:i,config:r,scaleName:s,scale:o,stack:a,offset:u,defaultRef:l}){return Me(t)&&a&&e.charAt(0)===a.fieldChannel.charAt(0)?Cu(t,s,{suffix:"start"},{offset:u}):q_({channel:e,channelDef:n,scaleName:s,scale:o,stack:a,markDef:i,config:r,offset:u,defaultRef:l})}function cm(e,t){const n=mi(e),i=ca(e);if(t[i]!==void 0)return{[i]:th(e,t[i])};if(t[e]!==void 0)return{[i]:th(e,t[e])};if(t[n]){const r=t[n];if(Eu(r))Z(zde(n));else return{[n]:th(e,r)}}}function fo(e,t){const{config:n,encoding:i,markDef:r}=e,s=r.type,o=gs(t),a=mi(t),u=i[t],l=i[o],c=e.getScaleComponent(t),f=c?c.get("type"):void 0,d=r.orient,h=i[a]??i.size??gt("size",r,n,{vgChannel:a}),p=zM(t),g=s==="bar"&&(t==="x"?d==="vertical":d==="horizontal")||s==="tick"&&(t==="y"?d==="vertical":d==="horizontal");return J(u)&&(_t(u.bin)||mn(u.bin)||u.timeUnit&&!l)&&!(h&&!Eu(h))&&!i[p]&&!on(f)?Hge({fieldDef:u,fieldDef2:l,channel:t,model:e}):(Me(u)&&on(f)||g)&&!l?Wge(u,t,e):TO(t,e,{defaultPos:"zeroOrMax",defaultPos2:"zeroOrMin"})}function qge(e,t,n,i,r,s,o){if(Eu(r))if(n){const u=n.get("type");if(u==="band"){let l=`bandwidth('${t}')`;r.band!==1&&(l=`${r.band} * ${l}`);const c=ys("minBandSize",{type:o},i);return{signal:c?`max(${Dr(c)}, ${l})`:l}}else r.band!==1&&(Z(Xde(u)),r=void 0)}else return{mult:r.band,field:{group:e}};else{if(me(r))return r;if(r)return{value:r}}if(n){const u=n.get("range");if(yu(u)&&Ze(u.step))return{value:u.step-2}}if(!s){const{bandPaddingInner:u,barBandPaddingInner:l,rectBandPaddingInner:c,tickBandPaddingInner:f}=i.scale,d=zt(u,o==="tick"?f:o==="bar"?l:c);if(me(d))return{signal:`(1 - (${d.signal})) * ${e}`};if(Ze(d))return{signal:`${1-d} * ${e}`}}return{value:fw(i.view,e)-2}}function Wge(e,t,n){var k,S;const{markDef:i,encoding:r,config:s,stack:o}=n,a=i.orient,u=n.scaleName(t),l=n.getScaleComponent(t),c=mi(t),f=gs(t),d=zM(t),h=n.scaleName(d),p=n.getScaleComponent(a_(t)),g=i.type==="tick"||a==="horizontal"&&t==="y"||a==="vertical"&&t==="x";let m;(r.size||i.size)&&(g?m=vn("size",n,{vgChannel:c,defaultRef:Et(i.size)}):Z(Qde(i.type)));const y=!!m,b=eR({channel:t,fieldDef:e,markDef:i,config:s,scaleType:(k=l||p)==null?void 0:k.get("type"),useVlSizeChannel:g});m=m||{[c]:qge(c,h||u,p||l,s,b,!!e,i.type)};const v=((S=l||p)==null?void 0:S.get("type"))==="band"&&Eu(b)&&!y?"top":"middle",x=DO(t,i,s,v),_=x==="xc"||x==="yc",{offset:E,offsetType:w}=Sc({channel:t,markDef:i,encoding:r,model:n,bandPosition:_?.5:0}),C=q_({channel:t,channelDef:e,markDef:i,config:s,scaleName:u,scale:l,stack:o,offset:E,defaultRef:Sw({model:n,defaultPos:"mid",channel:t,scaleName:u,scale:l}),bandPosition:_?w==="encoding"?0:.5:me(b)?{signal:`(1-${b})/2`}:Eu(b)?(1-b.band)/2:0});if(c)return{[x]:C,...m};{const $=ca(f),O=m[c],R=E?{...O,offset:E}:O;return{[x]:C,[$]:W(C)?[C[0],{...C[1],offset:R}]:{...C,offset:R}}}}function MO(e,t,n,i,r,s,o){if(NM(e))return 0;const a=e==="x"||e==="y2",u=a?-t/2:t/2;if(me(n)||me(r)||me(i)||s){const l=Dr(n),c=Dr(r),f=Dr(i),d=Dr(s),p=s?`(${o} < ${d} ? ${a?"":"-"}0.5 * (${d} - (${o})) : ${u})`:u,g=f?`${f} + `:"",m=l?`(${l} ? -1 : 1) * `:"",y=c?`(${c} + ${p})`:p;return{signal:g+m+y}}else return r=r||0,i+(n?-r-u:+r+u)}function Hge({fieldDef:e,fieldDef2:t,channel:n,model:i}){var S;const{config:r,markDef:s,encoding:o}=i,a=i.getScaleComponent(n),u=i.scaleName(n),l=a?a.get("type"):void 0,c=a.get("reverse"),f=eR({channel:n,fieldDef:e,markDef:s,config:r,scaleType:l}),d=(S=i.component.axes[n])==null?void 0:S[0],h=(d==null?void 0:d.get("translate"))??.5,p=Bt(n)?gt("binSpacing",s,r)??0:0,g=gs(n),m=ca(n),y=ca(g),b=ys("minBandSize",s,r),{offset:v}=Sc({channel:n,markDef:s,encoding:o,model:i,bandPosition:0}),{offset:x}=Sc({channel:g,markDef:s,encoding:o,model:i,bandPosition:0}),_=b0e({fieldDef:e,scaleName:u}),E=MO(n,p,c,h,v,b,_),w=MO(g,p,c,h,x??v,b,_),C=me(f)?{signal:`(1-${f.signal})/2`}:Eu(f)?(1-f.band)/2:.5,k=pa({fieldDef:e,fieldDef2:t,markDef:s,config:r});if(_t(e.bin)||e.timeUnit){const $=e.timeUnit&&k!==.5;return{[y]:NO({fieldDef:e,scaleName:u,bandPosition:C,offset:w,useRectOffsetField:$}),[m]:NO({fieldDef:e,scaleName:u,bandPosition:me(C)?{signal:`1-${C.signal}`}:1-C,offset:E,useRectOffsetField:$})}}else if(mn(e.bin)){const $=Cu(e,u,{},{offset:w});if(J(t))return{[y]:$,[m]:Cu(t,u,{},{offset:E})};if(mu(e.bin)&&e.bin.step)return{[y]:$,[m]:{signal:`scale("${u}", ${te(e,{expr:"datum"})} + ${e.bin.step})`,offset:E}}}Z(mN(g))}function NO({fieldDef:e,scaleName:t,bandPosition:n,offset:i,useRectOffsetField:r}){return U1({scaleName:t,fieldOrDatumDef:e,bandPosition:n,offset:i,...r?{startSuffix:sm,endSuffix:om}:{}})}const Gge=new Set(["aria","width","height"]);function rr(e,t){const{fill:n=void 0,stroke:i=void 0}=t.color==="include"?SO(e):{};return{...Vge(e.markDef,t),...RO("fill",n),...RO("stroke",i),...vn("opacity",e),...vn("fillOpacity",e),...vn("strokeOpacity",e),...vn("strokeWidth",e),...vn("strokeDash",e),...Ige(e),...kO(e),...$w(e,"href"),...Rge(e)}}function RO(e,t){return t?{[e]:t}:{}}function Vge(e,t){return lde.reduce((n,i)=>(!Gge.has(i)&&X(e,i)&&t[i]!=="ignore"&&(n[i]=Et(e[i])),n),{})}function Fw(e){const{config:t,markDef:n}=e,i=new Set;if(e.forEachFieldDef((r,s)=>{var l;let o;if(!ms(s)||!(o=e.getScaleType(s)))return;const a=D1(r.aggregate),u=j_({scaleChannel:s,markDef:n,config:t,scaleType:o,isCountAggregate:a});if(g0e(u)){const c=e.vgField(s,{expr:"datum",binSuffix:(l=e.stack)!=null&&l.impute?"mid":void 0});c&&i.add(c)}}),i.size>0)return{defined:{signal:[...i].map(s=>O1(s,!0)).join(" && ")}}}function OO(e,t){if(t!==void 0)return{[e]:Et(t)}}const Dw="voronoi",LO={defined:e=>e.type==="point"&&e.nearest,parse:(e,t)=>{if(t.events)for(const n of t.events)n.markname=e.getName(Dw)},marks:(e,t,n)=>{const{x:i,y:r}=t.project.hasChannel,s=e.mark;if(ha(s))return Z(gde(s)),n;const o={name:e.getName(Dw),type:"path",interactive:!0,from:{data:e.getName("marks")},encode:{update:{fill:{value:"transparent"},strokeWidth:{value:.35},stroke:{value:"transparent"},isVoronoi:{value:!0},...kO(e,{reactiveGeom:!0})}},transform:[{type:"voronoi",x:{expr:i||!r?"datum.datum.x || 0":"0"},y:{expr:r||!i?"datum.datum.y || 0":"0"},size:[e.getSizeSignalRef("width"),e.getSizeSignalRef("height")]}]};let a=0,u=!1;return n.forEach((l,c)=>{const f=l.name??"";f===e.component.mark[0].name?a=c:f.includes(Dw)&&(u=!0)}),u||n.splice(a+1,0,o),n}},IO={defined:e=>e.type==="point"&&e.resolve==="global"&&e.bind&&e.bind!=="scales"&&!sw(e.bind),parse:(e,t,n)=>VO(t,n),topLevelSignals:(e,t,n)=>{const i=t.name,r=t.project,s=t.bind,o=t.init&&t.init[0],a=LO.defined(t)?"(item().isVoronoi ? datum.datum : datum)":"datum";return r.items.forEach((u,l)=>{const c=At(`${i}_${u.field}`);n.filter(d=>d.name===c).length||n.unshift({name:c,...o?{init:Su(o[l])}:{value:null},on:t.events?[{events:t.events,update:`datum && item().mark.marktype !== 'group' ? ${a}[${Q(u.field)}] : null`}]:[],bind:s[u.field]??s[u.channel]??s})}),n},signals:(e,t,n)=>{const i=t.name,r=t.project,s=n.find(l=>l.name===i+ho),o=i+lh,a=r.items.map(l=>At(`${i}_${l.field}`)),u=a.map(l=>`${l} !== null`).join(" && ");return a.length&&(s.update=`${u} ? {fields: ${o}, values: [${a.join(", ")}]} : null`),delete s.value,delete s.on,n}},fm="_toggle",PO={defined:e=>e.type==="point"&&!ks(e)&&!!e.toggle,signals:(e,t,n)=>n.concat({name:t.name+fm,value:!1,on:[{events:t.events,update:t.toggle}]}),modifyExpr:(e,t)=>{const n=t.name+ho,i=t.name+fm;return`${i} ? null : ${n}, `+(t.resolve==="global"?`${i} ? null : true, `:`${i} ? null : {unit: ${Du(e)}}, `)+`${i} ? ${n} : null`}},Yge={defined:e=>e.clear!==void 0&&e.clear!==!1&&!ks(e),parse:(e,t)=>{t.clear&&(t.clear=se(t.clear)?ta(t.clear,"view"):t.clear)},topLevelSignals:(e,t,n)=>{if(IO.defined(t))for(const i of t.project.items){const r=n.findIndex(s=>s.name===At(`${t.name}_${i.field}`));r!==-1&&n[r].on.push({events:t.clear,update:"null"})}return n},signals:(e,t,n)=>{function i(r,s){r!==-1&&n[r].on&&n[r].on.push({events:t.clear,update:s})}if(t.type==="interval")for(const r of t.project.items){const s=n.findIndex(o=>o.name===r.signals.visual);if(i(s,"[0, 0]"),s===-1){const o=n.findIndex(a=>a.name===r.signals.data);i(o,"null")}}else{let r=n.findIndex(s=>s.name===t.name+ho);i(r,"null"),PO.defined(t)&&(r=n.findIndex(s=>s.name===t.name+fm),i(r,"false"))}return n}},zO={defined:e=>{const t=e.resolve==="global"&&e.bind&&sw(e.bind),n=e.project.items.length===1&&e.project.items[0].field!==Or;return t&&!n&&Z(vde),t&&n},parse:(e,t,n)=>{const i=De(n);if(i.select=se(i.select)?{type:i.select,toggle:t.toggle}:{...i.select,toggle:t.toggle},VO(t,i),re(n.select)&&(n.select.on||n.select.clear)){const o='event.item && indexof(event.item.mark.role, "legend") < 0';for(const a of t.events)a.filter=oe(a.filter??[]),a.filter.includes(o)||a.filter.push(o)}const r=ow(t.bind)?t.bind.legend:"click",s=se(r)?ta(r,"view"):oe(r);t.bind={legend:{merge:s}}},topLevelSignals:(e,t,n)=>{const i=t.name,r=ow(t.bind)&&t.bind.legend,s=o=>a=>{const u=De(a);return u.markname=o,u};for(const o of t.project.items){if(!o.hasLegend)continue;const a=`${At(o.field)}_legend`,u=`${i}_${a}`;if(n.filter(c=>c.name===u).length===0){const c=r.merge.map(s(`${a}_symbols`)).concat(r.merge.map(s(`${a}_labels`))).concat(r.merge.map(s(`${a}_entries`)));n.unshift({name:u,...t.init?{}:{value:null},on:[{events:c,update:"isDefined(datum.value) ? datum.value : item().items[0].items[0].datum.value",force:!0},{events:r.merge,update:`!event.item || !datum ? null : ${u}`,force:!0}]})}}return n},signals:(e,t,n)=>{const i=t.name,r=t.project,s=n.find(d=>d.name===i+ho),o=i+lh,a=r.items.filter(d=>d.hasLegend).map(d=>At(`${i}_${At(d.field)}_legend`)),l=`${a.map(d=>`${d} !== null`).join(" && ")} ? {fields: ${o}, values: [${a.join(", ")}]} : null`;t.events&&a.length>0?s.on.push({events:a.map(d=>({signal:d})),update:l}):a.length>0&&(s.update=l,delete s.value,delete s.on);const c=n.find(d=>d.name===i+fm),f=ow(t.bind)&&t.bind.legend;return c&&(t.events?c.on.push({...c.on[0],events:f}):c.on[0].events=f),n}};function Xge(e,t,n){var r;const i=(r=e.fieldDef(t))==null?void 0:r.field;for(const s of gn(e.component.selection??{})){const o=s.project.hasField[i]??s.project.hasChannel[t];if(o&&zO.defined(s)){const a=n.get("selections")??[];a.push(s.name),n.set("selections",a,!1),o.hasLegend=!0}}}const BO="_translate_anchor",UO="_translate_delta",Kge={defined:e=>e.type==="interval"&&e.translate,signals:(e,t,n)=>{const i=t.name,r=co.defined(t),s=i+BO,{x:o,y:a}=t.project.hasChannel;let u=ta(t.translate,"scope");return r||(u=u.map(l=>(l.between[0].markname=i+Ac,l))),n.push({name:s,value:{},on:[{events:u.map(l=>l.between[0]),update:"{x: x(unit), y: y(unit)"+(o!==void 0?`, extent_x: ${r?kw(e,$t):`slice(${o.signals.visual})`}`:"")+(a!==void 0?`, extent_y: ${r?kw(e,rn):`slice(${a.signals.visual})`}`:"")+"}"}]},{name:i+UO,value:{},on:[{events:u,update:`{x: ${s}.x - x(unit), y: ${s}.y - y(unit)}`}]}),o!==void 0&&jO(e,t,o,"width",n),a!==void 0&&jO(e,t,a,"height",n),n}};function jO(e,t,n,i,r){const s=t.name,o=s+BO,a=s+UO,u=n.channel,l=co.defined(t),c=r.find(_=>_.name===n.signals[l?"data":"visual"]),f=e.getSizeSignalRef(i).signal,d=e.getScaleComponent(u),h=d&&d.get("type"),p=d&&d.get("reverse"),g=l?u===$t?p?"":"-":p?"-":"":"",m=`${o}.extent_${u}`,y=`${g}${a}.${u} / ${l?`${f}`:`span(${m})`}`,b=!l||!d?"panLinear":h==="log"?"panLog":h==="symlog"?"panSymlog":h==="pow"?"panPow":"panLinear",v=l?h==="pow"?`, ${d.get("exponent")??1}`:h==="symlog"?`, ${d.get("constant")??1}`:"":"",x=`${b}(${m}, ${y}${v})`;c.on.push({events:{signal:a},update:l?x:`clampRange(${x}, 0, ${f})`})}const qO="_zoom_anchor",WO="_zoom_delta",Zge={defined:e=>e.type==="interval"&&e.zoom,signals:(e,t,n)=>{const i=t.name,r=co.defined(t),s=i+WO,{x:o,y:a}=t.project.hasChannel,u=Q(e.scaleName($t)),l=Q(e.scaleName(rn));let c=ta(t.zoom,"scope");return r||(c=c.map(f=>(f.markname=i+Ac,f))),n.push({name:i+qO,on:[{events:c,update:r?"{"+[u?`x: invert(${u}, x(unit))`:"",l?`y: invert(${l}, y(unit))`:""].filter(f=>f).join(", ")+"}":"{x: x(unit), y: y(unit)}"}]},{name:s,on:[{events:c,force:!0,update:"pow(1.001, event.deltaY * pow(16, event.deltaMode))"}]}),o!==void 0&&HO(e,t,o,"width",n),a!==void 0&&HO(e,t,a,"height",n),n}};function HO(e,t,n,i,r){const s=t.name,o=n.channel,a=co.defined(t),u=r.find(b=>b.name===n.signals[a?"data":"visual"]),l=e.getSizeSignalRef(i).signal,c=e.getScaleComponent(o),f=c&&c.get("type"),d=a?kw(e,o):u.name,h=s+WO,p=`${s}${qO}.${o}`,g=!a||!c?"zoomLinear":f==="log"?"zoomLog":f==="symlog"?"zoomSymlog":f==="pow"?"zoomPow":"zoomLinear",m=a?f==="pow"?`, ${c.get("exponent")??1}`:f==="symlog"?`, ${c.get("constant")??1}`:"":"",y=`${g}(${d}, ${p}, ${h}${m})`;u.on.push({events:{signal:h},update:a?y:`clampRange(${y}, 0, ${l})`})}const Fu="_store",ho="_tuple",Jge="_modify",GO="vlSelectionResolve",dm=[Cge,Mge,_ge,PO,IO,co,zO,Yge,Kge,Zge,LO];function Qge(e){let t=e.parent;for(;t&&!Oi(t);)t=t.parent;return t}function Du(e,{escape:t}={escape:!0}){let n=t?Q(e.name):e.name;const i=Qge(e);if(i){const{facet:r}=i;for(const s of ir)r[s]&&(n+=` + '__facet_${s}_' + (facet[${Q(i.vgField(s))}])`)}return n}function Tw(e){return gn(e.component.selection??{}).reduce((t,n)=>t||n.project.hasSelectionId,!1)}function VO(e,t){(se(t.select)||!t.select.on)&&delete e.events,(se(t.select)||!t.select.clear)&&delete e.clear,(se(t.select)||!t.select.toggle)&&delete e.toggle}function ks(e){var t;return(t=e.events)==null?void 0:t.find(n=>"type"in n&&n.type==="timer")}const e1e="RawCode",t1e="Literal",n1e="Property",i1e="Identifier",r1e="ArrayExpression",s1e="BinaryExpression",o1e="CallExpression",a1e="ConditionalExpression",u1e="LogicalExpression",l1e="MemberExpression",c1e="ObjectExpression",f1e="UnaryExpression";function Lr(e){this.type=e}Lr.prototype.visit=function(e){let t,n,i;if(e(this))return 1;for(t=d1e(this),n=0,i=t.length;n",As[Tu]="Identifier",As[ba]="Keyword",As[pm]="Null",As[Mu]="Numeric",As[bi]="Punctuator",As[dh]="String",As[h1e]="RegularExpression";var p1e="ArrayExpression",g1e="BinaryExpression",m1e="CallExpression",y1e="ConditionalExpression",YO="Identifier",b1e="Literal",v1e="LogicalExpression",x1e="MemberExpression",_1e="ObjectExpression",w1e="Property",E1e="UnaryExpression",an="Unexpected token %0",C1e="Unexpected number",k1e="Unexpected string",A1e="Unexpected identifier",$1e="Unexpected reserved word",S1e="Unexpected end of input",Mw="Invalid regular expression",Nw="Invalid regular expression: missing /",XO="Octal literals are not allowed in strict mode.",F1e="Duplicate data property in object literal not allowed in strict mode",xn="ILLEGAL",hh="Disabled.",D1e=new RegExp("[\\xAA\\xB5\\xBA\\xC0-\\xD6\\xD8-\\xF6\\xF8-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0370-\\u0374\\u0376\\u0377\\u037A-\\u037D\\u037F\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u048A-\\u052F\\u0531-\\u0556\\u0559\\u0561-\\u0587\\u05D0-\\u05EA\\u05F0-\\u05F2\\u0620-\\u064A\\u066E\\u066F\\u0671-\\u06D3\\u06D5\\u06E5\\u06E6\\u06EE\\u06EF\\u06FA-\\u06FC\\u06FF\\u0710\\u0712-\\u072F\\u074D-\\u07A5\\u07B1\\u07CA-\\u07EA\\u07F4\\u07F5\\u07FA\\u0800-\\u0815\\u081A\\u0824\\u0828\\u0840-\\u0858\\u08A0-\\u08B2\\u0904-\\u0939\\u093D\\u0950\\u0958-\\u0961\\u0971-\\u0980\\u0985-\\u098C\\u098F\\u0990\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BD\\u09CE\\u09DC\\u09DD\\u09DF-\\u09E1\\u09F0\\u09F1\\u0A05-\\u0A0A\\u0A0F\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32\\u0A33\\u0A35\\u0A36\\u0A38\\u0A39\\u0A59-\\u0A5C\\u0A5E\\u0A72-\\u0A74\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2\\u0AB3\\u0AB5-\\u0AB9\\u0ABD\\u0AD0\\u0AE0\\u0AE1\\u0B05-\\u0B0C\\u0B0F\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32\\u0B33\\u0B35-\\u0B39\\u0B3D\\u0B5C\\u0B5D\\u0B5F-\\u0B61\\u0B71\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99\\u0B9A\\u0B9C\\u0B9E\\u0B9F\\u0BA3\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BD0\\u0C05-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C39\\u0C3D\\u0C58\\u0C59\\u0C60\\u0C61\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBD\\u0CDE\\u0CE0\\u0CE1\\u0CF1\\u0CF2\\u0D05-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D3A\\u0D3D\\u0D4E\\u0D60\\u0D61\\u0D7A-\\u0D7F\\u0D85-\\u0D96\\u0D9A-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD\\u0DC0-\\u0DC6\\u0E01-\\u0E30\\u0E32\\u0E33\\u0E40-\\u0E46\\u0E81\\u0E82\\u0E84\\u0E87\\u0E88\\u0E8A\\u0E8D\\u0E94-\\u0E97\\u0E99-\\u0E9F\\u0EA1-\\u0EA3\\u0EA5\\u0EA7\\u0EAA\\u0EAB\\u0EAD-\\u0EB0\\u0EB2\\u0EB3\\u0EBD\\u0EC0-\\u0EC4\\u0EC6\\u0EDC-\\u0EDF\\u0F00\\u0F40-\\u0F47\\u0F49-\\u0F6C\\u0F88-\\u0F8C\\u1000-\\u102A\\u103F\\u1050-\\u1055\\u105A-\\u105D\\u1061\\u1065\\u1066\\u106E-\\u1070\\u1075-\\u1081\\u108E\\u10A0-\\u10C5\\u10C7\\u10CD\\u10D0-\\u10FA\\u10FC-\\u1248\\u124A-\\u124D\\u1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u1380-\\u138F\\u13A0-\\u13F4\\u1401-\\u166C\\u166F-\\u167F\\u1681-\\u169A\\u16A0-\\u16EA\\u16EE-\\u16F8\\u1700-\\u170C\\u170E-\\u1711\\u1720-\\u1731\\u1740-\\u1751\\u1760-\\u176C\\u176E-\\u1770\\u1780-\\u17B3\\u17D7\\u17DC\\u1820-\\u1877\\u1880-\\u18A8\\u18AA\\u18B0-\\u18F5\\u1900-\\u191E\\u1950-\\u196D\\u1970-\\u1974\\u1980-\\u19AB\\u19C1-\\u19C7\\u1A00-\\u1A16\\u1A20-\\u1A54\\u1AA7\\u1B05-\\u1B33\\u1B45-\\u1B4B\\u1B83-\\u1BA0\\u1BAE\\u1BAF\\u1BBA-\\u1BE5\\u1C00-\\u1C23\\u1C4D-\\u1C4F\\u1C5A-\\u1C7D\\u1CE9-\\u1CEC\\u1CEE-\\u1CF1\\u1CF5\\u1CF6\\u1D00-\\u1DBF\\u1E00-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FBC\\u1FBE\\u1FC2-\\u1FC4\\u1FC6-\\u1FCC\\u1FD0-\\u1FD3\\u1FD6-\\u1FDB\\u1FE0-\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FFC\\u2071\\u207F\\u2090-\\u209C\\u2102\\u2107\\u210A-\\u2113\\u2115\\u2119-\\u211D\\u2124\\u2126\\u2128\\u212A-\\u212D\\u212F-\\u2139\\u213C-\\u213F\\u2145-\\u2149\\u214E\\u2160-\\u2188\\u2C00-\\u2C2E\\u2C30-\\u2C5E\\u2C60-\\u2CE4\\u2CEB-\\u2CEE\\u2CF2\\u2CF3\\u2D00-\\u2D25\\u2D27\\u2D2D\\u2D30-\\u2D67\\u2D6F\\u2D80-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2E2F\\u3005-\\u3007\\u3021-\\u3029\\u3031-\\u3035\\u3038-\\u303C\\u3041-\\u3096\\u309D-\\u309F\\u30A1-\\u30FA\\u30FC-\\u30FF\\u3105-\\u312D\\u3131-\\u318E\\u31A0-\\u31BA\\u31F0-\\u31FF\\u3400-\\u4DB5\\u4E00-\\u9FCC\\uA000-\\uA48C\\uA4D0-\\uA4FD\\uA500-\\uA60C\\uA610-\\uA61F\\uA62A\\uA62B\\uA640-\\uA66E\\uA67F-\\uA69D\\uA6A0-\\uA6EF\\uA717-\\uA71F\\uA722-\\uA788\\uA78B-\\uA78E\\uA790-\\uA7AD\\uA7B0\\uA7B1\\uA7F7-\\uA801\\uA803-\\uA805\\uA807-\\uA80A\\uA80C-\\uA822\\uA840-\\uA873\\uA882-\\uA8B3\\uA8F2-\\uA8F7\\uA8FB\\uA90A-\\uA925\\uA930-\\uA946\\uA960-\\uA97C\\uA984-\\uA9B2\\uA9CF\\uA9E0-\\uA9E4\\uA9E6-\\uA9EF\\uA9FA-\\uA9FE\\uAA00-\\uAA28\\uAA40-\\uAA42\\uAA44-\\uAA4B\\uAA60-\\uAA76\\uAA7A\\uAA7E-\\uAAAF\\uAAB1\\uAAB5\\uAAB6\\uAAB9-\\uAABD\\uAAC0\\uAAC2\\uAADB-\\uAADD\\uAAE0-\\uAAEA\\uAAF2-\\uAAF4\\uAB01-\\uAB06\\uAB09-\\uAB0E\\uAB11-\\uAB16\\uAB20-\\uAB26\\uAB28-\\uAB2E\\uAB30-\\uAB5A\\uAB5C-\\uAB5F\\uAB64\\uAB65\\uABC0-\\uABE2\\uAC00-\\uD7A3\\uD7B0-\\uD7C6\\uD7CB-\\uD7FB\\uF900-\\uFA6D\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\uFB17\\uFB1D\\uFB1F-\\uFB28\\uFB2A-\\uFB36\\uFB38-\\uFB3C\\uFB3E\\uFB40\\uFB41\\uFB43\\uFB44\\uFB46-\\uFBB1\\uFBD3-\\uFD3D\\uFD50-\\uFD8F\\uFD92-\\uFDC7\\uFDF0-\\uFDFB\\uFE70-\\uFE74\\uFE76-\\uFEFC\\uFF21-\\uFF3A\\uFF41-\\uFF5A\\uFF66-\\uFFBE\\uFFC2-\\uFFC7\\uFFCA-\\uFFCF\\uFFD2-\\uFFD7\\uFFDA-\\uFFDC]"),T1e=new RegExp("[\\xAA\\xB5\\xBA\\xC0-\\xD6\\xD8-\\xF6\\xF8-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0300-\\u0374\\u0376\\u0377\\u037A-\\u037D\\u037F\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u0483-\\u0487\\u048A-\\u052F\\u0531-\\u0556\\u0559\\u0561-\\u0587\\u0591-\\u05BD\\u05BF\\u05C1\\u05C2\\u05C4\\u05C5\\u05C7\\u05D0-\\u05EA\\u05F0-\\u05F2\\u0610-\\u061A\\u0620-\\u0669\\u066E-\\u06D3\\u06D5-\\u06DC\\u06DF-\\u06E8\\u06EA-\\u06FC\\u06FF\\u0710-\\u074A\\u074D-\\u07B1\\u07C0-\\u07F5\\u07FA\\u0800-\\u082D\\u0840-\\u085B\\u08A0-\\u08B2\\u08E4-\\u0963\\u0966-\\u096F\\u0971-\\u0983\\u0985-\\u098C\\u098F\\u0990\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BC-\\u09C4\\u09C7\\u09C8\\u09CB-\\u09CE\\u09D7\\u09DC\\u09DD\\u09DF-\\u09E3\\u09E6-\\u09F1\\u0A01-\\u0A03\\u0A05-\\u0A0A\\u0A0F\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32\\u0A33\\u0A35\\u0A36\\u0A38\\u0A39\\u0A3C\\u0A3E-\\u0A42\\u0A47\\u0A48\\u0A4B-\\u0A4D\\u0A51\\u0A59-\\u0A5C\\u0A5E\\u0A66-\\u0A75\\u0A81-\\u0A83\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2\\u0AB3\\u0AB5-\\u0AB9\\u0ABC-\\u0AC5\\u0AC7-\\u0AC9\\u0ACB-\\u0ACD\\u0AD0\\u0AE0-\\u0AE3\\u0AE6-\\u0AEF\\u0B01-\\u0B03\\u0B05-\\u0B0C\\u0B0F\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32\\u0B33\\u0B35-\\u0B39\\u0B3C-\\u0B44\\u0B47\\u0B48\\u0B4B-\\u0B4D\\u0B56\\u0B57\\u0B5C\\u0B5D\\u0B5F-\\u0B63\\u0B66-\\u0B6F\\u0B71\\u0B82\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99\\u0B9A\\u0B9C\\u0B9E\\u0B9F\\u0BA3\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BBE-\\u0BC2\\u0BC6-\\u0BC8\\u0BCA-\\u0BCD\\u0BD0\\u0BD7\\u0BE6-\\u0BEF\\u0C00-\\u0C03\\u0C05-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C39\\u0C3D-\\u0C44\\u0C46-\\u0C48\\u0C4A-\\u0C4D\\u0C55\\u0C56\\u0C58\\u0C59\\u0C60-\\u0C63\\u0C66-\\u0C6F\\u0C81-\\u0C83\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBC-\\u0CC4\\u0CC6-\\u0CC8\\u0CCA-\\u0CCD\\u0CD5\\u0CD6\\u0CDE\\u0CE0-\\u0CE3\\u0CE6-\\u0CEF\\u0CF1\\u0CF2\\u0D01-\\u0D03\\u0D05-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D3A\\u0D3D-\\u0D44\\u0D46-\\u0D48\\u0D4A-\\u0D4E\\u0D57\\u0D60-\\u0D63\\u0D66-\\u0D6F\\u0D7A-\\u0D7F\\u0D82\\u0D83\\u0D85-\\u0D96\\u0D9A-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD\\u0DC0-\\u0DC6\\u0DCA\\u0DCF-\\u0DD4\\u0DD6\\u0DD8-\\u0DDF\\u0DE6-\\u0DEF\\u0DF2\\u0DF3\\u0E01-\\u0E3A\\u0E40-\\u0E4E\\u0E50-\\u0E59\\u0E81\\u0E82\\u0E84\\u0E87\\u0E88\\u0E8A\\u0E8D\\u0E94-\\u0E97\\u0E99-\\u0E9F\\u0EA1-\\u0EA3\\u0EA5\\u0EA7\\u0EAA\\u0EAB\\u0EAD-\\u0EB9\\u0EBB-\\u0EBD\\u0EC0-\\u0EC4\\u0EC6\\u0EC8-\\u0ECD\\u0ED0-\\u0ED9\\u0EDC-\\u0EDF\\u0F00\\u0F18\\u0F19\\u0F20-\\u0F29\\u0F35\\u0F37\\u0F39\\u0F3E-\\u0F47\\u0F49-\\u0F6C\\u0F71-\\u0F84\\u0F86-\\u0F97\\u0F99-\\u0FBC\\u0FC6\\u1000-\\u1049\\u1050-\\u109D\\u10A0-\\u10C5\\u10C7\\u10CD\\u10D0-\\u10FA\\u10FC-\\u1248\\u124A-\\u124D\\u1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u135D-\\u135F\\u1380-\\u138F\\u13A0-\\u13F4\\u1401-\\u166C\\u166F-\\u167F\\u1681-\\u169A\\u16A0-\\u16EA\\u16EE-\\u16F8\\u1700-\\u170C\\u170E-\\u1714\\u1720-\\u1734\\u1740-\\u1753\\u1760-\\u176C\\u176E-\\u1770\\u1772\\u1773\\u1780-\\u17D3\\u17D7\\u17DC\\u17DD\\u17E0-\\u17E9\\u180B-\\u180D\\u1810-\\u1819\\u1820-\\u1877\\u1880-\\u18AA\\u18B0-\\u18F5\\u1900-\\u191E\\u1920-\\u192B\\u1930-\\u193B\\u1946-\\u196D\\u1970-\\u1974\\u1980-\\u19AB\\u19B0-\\u19C9\\u19D0-\\u19D9\\u1A00-\\u1A1B\\u1A20-\\u1A5E\\u1A60-\\u1A7C\\u1A7F-\\u1A89\\u1A90-\\u1A99\\u1AA7\\u1AB0-\\u1ABD\\u1B00-\\u1B4B\\u1B50-\\u1B59\\u1B6B-\\u1B73\\u1B80-\\u1BF3\\u1C00-\\u1C37\\u1C40-\\u1C49\\u1C4D-\\u1C7D\\u1CD0-\\u1CD2\\u1CD4-\\u1CF6\\u1CF8\\u1CF9\\u1D00-\\u1DF5\\u1DFC-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FBC\\u1FBE\\u1FC2-\\u1FC4\\u1FC6-\\u1FCC\\u1FD0-\\u1FD3\\u1FD6-\\u1FDB\\u1FE0-\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FFC\\u200C\\u200D\\u203F\\u2040\\u2054\\u2071\\u207F\\u2090-\\u209C\\u20D0-\\u20DC\\u20E1\\u20E5-\\u20F0\\u2102\\u2107\\u210A-\\u2113\\u2115\\u2119-\\u211D\\u2124\\u2126\\u2128\\u212A-\\u212D\\u212F-\\u2139\\u213C-\\u213F\\u2145-\\u2149\\u214E\\u2160-\\u2188\\u2C00-\\u2C2E\\u2C30-\\u2C5E\\u2C60-\\u2CE4\\u2CEB-\\u2CF3\\u2D00-\\u2D25\\u2D27\\u2D2D\\u2D30-\\u2D67\\u2D6F\\u2D7F-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2DE0-\\u2DFF\\u2E2F\\u3005-\\u3007\\u3021-\\u302F\\u3031-\\u3035\\u3038-\\u303C\\u3041-\\u3096\\u3099\\u309A\\u309D-\\u309F\\u30A1-\\u30FA\\u30FC-\\u30FF\\u3105-\\u312D\\u3131-\\u318E\\u31A0-\\u31BA\\u31F0-\\u31FF\\u3400-\\u4DB5\\u4E00-\\u9FCC\\uA000-\\uA48C\\uA4D0-\\uA4FD\\uA500-\\uA60C\\uA610-\\uA62B\\uA640-\\uA66F\\uA674-\\uA67D\\uA67F-\\uA69D\\uA69F-\\uA6F1\\uA717-\\uA71F\\uA722-\\uA788\\uA78B-\\uA78E\\uA790-\\uA7AD\\uA7B0\\uA7B1\\uA7F7-\\uA827\\uA840-\\uA873\\uA880-\\uA8C4\\uA8D0-\\uA8D9\\uA8E0-\\uA8F7\\uA8FB\\uA900-\\uA92D\\uA930-\\uA953\\uA960-\\uA97C\\uA980-\\uA9C0\\uA9CF-\\uA9D9\\uA9E0-\\uA9FE\\uAA00-\\uAA36\\uAA40-\\uAA4D\\uAA50-\\uAA59\\uAA60-\\uAA76\\uAA7A-\\uAAC2\\uAADB-\\uAADD\\uAAE0-\\uAAEF\\uAAF2-\\uAAF6\\uAB01-\\uAB06\\uAB09-\\uAB0E\\uAB11-\\uAB16\\uAB20-\\uAB26\\uAB28-\\uAB2E\\uAB30-\\uAB5A\\uAB5C-\\uAB5F\\uAB64\\uAB65\\uABC0-\\uABEA\\uABEC\\uABED\\uABF0-\\uABF9\\uAC00-\\uD7A3\\uD7B0-\\uD7C6\\uD7CB-\\uD7FB\\uF900-\\uFA6D\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\uFB17\\uFB1D-\\uFB28\\uFB2A-\\uFB36\\uFB38-\\uFB3C\\uFB3E\\uFB40\\uFB41\\uFB43\\uFB44\\uFB46-\\uFBB1\\uFBD3-\\uFD3D\\uFD50-\\uFD8F\\uFD92-\\uFDC7\\uFDF0-\\uFDFB\\uFE00-\\uFE0F\\uFE20-\\uFE2D\\uFE33\\uFE34\\uFE4D-\\uFE4F\\uFE70-\\uFE74\\uFE76-\\uFEFC\\uFF10-\\uFF19\\uFF21-\\uFF3A\\uFF3F\\uFF41-\\uFF5A\\uFF66-\\uFFBE\\uFFC2-\\uFFC7\\uFFCA-\\uFFCF\\uFFD2-\\uFFD7\\uFFDA-\\uFFDC]");function gm(e,t){if(!e)throw new Error("ASSERT: "+t)}function po(e){return e>=48&&e<=57}function Rw(e){return"0123456789abcdefABCDEF".includes(e)}function ph(e){return"01234567".includes(e)}function M1e(e){return e===32||e===9||e===11||e===12||e===160||e>=5760&&[5760,6158,8192,8193,8194,8195,8196,8197,8198,8199,8200,8201,8202,8239,8287,12288,65279].includes(e)}function gh(e){return e===10||e===13||e===8232||e===8233}function mh(e){return e===36||e===95||e>=65&&e<=90||e>=97&&e<=122||e===92||e>=128&&D1e.test(String.fromCharCode(e))}function mm(e){return e===36||e===95||e>=65&&e<=90||e>=97&&e<=122||e>=48&&e<=57||e===92||e>=128&&T1e.test(String.fromCharCode(e))}const N1e={if:1,in:1,do:1,var:1,for:1,new:1,try:1,let:1,this:1,else:1,case:1,void:1,with:1,enum:1,while:1,break:1,catch:1,throw:1,const:1,yield:1,class:1,super:1,return:1,typeof:1,delete:1,switch:1,export:1,import:1,public:1,static:1,default:1,finally:1,extends:1,package:1,private:1,function:1,continue:1,debugger:1,interface:1,protected:1,instanceof:1,implements:1};function KO(){for(;j1114111||e!=="}")&&tt({},an,xn),t<=65535?String.fromCharCode(t):(n=(t-65536>>10)+55296,i=(t-65536&1023)+56320,String.fromCharCode(n,i))}function ZO(){var e,t;for(e=ge.charCodeAt(j++),t=String.fromCharCode(e),e===92&&(ge.charCodeAt(j)!==117&&tt({},an,xn),++j,e=Ow("u"),(!e||e==="\\"||!mh(e.charCodeAt(0)))&&tt({},an,xn),t=e);j>>=")return j+=4,{type:bi,value:o,start:e,end:j};if(s=o.substr(0,3),s===">>>"||s==="<<="||s===">>=")return j+=3,{type:bi,value:s,start:e,end:j};if(r=s.substr(0,2),i===r[1]&&"+-<>&|".includes(i)||r==="=>")return j+=2,{type:bi,value:r,start:e,end:j};if(r==="//"&&tt({},an,xn),"<>=!+-*%&|^/".includes(i))return++j,{type:bi,value:i,start:e,end:j};tt({},an,xn)}function I1e(e){let t="";for(;j{if(parseInt(r,16)<=1114111)return"x";tt({},Mw)}).replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g,"x"));try{new RegExp(n)}catch{tt({},Mw)}try{return new RegExp(e,t)}catch{return null}}function U1e(){var e,t,n,i,r;for(e=ge[j],gm(e==="/","Regular expression literal must start with a slash"),t=ge[j++],n=!1,i=!1;j=0&&tt({},Mw,n),{value:n,literal:t}}function q1e(){var e,t,n,i;return lt=null,KO(),e=j,t=U1e(),n=j1e(),i=B1e(t.value,n.value),{literal:t.literal+n.literal,value:i,regex:{pattern:t.value,flags:n.value},start:e,end:j}}function W1e(e){return e.type===Tu||e.type===ba||e.type===hm||e.type===pm}function QO(){if(KO(),j>=On)return{type:fh,start:j,end:j};const e=ge.charCodeAt(j);return mh(e)?L1e():e===40||e===41||e===59?Lw():e===39||e===34?z1e():e===46?po(ge.charCodeAt(j+1))?JO():Lw():po(e)?JO():Lw()}function vi(){const e=lt;return j=e.end,lt=QO(),j=e.end,e}function eL(){const e=j;lt=QO(),j=e}function H1e(e){const t=new Lr(p1e);return t.elements=e,t}function tL(e,t,n){const i=new Lr(e==="||"||e==="&&"?v1e:g1e);return i.operator=e,i.left=t,i.right=n,i}function G1e(e,t){const n=new Lr(m1e);return n.callee=e,n.arguments=t,n}function V1e(e,t,n){const i=new Lr(y1e);return i.test=e,i.consequent=t,i.alternate=n,i}function Iw(e){const t=new Lr(YO);return t.name=e,t}function yh(e){const t=new Lr(b1e);return t.value=e.value,t.raw=ge.slice(e.start,e.end),e.regex&&(t.raw==="//"&&(t.raw="/(?:)/"),t.regex=e.regex),t}function nL(e,t,n){const i=new Lr(x1e);return i.computed=e==="[",i.object=t,i.property=n,i.computed||(n.member=!0),i}function Y1e(e){const t=new Lr(_1e);return t.properties=e,t}function iL(e,t,n){const i=new Lr(w1e);return i.key=t,i.value=n,i.kind=e,i}function X1e(e,t){const n=new Lr(E1e);return n.operator=e,n.argument=t,n.prefix=!0,n}function tt(e,t){var n,i=Array.prototype.slice.call(arguments,2),r=t.replace(/%(\d)/g,(s,o)=>(gm(o":case"<=":case">=":case"instanceof":case"in":t=7;break;case"<<":case">>":case">>>":t=8;break;case"+":case"-":t=9;break;case"*":case"/":case"%":t=11;break}return t}function ame(){var e,t,n,i,r,s,o,a,u,l;if(e=lt,u=bm(),i=lt,r=oL(i),r===0)return u;for(i.prec=r,vi(),t=[e,lt],o=bm(),s=[u,i,o];(r=oL(lt))>0;){for(;s.length>2&&r<=s[s.length-2].prec;)o=s.pop(),a=s.pop().value,u=s.pop(),t.pop(),n=tL(a,u,o),s.push(n);i=vi(),i.prec=r,s.push(i),t.push(lt),n=bm(),s.push(n)}for(l=s.length-1,n=s[l],t.pop();l>1;)t.pop(),n=tL(s[l-1].value,s[l-2],n),l-=2;return n}function Nu(){var e,t,n;return e=ame(),Ct("?")&&(vi(),t=Nu(),Ln(":"),n=Nu(),e=V1e(e,t,n)),e}function zw(){const e=Nu();if(Ct(","))throw new Error(hh);return e}function ume(e){ge=e,j=0,On=ge.length,lt=null,eL();const t=zw();if(lt.type!==fh)throw new Error("Unexpect token after expression.");return t}function Bw(e){const t=[];return e.type==="Identifier"?[e.name]:e.type==="Literal"?[e.value]:(e.type==="MemberExpression"&&(t.push(...Bw(e.object)),t.push(...Bw(e.property))),t)}function aL(e){return e.object.type==="MemberExpression"?aL(e.object):e.object.name==="datum"}function uL(e){const t=ume(e),n=new Set;return t.visit(i=>{i.type==="MemberExpression"&&aL(i)&&n.add(Bw(i).slice(1).join("."))}),n}class Fc extends ut{clone(){return new Fc(null,this.model,De(this.filter))}constructor(t,n,i){super(t),this.model=n,this.filter=i,this.expr=vm(this.model,this.filter,this),this._dependentFields=uL(this.expr)}dependentFields(){return this._dependentFields}producedFields(){return new Set}assemble(){return{type:"filter",expr:this.expr}}hash(){return`Filter ${this.expr}`}}function lme(e,t){const n={},i=e.config.selection;if(!t||!t.length)return n;let r=0;for(const s of t){const o=At(s.name),a=s.select,u=se(a)?a:a.type,l=re(a)?De(a):{type:u},c=i[u];for(const h in c)h==="fields"||h==="encodings"||(h==="mark"&&(l.mark={...c.mark,...l.mark}),(l[h]===void 0||l[h]===!0)&&(l[h]=De(c[h]??l[h])));const f=n[o]={...l,name:o,type:u,init:s.value,bind:s.bind,events:se(l.on)?ta(l.on,"scope"):oe(De(l.on))};if(ks(f)&&(r++,r>1)){delete n[o];continue}const d=De(s);for(const h of dm)h.defined(f)&&h.parse&&h.parse(e,f,d)}return r>1&&Z(kde),n}function lL(e,t,n,i="datum"){const r=se(t)?t:t.param,s=At(r),o=Q(s+Fu);let a;try{a=e.getSelectionComponent(s,r)}catch{return`!!${s}`}if(a.project.timeUnit){const d=n??e.component.data.raw,h=a.project.timeUnit.clone();d.parent?h.insertAsParentOf(d):d.parent=h}const u=a.project.hasSelectionId?"vlSelectionIdTest(":"vlSelectionTest(",l=a.resolve==="global"?")":`, ${Q(a.resolve)})`,c=`${u}${o}, ${i}${l}`,f=`length(data(${o}))`;return t.empty===!1?`${f} && ${c}`:`!${f} || ${c}`}function cL(e,t,n){const i=At(t),r=n.encoding;let s=n.field,o;try{o=e.getSelectionComponent(i,t)}catch{return i}if(!r&&!s)s=o.project.items[0].field,o.project.items.length>1&&Z(Ade(s));else if(r&&!s){const a=o.project.items.filter(u=>u.channel===r);!a.length||a.length>1?(s=o.project.items[0].field,Z($de(a,r,n,s))):s=a[0].field}return`${o.name}[${Q(er(s))}]`}function cme(e,t){for(const[n,i]of ia(e.component.selection??{})){const r=e.getName(`lookup_${n}`);e.component.data.outputNodes[r]=i.materialized=new yi(new Fc(t,e,{param:n}),r,Ft.Lookup,e.component.data.outputNodeRefCounts)}}function vm(e,t,n){return Gd(t,i=>se(i)?i:Lhe(i)?lL(e,i,n):SN(i))}function fme(e,t){if(e)return W(e)&&!da(e)?e.map(n=>K_(n,t)).join(", "):e}function Uw(e,t,n,i){var r,s;e.encode??(e.encode={}),(r=e.encode)[t]??(r[t]={}),(s=e.encode[t]).update??(s.update={}),e.encode[t].update[n]=i}function bh(e,t,n,i={header:!1}){var f,d;const{disable:r,orient:s,scale:o,labelExpr:a,title:u,zindex:l,...c}=e.combine();if(!r){for(const h in c){const p=h,g=P0e[p],m=c[p];if(g&&g!==t&&g!=="both")delete c[p];else if(ah(m)){const{condition:y,...b}=m,v=oe(y),x=fR[p];if(x){const{vgProp:_,part:E}=x,w=[...v.map(C=>{const{test:k,...S}=C;return{test:vm(null,k),...S}}),b];Uw(c,E,_,w),delete c[p]}else if(x===null){const _={signal:v.map(E=>{const{test:w,...C}=E;return`${vm(null,w)} ? ${XM(C)} : `}).join("")+XM(b)};c[p]=_}}else if(me(m)){const y=fR[p];if(y){const{vgProp:b,part:v}=y;Uw(c,v,b,m),delete c[p]}}qe(["labelAlign","labelBaseline"],p)&&c[p]===null&&delete c[p]}if(t==="grid"){if(!c.grid)return;if(c.encode){const{grid:h}=c.encode;c.encode={...h?{grid:h}:{}},ht(c.encode)&&delete c.encode}return{scale:o,orient:s,...c,domain:!1,labels:!1,aria:!1,maxExtent:0,minExtent:0,ticks:!1,zindex:zt(l,0)}}else{if(!i.header&&e.mainExtracted)return;if(a!==void 0){let p=a;(d=(f=c.encode)==null?void 0:f.labels)!=null&&d.update&&me(c.encode.labels.update.text)&&(p=du(a,"datum.label",c.encode.labels.update.text.signal)),Uw(c,"labels","text",{signal:p})}if(c.labelAlign===null&&delete c.labelAlign,c.encode){for(const p of dR)e.hasAxisPart(p)||delete c.encode[p];ht(c.encode)&&delete c.encode}const h=fme(u,n);return{scale:o,orient:s,grid:!1,...h?{title:h}:{},...c,...n.aria===!1?{aria:!1}:{},zindex:zt(l,0)}}}}function fL(e){const{axes:t}=e.component,n=[];for(const i of io)if(t[i]){for(const r of t[i])if(!r.get("disable")&&!r.get("gridScale")){const s=i==="x"?"height":"width",o=e.getSizeSignalRef(s).signal;s!==o&&n.push({name:s,update:o})}}return n}function dme(e,t){const{x:n=[],y:i=[]}=e;return[...n.map(r=>bh(r,"grid",t)),...i.map(r=>bh(r,"grid",t)),...n.map(r=>bh(r,"main",t)),...i.map(r=>bh(r,"main",t))].filter(r=>r)}function dL(e,t,n,i){return Object.assign.apply(null,[{},...e.map(r=>{if(r==="axisOrient"){const s=n==="x"?"bottom":"left",o=t[n==="x"?"axisBottom":"axisLeft"]||{},a=t[n==="x"?"axisTop":"axisRight"]||{},u=new Set([...Y(o),...Y(a)]),l={};for(const c of u.values())l[c]={signal:`${i.signal} === "${s}" ? ${Dr(o[c])} : ${Dr(a[c])}`};return l}return t[r]})])}function hme(e,t,n,i){const r=t==="band"?["axisDiscrete","axisBand"]:t==="point"?["axisDiscrete","axisPoint"]:NN(t)?["axisQuantitative"]:t==="time"||t==="utc"?["axisTemporal"]:[],s=e==="x"?"axisX":"axisY",o=me(n)?"axisOrient":`axis${Vd(n)}`,a=[...r,...r.map(l=>s+l.substr(4))],u=["axis",o,s];return{vlOnlyAxisConfig:dL(a,i,e,n),vgAxisConfig:dL(u,i,e,n),axisConfigStyle:pme([...u,...a],i)}}function pme(e,t){var i;const n=[{}];for(const r of e){let s=(i=t[r])==null?void 0:i.style;if(s){s=oe(s);for(const o of s)n.push(t.style[o])}}return Object.assign.apply(null,n)}function jw(e,t,n,i={}){var s;const r=ZM(e,n,t);if(r!==void 0)return{configFrom:"style",configValue:r};for(const o of["vlOnlyAxisConfig","vgAxisConfig","axisConfigStyle"])if(((s=i[o])==null?void 0:s[e])!==void 0)return{configFrom:o,configValue:i[o][e]};return{}}const hL={scale:({model:e,channel:t})=>e.scaleName(t),format:({format:e})=>e,formatType:({formatType:e})=>e,grid:({fieldOrDatumDef:e,axis:t,scaleType:n})=>t.grid??gme(n,e),gridScale:({model:e,channel:t})=>mme(e,t),labelAlign:({axis:e,labelAngle:t,orient:n,channel:i})=>e.labelAlign||gL(t,n,i),labelAngle:({labelAngle:e})=>e,labelBaseline:({axis:e,labelAngle:t,orient:n,channel:i})=>e.labelBaseline||pL(t,n,i),labelFlush:({axis:e,fieldOrDatumDef:t,channel:n})=>e.labelFlush??bme(t.type,n),labelOverlap:({axis:e,fieldOrDatumDef:t,scaleType:n})=>e.labelOverlap??vme(t.type,n,J(t)&&!!t.timeUnit,J(t)?t.sort:void 0),orient:({orient:e})=>e,tickCount:({channel:e,model:t,axis:n,fieldOrDatumDef:i,scaleType:r})=>{const s=e==="x"?"width":e==="y"?"height":void 0,o=s?t.getSizeSignalRef(s):void 0;return n.tickCount??_me({fieldOrDatumDef:i,scaleType:r,size:o,values:n.values})},tickMinStep:wme,title:({axis:e,model:t,channel:n})=>{if(e.title!==void 0)return e.title;const i=mL(t,n);if(i!==void 0)return i;const r=t.typedFieldDef(n),s=n==="x"?"x2":"y2",o=t.fieldDef(s);return QM(r?[QN(r)]:[],J(o)?[QN(o)]:[])},values:({axis:e,fieldOrDatumDef:t})=>Eme(e,t),zindex:({axis:e,fieldOrDatumDef:t,mark:n})=>e.zindex??Cme(n,t)};function gme(e,t){return!on(e)&&J(t)&&!_t(t==null?void 0:t.bin)&&!mn(t==null?void 0:t.bin)}function mme(e,t){const n=t==="x"?"y":"x";if(e.getScaleComponent(n))return e.scaleName(n)}function yme(e,t,n,i,r){const s=t==null?void 0:t.labelAngle;if(s!==void 0)return me(s)?s:Yd(s);{const{configValue:o}=jw("labelAngle",i,t==null?void 0:t.style,r);return o!==void 0?Yd(o):n===$t&&qe([R_,N_],e.type)&&!(J(e)&&e.timeUnit)?270:void 0}}function qw(e){return`(((${e.signal} % 360) + 360) % 360)`}function pL(e,t,n,i){if(e!==void 0)if(n==="x"){if(me(e)){const r=qw(e),s=me(t)?`(${t.signal} === "top")`:t==="top";return{signal:`(45 < ${r} && ${r} < 135) || (225 < ${r} && ${r} < 315) ? "middle" :(${r} <= 45 || 315 <= ${r}) === ${s} ? "bottom" : "top"`}}if(45{if(Au(i)&&JN(i.sort)){const{field:s,timeUnit:o}=i,a=i.sort,u=a.map((l,c)=>`${SN({field:s,timeUnit:o,equal:l})} ? ${c} : `).join("")+a.length;t=new Dc(t,{calculate:u,as:Tc(i,r,{forAs:!0})})}}),t}producedFields(){return new Set([this.transform.as])}dependentFields(){return this._dependentFields}assemble(){return{type:"formula",expr:this.transform.calculate,as:this.transform.as}}hash(){return`Calculate ${We(this.transform)}`}}function Tc(e,t,n){return te(e,{prefix:t,suffix:"sort_index",...n})}function xm(e,t){return qe(["top","bottom"],t)?"column":qe(["left","right"],t)||e==="row"?"row":"column"}function Mc(e,t,n,i){const r=i==="row"?n.headerRow:i==="column"?n.headerColumn:n.headerFacet;return zt((t||{})[e],r[e],n.header[e])}function _m(e,t,n,i){const r={};for(const s of e){const o=Mc(s,t||{},n,i);o!==void 0&&(r[s]=o)}return r}const Ww=["row","column"],Hw=["header","footer"];function kme(e,t){const n=e.component.layoutHeaders[t].title,i=e.config?e.config:void 0,r=e.component.layoutHeaders[t].facetFieldDef?e.component.layoutHeaders[t].facetFieldDef:void 0,{titleAnchor:s,titleAngle:o,titleOrient:a}=_m(["titleAnchor","titleAngle","titleOrient"],r.header,i,t),u=xm(t,a),l=Yd(o);return{name:`${t}-title`,type:"group",role:`${u}-title`,title:{text:n,...t==="row"?{orient:"left"}:{},style:"guide-title",...bL(l,u),...yL(u,l,s),...vL(i,r,t,spe,MR)}}}function yL(e,t,n="middle"){switch(n){case"start":return{align:"left"};case"end":return{align:"right"}}const i=gL(t,e==="row"?"left":"top",e==="row"?"y":"x");return i?{align:i}:{}}function bL(e,t){const n=pL(e,t==="row"?"left":"top",t==="row"?"y":"x",!0);return n?{baseline:n}:{}}function Ame(e,t){const n=e.component.layoutHeaders[t],i=[];for(const r of Hw)if(n[r])for(const s of n[r]){const o=Sme(e,t,r,n,s);o!=null&&i.push(o)}return i}function $me(e,t){const{sort:n}=e;return oo(n)?{field:te(n,{expr:"datum"}),order:n.order??"ascending"}:W(n)?{field:Tc(e,t,{expr:"datum"}),order:"ascending"}:{field:te(e,{expr:"datum"}),order:n??"ascending"}}function Gw(e,t,n){const{format:i,formatType:r,labelAngle:s,labelAnchor:o,labelOrient:a,labelExpr:u}=_m(["format","formatType","labelAngle","labelAnchor","labelOrient","labelExpr"],e.header,n,t),l=H_({fieldOrDatumDef:e,format:i,formatType:r,expr:"parent",config:n}).signal,c=xm(t,a);return{text:{signal:u?du(du(u,"datum.label",l),"datum.value",te(e,{expr:"parent"})):l},...t==="row"?{orient:"left"}:{},style:"guide-label",frame:"group",...bL(s,c),...yL(c,s,o),...vL(n,e,t,ope,NR)}}function Sme(e,t,n,i,r){if(r){let s=null;const{facetFieldDef:o}=i,a=e.config?e.config:void 0;if(o&&r.labels){const{labelOrient:f}=_m(["labelOrient"],o.header,a,t);(t==="row"&&!qe(["top","bottom"],f)||t==="column"&&!qe(["left","right"],f))&&(s=Gw(o,t,a))}const u=Oi(e)&&!ih(e.facet),l=r.axes,c=(l==null?void 0:l.length)>0;if(s||c){const f=t==="row"?"height":"width";return{name:e.getName(`${t}_${n}`),type:"group",role:`${t}-${n}`,...i.facetFieldDef?{from:{data:e.getName(`${t}_domain`)},sort:$me(o,t)}:{},...c&&u?{from:{data:e.getName(`facet_domain_${t}`)}}:{},...s?{title:s}:{},...r.sizeSignal?{encode:{update:{[f]:r.sizeSignal}}}:{},...c?{axes:l}:{}}}}return null}const Fme={column:{start:0,end:1},row:{start:1,end:0}};function Dme(e,t){return Fme[t][e]}function Tme(e,t){const n={};for(const i of ir){const r=e[i];if(r!=null&&r.facetFieldDef){const{titleAnchor:s,titleOrient:o}=_m(["titleAnchor","titleOrient"],r.facetFieldDef.header,t,i),a=xm(i,o),u=Dme(s,a);u!==void 0&&(n[a]=u)}}return ht(n)?void 0:n}function vL(e,t,n,i,r){const s={};for(const o of i){if(!r[o])continue;const a=Mc(o,t==null?void 0:t.header,e,n);a!==void 0&&(s[r[o]]=a)}return s}function Vw(e){return[...wm(e,"width"),...wm(e,"height"),...wm(e,"childWidth"),...wm(e,"childHeight")]}function wm(e,t){const n=t==="width"?"x":"y",i=e.component.layoutSize.get(t);if(!i||i==="merged")return[];const r=e.getSizeSignalRef(t).signal;if(i==="step"){const s=e.getScaleComponent(n);if(s){const o=s.get("type"),a=s.get("range");if(on(o)&&yu(a)){const u=e.scaleName(n);return Oi(e.parent)&&e.parent.component.resolve.scale[n]==="independent"?[xL(u,a)]:[xL(u,a),{name:r,update:_L(u,s,`domain('${u}').length`)}]}}throw new Error("layout size is step although width/height is not step.")}else if(i=="container"){const s=r.endsWith("width"),o=s?"containerSize()[0]":"containerSize()[1]",a=cw(e.config.view,s?"width":"height"),u=`isFinite(${o}) ? ${o} : ${a}`;return[{name:r,init:u,on:[{update:u,events:"window:resize"}]}]}else return[{name:r,value:i}]}function xL(e,t){const n=`${e}_step`;return me(t.step)?{name:n,update:t.step.signal}:{name:n,value:t.step}}function _L(e,t,n){const i=t.get("type"),r=t.get("padding"),s=zt(t.get("paddingOuter"),r);let o=t.get("paddingInner");return o=i==="band"?o!==void 0?o:r:1,`bandspace(${n}, ${Dr(o)}, ${Dr(s)}) * ${e}_step`}function wL(e){return e==="childWidth"?"width":e==="childHeight"?"height":e}function EL(e,t){return Y(e).reduce((n,i)=>({...n,...$c({model:t,channelDef:e[i],vgChannel:i,mainRefFn:r=>Et(r.value),invalidValueRef:void 0})}),{})}function CL(e,t){if(Oi(t))return e==="theta"?"independent":"shared";if(Lc(t))return"shared";if(l6(t))return Bt(e)||e==="theta"||e==="radius"?"independent":"shared";throw new Error("invalid model type for resolve")}function Yw(e,t){const n=e.scale[t],i=Bt(t)?"axis":"legend";return n==="independent"?(e[i][t]==="shared"&&Z(rhe(t)),"independent"):e[i][t]||"shared"}const Mme={...upe,disable:1,labelExpr:1,selections:1,opacity:1,shape:1,stroke:1,fill:1,size:1,strokeWidth:1,strokeDash:1,encode:1},kL=Y(Mme);class Nme extends lo{}const AL={symbols:Rme,gradient:Ome,labels:Lme,entries:Ime};function Rme(e,{fieldOrDatumDef:t,model:n,channel:i,legendCmpt:r,legendType:s}){if(s!=="symbol")return;const{markDef:o,encoding:a,config:u,mark:l}=n,c=o.filled&&l!=="trail";let f={...dde({},n,u0e),...SO(n,{filled:c})};const d=r.get("symbolOpacity")??u.legend.symbolOpacity,h=r.get("symbolFillColor")??u.legend.symbolFillColor,p=r.get("symbolStrokeColor")??u.legend.symbolStrokeColor,g=d===void 0?$L(a.opacity)??o.opacity:void 0;if(f.fill){if(i==="fill"||c&&i===pi)delete f.fill;else if(X(f.fill,"field"))h?delete f.fill:(f.fill=Et(u.legend.symbolBaseFillColor??"black"),f.fillOpacity=Et(g??1));else if(W(f.fill)){const m=Xw(a.fill??a.color)??o.fill??(c&&o.color);m&&(f.fill=Et(m))}}if(f.stroke){if(i==="stroke"||!c&&i===pi)delete f.stroke;else if(X(f.stroke,"field")||p)delete f.stroke;else if(W(f.stroke)){const m=zt(Xw(a.stroke||a.color),o.stroke,c?o.color:void 0);m&&(f.stroke={value:m})}}if(i!==no){const m=J(t)&&FL(n,r,t);m?f.opacity=[{test:m,...Et(g??1)},Et(u.legend.unselectedOpacity)]:g&&(f.opacity=Et(g))}return f={...f,...e},ht(f)?void 0:f}function Ome(e,{model:t,legendType:n,legendCmpt:i}){if(n!=="gradient")return;const{config:r,markDef:s,encoding:o}=t;let a={};const l=(i.get("gradientOpacity")??r.legend.gradientOpacity)===void 0?$L(o.opacity)||s.opacity:void 0;return l&&(a.opacity=Et(l)),a={...a,...e},ht(a)?void 0:a}function Lme(e,{fieldOrDatumDef:t,model:n,channel:i,legendCmpt:r}){const s=n.legend(i)||{},o=n.config,a=J(t)?FL(n,r,t):void 0,u=a?[{test:a,value:1},{value:o.legend.unselectedOpacity}]:void 0,{format:l,formatType:c}=s;let f;ku(c)?f=Mr({fieldOrDatumDef:t,field:"datum.value",format:l,formatType:c,config:o}):l===void 0&&c===void 0&&o.customFormatTypes&&(t.type==="quantitative"&&o.numberFormatType?f=Mr({fieldOrDatumDef:t,field:"datum.value",format:o.numberFormat,formatType:o.numberFormatType,config:o}):t.type==="temporal"&&o.timeFormatType&&J(t)&&t.timeUnit===void 0&&(f=Mr({fieldOrDatumDef:t,field:"datum.value",format:o.timeFormat,formatType:o.timeFormatType,config:o})));const d={...u?{opacity:u}:{},...f?{text:f}:{},...e};return ht(d)?void 0:d}function Ime(e,{legendCmpt:t}){const n=t.get("selections");return n!=null&&n.length?{...e,fill:{value:"transparent"}}:e}function $L(e){return SL(e,(t,n)=>Math.max(t,n.value))}function Xw(e){return SL(e,(t,n)=>zt(t,n.value))}function SL(e,t){if(A0e(e))return oe(e.condition).reduce(t,e.value);if(Nr(e))return e.value}function FL(e,t,n){const i=t.get("selections");if(!(i!=null&&i.length))return;const r=Q(n.field);return i.map(s=>`(!length(data(${Q(At(s)+Fu)})) || (${s}[${r}] && indexof(${s}[${r}], datum.value) >= 0))`).join(" || ")}const DL={direction:({direction:e})=>e,format:({fieldOrDatumDef:e,legend:t,config:n})=>{const{format:i,formatType:r}=t;return VN(e,e.type,i,r,n,!1)},formatType:({legend:e,fieldOrDatumDef:t,scaleType:n})=>{const{formatType:i}=e;return YN(i,t,n)},gradientLength:e=>{const{legend:t,legendConfig:n}=e;return t.gradientLength??n.gradientLength??Wme(e)},labelOverlap:({legend:e,legendConfig:t,scaleType:n})=>e.labelOverlap??t.labelOverlap??Hme(n),symbolType:({legend:e,markDef:t,channel:n,encoding:i})=>e.symbolType??zme(t.type,n,i.shape,t.shape),title:({fieldOrDatumDef:e,config:t})=>xc(e,t,{allowDisabling:!0}),type:({legendType:e,scaleType:t,channel:n})=>{if(pc(n)&&vs(t)){if(e==="gradient")return}else if(e==="symbol")return;return e},values:({fieldOrDatumDef:e,legend:t})=>Pme(t,e)};function Pme(e,t){const n=e.values;if(W(n))return cR(t,n);if(me(n))return n}function zme(e,t,n,i){if(t!=="shape"){const r=Xw(n)??i;if(r)return r}switch(e){case"bar":case"rect":case"image":case"square":return"square";case"line":case"trail":case"rule":return"stroke";case"arc":case"point":case"circle":case"tick":case"geoshape":case"area":case"text":return"circle"}}function Bme(e){const{legend:t}=e;return zt(t.type,Ume(e))}function Ume({channel:e,timeUnit:t,scaleType:n}){if(pc(e)){if(qe(["quarter","month","day"],t))return"symbol";if(vs(n))return"gradient"}return"symbol"}function jme({legendConfig:e,legendType:t,orient:n,legend:i}){return i.direction??e[t?"gradientDirection":"symbolDirection"]??qme(n,t)}function qme(e,t){switch(e){case"top":case"bottom":return"horizontal";case"left":case"right":case"none":case void 0:return;default:return t==="gradient"?"horizontal":void 0}}function Wme({legendConfig:e,model:t,direction:n,orient:i,scaleType:r}){const{gradientHorizontalMaxLength:s,gradientHorizontalMinLength:o,gradientVerticalMaxLength:a,gradientVerticalMinLength:u}=e;if(vs(r))return n==="horizontal"?i==="top"||i==="bottom"?TL(t,"width",o,s):o:TL(t,"height",u,a)}function TL(e,t,n,i){return{signal:`clamp(${e.getSizeSignalRef(t).signal}, ${n}, ${i})`}}function Hme(e){if(qe(["quantile","threshold","log","symlog"],e))return"greedy"}function ML(e){const t=Dt(e)?Gme(e):Kme(e);return e.component.legends=t,t}function Gme(e){const{encoding:t}=e,n={};for(const i of[pi,...OR]){const r=Xt(t[i]);!r||!e.getScaleComponent(i)||i===gi&&J(r)&&r.type===mc||(n[i]=Xme(e,i))}return n}function Vme(e,t){const n=e.scaleName(t);if(e.mark==="trail"){if(t==="color")return{stroke:n};if(t==="size")return{strokeWidth:n}}return t==="color"?e.markDef.filled?{fill:n}:{stroke:n}:{[t]:n}}function Yme(e,t,n,i){switch(t){case"disable":return n!==void 0;case"values":return!!(n!=null&&n.values);case"title":if(t==="title"&&e===(i==null?void 0:i.title))return!0}return e===(n||{})[t]}function Xme(e,t){var x;let n=e.legend(t);const{markDef:i,encoding:r,config:s}=e,o=s.legend,a=new Nme({},Vme(e,t));Xge(e,t,a);const u=n!==void 0?!n:o.disable;if(a.set("disable",u,n!==void 0),u)return a;n=n||{};const l=e.getScaleComponent(t).get("type"),c=Xt(r[t]),f=J(c)?(x=sn(c.timeUnit))==null?void 0:x.unit:void 0,d=n.orient||s.legend.orient||"right",h=Bme({legend:n,channel:t,timeUnit:f,scaleType:l}),p=jme({legend:n,legendType:h,orient:d,legendConfig:o}),g={legend:n,channel:t,model:e,markDef:i,encoding:r,fieldOrDatumDef:c,legendConfig:o,config:s,scaleType:l,orient:d,legendType:h,direction:p};for(const _ of kL){if(h==="gradient"&&_.startsWith("symbol")||h==="symbol"&&_.startsWith("gradient"))continue;const E=_ in DL?DL[_](g):n[_];if(E!==void 0){const w=Yme(E,_,n,e.fieldDef(t));(w||s.legend[_]===void 0)&&a.set(_,E,w)}}const m=(n==null?void 0:n.encoding)??{},y=a.get("selections"),b={},v={fieldOrDatumDef:c,model:e,channel:t,legendCmpt:a,legendType:h};for(const _ of["labels","legend","title","symbols","gradient","entries"]){const E=EL(m[_]??{},e),w=_ in AL?AL[_](E,v):E;w!==void 0&&!ht(w)&&(b[_]={...y!=null&&y.length&&J(c)?{name:`${At(c.field)}_legend_${_}`}:{},...y!=null&&y.length?{interactive:!!y}:{},update:w})}return ht(b)||a.set("encode",b,!!(n!=null&&n.encoding)),a}function Kme(e){const{legends:t,resolve:n}=e.component;for(const i of e.children){ML(i);for(const r of Y(i.component.legends))n.legend[r]=Yw(e.component.resolve,r),n.legend[r]==="shared"&&(t[r]=NL(t[r],i.component.legends[r]),t[r]||(n.legend[r]="independent",delete t[r]))}for(const i of Y(t))for(const r of e.children)r.component.legends[i]&&n.legend[i]==="shared"&&delete r.component.legends[i];return t}function NL(e,t){var s,o,a,u;if(!e)return t.clone();const n=e.getWithExplicit("orient"),i=t.getWithExplicit("orient");if(n.explicit&&i.explicit&&n.value!==i.value)return;let r=!1;for(const l of kL){const c=ma(e.getWithExplicit(l),t.getWithExplicit(l),l,"legend",(f,d)=>{switch(l){case"symbolType":return Zme(f,d);case"title":return tN(f,d);case"type":return r=!0,Ri("symbol")}return rm(f,d,l,"legend")});e.setWithExplicit(l,c)}return r&&((o=(s=e.implicit)==null?void 0:s.encode)!=null&&o.gradient&&w1(e.implicit,["encode","gradient"]),(u=(a=e.explicit)==null?void 0:a.encode)!=null&&u.gradient&&w1(e.explicit,["encode","gradient"])),e}function Zme(e,t){return t.value==="circle"?t:e}function Jme(e,t,n,i){var r,s;e.encode??(e.encode={}),(r=e.encode)[t]??(r[t]={}),(s=e.encode[t]).update??(s.update={}),e.encode[t].update[n]=i}function RL(e){const t=e.component.legends,n={};for(const r of Y(t)){const s=e.getScaleComponent(r),o=pt(s.get("domains"));if(n[o])for(const a of n[o])NL(a,t[r])||n[o].push(t[r]);else n[o]=[t[r].clone()]}return gn(n).flat().map(r=>Qme(r,e.config)).filter(r=>r!==void 0)}function Qme(e,t){var o,a,u;const{disable:n,labelExpr:i,selections:r,...s}=e.combine();if(!n){if(t.aria===!1&&s.aria==null&&(s.aria=!1),(o=s.encode)!=null&&o.symbols){const l=s.encode.symbols.update;l.fill&&l.fill.value!=="transparent"&&!l.stroke&&!s.stroke&&(l.stroke={value:"transparent"});for(const c of OR)s[c]&&delete l[c]}if(s.title||delete s.title,i!==void 0){let l=i;(u=(a=s.encode)==null?void 0:a.labels)!=null&&u.update&&me(s.encode.labels.update.text)&&(l=du(i,"datum.label",s.encode.labels.update.text.signal)),Jme(s,"labels","text",{signal:l})}return s}}function e2e(e){return Lc(e)||l6(e)?t2e(e):OL(e)}function t2e(e){return e.children.reduce((t,n)=>t.concat(n.assembleProjections()),OL(e))}function OL(e){const t=e.component.projection;if(!t||t.merged)return[];const n=t.combine(),{name:i}=n;if(t.data){const r={signal:`[${t.size.map(o=>o.signal).join(", ")}]`},s=t.data.reduce((o,a)=>{const u=me(a)?a.signal:`data('${e.lookupDataSource(a)}')`;return qe(o,u)||o.push(u),o},[]);if(s.length<=0)throw new Error("Projection's fit didn't find any data sources");return[{name:i,size:r,fit:{signal:s.length>1?`[${s.join(", ")}]`:s[0]},...n}]}else return[{name:i,translate:{signal:"[width / 2, height / 2]"},...n}]}const n2e=["type","clipAngle","clipExtent","center","rotate","precision","reflectX","reflectY","coefficient","distance","fraction","lobes","parallel","radius","ratio","spacing","tilt"];class LL extends lo{constructor(t,n,i,r){super({...n},{name:t}),this.specifiedProjection=n,this.size=i,this.data=r,this.merged=!1}get isFit(){return!!this.data}}function IL(e){e.component.projection=Dt(e)?i2e(e):o2e(e)}function i2e(e){if(e.hasProjection){const t=yn(e.specifiedProjection),n=!(t&&(t.scale!=null||t.translate!=null)),i=n?[e.getSizeSignalRef("width"),e.getSizeSignalRef("height")]:void 0,r=n?r2e(e):void 0,s=new LL(e.projectionName(!0),{...yn(e.config.projection),...t},i,r);return s.get("type")||s.set("type","equalEarth",!1),s}}function r2e(e){const t=[],{encoding:n}=e;for(const i of[[Sr,$r],[nr,Fr]])(Xt(n[i[0]])||Xt(n[i[1]]))&&t.push({signal:e.getName(`geojson_${t.length}`)});return e.channelHasField(gi)&&e.typedFieldDef(gi).type===mc&&t.push({signal:e.getName(`geojson_${t.length}`)}),t.length===0&&t.push(e.requestDataName(Ft.Main)),t}function s2e(e,t){const n=Q7(n2e,r=>!!(!ue(e.explicit,r)&&!ue(t.explicit,r)||ue(e.explicit,r)&&ue(t.explicit,r)&&Mi(e.get(r),t.get(r))));if(Mi(e.size,t.size)){if(n)return e;if(Mi(e.explicit,{}))return t;if(Mi(t.explicit,{}))return e}return null}function o2e(e){if(e.children.length===0)return;let t;for(const i of e.children)IL(i);const n=Q7(e.children,i=>{const r=i.component.projection;if(r)if(t){const s=s2e(t,r);return s&&(t=s),!!s}else return t=r,!0;else return!0});if(t&&n){const i=e.projectionName(!0),r=new LL(i,t.specifiedProjection,t.size,De(t.data));for(const s of e.children){const o=s.component.projection;o&&(o.isFit&&r.data.push(...s.component.projection.data),s.renameProjection(o.get("name"),i),o.merged=!0)}return r}}function a2e(e,t,n,i){if(oh(t,n)){const r=Dt(e)?e.axis(n)??e.legend(n)??{}:{},s=te(t,{expr:"datum"}),o=te(t,{expr:"datum",binSuffix:"end"});return{formulaAs:te(t,{binSuffix:"range",forAs:!0}),formula:nh(s,o,r.format,r.formatType,i)}}return{}}function PL(e,t){return`${WM(e)}_${t}`}function u2e(e,t){return{signal:e.getName(`${t}_bins`),extentSignal:e.getName(`${t}_extent`)}}function Kw(e,t,n){const i=X1(n,void 0)??{},r=PL(i,t);return e.getName(`${r}_bins`)}function l2e(e){return"as"in e}function zL(e,t,n){let i,r;l2e(e)?i=se(e.as)?[e.as,`${e.as}_end`]:[e.as[0],e.as[1]]:i=[te(e,{forAs:!0}),te(e,{binSuffix:"end",forAs:!0})];const s={...X1(t,void 0)},o=PL(s,e.field),{signal:a,extentSignal:u}=u2e(n,o);if(T1(s.extent)){const c=s.extent;r=cL(n,c.param,c),delete s.extent}const l={bin:s,field:e.field,as:[i],...a?{signal:a}:{},...u?{extentSignal:u}:{},...r?{span:r}:{}};return{key:o,binComponent:l}}class $s extends ut{clone(){return new $s(null,De(this.bins))}constructor(t,n){super(t),this.bins=n}static makeFromEncoding(t,n){const i=n.reduceFieldDef((r,s,o)=>{if(ei(s)&&_t(s.bin)){const{key:a,binComponent:u}=zL(s,s.bin,n);r[a]={...u,...r[a],...a2e(n,s,o,n.config)}}return r},{});return ht(i)?null:new $s(t,i)}static makeFromTransform(t,n,i){const{key:r,binComponent:s}=zL(n,n.bin,i);return new $s(t,{[r]:s})}merge(t,n){for(const i of Y(t.bins))i in this.bins?(n(t.bins[i].signal,this.bins[i].signal),this.bins[i].as=fs([...this.bins[i].as,...t.bins[i].as],We)):this.bins[i]=t.bins[i];for(const i of t.children)t.removeChild(i),i.parent=this;t.remove()}producedFields(){return new Set(gn(this.bins).map(t=>t.as).flat(2))}dependentFields(){return new Set(gn(this.bins).map(t=>t.field))}hash(){return`Bin ${We(this.bins)}`}assemble(){return gn(this.bins).flatMap(t=>{const n=[],[i,...r]=t.as,{extent:s,...o}=t.bin,a={type:"bin",field:er(t.field),as:i,signal:t.signal,...T1(s)?{extent:null}:{extent:s},...t.span?{span:{signal:`span(${t.span})`}}:{},...o};!s&&t.extentSignal&&(n.push({type:"extent",field:er(t.field),signal:t.extentSignal}),a.extent={signal:t.extentSignal}),n.push(a);for(const u of r)for(let l=0;l<2;l++)n.push({type:"formula",expr:te({field:i[l]},{expr:"datum"}),as:u[l]});return t.formula&&n.push({type:"formula",expr:t.formula,as:t.formulaAs}),n})}}function c2e(e,t,n,i){var s;const r=Dt(i)?i.encoding[gs(t)]:void 0;if(ei(n)&&Dt(i)&&tR(n,r,i.markDef,i.config)){e.add(te(n,{})),e.add(te(n,{suffix:"end"}));const{mark:o,markDef:a,config:u}=i,l=pa({fieldDef:n,markDef:a,config:u});eh(o)&&l!==.5&&Bt(t)&&(e.add(te(n,{suffix:sm})),e.add(te(n,{suffix:om}))),n.bin&&oh(n,t)&&e.add(te(n,{binSuffix:"range"}))}else if(OM(t)){const o=RM(t);e.add(i.getName(o))}else e.add(te(n));return Au(n)&&Xhe((s=n.scale)==null?void 0:s.range)&&e.add(n.scale.range.field),e}function f2e(e,t){for(const n of Y(t)){const i=t[n];for(const r of Y(i))n in e?e[n][r]=new Set([...e[n][r]??[],...i[r]]):e[n]={[r]:i[r]}}}class Ir extends ut{clone(){return new Ir(null,new Set(this.dimensions),De(this.measures))}constructor(t,n,i){super(t),this.dimensions=n,this.measures=i}get groupBy(){return this.dimensions}static makeFromEncoding(t,n){let i=!1;n.forEachFieldDef(o=>{o.aggregate&&(i=!0)});const r={},s=new Set;return!i||(n.forEachFieldDef((o,a)=>{const{aggregate:u,field:l}=o;if(u)if(u==="count")r["*"]??(r["*"]={}),r["*"].count=new Set([te(o,{forAs:!0})]);else{if(ro(u)||fa(u)){const c=ro(u)?"argmin":"argmax",f=u[c];r[f]??(r[f]={}),r[f][c]=new Set([te({op:c,field:f},{forAs:!0})])}else r[l]??(r[l]={}),r[l][u]=new Set([te(o,{forAs:!0})]);ms(a)&&n.scaleDomain(a)==="unaggregated"&&(r[l]??(r[l]={}),r[l].min=new Set([te({field:l,aggregate:"min"},{forAs:!0})]),r[l].max=new Set([te({field:l,aggregate:"max"},{forAs:!0})]))}else c2e(s,a,o,n)}),s.size+Y(r).length===0)?null:new Ir(t,s,r)}static makeFromTransform(t,n){var i;const r=new Set,s={};for(const o of n.aggregate){const{op:a,field:u,as:l}=o;a&&(a==="count"?(s["*"]??(s["*"]={}),s["*"].count=new Set([l||te(o,{forAs:!0})])):(s[u]??(s[u]={}),(i=s[u])[a]??(i[a]=new Set),s[u][a].add(l||te(o,{forAs:!0}))))}for(const o of n.groupby??[])r.add(o);return r.size+Y(s).length===0?null:new Ir(t,r,s)}merge(t){return kM(this.dimensions,t.dimensions)?(f2e(this.measures,t.measures),!0):(xhe("different dimensions, cannot merge"),!1)}addDimensions(t){t.forEach(this.dimensions.add,this.dimensions)}dependentFields(){return new Set([...this.dimensions,...Y(this.measures)])}producedFields(){const t=new Set;for(const n of Y(this.measures))for(const i of Y(this.measures[n])){const r=this.measures[n][i];r.size===0?t.add(`${i}_${n}`):r.forEach(t.add,t)}return t}hash(){return`Aggregate ${We({dimensions:this.dimensions,measures:this.measures})}`}assemble(){const t=[],n=[],i=[];for(const s of Y(this.measures))for(const o of Y(this.measures[s]))for(const a of this.measures[s][o])i.push(a),t.push(o),n.push(s==="*"?null:er(s));return{type:"aggregate",groupby:[...this.dimensions].map(er),ops:t,fields:n,as:i}}}class Nc extends ut{constructor(t,n,i,r){super(t),this.model=n,this.name=i,this.data=r;for(const s of ir){const o=n.facet[s];if(o){const{bin:a,sort:u}=o;this[s]={name:n.getName(`${s}_domain`),fields:[te(o),..._t(a)?[te(o,{binSuffix:"end"})]:[]],...oo(u)?{sortField:u}:W(u)?{sortIndexField:Tc(o,s)}:{}}}}this.childModel=n.child}hash(){let t="Facet";for(const n of ir)this[n]&&(t+=` ${n.charAt(0)}:${We(this[n])}`);return t}get fields(){var n;const t=[];for(const i of ir)(n=this[i])!=null&&n.fields&&t.push(...this[i].fields);return t}dependentFields(){const t=new Set(this.fields);for(const n of ir)this[n]&&(this[n].sortField&&t.add(this[n].sortField.field),this[n].sortIndexField&&t.add(this[n].sortIndexField));return t}producedFields(){return new Set}getSource(){return this.name}getChildIndependentFieldsWithStep(){const t={};for(const n of io){const i=this.childModel.component.scales[n];if(i&&!i.merged){const r=i.get("type"),s=i.get("range");if(on(r)&&yu(s)){const o=Cm(this.childModel,n),a=a6(o);a?t[n]=a:Z(b_(n))}}}return t}assembleRowColumnHeaderData(t,n,i){const r={row:"y",column:"x",facet:void 0}[t],s=[],o=[],a=[];r&&i&&i[r]&&(n?(s.push(`distinct_${i[r]}`),o.push("max")):(s.push(i[r]),o.push("distinct")),a.push(`distinct_${i[r]}`));const{sortField:u,sortIndexField:l}=this[t];if(u){const{op:c=j1,field:f}=u;s.push(f),o.push(c),a.push(te(u,{forAs:!0}))}else l&&(s.push(l),o.push("max"),a.push(l));return{name:this[t].name,source:n??this.data,transform:[{type:"aggregate",groupby:this[t].fields,...s.length?{fields:s,ops:o,as:a}:{}}]}}assembleFacetHeaderData(t){var u;const{columns:n}=this.model.layout,{layoutHeaders:i}=this.model.component,r=[],s={};for(const l of Ww){for(const c of Hw){const f=(i[l]&&i[l][c])??[];for(const d of f)if(((u=d.axes)==null?void 0:u.length)>0){s[l]=!0;break}}if(s[l]){const c=`length(data("${this.facet.name}"))`,f=l==="row"?n?{signal:`ceil(${c} / ${n})`}:1:n?{signal:`min(${c}, ${n})`}:{signal:c};r.push({name:`${this.facet.name}_${l}`,transform:[{type:"sequence",start:0,stop:f}]})}}const{row:o,column:a}=s;return(o||a)&&r.unshift(this.assembleRowColumnHeaderData("facet",null,t)),r}assemble(){const t=[];let n=null;const i=this.getChildIndependentFieldsWithStep(),{column:r,row:s,facet:o}=this;if(r&&s&&(i.x||i.y)){n=`cross_${this.column.name}_${this.row.name}`;const a=[].concat(i.x??[],i.y??[]),u=a.map(()=>"distinct");t.push({name:n,source:this.data,transform:[{type:"aggregate",groupby:this.fields,fields:a,ops:u}]})}for(const a of[Js,Zs])this[a]&&t.push(this.assembleRowColumnHeaderData(a,n,i));if(o){const a=this.assembleFacetHeaderData(i);a&&t.push(...a)}return t}}function BL(e){return e.startsWith("'")&&e.endsWith("'")||e.startsWith('"')&&e.endsWith('"')?e.slice(1,-1):e}function d2e(e,t){const n=i_(e);if(t==="number")return`toNumber(${n})`;if(t==="boolean")return`toBoolean(${n})`;if(t==="string")return`toString(${n})`;if(t==="date")return`toDate(${n})`;if(t==="flatten")return n;if(t.startsWith("date:")){const i=BL(t.slice(5,t.length));return`timeParse(${n},'${i}')`}else if(t.startsWith("utc:")){const i=BL(t.slice(4,t.length));return`utcParse(${n},'${i}')`}else return Z(Fde(t)),null}function h2e(e){const t={};return _1(e.filter,n=>{if($N(n)){let i=null;A_(n)?i=Ni(n.equal):S_(n)?i=Ni(n.lte):$_(n)?i=Ni(n.lt):F_(n)?i=Ni(n.gt):D_(n)?i=Ni(n.gte):T_(n)?i=n.range[0]:M_(n)&&(i=(n.oneOf??n.in)[0]),i&&(vu(i)?t[n.field]="date":Ze(i)?t[n.field]="number":se(i)&&(t[n.field]="string")),n.timeUnit&&(t[n.field]="date")}}),t}function p2e(e){const t={};function n(i){wc(i)?t[i.field]="date":i.type==="quantitative"&&rde(i.aggregate)?t[i.field]="number":fc(i.field)>1?i.field in t||(t[i.field]="flatten"):Au(i)&&oo(i.sort)&&fc(i.sort.field)>1&&(i.sort.field in t||(t[i.sort.field]="flatten"))}if((Dt(e)||Oi(e))&&e.forEachFieldDef((i,r)=>{if(ei(i))n(i);else{const s=gu(r),o=e.fieldDef(s);n({...i,type:o.type})}}),Dt(e)){const{mark:i,markDef:r,encoding:s}=e;if(ha(i)&&!e.encoding.order){const o=r.orient==="horizontal"?"y":"x",a=s[o];J(a)&&a.type==="quantitative"&&!(a.field in t)&&(t[a.field]="number")}}return t}function g2e(e){const t={};if(Dt(e)&&e.component.selection)for(const n of Y(e.component.selection)){const i=e.component.selection[n];for(const r of i.project.items)!r.channel&&fc(r.field)>1&&(t[r.field]="flatten")}return t}class In extends ut{clone(){return new In(null,De(this._parse))}constructor(t,n){super(t),this._parse=n}hash(){return`Parse ${We(this._parse)}`}static makeExplicit(t,n,i){var o;let r={};const s=n.data;return!ya(s)&&((o=s==null?void 0:s.format)!=null&&o.parse)&&(r=s.format.parse),this.makeWithAncestors(t,r,{},i)}static makeWithAncestors(t,n,i,r){for(const a of Y(i)){const u=r.getWithExplicit(a);u.value!==void 0&&(u.explicit||u.value===i[a]||u.value==="derived"||i[a]==="flatten"?delete i[a]:Z(uN(a,i[a],u.value)))}for(const a of Y(n)){const u=r.get(a);u!==void 0&&(u===n[a]?delete n[a]:Z(uN(a,n[a],u)))}const s=new lo(n,i);r.copyAll(s);const o={};for(const a of Y(s.combine())){const u=s.get(a);u!==null&&(o[a]=u)}return Y(o).length===0||r.parseNothing?null:new In(t,o)}get parse(){return this._parse}merge(t){this._parse={...this._parse,...t.parse},t.remove()}assembleFormatParse(){const t={};for(const n of Y(this._parse)){const i=this._parse[n];fc(n)===1&&(t[n]=i)}return t}producedFields(){return new Set(Y(this._parse))}dependentFields(){return new Set(Y(this._parse))}assembleTransforms(t=!1){return Y(this._parse).filter(n=>t?fc(n)>1:!0).map(n=>{const i=d2e(n,this._parse[n]);return i?{type:"formula",expr:i,as:cc(n)}:null}).filter(n=>n!==null)}}class va extends ut{clone(){return new va(null)}constructor(t){super(t)}dependentFields(){return new Set}producedFields(){return new Set([Or])}hash(){return"Identifier"}assemble(){return{type:"identifier",as:Or}}}class vh extends ut{clone(){return new vh(null,this.params)}constructor(t,n){super(t),this.params=n}dependentFields(){return new Set}producedFields(){}hash(){return`Graticule ${We(this.params)}`}assemble(){return{type:"graticule",...this.params===!0?{}:this.params}}}class xh extends ut{clone(){return new xh(null,this.params)}constructor(t,n){super(t),this.params=n}dependentFields(){return new Set}producedFields(){return new Set([this.params.as??"data"])}hash(){return`Hash ${We(this.params)}`}assemble(){return{type:"sequence",...this.params}}}class Ru extends ut{constructor(t){super(null),t??(t={name:"source"});let n;if(ya(t)||(n=t.format?{...hi(t.format,["parse"])}:{}),uh(t))this._data={values:t.values};else if(Cc(t)){if(this._data={url:t.url},!n.type){let i=/(?:\.([^.]+))?$/.exec(t.url)[1];qe(["json","csv","tsv","dsv","topojson"],i)||(i="json"),n.type=i}}else dO(t)?this._data={values:[{type:"Sphere"}]}:(cO(t)||ya(t))&&(this._data={});this._generator=ya(t),t.name&&(this._name=t.name),n&&!ht(n)&&(this._data.format=n)}dependentFields(){return new Set}producedFields(){}get data(){return this._data}hasName(){return!!this._name}get isGenerator(){return this._generator}get dataName(){return this._name}set dataName(t){this._name=t}set parent(t){throw new Error("Source nodes have to be roots.")}remove(){throw new Error("Source nodes are roots and cannot be removed.")}hash(){throw new Error("Cannot hash sources")}assemble(){return{name:this._name,...this._data,transform:[]}}}var UL=function(e,t,n,i,r){if(i==="m")throw new TypeError("Private method is not writable");if(i==="a"&&!r)throw new TypeError("Private accessor was defined without a setter");if(typeof t=="function"?e!==t||!r:!t.has(e))throw new TypeError("Cannot write private member to an object whose class did not declare it");return i==="a"?r.call(e,n):r?r.value=n:t.set(e,n),n},m2e=function(e,t,n,i){if(n==="a"&&!i)throw new TypeError("Private accessor was defined without a getter");if(typeof t=="function"?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return n==="m"?i:n==="a"?i.call(e):i?i.value:t.get(e)},_h;function Zw(e){return e instanceof Ru||e instanceof vh||e instanceof xh}class Jw{constructor(){_h.set(this,void 0),UL(this,_h,!1,"f")}setModified(){UL(this,_h,!0,"f")}get modifiedFlag(){return m2e(this,_h,"f")}}_h=new WeakMap;class Ou extends Jw{getNodeDepths(t,n,i){i.set(t,n);for(const r of t.children)this.getNodeDepths(r,n+1,i);return i}optimize(t){const i=[...this.getNodeDepths(t,0,new Map).entries()].sort((r,s)=>s[1]-r[1]);for(const r of i)this.run(r[0]);return this.modifiedFlag}}class Qw extends Jw{optimize(t){this.run(t);for(const n of t.children)this.optimize(n);return this.modifiedFlag}}class y2e extends Qw{mergeNodes(t,n){const i=n.shift();for(const r of n)t.removeChild(r),r.parent=i,r.remove()}run(t){const n=t.children.map(r=>r.hash()),i={};for(let r=0;r1&&(this.setModified(),this.mergeNodes(t,i[r]))}}class b2e extends Qw{constructor(t){super(),this.requiresSelectionId=t&&Tw(t)}run(t){t instanceof va&&(this.requiresSelectionId&&(Zw(t.parent)||t.parent instanceof Ir||t.parent instanceof In)||(this.setModified(),t.remove()))}}class v2e extends Jw{optimize(t){return this.run(t,new Set),this.modifiedFlag}run(t,n){let i=new Set;t instanceof Cs&&(i=t.producedFields(),e_(i,n)&&(this.setModified(),t.removeFormulas(n),t.producedFields.length===0&&t.remove()));for(const r of t.children)this.run(r,new Set([...n,...i]))}}class x2e extends Qw{constructor(){super()}run(t){t instanceof yi&&!t.isRequired()&&(this.setModified(),t.remove())}}class _2e extends Ou{run(t){if(!Zw(t)&&!(t.numChildren()>1)){for(const n of t.children)if(n instanceof In)if(t instanceof In)this.setModified(),t.merge(n);else{if(n_(t.producedFields(),n.dependentFields()))continue;this.setModified(),n.swapWithParent()}}}}class w2e extends Ou{run(t){const n=[...t.children],i=t.children.filter(r=>r instanceof In);if(t.numChildren()>1&&i.length>=1){const r={},s=new Set;for(const o of i){const a=o.parse;for(const u of Y(a))u in r?r[u]!==a[u]&&s.add(u):r[u]=a[u]}for(const o of s)delete r[o];if(!ht(r)){this.setModified();const o=new In(t,r);for(const a of n){if(a instanceof In)for(const u of Y(r))delete a.parse[u];t.removeChild(a),a.parent=o,a instanceof In&&Y(a.parse).length===0&&a.remove()}}}}}class E2e extends Ou{run(t){t instanceof yi||t.numChildren()>0||t instanceof Nc||t instanceof Ru||(this.setModified(),t.remove())}}class C2e extends Ou{run(t){const n=t.children.filter(r=>r instanceof Cs),i=n.pop();for(const r of n)this.setModified(),i.merge(r)}}class k2e extends Ou{run(t){const n=t.children.filter(r=>r instanceof Ir),i={};for(const r of n){const s=We(r.groupBy);s in i||(i[s]=[]),i[s].push(r)}for(const r of Y(i)){const s=i[r];if(s.length>1){const o=s.pop();for(const a of s)o.merge(a)&&(t.removeChild(a),a.parent=o,a.remove(),this.setModified())}}}}class A2e extends Ou{constructor(t){super(),this.model=t}run(t){const n=!(Zw(t)||t instanceof Fc||t instanceof In||t instanceof va),i=[],r=[];for(const s of t.children)s instanceof $s&&(n&&!n_(t.producedFields(),s.dependentFields())?i.push(s):r.push(s));if(i.length>0){const s=i.pop();for(const o of i)s.merge(o,this.model.renameSignal.bind(this.model));this.setModified(),t instanceof $s?t.merge(s,this.model.renameSignal.bind(this.model)):s.swapWithParent()}if(r.length>1){const s=r.pop();for(const o of r)s.merge(o,this.model.renameSignal.bind(this.model));this.setModified()}}}class $2e extends Ou{run(t){const n=[...t.children];if(!lc(n,o=>o instanceof yi)||t.numChildren()<=1)return;const r=[];let s;for(const o of n)if(o instanceof yi){let a=o;for(;a.numChildren()===1;){const[u]=a.children;if(u instanceof yi)a=u;else break}r.push(...a.children),s?(t.removeChild(o),o.parent=s.parent,s.parent.removeChild(s),s.parent=a,this.setModified()):s=a}else r.push(o);if(r.length){this.setModified();for(const o of r)o.parent.removeChild(o),o.parent=s}}}class Lu extends ut{clone(){return new Lu(null,De(this.transform))}constructor(t,n){super(t),this.transform=n}addDimensions(t){this.transform.groupby=fs(this.transform.groupby.concat(t),n=>n)}dependentFields(){const t=new Set;return this.transform.groupby&&this.transform.groupby.forEach(t.add,t),this.transform.joinaggregate.map(n=>n.field).filter(n=>n!==void 0).forEach(t.add,t),t}producedFields(){return new Set(this.transform.joinaggregate.map(this.getDefaultName))}getDefaultName(t){return t.as??te(t)}hash(){return`JoinAggregateTransform ${We(this.transform)}`}assemble(){const t=[],n=[],i=[];for(const s of this.transform.joinaggregate)n.push(s.op),i.push(this.getDefaultName(s)),t.push(s.field===void 0?null:s.field);const r=this.transform.groupby;return{type:"joinaggregate",as:i,ops:n,fields:t,...r!==void 0?{groupby:r}:{}}}}class Rc extends ut{clone(){return new Rc(null,{...this.filter})}constructor(t,n){super(t),this.filter=n}static make(t,n,i){const{config:r,markDef:s}=n,{marks:o,scales:a}=i;if(o==="include-invalid-values"&&a==="include-invalid-values")return null;const u=n.reduceFieldDef((l,c,f)=>{const d=ms(f)&&n.getScaleComponent(f);if(d){const h=d.get("type"),{aggregate:p}=c,g=j_({scaleChannel:f,markDef:s,config:r,scaleType:h,isCountAggregate:D1(p)});g!=="show"&&g!=="always-valid"&&(l[c.field]=c)}return l},{});return Y(u).length?new Rc(t,u):null}dependentFields(){return new Set(Y(this.filter))}producedFields(){return new Set}hash(){return`FilterInvalid ${We(this.filter)}`}assemble(){const t=Y(this.filter).reduce((n,i)=>{const r=this.filter[i],s=te(r,{expr:"datum"});return r!==null&&(r.type==="temporal"?n.push(`(isDate(${s}) || (${e6(s)}))`):r.type==="quantitative"&&n.push(e6(s))),n},[]);return t.length>0?{type:"filter",expr:t.join(" && ")}:null}}function e6(e){return`isValid(${e}) && isFinite(+${e})`}function S2e(e){return e.stack.stackBy.reduce((t,n)=>{const i=n.fieldDef,r=te(i);return r&&t.push(r),t},[])}function F2e(e){return W(e)&&e.every(t=>se(t))&&e.length>1}class go extends ut{clone(){return new go(null,De(this._stack))}constructor(t,n){super(t),this._stack=n}static makeFromTransform(t,n){const{stack:i,groupby:r,as:s,offset:o="zero"}=n,a=[],u=[];if(n.sort!==void 0)for(const f of n.sort)a.push(f.field),u.push(zt(f.order,"ascending"));const l={field:a,order:u};let c;return F2e(s)?c=s:se(s)?c=[s,`${s}_end`]:c=[`${n.stack}_start`,`${n.stack}_end`],new go(t,{dimensionFieldDefs:[],stackField:i,groupby:r,offset:o,sort:l,facetby:[],as:c})}static makeFromEncoding(t,n){const i=n.stack,{encoding:r}=n;if(!i)return null;const{groupbyChannels:s,fieldChannel:o,offset:a,impute:u}=i,l=s.map(h=>{const p=r[h];return Rr(p)}).filter(h=>!!h),c=S2e(n),f=n.encoding.order;let d;if(W(f)||J(f))d=JM(f);else{const h=nR(f)?f.sort:o==="y"?"descending":"ascending";d=c.reduce((p,g)=>(p.field.includes(g)||(p.field.push(g),p.order.push(h)),p),{field:[],order:[]})}return new go(t,{dimensionFieldDefs:l,stackField:n.vgField(o),facetby:[],stackby:c,sort:d,offset:a,impute:u,as:[n.vgField(o,{suffix:"start",forAs:!0}),n.vgField(o,{suffix:"end",forAs:!0})]})}get stack(){return this._stack}addDimensions(t){this._stack.facetby.push(...t)}dependentFields(){const t=new Set;return t.add(this._stack.stackField),this.getGroupbyFields().forEach(t.add,t),this._stack.facetby.forEach(t.add,t),this._stack.sort.field.forEach(t.add,t),t}producedFields(){return new Set(this._stack.as)}hash(){return`Stack ${We(this._stack)}`}getGroupbyFields(){const{dimensionFieldDefs:t,impute:n,groupby:i}=this._stack;return t.length>0?t.map(r=>r.bin?n?[te(r,{binSuffix:"mid"})]:[te(r,{}),te(r,{binSuffix:"end"})]:[te(r)]).flat():i??[]}assemble(){const t=[],{facetby:n,dimensionFieldDefs:i,stackField:r,stackby:s,sort:o,offset:a,impute:u,as:l}=this._stack;if(u)for(const c of i){const{bandPosition:f=.5,bin:d}=c;if(d){const h=te(c,{expr:"datum"}),p=te(c,{expr:"datum",binSuffix:"end"});t.push({type:"formula",expr:`${e6(h)} ? ${f}*${h}+${1-f}*${p} : ${h}`,as:te(c,{binSuffix:"mid",forAs:!0})})}t.push({type:"impute",field:r,groupby:[...s,...n],key:te(c,{binSuffix:"mid"}),method:"value",value:0})}return t.push({type:"stack",groupby:[...this.getGroupbyFields(),...n],field:r,sort:o,as:l,offset:a}),t}}class Oc extends ut{clone(){return new Oc(null,De(this.transform))}constructor(t,n){super(t),this.transform=n}addDimensions(t){this.transform.groupby=fs(this.transform.groupby.concat(t),n=>n)}dependentFields(){const t=new Set;return(this.transform.groupby??[]).forEach(t.add,t),(this.transform.sort??[]).forEach(n=>t.add(n.field)),this.transform.window.map(n=>n.field).filter(n=>n!==void 0).forEach(t.add,t),t}producedFields(){return new Set(this.transform.window.map(this.getDefaultName))}getDefaultName(t){return t.as??te(t)}hash(){return`WindowTransform ${We(this.transform)}`}assemble(){const t=[],n=[],i=[],r=[];for(const f of this.transform.window)n.push(f.op),i.push(this.getDefaultName(f)),r.push(f.param===void 0?null:f.param),t.push(f.field===void 0?null:f.field);const s=this.transform.frame,o=this.transform.groupby;if(s&&s[0]===null&&s[1]===null&&n.every(f=>p_(f)))return{type:"joinaggregate",as:i,ops:n,fields:t,...o!==void 0?{groupby:o}:{}};const a=[],u=[];if(this.transform.sort!==void 0)for(const f of this.transform.sort)a.push(f.field),u.push(f.order??"ascending");const l={field:a,order:u},c=this.transform.ignorePeers;return{type:"window",params:r,as:i,ops:n,fields:t,sort:l,...c!==void 0?{ignorePeers:c}:{},...o!==void 0?{groupby:o}:{},...s!==void 0?{frame:s}:{}}}}function D2e(e){function t(n){if(!(n instanceof Nc)){const i=n.clone();if(i instanceof yi){const r=n6+i.getSource();i.setSource(r),e.model.component.data.outputNodes[r]=i}else(i instanceof Ir||i instanceof go||i instanceof Oc||i instanceof Lu)&&i.addDimensions(e.fields);for(const r of n.children.flatMap(t))r.parent=i;return[i]}return n.children.flatMap(t)}return t}function t6(e){if(e instanceof Nc)if(e.numChildren()===1&&!(e.children[0]instanceof yi)){const t=e.children[0];(t instanceof Ir||t instanceof go||t instanceof Oc||t instanceof Lu)&&t.addDimensions(e.fields),t.swapWithParent(),t6(e)}else{const t=e.model.component.data.main;jL(t);const n=D2e(e),i=e.children.map(n).flat();for(const r of i)r.parent=t}else e.children.map(t6)}function jL(e){if(e instanceof yi&&e.type===Ft.Main&&e.numChildren()===1){const t=e.children[0];t instanceof Nc||(t.swapWithParent(),jL(e))}}const n6="scale_",Em=5;function i6(e){for(const t of e){for(const n of t.children)if(n.parent!==t)return!1;if(!i6(t.children))return!1}return!0}function Pr(e,t){let n=!1;for(const i of t)n=e.optimize(i)||n;return n}function qL(e,t,n){let i=e.sources,r=!1;return r=Pr(new x2e,i)||r,r=Pr(new b2e(t),i)||r,i=i.filter(s=>s.numChildren()>0),r=Pr(new E2e,i)||r,i=i.filter(s=>s.numChildren()>0),n||(r=Pr(new _2e,i)||r,r=Pr(new A2e(t),i)||r,r=Pr(new v2e,i)||r,r=Pr(new w2e,i)||r,r=Pr(new k2e,i)||r,r=Pr(new C2e,i)||r,r=Pr(new y2e,i)||r,r=Pr(new $2e,i)||r),e.sources=i,r}function T2e(e,t){i6(e.sources);let n=0,i=0;for(let r=0;rt(n))}}function WL(e){Dt(e)?M2e(e):N2e(e)}function M2e(e){const t=e.component.scales;for(const n of Y(t)){const i=O2e(e,n);if(t[n].setWithExplicit("domains",i),I2e(e,n),e.component.data.isFaceted){let s=e;for(;!Oi(s)&&s.parent;)s=s.parent;if(s.component.resolve.scale[n]==="shared")for(const a of i.value)so(a)&&(a.data=n6+a.data.replace(n6,""))}}}function N2e(e){for(const n of e.children)WL(n);const t=e.component.scales;for(const n of Y(t)){let i,r=null;for(const s of e.children){const o=s.component.scales[n];if(o){i===void 0?i=o.getWithExplicit("domains"):i=ma(i,o.getWithExplicit("domains"),"domains","scale",o6);const a=o.get("selectionExtent");r&&a&&r.param!==a.param&&Z(wde),r=a}}t[n].setWithExplicit("domains",i),r&&t[n].set("selectionExtent",r,!0)}}function R2e(e,t,n,i){if(e==="unaggregated"){const{valid:r,reason:s}=HL(t,n);if(!r){Z(s);return}}else if(e===void 0&&i.useUnaggregatedDomain){const{valid:r}=HL(t,n);if(r)return"unaggregated"}return e}function O2e(e,t){const n=e.getScaleComponent(t).get("type"),{encoding:i}=e,r=R2e(e.scaleDomain(t),e.typedFieldDef(t),n,e.config.scale);return r!==e.scaleDomain(t)&&(e.specifiedScales[t]={...e.specifiedScales[t],domain:r}),t==="x"&&Xt(i.x2)?Xt(i.x)?ma(xa(n,r,e,"x"),xa(n,r,e,"x2"),"domain","scale",o6):xa(n,r,e,"x2"):t==="y"&&Xt(i.y2)?Xt(i.y)?ma(xa(n,r,e,"y"),xa(n,r,e,"y2"),"domain","scale",o6):xa(n,r,e,"y2"):xa(n,r,e,t)}function L2e(e,t,n){return e.map(i=>({signal:`{data: ${K1(i,{timeUnit:n,type:t})}}`}))}function r6(e,t,n){var r;const i=(r=sn(n))==null?void 0:r.unit;return t==="temporal"||i?L2e(e,t,i):[e]}function xa(e,t,n,i){const{encoding:r,markDef:s,mark:o,config:a,stack:u}=n,l=Xt(r[i]),{type:c}=l,f=l.timeUnit,d=vge({invalid:ys("invalid",s,a),isPath:ha(o)});if(Yhe(t)){const g=xa(e,void 0,n,i),m=r6(t.unionWith,c,f);return Es([...m,...g.value])}else{if(me(t))return Es([t]);if(t&&t!=="unaggregated"&&!ON(t))return Es(r6(t,c,f))}if(u&&i===u.fieldChannel){if(u.offset==="normalize")return Ri([[0,1]]);const g=n.requestDataName(d);return Ri([{data:g,field:n.vgField(i,{suffix:"start"})},{data:g,field:n.vgField(i,{suffix:"end"})}])}const h=ms(i)&&J(l)?P2e(n,i,e):void 0;if(_s(l)){const g=r6([l.datum],c,f);return Ri(g)}const p=l;if(t==="unaggregated"){const{field:g}=l;return Ri([{data:n.requestDataName(d),field:te({field:g,aggregate:"min"})},{data:n.requestDataName(d),field:te({field:g,aggregate:"max"})}])}else if(_t(p.bin)){if(on(e))return Ri(e==="bin-ordinal"?[]:[{data:Hd(h)?n.requestDataName(d):n.requestDataName(Ft.Raw),field:n.vgField(i,oh(p,i)?{binSuffix:"range"}:{}),sort:h===!0||!re(h)?{field:n.vgField(i,{}),op:"min"}:h}]);{const{bin:g}=p;if(_t(g)){const m=Kw(n,p.field,g);return Ri([new un(()=>{const y=n.getSignalName(m);return`[${y}.start, ${y}.stop]`})])}else return Ri([{data:n.requestDataName(d),field:n.vgField(i,{})}])}}else if(p.timeUnit&&qe(["time","utc"],e)){const g=r[gs(i)];if(tR(p,g,s,a)){const m=n.requestDataName(d),y=pa({fieldDef:p,fieldDef2:g,markDef:s,config:a}),b=eh(o)&&y!==.5&&Bt(i);return Ri([{data:m,field:n.vgField(i,b?{suffix:sm}:{})},{data:m,field:n.vgField(i,{suffix:b?om:"end"})}])}}return Ri(h?[{data:Hd(h)?n.requestDataName(d):n.requestDataName(Ft.Raw),field:n.vgField(i),sort:h}]:[{data:n.requestDataName(d),field:n.vgField(i)}])}function s6(e,t){const{op:n,field:i,order:r}=e;return{op:n??(t?"sum":j1),...i?{field:er(i)}:{},...r?{order:r}:{}}}function I2e(e,t){var a;const n=e.component.scales[t],i=e.specifiedScales[t].domain,r=(a=e.fieldDef(t))==null?void 0:a.bin,s=ON(i)?i:void 0,o=mu(r)&&T1(r.extent)?r.extent:void 0;(s||o)&&n.set("selectionExtent",s??o,!0)}function P2e(e,t,n){if(!on(n))return;const i=e.fieldDef(t),r=i.sort;if(JN(r))return{op:"min",field:Tc(i,t),order:"ascending"};const{stack:s}=e,o=s?new Set([...s.groupbyFields,...s.stackBy.map(a=>a.fieldDef.field)]):void 0;if(oo(r)){const a=s&&!o.has(r.field);return s6(r,a)}else if(E0e(r)){const{encoding:a,order:u}=r,l=e.fieldDef(a),{aggregate:c,field:f}=l,d=s&&!o.has(f);if(ro(c)||fa(c))return s6({field:te(l),order:u},d);if(p_(c)||!c)return s6({op:c,field:f,order:u},d)}else{if(r==="descending")return{op:"min",field:e.vgField(t),order:"descending"};if(qe(["ascending",void 0],r))return!0}}function HL(e,t){const{aggregate:n,type:i}=e;return n?se(n)&&!ode.has(n)?{valid:!1,reason:Zde(n)}:i==="quantitative"&&t==="log"?{valid:!1,reason:Jde(e)}:{valid:!0}:{valid:!1,reason:Kde(e)}}function o6(e,t,n,i){return e.explicit&&t.explicit&&Z(ihe(n,i,e.value,t.value)),{explicit:e.explicit,value:[...e.value,...t.value]}}function z2e(e){const t=fs(e.map(o=>{if(so(o)){const{sort:a,...u}=o;return u}return o}),We),n=fs(e.map(o=>{if(so(o)){const a=o.sort;return a!==void 0&&!Hd(a)&&("op"in a&&a.op==="count"&&delete a.field,a.order==="ascending"&&delete a.order),a}}).filter(o=>o!==void 0),We);if(t.length===0)return;if(t.length===1){const o=e[0];if(so(o)&&n.length>0){let a=n[0];if(n.length>1){Z(pN);const u=n.filter(l=>re(l)&&"op"in l&&l.op!=="min");n.every(l=>re(l)&&"op"in l)&&u.length===1?a=u[0]:a=!0}else if(re(a)&&"field"in a){const u=a.field;o.field===u&&(a=a.order?{order:a.order}:!0)}return{...o,sort:a}}return o}const i=fs(n.map(o=>Hd(o)||!("op"in o)||se(o.op)&&ue(nde,o.op)?o:(Z(she(o)),!0)),We);let r;i.length===1?r=i[0]:i.length>1&&(Z(pN),r=!0);const s=fs(e.map(o=>so(o)?o.data:null),o=>o);return s.length===1&&s[0]!==null?{data:s[0],fields:t.map(a=>a.field),...r?{sort:r}:{}}:{fields:t,...r?{sort:r}:{}}}function a6(e){if(so(e)&&se(e.field))return e.field;if(ade(e)){let t;for(const n of e.fields)if(so(n)&&se(n.field)){if(!t)t=n.field;else if(t!==n.field)return Z(ohe),t}return Z(ahe),t}else if(ude(e)){Z(uhe);const t=e.fields[0];return se(t)?t:void 0}}function Cm(e,t){const i=e.component.scales[t].get("domains").map(r=>(so(r)&&(r.data=e.lookupDataSource(r.data)),r));return z2e(i)}function GL(e){return Lc(e)||l6(e)?e.children.reduce((t,n)=>t.concat(GL(n)),VL(e)):VL(e)}function VL(e){return Y(e.component.scales).reduce((t,n)=>{const i=e.component.scales[n];if(i.merged)return t;const r=i.combine(),{name:s,type:o,selectionExtent:a,domains:u,range:l,reverse:c,...f}=r,d=B2e(r.range,s,n,e),h=Cm(e,n),p=a?Dge(e,a,i,h):null;return t.push({name:s,type:o,...h?{domain:h}:{},...p?{domainRaw:p}:{},range:d,...c!==void 0?{reverse:c}:{},...f}),t},[])}function B2e(e,t,n,i){if(Bt(n)){if(yu(e))return{step:{signal:`${t}_step`}}}else if(re(e)&&so(e))return{...e,data:i.lookupDataSource(e.data)};return e}class YL extends lo{constructor(t,n){super({},{name:t}),this.merged=!1,this.setWithExplicit("type",n)}domainHasZero(){const t=this.get("type");if(qe([bn.LOG,bn.TIME,bn.UTC],t))return"definitely-not";const n=this.get("zero");if(n===!0||n===void 0&&qe([bn.LINEAR,bn.SQRT,bn.POW],t))return"definitely";const i=this.get("domains");if(i.length>0){let r=!1,s=!1,o=!1;for(const a of i){if(W(a)){const u=a[0],l=a[a.length-1];if(Ze(u)&&Ze(l))if(u<=0&&l>=0){r=!0;continue}else{s=!0;continue}}o=!0}if(r)return"definitely";if(s&&!o)return"definitely-not"}return"maybe"}}const U2e=["range","scheme"];function j2e(e){const t=e.component.scales;for(const n of d_){const i=t[n];if(!i)continue;const r=q2e(n,e);i.setWithExplicit("range",r)}}function XL(e,t){const n=e.fieldDef(t);if(n!=null&&n.bin){const{bin:i,field:r}=n,s=mi(t),o=e.getName(s);if(re(i)&&i.binned&&i.step!==void 0)return new un(()=>{const a=e.scaleName(t),u=`(domain("${a}")[1] - domain("${a}")[0]) / ${i.step}`;return`${e.getSignalName(o)} / (${u})`});if(_t(i)){const a=Kw(e,r,i);return new un(()=>{const u=e.getSignalName(a),l=`(${u}.stop - ${u}.start) / ${u}.step`;return`${e.getSignalName(o)} / (${l})`})}}}function q2e(e,t){const n=t.specifiedScales[e],{size:i}=t,s=t.getScaleComponent(e).get("type");for(const f of U2e)if(n[f]!==void 0){const d=L_(s,f),h=LN(e,f);if(!d)Z(dN(s,f,e));else if(h)Z(h);else switch(f){case"range":{const p=n.range;if(W(p)){if(Bt(e))return Es(p.map(g=>{if(g==="width"||g==="height"){const m=t.getName(g),y=t.getSignalName.bind(t);return un.fromName(y,m)}return g}))}else if(re(p))return Es({data:t.requestDataName(Ft.Main),field:p.field,sort:{op:"min",field:t.vgField(e)}});return Es(p)}case"scheme":return Es(W2e(n[f]))}}const o=e===$t||e==="xOffset"?"width":"height",a=i[o];if(ws(a)){if(Bt(e))if(on(s)){const f=ZL(a,t,e);if(f)return Es({step:f})}else Z(hN(o));else if(Zd(e)){const f=e===ra?"x":"y";if(t.getScaleComponent(f).get("type")==="band"){const p=JL(a,s);if(p)return Es(p)}}}const{rangeMin:u,rangeMax:l}=n,c=H2e(e,t);return(u!==void 0||l!==void 0)&&L_(s,"rangeMin")&&W(c)&&c.length===2?Es([u??c[0],l??c[1]]):Ri(c)}function W2e(e){return Vhe(e)?{scheme:e.name,...hi(e,["name"])}:{scheme:e}}function KL(e,t,n,{center:i}={}){const r=mi(e),s=t.getName(r),o=t.getSignalName.bind(t);return e===rn&&Tr(n)?i?[un.fromName(a=>`${o(a)}/2`,s),un.fromName(a=>`-${o(a)}/2`,s)]:[un.fromName(o,s),0]:i?[un.fromName(a=>`-${o(a)}/2`,s),un.fromName(a=>`${o(a)}/2`,s)]:[0,un.fromName(o,s)]}function H2e(e,t){const{size:n,config:i,mark:r,encoding:s}=t,{type:o}=Xt(s[e]),u=t.getScaleComponent(e).get("type"),{domain:l,domainMid:c}=t.specifiedScales[e];switch(e){case $t:case rn:{if(qe(["point","band"],u)){const f=QL(e,n,i.view);if(ws(f))return{step:ZL(f,t,e)}}return KL(e,t,u)}case ra:case dc:return G2e(e,t,u);case to:{const f=X2e(r,i),d=K2e(r,n,t,i);return yc(u)?Y2e(f,d,V2e(u,i,l,e)):[f,d]}case tr:return[0,Math.PI*2];case hu:return[0,360];case Ar:return[0,new un(()=>{const f=t.getSignalName(Oi(t.parent)?"child_width":"width"),d=t.getSignalName(Oi(t.parent)?"child_height":"height");return`min(${f},${d})/2`})];case sa:return{step:1e3/i.scale.framesPerSecond};case ua:return[i.scale.minStrokeWidth,i.scale.maxStrokeWidth];case la:return[[1,0],[4,2],[2,1],[1,1],[1,2,4,2]];case gi:return"symbol";case pi:case hs:case ps:return u==="ordinal"?o==="nominal"?"category":"ordinal":c!==void 0?"diverging":r==="rect"||r==="geoshape"?"heatmap":"ramp";case no:case oa:case aa:return[i.scale.minOpacity,i.scale.maxOpacity]}}function ZL(e,t,n){const{encoding:i}=t,r=t.getScaleComponent(n),s=a_(n),o=i[s];if(IR({step:e,offsetIsDiscrete:Me(o)&&FN(o.type)})==="offset"&&mR(i,s)){const u=t.getScaleComponent(s);let c=`domain('${t.scaleName(s)}').length`;if(u.get("type")==="band"){const d=u.get("paddingInner")??u.get("padding")??0,h=u.get("paddingOuter")??u.get("padding")??0;c=`bandspace(${c}, ${d}, ${h})`}const f=r.get("paddingInner")??r.get("padding");return{signal:`${e.step} * ${c} / (1-${fde(f)})`}}else return e.step}function JL(e,t){if(IR({step:e,offsetIsDiscrete:on(t)})==="offset")return{step:e.step}}function G2e(e,t,n){const i=e===ra?"x":"y",r=t.getScaleComponent(i);if(!r)return KL(i,t,n,{center:!0});const s=r.get("type"),o=t.scaleName(i),{markDef:a,config:u}=t;if(s==="band"){const l=QL(i,t.size,t.config.view);if(ws(l)){const c=JL(l,n);if(c)return c}return[0,{signal:`bandwidth('${o}')`}]}else{const l=t.encoding[i];if(J(l)&&l.timeUnit){const c=kN(l.timeUnit,p=>`scale('${o}', ${p})`),f=t.config.scale.bandWithNestedOffsetPaddingInner,d=pa({fieldDef:l,markDef:a,config:u})-.5,h=d!==0?` + ${d}`:"";if(f){const p=me(f)?`${f.signal}/2`+h:`${f/2+d}`,g=me(f)?`(1 - ${f.signal}/2)`+h:`${1-f/2+d}`;return[{signal:`${p} * (${c})`},{signal:`${g} * (${c})`}]}return[0,{signal:c}]}return EM(`Cannot use ${e} scale if ${i} scale is not discrete.`)}}function QL(e,t,n){const i=e===$t?"width":"height",r=t[i];return r||nm(n,i)}function V2e(e,t,n,i){switch(e){case"quantile":return t.scale.quantileCount;case"quantize":return t.scale.quantizeCount;case"threshold":return n!==void 0&&W(n)?n.length+1:(Z(yhe(i)),3)}}function Y2e(e,t,n){const i=()=>{const r=Dr(t),s=Dr(e),o=`(${r} - ${s}) / (${n} - 1)`;return`sequence(${s}, ${r} + ${o}, ${o})`};return me(t)?new un(i):{signal:i()}}function X2e(e,t){switch(e){case"bar":case"tick":return t.scale.minBandSize;case"line":case"trail":case"rule":return t.scale.minStrokeWidth;case"text":return t.scale.minFontSize;case"point":case"square":case"circle":return t.scale.minSize}throw new Error(M1("size",e))}const eI=.95;function K2e(e,t,n,i){const r={x:XL(n,"x"),y:XL(n,"y")};switch(e){case"bar":case"tick":{if(i.scale.maxBandSize!==void 0)return i.scale.maxBandSize;const s=tI(t,r,i.view);return Ze(s)?s-1:new un(()=>`${s.signal} - 1`)}case"line":case"trail":case"rule":return i.scale.maxStrokeWidth;case"text":return i.scale.maxFontSize;case"point":case"square":case"circle":{if(i.scale.maxSize)return i.scale.maxSize;const s=tI(t,r,i.view);return Ze(s)?Math.pow(eI*s,2):new un(()=>`pow(${eI} * ${s.signal}, 2)`)}}throw new Error(M1("size",e))}function tI(e,t,n){const i=ws(e.width)?e.width.step:fw(n,"width"),r=ws(e.height)?e.height.step:fw(n,"height");return t.x||t.y?new un(()=>`min(${[t.x?t.x.signal:i,t.y?t.y.signal:r].join(", ")})`):Math.min(i,r)}function nI(e,t){Dt(e)?Z2e(e,t):sI(e,t)}function Z2e(e,t){const n=e.component.scales,{config:i,encoding:r,markDef:s,specifiedScales:o}=e;for(const a of Y(n)){const u=o[a],l=n[a],c=e.getScaleComponent(a),f=Xt(r[a]),d=u[t],h=c.get("type"),p=c.get("padding"),g=c.get("paddingInner"),m=L_(h,t),y=LN(a,t);if(d!==void 0&&(m?y&&Z(y):Z(dN(h,t,a))),m&&y===void 0)if(d!==void 0){const b=f.timeUnit,v=f.type;switch(t){case"domainMax":case"domainMin":vu(u[t])||v==="temporal"||b?l.set(t,{signal:K1(u[t],{type:v,timeUnit:b})},!0):l.set(t,u[t],!0);break;default:l.copyKeyFromObject(t,u)}}else{const b=X(iI,t)?iI[t]({model:e,channel:a,fieldOrDatumDef:f,scaleType:h,scalePadding:p,scalePaddingInner:g,domain:u.domain,domainMin:u.domainMin,domainMax:u.domainMax,markDef:s,config:i,hasNestedOffsetScale:yR(r,a),hasSecondaryRangeChannel:!!r[gs(a)]}):i.scale[t];b!==void 0&&l.set(t,b,!1)}}}const iI={bins:({model:e,fieldOrDatumDef:t})=>J(t)?J2e(e,t):void 0,interpolate:({channel:e,fieldOrDatumDef:t})=>Q2e(e,t.type),nice:({scaleType:e,channel:t,domain:n,domainMin:i,domainMax:r,fieldOrDatumDef:s})=>eye(e,t,n,i,r,s),padding:({channel:e,scaleType:t,fieldOrDatumDef:n,markDef:i,config:r})=>tye(e,t,r.scale,n,i,r.bar),paddingInner:({scalePadding:e,channel:t,markDef:n,scaleType:i,config:r,hasNestedOffsetScale:s})=>nye(e,t,n.type,i,r.scale,s),paddingOuter:({scalePadding:e,channel:t,scaleType:n,scalePaddingInner:i,config:r,hasNestedOffsetScale:s})=>iye(e,t,n,i,r.scale,s),reverse:({fieldOrDatumDef:e,scaleType:t,channel:n,config:i})=>{const r=J(e)?e.sort:void 0;return rye(t,r,n,i.scale)},zero:({channel:e,fieldOrDatumDef:t,domain:n,markDef:i,scaleType:r,config:s,hasSecondaryRangeChannel:o})=>sye(e,t,n,i,r,s.scale,o)};function rI(e){Dt(e)?j2e(e):sI(e,"range")}function sI(e,t){const n=e.component.scales;for(const i of e.children)t==="range"?rI(i):nI(i,t);for(const i of Y(n)){let r;for(const s of e.children){const o=s.component.scales[i];if(o){const a=o.getWithExplicit(t);r=ma(r,a,t,"scale",lO((u,l)=>{switch(t){case"range":return u.step&&l.step?u.step-l.step:0}return 0}))}}n[i].setWithExplicit(t,r)}}function J2e(e,t){const n=t.bin;if(_t(n)){const i=Kw(e,t.field,n);return new un(()=>e.getSignalName(i))}else if(mn(n)&&mu(n)&&n.step!==void 0)return{step:n.step}}function Q2e(e,t){if(qe([pi,hs,ps],e)&&t!=="nominal")return"hcl"}function eye(e,t,n,i,r,s){var o;if(!((o=Rr(s))!=null&&o.bin||W(n)||r!=null||i!=null||qe([bn.TIME,bn.UTC],e)))return Bt(t)?!0:void 0}function tye(e,t,n,i,r,s){if(Bt(e)){if(vs(t)){if(n.continuousPadding!==void 0)return n.continuousPadding;const{type:o,orient:a}=r;if(o==="bar"&&!(J(i)&&(i.bin||i.timeUnit))&&(a==="vertical"&&e==="x"||a==="horizontal"&&e==="y"))return s.continuousBandSize}if(t===bn.POINT)return n.pointPadding}}function nye(e,t,n,i,r,s=!1){if(e===void 0){if(Bt(t)){const{bandPaddingInner:o,barBandPaddingInner:a,rectBandPaddingInner:u,tickBandPaddingInner:l,bandWithNestedOffsetPaddingInner:c}=r;return s?c:zt(o,n==="bar"?a:n==="tick"?l:u)}else if(Zd(t)&&i===bn.BAND)return r.offsetBandPaddingInner}}function iye(e,t,n,i,r,s=!1){if(e===void 0){if(Bt(t)){const{bandPaddingOuter:o,bandWithNestedOffsetPaddingOuter:a}=r;if(s)return a;if(n===bn.BAND)return zt(o,me(i)?{signal:`${i.signal}/2`}:i/2)}else if(Zd(t)){if(n===bn.POINT)return .5;if(n===bn.BAND)return r.offsetBandPaddingOuter}}}function rye(e,t,n,i){if(n==="x"&&i.xReverse!==void 0)return Tr(e)&&t==="descending"?me(i.xReverse)?{signal:`!${i.xReverse.signal}`}:!i.xReverse:i.xReverse;if(Tr(e)&&t==="descending")return!0}function sye(e,t,n,i,r,s,o){if(!!n&&n!=="unaggregated"&&Tr(r)){if(W(n)){const u=n[0],l=n[n.length-1];if(Ze(u)&&u<=0&&Ze(l)&&l>=0)return!0}return!1}if(e==="size"&&t.type==="quantitative"&&!yc(r))return!0;if(!(J(t)&&t.bin)&&qe([...io,...Vfe],e)){const{orient:u,type:l}=i;return qe(["bar","area","line","trail"],l)&&(u==="horizontal"&&e==="y"||u==="vertical"&&e==="x")?!1:qe(["bar","area"],l)&&!o?!0:s==null?void 0:s.zero}return!1}function oye(e,t,n,i,r=!1){const s=aye(t,n,i,r),{type:o}=e;return ms(t)?o!==void 0?e0e(t,o)?J(n)&&!Qhe(o,n.type)?(Z(the(o,s)),s):o:(Z(ehe(t,o,s)),s):s:null}function aye(e,t,n,i){var r;switch(t.type){case"nominal":case"ordinal":{if(pc(e)||h_(e)==="discrete")return e==="shape"&&t.type==="ordinal"&&Z(__(e,"ordinal")),"ordinal";if(f_(e))return"band";if(Bt(e)||Zd(e)){if(qe(["rect","bar","image","rule","tick"],n.type)||i)return"band"}else if(n.type==="arc"&&e in c_)return"band";const s=n[mi(e)];return Eu(s)||vc(t)&&((r=t.axis)!=null&&r.tickBand)?"band":"point"}case"temporal":return pc(e)?"time":h_(e)==="discrete"?(Z(__(e,"temporal")),"ordinal"):J(t)&&t.timeUnit&&sn(t.timeUnit).utc?"utc":f_(e)?"band":"time";case"quantitative":return pc(e)?J(t)&&_t(t.bin)?"bin-ordinal":"linear":h_(e)==="discrete"?(Z(__(e,"quantitative")),"ordinal"):f_(e)?"band":"linear";case"geojson":return}throw new Error(cN(t.type))}function uye(e,{ignoreRange:t}={}){oI(e),WL(e);for(const n of Jhe)nI(e,n);t||rI(e)}function oI(e){Dt(e)?e.component.scales=lye(e):e.component.scales=fye(e)}function lye(e){const{encoding:t,mark:n,markDef:i}=e,r={};for(const s of d_){const o=Xt(t[s]);if(o&&n===zN&&s===gi&&o.type===mc)continue;let a=o&&o.scale;if(o&&a!==null&&a!==!1){a??(a={});const u=yR(t,s),l=oye(a,s,o,i,u);r[s]=new YL(e.scaleName(`${s}`,!0),{value:l,explicit:a.type===l})}}return r}const cye=lO((e,t)=>DN(e)-DN(t));function fye(e){var t;const n=e.component.scales={},i={},r=e.component.resolve;for(const s of e.children){oI(s);for(const o of Y(s.component.scales))if((t=r.scale)[o]??(t[o]=CL(o,e)),r.scale[o]==="shared"){const a=i[o],u=s.component.scales[o].getWithExplicit("type");a?jhe(a.value,u.value)?i[o]=ma(a,u,"type","scale",cye):(r.scale[o]="independent",delete i[o]):i[o]=u}}for(const s of Y(i)){const o=e.scaleName(s,!0),a=i[s];n[s]=new YL(o,a);for(const u of e.children){const l=u.component.scales[s];l&&(u.renameScale(l.get("name"),o),l.merged=!0)}}return n}class u6{constructor(){this.nameMap={}}rename(t,n){this.nameMap[t]=n}has(t){return this.nameMap[t]!==void 0}get(t){for(;this.nameMap[t]&&t!==this.nameMap[t];)t=this.nameMap[t];return t}}function Dt(e){return(e==null?void 0:e.type)==="unit"}function Oi(e){return(e==null?void 0:e.type)==="facet"}function l6(e){return(e==null?void 0:e.type)==="concat"}function Lc(e){return(e==null?void 0:e.type)==="layer"}class c6{constructor(t,n,i,r,s,o,a){this.type=n,this.parent=i,this.config=s,this.parent=i,this.config=s,this.view=yn(a),this.name=t.name??r,this.title=da(t.title)?{text:t.title}:t.title?yn(t.title):void 0,this.scaleNameMap=i?i.scaleNameMap:new u6,this.projectionNameMap=i?i.projectionNameMap:new u6,this.signalNameMap=i?i.signalNameMap:new u6,this.data=t.data,this.description=t.description,this.transforms=age(t.transform??[]),this.layout=n==="layer"||n==="unit"?{}:dpe(t,n,s),this.component={data:{sources:i?i.component.data.sources:[],outputNodes:i?i.component.data.outputNodes:{},outputNodeRefCounts:i?i.component.data.outputNodeRefCounts:{},isFaceted:q1(t)||(i==null?void 0:i.component.data.isFaceted)&&t.data===void 0},layoutSize:new lo,layoutHeaders:{row:{},column:{},facet:{}},mark:null,resolve:{scale:{},axis:{},legend:{},...o?De(o):{}},selection:null,scales:null,projection:null,axes:{},legends:{}}}get width(){return this.getSizeSignalRef("width")}get height(){return this.getSizeSignalRef("height")}parse(){this.parseScale(),this.parseLayoutSize(),this.renameTopLevelLayoutSizeSignal(),this.parseSelections(),this.parseProjection(),this.parseData(),this.parseAxesAndHeaders(),this.parseLegends(),this.parseMarkGroup()}parseScale(){uye(this)}parseProjection(){IL(this)}renameTopLevelLayoutSizeSignal(){this.getName("width")!=="width"&&this.renameSignal(this.getName("width"),"width"),this.getName("height")!=="height"&&this.renameSignal(this.getName("height"),"height")}parseLegends(){ML(this)}assembleEncodeFromView(t){const{style:n,...i}=t,r={};for(const s of Y(i)){const o=i[s];o!==void 0&&(r[s]=Et(o))}return r}assembleGroupEncodeEntry(t){let n={};return this.view&&(n=this.assembleEncodeFromView(this.view)),!t&&(this.description&&(n.description=Et(this.description)),this.type==="unit"||this.type==="layer")?{width:this.getSizeSignalRef("width"),height:this.getSizeSignalRef("height"),...n}:ht(n)?void 0:n}assembleLayout(){if(!this.layout)return;const{spacing:t,...n}=this.layout,{component:i,config:r}=this,s=Tme(i.layoutHeaders,r);return{padding:t,...this.assembleDefaultLayout(),...n,...s?{titleBand:s}:{}}}assembleDefaultLayout(){return{}}assembleHeaderMarks(){const{layoutHeaders:t}=this.component;let n=[];for(const i of ir)t[i].title&&n.push(kme(this,i));for(const i of Ww)n=n.concat(Ame(this,i));return n}assembleAxes(){return dme(this.component.axes,this.config)}assembleLegends(){return RL(this)}assembleProjections(){return e2e(this)}assembleTitle(){const{encoding:t,...n}=this.title??{},i={...GM(this.config.title).nonMarkTitleProperties,...n,...t?{encode:{update:t}}:{}};if(i.text)return qe(["unit","layer"],this.type)?qe(["middle",void 0],i.anchor)&&(i.frame??(i.frame="group")):i.anchor??(i.anchor="start"),ht(i)?void 0:i}assembleGroup(t=[]){const n={};t=t.concat(this.assembleSignals()),t.length>0&&(n.signals=t);const i=this.assembleLayout();i&&(n.layout=i),n.marks=[].concat(this.assembleHeaderMarks(),this.assembleMarks());const r=!this.parent||Oi(this.parent)?GL(this):[];r.length>0&&(n.scales=r);const s=this.assembleAxes();s.length>0&&(n.axes=s);const o=this.assembleLegends();return o.length>0&&(n.legends=o),n}getName(t){return At((this.name?`${this.name}_`:"")+t)}getDataName(t){return this.getName(Ft[t].toLowerCase())}requestDataName(t){const n=this.getDataName(t),i=this.component.data.outputNodeRefCounts;return i[n]=(i[n]||0)+1,n}getSizeSignalRef(t){if(Oi(this.parent)){const n=wL(t),i=F1(n),r=this.component.scales[i];if(r&&!r.merged){const s=r.get("type"),o=r.get("range");if(on(s)&&yu(o)){const a=r.get("name"),u=Cm(this,i),l=a6(u);if(l){const c=te({aggregate:"distinct",field:l},{expr:"datum"});return{signal:_L(a,r,c)}}else return Z(b_(i)),null}}}return{signal:this.signalNameMap.get(this.getName(t))}}lookupDataSource(t){const n=this.component.data.outputNodes[t];return n?n.getSource():t}getSignalName(t){return this.signalNameMap.get(t)}renameSignal(t,n){this.signalNameMap.rename(t,n)}renameScale(t,n){this.scaleNameMap.rename(t,n)}renameProjection(t,n){this.projectionNameMap.rename(t,n)}scaleName(t,n){if(n)return this.getName(t);if(IM(t)&&ms(t)&&this.component.scales[t]||this.scaleNameMap.has(this.getName(t)))return this.scaleNameMap.get(this.getName(t))}projectionName(t){if(t)return this.getName("projection");if(this.component.projection&&!this.component.projection.merged||this.projectionNameMap.has(this.getName("projection")))return this.projectionNameMap.get(this.getName("projection"))}getScaleComponent(t){if(!this.component.scales)throw new Error("getScaleComponent cannot be called before parseScale(). Make sure you have called parseScale or use parseUnitModelWithScale().");const n=this.component.scales[t];return n&&!n.merged?n:this.parent?this.parent.getScaleComponent(t):void 0}getScaleType(t){const n=this.getScaleComponent(t);return n?n.get("type"):void 0}getSelectionComponent(t,n){let i=this.component.selection[t];if(!i&&this.parent&&(i=this.parent.getSelectionComponent(t,n)),!i)throw new Error(mde(n));return i}hasAxisOrientSignalRef(){var t,n;return((t=this.component.axes.x)==null?void 0:t.some(i=>i.hasOrientSignalRef()))||((n=this.component.axes.y)==null?void 0:n.some(i=>i.hasOrientSignalRef()))}}class aI extends c6{vgField(t,n={}){const i=this.fieldDef(t);if(i)return te(i,n)}reduceFieldDef(t,n){return q0e(this.getMapping(),(i,r,s)=>{const o=Rr(r);return o?t(i,o,s):i},n)}forEachFieldDef(t,n){J_(this.getMapping(),(i,r)=>{const s=Rr(i);s&&t(s,r)},n)}}class km extends ut{clone(){return new km(null,De(this.transform))}constructor(t,n){super(t),this.transform=n,this.transform=De(n);const i=this.transform.as??[void 0,void 0];this.transform.as=[i[0]??"value",i[1]??"density"];const r=this.transform.resolve??"shared";this.transform.resolve=r}dependentFields(){return new Set([this.transform.density,...this.transform.groupby??[]])}producedFields(){return new Set(this.transform.as)}hash(){return`DensityTransform ${We(this.transform)}`}assemble(){const{density:t,...n}=this.transform,i={type:"kde",field:t,...n};return i.resolve=this.transform.resolve,i}}class Am extends ut{clone(){return new Am(null,De(this.transform))}constructor(t,n){super(t),this.transform=n,this.transform=De(n)}dependentFields(){return new Set([this.transform.extent])}producedFields(){return new Set([])}hash(){return`ExtentTransform ${We(this.transform)}`}assemble(){const{extent:t,param:n}=this.transform;return{type:"extent",field:t,signal:n}}}class $m extends ut{clone(){return new $m(this.parent,De(this.transform))}constructor(t,n){super(t),this.transform=n,this.transform=De(n);const{flatten:i,as:r=[]}=this.transform;this.transform.as=i.map((s,o)=>r[o]??s)}dependentFields(){return new Set(this.transform.flatten)}producedFields(){return new Set(this.transform.as)}hash(){return`FlattenTransform ${We(this.transform)}`}assemble(){const{flatten:t,as:n}=this.transform;return{type:"flatten",fields:t,as:n}}}class Sm extends ut{clone(){return new Sm(null,De(this.transform))}constructor(t,n){super(t),this.transform=n,this.transform=De(n);const i=this.transform.as??[void 0,void 0];this.transform.as=[i[0]??"key",i[1]??"value"]}dependentFields(){return new Set(this.transform.fold)}producedFields(){return new Set(this.transform.as)}hash(){return`FoldTransform ${We(this.transform)}`}assemble(){const{fold:t,as:n}=this.transform;return{type:"fold",fields:t,as:n}}}class Ic extends ut{clone(){return new Ic(null,De(this.fields),this.geojson,this.signal)}static parseAll(t,n){if(n.component.projection&&!n.component.projection.isFit)return t;let i=0;for(const r of[[Sr,$r],[nr,Fr]]){const s=r.map(o=>{const a=Xt(n.encoding[o]);return J(a)?a.field:_s(a)?{expr:`${a.datum}`}:Nr(a)?{expr:`${a.value}`}:void 0});(s[0]||s[1])&&(t=new Ic(t,s,null,n.getName(`geojson_${i++}`)))}if(n.channelHasField(gi)){const r=n.typedFieldDef(gi);r.type===mc&&(t=new Ic(t,null,r.field,n.getName(`geojson_${i++}`)))}return t}constructor(t,n,i,r){super(t),this.fields=n,this.geojson=i,this.signal=r}dependentFields(){const t=(this.fields??[]).filter(se);return new Set([...this.geojson?[this.geojson]:[],...t])}producedFields(){return new Set}hash(){return`GeoJSON ${this.geojson} ${this.signal} ${We(this.fields)}`}assemble(){return[...this.geojson?[{type:"filter",expr:`isValid(datum["${this.geojson}"])`}]:[],{type:"geojson",...this.fields?{fields:this.fields}:{},...this.geojson?{geojson:this.geojson}:{},signal:this.signal}]}}class wh extends ut{clone(){return new wh(null,this.projection,De(this.fields),De(this.as))}constructor(t,n,i,r){super(t),this.projection=n,this.fields=i,this.as=r}static parseAll(t,n){if(!n.projectionName())return t;for(const i of[[Sr,$r],[nr,Fr]]){const r=i.map(o=>{const a=Xt(n.encoding[o]);return J(a)?a.field:_s(a)?{expr:`${a.datum}`}:Nr(a)?{expr:`${a.value}`}:void 0}),s=i[0]===nr?"2":"";(r[0]||r[1])&&(t=new wh(t,n.projectionName(),r,[n.getName(`x${s}`),n.getName(`y${s}`)]))}return t}dependentFields(){return new Set(this.fields.filter(se))}producedFields(){return new Set(this.as)}hash(){return`Geopoint ${this.projection} ${We(this.fields)} ${We(this.as)}`}assemble(){return{type:"geopoint",projection:this.projection,fields:this.fields,as:this.as}}}class Iu extends ut{clone(){return new Iu(null,De(this.transform))}constructor(t,n){super(t),this.transform=n}dependentFields(){return new Set([this.transform.impute,this.transform.key,...this.transform.groupby??[]])}producedFields(){return new Set([this.transform.impute])}processSequence(t){const{start:n=0,stop:i,step:r}=t;return{signal:`sequence(${[n,i,...r?[r]:[]].join(",")})`}}static makeFromTransform(t,n){return new Iu(t,n)}static makeFromEncoding(t,n){const i=n.encoding,r=i.x,s=i.y;if(J(r)&&J(s)){const o=r.impute?r:s.impute?s:void 0;if(o===void 0)return;const a=r.impute?s:s.impute?r:void 0,{method:u,value:l,frame:c,keyvals:f}=o.impute,d=xR(n.mark,i);return new Iu(t,{impute:o.field,key:a.field,...u?{method:u}:{},...l!==void 0?{value:l}:{},...c?{frame:c}:{},...f!==void 0?{keyvals:f}:{},...d.length?{groupby:d}:{}})}return null}hash(){return`Impute ${We(this.transform)}`}assemble(){const{impute:t,key:n,keyvals:i,method:r,groupby:s,value:o,frame:a=[null,null]}=this.transform,u={type:"impute",field:t,key:n,...i?{keyvals:jpe(i)?this.processSequence(i):i}:{},method:"value",...s?{groupby:s}:{},value:!r||r==="value"?o:null};if(r&&r!=="value"){const l={type:"window",as:[`imputed_${t}_value`],ops:[r],fields:[t],frame:a,ignorePeers:!1,...s?{groupby:s}:{}},c={type:"formula",expr:`datum.${t} === null ? datum.imputed_${t}_value : datum.${t}`,as:t};return[u,l,c]}else return[u]}}class Fm extends ut{clone(){return new Fm(null,De(this.transform))}constructor(t,n){super(t),this.transform=n,this.transform=De(n);const i=this.transform.as??[void 0,void 0];this.transform.as=[i[0]??n.on,i[1]??n.loess]}dependentFields(){return new Set([this.transform.loess,this.transform.on,...this.transform.groupby??[]])}producedFields(){return new Set(this.transform.as)}hash(){return`LoessTransform ${We(this.transform)}`}assemble(){const{loess:t,on:n,...i}=this.transform;return{type:"loess",x:n,y:t,...i}}}class Eh extends ut{clone(){return new Eh(null,De(this.transform),this.secondary)}constructor(t,n,i){super(t),this.transform=n,this.secondary=i}static make(t,n,i,r){const s=n.component.data.sources,{from:o}=i;let a=null;if(qpe(o)){let u=fI(o.data,s);u||(u=new Ru(o.data),s.push(u));const l=n.getName(`lookup_${r}`);a=new yi(u,l,Ft.Lookup,n.component.data.outputNodeRefCounts),n.component.data.outputNodes[l]=a}else if(Wpe(o)){const u=o.param;i={as:u,...i};let l;try{l=n.getSelectionComponent(At(u),u)}catch{throw new Error(xde(u))}if(a=l.materialized,!a)throw new Error(_de(u))}return new Eh(t,i,a.getSource())}dependentFields(){return new Set([this.transform.lookup])}producedFields(){return new Set(this.transform.as?oe(this.transform.as):this.transform.from.fields)}hash(){return`Lookup ${We({transform:this.transform,secondary:this.secondary})}`}assemble(){let t;if(this.transform.from.fields)t={values:this.transform.from.fields,...this.transform.as?{as:oe(this.transform.as)}:{}};else{let n=this.transform.as;se(n)||(Z(Mde),n="_lookup"),t={as:[n]}}return{type:"lookup",from:this.secondary,key:this.transform.from.key,fields:[this.transform.lookup],...t,...this.transform.default?{default:this.transform.default}:{}}}}class Dm extends ut{clone(){return new Dm(null,De(this.transform))}constructor(t,n){super(t),this.transform=n,this.transform=De(n);const i=this.transform.as??[void 0,void 0];this.transform.as=[i[0]??"prob",i[1]??"value"]}dependentFields(){return new Set([this.transform.quantile,...this.transform.groupby??[]])}producedFields(){return new Set(this.transform.as)}hash(){return`QuantileTransform ${We(this.transform)}`}assemble(){const{quantile:t,...n}=this.transform;return{type:"quantile",field:t,...n}}}class Tm extends ut{clone(){return new Tm(null,De(this.transform))}constructor(t,n){super(t),this.transform=n,this.transform=De(n);const i=this.transform.as??[void 0,void 0];this.transform.as=[i[0]??n.on,i[1]??n.regression]}dependentFields(){return new Set([this.transform.regression,this.transform.on,...this.transform.groupby??[]])}producedFields(){return new Set(this.transform.as)}hash(){return`RegressionTransform ${We(this.transform)}`}assemble(){const{regression:t,on:n,...i}=this.transform;return{type:"regression",x:n,y:t,...i}}}class Mm extends ut{clone(){return new Mm(null,De(this.transform))}constructor(t,n){super(t),this.transform=n}addDimensions(t){this.transform.groupby=fs((this.transform.groupby??[]).concat(t),n=>n)}producedFields(){}dependentFields(){return new Set([this.transform.pivot,this.transform.value,...this.transform.groupby??[]])}hash(){return`PivotTransform ${We(this.transform)}`}assemble(){const{pivot:t,value:n,groupby:i,limit:r,op:s}=this.transform;return{type:"pivot",field:t,value:n,...r!==void 0?{limit:r}:{},...s!==void 0?{op:s}:{},...i!==void 0?{groupby:i}:{}}}}class Nm extends ut{clone(){return new Nm(null,De(this.transform))}constructor(t,n){super(t),this.transform=n}dependentFields(){return new Set}producedFields(){return new Set}hash(){return`SampleTransform ${We(this.transform)}`}assemble(){return{type:"sample",size:this.transform.sample}}}function uI(e){let t=0;function n(i,r){if(i instanceof Ru&&!i.isGenerator&&!Cc(i.data)&&(e.push(r),r={name:null,source:r.name,transform:[]}),i instanceof In&&(i.parent instanceof Ru&&!r.source?(r.format={...r.format,parse:i.assembleFormatParse()},r.transform.push(...i.assembleTransforms(!0))):r.transform.push(...i.assembleTransforms())),i instanceof Nc){r.name||(r.name=`data_${t++}`),!r.source||r.transform.length>0?(e.push(r),i.data=r.name):i.data=r.source,e.push(...i.assemble());return}switch((i instanceof vh||i instanceof xh||i instanceof Rc||i instanceof Fc||i instanceof Dc||i instanceof wh||i instanceof Ir||i instanceof Eh||i instanceof Oc||i instanceof Lu||i instanceof Sm||i instanceof $m||i instanceof km||i instanceof Fm||i instanceof Dm||i instanceof Tm||i instanceof va||i instanceof Nm||i instanceof Mm||i instanceof Am)&&r.transform.push(i.assemble()),(i instanceof $s||i instanceof Cs||i instanceof Iu||i instanceof go||i instanceof Ic)&&r.transform.push(...i.assemble()),i instanceof yi&&(r.source&&r.transform.length===0?i.setSource(r.source):i.parent instanceof yi?i.setSource(r.name):(r.name||(r.name=`data_${t++}`),i.setSource(r.name),i.numChildren()===1&&(e.push(r),r={name:null,source:r.name,transform:[]}))),i.numChildren()){case 0:i instanceof yi&&(!r.source||r.transform.length>0)&&e.push(r);break;case 1:n(i.children[0],r);break;default:{r.name||(r.name=`data_${t++}`);let s=r.name;!r.source||r.transform.length>0?e.push(r):s=r.source;for(const o of i.children)n(o,{name:null,source:s,transform:[]});break}}}return n}function dye(e){const t=[],n=uI(t);for(const i of e.children)n(i,{source:e.name,name:null,transform:[]});return t}function hye(e,t){const n=[],i=uI(n);let r=0;for(const o of e.sources){o.hasName()||(o.dataName=`source_${r++}`);const a=o.assemble();i(o,a)}for(const o of n)o.transform.length===0&&delete o.transform;let s=0;for(const[o,a]of n.entries())(a.transform??[]).length===0&&!a.source&&n.splice(s++,0,n.splice(o,1)[0]);for(const o of n)for(const a of o.transform??[])a.type==="lookup"&&(a.from=e.outputNodes[a.from].getSource());for(const o of n)o.name in t&&(o.values=t[o.name]);return n}function pye(e){return e==="top"||e==="left"||me(e)?"header":"footer"}function gye(e){for(const t of ir)mye(e,t);cI(e,"x"),cI(e,"y")}function mye(e,t){var o;const{facet:n,config:i,child:r,component:s}=e;if(e.channelHasField(t)){const a=n[t],u=Mc("title",null,i,t);let l=xc(a,i,{allowDisabling:!0,includeDefault:u===void 0||!!u});r.component.layoutHeaders[t].title&&(l=W(l)?l.join(", "):l,l+=` / ${r.component.layoutHeaders[t].title}`,r.component.layoutHeaders[t].title=null);const c=Mc("labelOrient",a.header,i,t),f=a.header!==null?zt((o=a.header)==null?void 0:o.labels,i.header.labels,!0):!1,d=qe(["bottom","right"],c)?"footer":"header";s.layoutHeaders[t]={title:a.header!==null?l:null,facetFieldDef:a,[d]:t==="facet"?[]:[lI(e,t,f)]}}}function lI(e,t,n){const i=t==="row"?"height":"width";return{labels:n,sizeSignal:e.child.component.layoutSize.get(i)?e.child.getSizeSignalRef(i):void 0,axes:[]}}function cI(e,t){const{child:n}=e;if(n.component.axes[t]){const{layoutHeaders:i,resolve:r}=e.component;if(r.axis[t]=Yw(r,t),r.axis[t]==="shared"){const s=t==="x"?"column":"row",o=i[s];for(const a of n.component.axes[t]){const u=pye(a.get("orient"));o[u]??(o[u]=[lI(e,s,!1)]);const l=bh(a,"main",e.config,{header:!0});l&&o[u][0].axes.push(l),a.mainExtracted=!0}}}}function yye(e){f6(e),Rm(e,"width"),Rm(e,"height")}function bye(e){f6(e);const t=e.layout.columns===1?"width":"childWidth",n=e.layout.columns===void 0?"height":"childHeight";Rm(e,t),Rm(e,n)}function f6(e){for(const t of e.children)t.parseLayoutSize()}function Rm(e,t){const n=wL(t),i=F1(n),r=e.component.resolve,s=e.component.layoutSize;let o;for(const a of e.children){const u=a.component.layoutSize.getWithExplicit(n),l=r.scale[i]??CL(i,e);if(l==="independent"&&u.value==="step"){o=void 0;break}if(o){if(l==="independent"&&o.value!==u.value){o=void 0;break}o=ma(o,u,n,"")}else o=u}if(o){for(const a of e.children)e.renameSignal(a.getName(n),e.getName(t)),a.component.layoutSize.set(n,"merged",!1);s.setWithExplicit(t,o)}else s.setWithExplicit(t,{explicit:!1,value:void 0})}function vye(e){const{size:t,component:n}=e;for(const i of io){const r=mi(i);if(t[r]){const s=t[r];n.layoutSize.set(r,ws(s)?"step":s,!0)}else{const s=xye(e,r);n.layoutSize.set(r,s,!1)}}}function xye(e,t){const n=t==="width"?"x":"y",i=e.config,r=e.getScaleComponent(n);if(r){const s=r.get("type"),o=r.get("range");if(on(s)){const a=nm(i.view,t);return yu(o)||ws(a)?"step":a}else return cw(i.view,t)}else{if(e.hasProjection||e.mark==="arc")return cw(i.view,t);{const s=nm(i.view,t);return ws(s)?s.step:s}}}function d6(e,t,n){return te(t,{suffix:`by_${te(e)}`,...n})}class Ch extends aI{constructor(t,n,i,r){super(t,"facet",n,i,r,t.resolve),this.child=y6(t.spec,this,this.getName("child"),void 0,r),this.children=[this.child],this.facet=this.initFacet(t.facet)}initFacet(t){if(!ih(t))return{facet:this.initFacetFieldDef(t,"facet")};const n=Y(t),i={};for(const r of n){if(![Zs,Js].includes(r)){Z(M1(r,"facet"));break}const s=t[r];if(s.field===void 0){Z(x_(s,r));break}i[r]=this.initFacetFieldDef(s,r)}return i}initFacetFieldDef(t,n){const i=Z_(t,n);return i.header?i.header=yn(i.header):i.header===null&&(i.header=null),i}channelHasField(t){return X(this.facet,t)}fieldDef(t){return this.facet[t]}parseData(){this.component.data=Om(this),this.child.parseData()}parseLayoutSize(){f6(this)}parseSelections(){this.child.parseSelections(),this.component.selection=this.child.component.selection,Object.values(this.component.selection).some(t=>ks(t))&&w_(v_)}parseMarkGroup(){this.child.parseMarkGroup()}parseAxesAndHeaders(){this.child.parseAxesAndHeaders(),gye(this)}assembleSelectionTopLevelSignals(t){return this.child.assembleSelectionTopLevelSignals(t)}assembleSignals(){return this.child.assembleSignals(),[]}assembleSelectionData(t){return this.child.assembleSelectionData(t)}getHeaderLayoutMixins(){const t={};for(const n of ir)for(const i of Hw){const r=this.component.layoutHeaders[n],s=r[i],{facetFieldDef:o}=r;if(o){const a=Mc("titleOrient",o.header,this.config,n);if(["right","bottom"].includes(a)){const u=xm(n,a);t.titleAnchor??(t.titleAnchor={}),t.titleAnchor[u]="end"}}if(s!=null&&s[0]){const a=n==="row"?"height":"width",u=i==="header"?"headerBand":"footerBand";n!=="facet"&&!this.child.component.layoutSize.get(a)&&(t[u]??(t[u]={}),t[u][n]=.5),r.title&&(t.offset??(t.offset={}),t.offset[n==="row"?"rowTitle":"columnTitle"]=10)}}return t}assembleDefaultLayout(){const{column:t,row:n}=this.facet,i=t?this.columnDistinctSignal():n?1:void 0;let r="all";return(!n&&this.component.resolve.scale.x==="independent"||!t&&this.component.resolve.scale.y==="independent")&&(r="none"),{...this.getHeaderLayoutMixins(),...i?{columns:i}:{},bounds:"full",align:r}}assembleLayoutSignals(){return this.child.assembleLayoutSignals()}columnDistinctSignal(){if(!(this.parent&&this.parent instanceof Ch))return{signal:`length(data('${this.getName("column_domain")}'))`}}assembleGroupStyle(){}assembleGroup(t){return this.parent&&this.parent instanceof Ch?{...this.channelHasField("column")?{encode:{update:{columns:{field:te(this.facet.column,{prefix:"distinct"})}}}}:{},...super.assembleGroup(t)}:super.assembleGroup(t)}getCardinalityAggregateForChild(){const t=[],n=[],i=[];if(this.child instanceof Ch){if(this.child.channelHasField("column")){const r=te(this.child.facet.column);t.push(r),n.push("distinct"),i.push(`distinct_${r}`)}}else for(const r of io){const s=this.child.component.scales[r];if(s&&!s.merged){const o=s.get("type"),a=s.get("range");if(on(o)&&yu(a)){const u=Cm(this.child,r),l=a6(u);l?(t.push(l),n.push("distinct"),i.push(`distinct_${l}`)):Z(b_(r))}}}return{fields:t,ops:n,as:i}}assembleFacet(){const{name:t,data:n}=this.component.data.facetRoot,{row:i,column:r}=this.facet,{fields:s,ops:o,as:a}=this.getCardinalityAggregateForChild(),u=[];for(const c of ir){const f=this.facet[c];if(f){u.push(te(f));const{bin:d,sort:h}=f;if(_t(d)&&u.push(te(f,{binSuffix:"end"})),oo(h)){const{field:p,op:g=j1}=h,m=d6(f,h);i&&r?(s.push(m),o.push("max"),a.push(m)):(s.push(p),o.push(g),a.push(m))}else if(W(h)){const p=Tc(f,c);s.push(p),o.push("max"),a.push(p)}}}const l=!!i&&!!r;return{name:t,data:n,groupby:u,...l||s.length>0?{aggregate:{...l?{cross:l}:{},...s.length?{fields:s,ops:o,as:a}:{}}}:{}}}facetSortFields(t){const{facet:n}=this,i=n[t];return i?oo(i.sort)?[d6(i,i.sort,{expr:"datum"})]:W(i.sort)?[Tc(i,t,{expr:"datum"})]:[te(i,{expr:"datum"})]:[]}facetSortOrder(t){const{facet:n}=this,i=n[t];if(i){const{sort:r}=i;return[(oo(r)?r.order:!W(r)&&r)||"ascending"]}return[]}assembleLabelTitle(){var r;const{facet:t,config:n}=this;if(t.facet)return Gw(t.facet,"facet",n);const i={row:["top","bottom"],column:["left","right"]};for(const s of Ww)if(t[s]){const o=Mc("labelOrient",(r=t[s])==null?void 0:r.header,n,s);if(i[s].includes(o))return Gw(t[s],s,n)}}assembleMarks(){const{child:t}=this,n=this.component.data.facetRoot,i=dye(n),r=t.assembleGroupEncodeEntry(!1),s=this.assembleLabelTitle()||t.assembleTitle(),o=t.assembleGroupStyle();return[{name:this.getName("cell"),type:"group",...s?{title:s}:{},...o?{style:o}:{},from:{facet:this.assembleFacet()},sort:{field:ir.map(u=>this.facetSortFields(u)).flat(),order:ir.map(u=>this.facetSortOrder(u)).flat()},...i.length>0?{data:i}:{},...r?{encode:{update:r}}:{},...t.assembleGroup(Age(this,[]))}]}getMapping(){return this.facet}}function _ye(e,t){const{row:n,column:i}=t;if(n&&i){let r=null;for(const s of[n,i])if(oo(s.sort)){const{field:o,op:a=j1}=s.sort;e=r=new Lu(e,{joinaggregate:[{op:a,field:o,as:d6(s,s.sort,{forAs:!0})}],groupby:[te(s)]})}return r}return null}function fI(e,t){var n,i,r,s;for(const o of t){const a=o.data;if(e.name&&o.hasName()&&e.name!==o.dataName)continue;const u=(n=e.format)==null?void 0:n.mesh,l=(i=a.format)==null?void 0:i.feature;if(u&&l)continue;const c=(r=e.format)==null?void 0:r.feature;if((c||l)&&c!==l)continue;const f=(s=a.format)==null?void 0:s.mesh;if(!((u||f)&&u!==f)){if(uh(e)&&uh(a)){if(Mi(e.values,a.values))return o}else if(Cc(e)&&Cc(a)){if(e.url===a.url)return o}else if(cO(e)&&e.name===o.dataName)return o}}return null}function wye(e,t){if(e.data||!e.parent){if(e.data===null){const i=new Ru({values:[]});return t.push(i),i}const n=fI(e.data,t);if(n)return ya(e.data)||(n.data.format=CM({},e.data.format,n.data.format)),!n.hasName()&&e.data.name&&(n.dataName=e.data.name),n;{const i=new Ru(e.data);return t.push(i),i}}else return e.parent.component.data.facetRoot?e.parent.component.data.facetRoot:e.parent.component.data.main}function Eye(e,t,n){let i=0;for(const r of t.transforms){let s,o;if(ege(r))o=e=new Dc(e,r),s="derived";else if(mw(r)){const a=h2e(r);o=e=In.makeWithAncestors(e,{},a,n)??e,e=new Fc(e,t,r.filter)}else if(nO(r))o=e=$s.makeFromTransform(e,r,t),s="number";else if(nge(r))s="date",n.getWithExplicit(r.field).value===void 0&&(e=new In(e,{[r.field]:s}),n.set(r.field,s,!1)),o=e=Cs.makeFromTransform(e,r);else if(ige(r))o=e=Ir.makeFromTransform(e,r),s="number",Tw(t)&&(e=new va(e));else if(tO(r))o=e=Eh.make(e,t,r,i++),s="derived";else if(Zpe(r))o=e=new Oc(e,r),s="number";else if(Jpe(r))o=e=new Lu(e,r),s="number";else if(rge(r))o=e=go.makeFromTransform(e,r),s="derived";else if(sge(r))o=e=new Sm(e,r),s="derived";else if(oge(r))o=e=new Am(e,r),s="derived";else if(Qpe(r))o=e=new $m(e,r),s="derived";else if(Hpe(r))o=e=new Mm(e,r),s="derived";else if(Kpe(r))e=new Nm(e,r);else if(tge(r))o=e=Iu.makeFromTransform(e,r),s="derived";else if(Gpe(r))o=e=new km(e,r),s="derived";else if(Vpe(r))o=e=new Dm(e,r),s="derived";else if(Ype(r))o=e=new Tm(e,r),s="derived";else if(Xpe(r))o=e=new Fm(e,r),s="derived";else{Z(Tde(r));continue}if(o&&s!==void 0)for(const a of o.producedFields()??[])n.set(a,s,!1)}return e}function Om(e){var m;let t=wye(e,e.component.data.sources);const{outputNodes:n,outputNodeRefCounts:i}=e.component.data,r=e.data,o=!(r&&(ya(r)||Cc(r)||uh(r)))&&e.parent?e.parent.component.data.ancestorParse.clone():new bge;ya(r)?(fO(r)?t=new xh(t,r.sequence):vw(r)&&(t=new vh(t,r.graticule)),o.parseNothing=!0):((m=r==null?void 0:r.format)==null?void 0:m.parse)===null&&(o.parseNothing=!0),t=In.makeExplicit(t,e,o)??t,t=new va(t);const a=e.parent&&Lc(e.parent);(Dt(e)||Oi(e))&&a&&(t=$s.makeFromEncoding(t,e)??t),e.transforms.length>0&&(t=Eye(t,e,o));const u=g2e(e),l=p2e(e);t=In.makeWithAncestors(t,{},{...u,...l},o)??t,Dt(e)&&(t=Ic.parseAll(t,e),t=wh.parseAll(t,e)),(Dt(e)||Oi(e))&&(a||(t=$s.makeFromEncoding(t,e)??t),t=Cs.makeFromEncoding(t,e)??t,t=Dc.parseAllForSortIndex(t,e));const c=t=Lm(Ft.Raw,e,t);if(Dt(e)){const y=Ir.makeFromEncoding(t,e);y&&(t=y,Tw(e)&&(t=new va(t))),t=Iu.makeFromEncoding(t,e)??t,t=go.makeFromEncoding(t,e)??t}let f,d;if(Dt(e)){const{markDef:y,mark:b,config:v}=e,x=gt("invalid",y,v),{marks:_,scales:E}=d=hO({invalid:x,isPath:ha(b)});_!==E&&E==="include-invalid-values"&&(f=t=Lm(Ft.PreFilterInvalid,e,t)),_==="exclude-invalid-values"&&(t=Rc.make(t,e,d)??t)}const h=t=Lm(Ft.Main,e,t);let p;if(Dt(e)&&d){const{marks:y,scales:b}=d;y==="include-invalid-values"&&b==="exclude-invalid-values"&&(t=Rc.make(t,e,d)??t,p=t=Lm(Ft.PostFilterInvalid,e,t))}Dt(e)&&cme(e,h);let g=null;if(Oi(e)){const y=e.getName("facet");t=_ye(t,e.facet)??t,g=new Nc(t,e,y,h.getSource()),n[y]=g}return{...e.component.data,outputNodes:n,outputNodeRefCounts:i,raw:c,main:h,facetRoot:g,ancestorParse:o,preFilterInvalid:f,postFilterInvalid:p}}function Lm(e,t,n){const{outputNodes:i,outputNodeRefCounts:r}=t.component.data,s=t.getDataName(e),o=new yi(n,s,e,r);return i[s]=o,o}class Cye extends c6{constructor(t,n,i,r){var s,o,a,u;super(t,"concat",n,i,r,t.resolve),(((o=(s=t.resolve)==null?void 0:s.axis)==null?void 0:o.x)==="shared"||((u=(a=t.resolve)==null?void 0:a.axis)==null?void 0:u.y)==="shared")&&Z(Sde),this.children=this.getChildren(t).map((l,c)=>y6(l,this,this.getName(`concat_${c}`),void 0,r))}parseData(){this.component.data=Om(this);for(const t of this.children)t.parseData()}parseSelections(){this.component.selection={};for(const t of this.children){t.parseSelections();for(const n of Y(t.component.selection))this.component.selection[n]=t.component.selection[n]}Object.values(this.component.selection).some(t=>ks(t))&&w_(v_)}parseMarkGroup(){for(const t of this.children)t.parseMarkGroup()}parseAxesAndHeaders(){for(const t of this.children)t.parseAxesAndHeaders()}getChildren(t){return tm(t)?t.vconcat:lw(t)?t.hconcat:t.concat}parseLayoutSize(){bye(this)}parseAxisGroup(){return null}assembleSelectionTopLevelSignals(t){return this.children.reduce((n,i)=>i.assembleSelectionTopLevelSignals(n),t)}assembleSignals(){return this.children.forEach(t=>t.assembleSignals()),[]}assembleLayoutSignals(){const t=Vw(this);for(const n of this.children)t.push(...n.assembleLayoutSignals());return t}assembleSelectionData(t){return this.children.reduce((n,i)=>i.assembleSelectionData(n),t)}assembleMarks(){return this.children.map(t=>{const n=t.assembleTitle(),i=t.assembleGroupStyle(),r=t.assembleGroupEncodeEntry(!1);return{type:"group",name:t.getName("group"),...n?{title:n}:{},...i?{style:i}:{},...r?{encode:{update:r}}:{},...t.assembleGroup()}})}assembleGroupStyle(){}assembleDefaultLayout(){const t=this.layout.columns;return{...t!=null?{columns:t}:{},bounds:"full",align:"each"}}}function kye(e){return e===!1||e===null}const Aye={disable:1,gridScale:1,scale:1,...hR,labelExpr:1,encode:1},dI=Y(Aye);class h6 extends lo{constructor(t={},n={},i=!1){super(),this.explicit=t,this.implicit=n,this.mainExtracted=i}clone(){return new h6(De(this.explicit),De(this.implicit),this.mainExtracted)}hasAxisPart(t){return t==="axis"?!0:t==="grid"||t==="title"?!!this.get(t):!kye(this.get(t))}hasOrientSignalRef(){return me(this.explicit.orient)}}function $ye(e,t,n){const{encoding:i,config:r}=e,s=Xt(i[t])??Xt(i[gs(t)]),o=e.axis(t)||{},{format:a,formatType:u}=o;if(ku(u))return{text:Mr({fieldOrDatumDef:s,field:"datum.value",format:a,formatType:u,config:r}),...n};if(a===void 0&&u===void 0&&r.customFormatTypes){if(bc(s)==="quantitative"){if(vc(s)&&s.stack==="normalize"&&r.normalizedNumberFormatType)return{text:Mr({fieldOrDatumDef:s,field:"datum.value",format:r.normalizedNumberFormat,formatType:r.normalizedNumberFormatType,config:r}),...n};if(r.numberFormatType)return{text:Mr({fieldOrDatumDef:s,field:"datum.value",format:r.numberFormat,formatType:r.numberFormatType,config:r}),...n}}if(bc(s)==="temporal"&&r.timeFormatType&&J(s)&&!s.timeUnit)return{text:Mr({fieldOrDatumDef:s,field:"datum.value",format:r.timeFormat,formatType:r.timeFormatType,config:r}),...n}}return n}function Sye(e){return io.reduce((t,n)=>(e.component.scales[n]&&(t[n]=[Oye(n,e)]),t),{})}const Fye={bottom:"top",top:"bottom",left:"right",right:"left"};function Dye(e){const{axes:t,resolve:n}=e.component,i={top:0,bottom:0,right:0,left:0};for(const r of e.children){r.parseAxesAndHeaders();for(const s of Y(r.component.axes))n.axis[s]=Yw(e.component.resolve,s),n.axis[s]==="shared"&&(t[s]=Tye(t[s],r.component.axes[s]),t[s]||(n.axis[s]="independent",delete t[s]))}for(const r of io){for(const s of e.children)if(s.component.axes[r]){if(n.axis[r]==="independent"){t[r]=(t[r]??[]).concat(s.component.axes[r]);for(const o of s.component.axes[r]){const{value:a,explicit:u}=o.getWithExplicit("orient");if(!me(a)){if(i[a]>0&&!u){const l=Fye[a];i[a]>i[l]&&o.set("orient",l,!1)}i[a]++}}}delete s.component.axes[r]}if(n.axis[r]==="independent"&&t[r]&&t[r].length>1)for(const[s,o]of(t[r]||[]).entries())s>0&&o.get("grid")&&!o.explicit.grid&&(o.implicit.grid=!1)}}function Tye(e,t){if(e){if(e.length!==t.length)return;const n=e.length;for(let i=0;in.clone());return e}function Mye(e,t){for(const n of dI){const i=ma(e.getWithExplicit(n),t.getWithExplicit(n),n,"axis",(r,s)=>{switch(n){case"title":return tN(r,s);case"gridScale":return{explicit:r.explicit,value:zt(r.value,s.value)}}return rm(r,s,n,"axis")});e.setWithExplicit(n,i)}return e}function Nye(e,t,n,i,r){if(t==="disable")return n!==void 0;switch(n=n||{},t){case"titleAngle":case"labelAngle":return e===(me(n.labelAngle)?n.labelAngle:Yd(n.labelAngle));case"values":return!!n.values;case"encode":return!!n.encoding||!!n.labelAngle;case"title":if(e===mL(i,r))return!0}return e===n[t]}const Rye=new Set(["grid","translate","format","formatType","orient","labelExpr","tickCount","position","tickMinStep"]);function Oye(e,t){var y,b;let n=t.axis(e);const i=new h6,r=Xt(t.encoding[e]),{mark:s,config:o}=t,a=(n==null?void 0:n.orient)||((y=o[e==="x"?"axisX":"axisY"])==null?void 0:y.orient)||((b=o.axis)==null?void 0:b.orient)||xme(e),u=t.getScaleComponent(e).get("type"),l=hme(e,u,a,t.config),c=n!==void 0?!n:jw("disable",o.style,n==null?void 0:n.style,l).configValue;if(i.set("disable",c,n!==void 0),c)return i;n=n||{};const f=yme(r,n,e,o.style,l),d=YN(n.formatType,r,u),h=VN(r,r.type,n.format,n.formatType,o,!0),p={fieldOrDatumDef:r,axis:n,channel:e,model:t,scaleType:u,orient:a,labelAngle:f,format:h,formatType:d,mark:s,config:o};for(const v of dI){const x=v in hL?hL[v](p):pR(v)?n[v]:void 0,_=x!==void 0,E=Nye(x,v,n,t,e);if(_&&E)i.set(v,x,E);else{const{configValue:w=void 0,configFrom:C=void 0}=pR(v)&&v!=="values"?jw(v,o.style,n.style,l):{},k=w!==void 0;_&&!k?i.set(v,x,E):(C!=="vgAxisConfig"||Rye.has(v)&&k||ah(w)||me(w))&&i.set(v,w,!1)}}const g=n.encoding??{},m=dR.reduce((v,x)=>{if(!i.hasAxisPart(x))return v;const _=EL(g[x]??{},t),E=x==="labels"?$ye(t,e,_):_;return E!==void 0&&!ht(E)&&(v[x]={update:E}),v},{});return ht(m)||i.set("encode",m,!!n.encoding||n.labelAngle!==void 0),i}function Lye({encoding:e,size:t}){for(const n of io){const i=mi(n);ws(t[i])&&ga(e[n])&&(delete t[i],Z(hN(i)))}return t}const Iye={vgMark:"arc",encodeEntry:e=>({...rr(e,{align:"ignore",baseline:"ignore",color:"include",size:"ignore",orient:"ignore",theta:"ignore"}),...ti("x",e,{defaultPos:"mid"}),...ti("y",e,{defaultPos:"mid"}),...fo(e,"radius"),...fo(e,"theta")})},Pye={vgMark:"area",encodeEntry:e=>({...rr(e,{align:"ignore",baseline:"ignore",color:"include",orient:"include",size:"ignore",theta:"ignore"}),...lm("x",e,{defaultPos:"zeroOrMin",defaultPos2:"zeroOrMin",range:e.markDef.orient==="horizontal"}),...lm("y",e,{defaultPos:"zeroOrMin",defaultPos2:"zeroOrMin",range:e.markDef.orient==="vertical"}),...Fw(e)})},zye={vgMark:"rect",encodeEntry:e=>({...rr(e,{align:"ignore",baseline:"ignore",color:"include",orient:"ignore",size:"ignore",theta:"ignore"}),...fo(e,"x"),...fo(e,"y")})},Bye={vgMark:"shape",encodeEntry:e=>({...rr(e,{align:"ignore",baseline:"ignore",color:"include",size:"ignore",orient:"ignore",theta:"ignore"})}),postEncodingTransform:e=>{const{encoding:t}=e,n=t.shape;return[{type:"geoshape",projection:e.projectionName(),...n&&J(n)&&n.type===mc?{field:te(n,{expr:"datum"})}:{}}]}},Uye={vgMark:"image",encodeEntry:e=>({...rr(e,{align:"ignore",baseline:"ignore",color:"ignore",orient:"ignore",size:"ignore",theta:"ignore"}),...fo(e,"x"),...fo(e,"y"),...$w(e,"url")})},jye={vgMark:"line",encodeEntry:e=>({...rr(e,{align:"ignore",baseline:"ignore",color:"include",size:"ignore",orient:"ignore",theta:"ignore"}),...ti("x",e,{defaultPos:"mid"}),...ti("y",e,{defaultPos:"mid"}),...vn("size",e,{vgChannel:"strokeWidth"}),...Fw(e)})},qye={vgMark:"trail",encodeEntry:e=>({...rr(e,{align:"ignore",baseline:"ignore",color:"include",size:"include",orient:"ignore",theta:"ignore"}),...ti("x",e,{defaultPos:"mid"}),...ti("y",e,{defaultPos:"mid"}),...vn("size",e),...Fw(e)})};function p6(e,t){const{config:n}=e;return{...rr(e,{align:"ignore",baseline:"ignore",color:"include",size:"include",orient:"ignore",theta:"ignore"}),...ti("x",e,{defaultPos:"mid"}),...ti("y",e,{defaultPos:"mid"}),...vn("size",e),...vn("angle",e),...Wye(e,n,t)}}function Wye(e,t,n){return n?{shape:{value:n}}:vn("shape",e)}const Hye={vgMark:"symbol",encodeEntry:e=>p6(e)},Gye={vgMark:"symbol",encodeEntry:e=>p6(e,"circle")},Vye={vgMark:"symbol",encodeEntry:e=>p6(e,"square")},Yye={vgMark:"rect",encodeEntry:e=>({...rr(e,{align:"ignore",baseline:"ignore",color:"include",orient:"ignore",size:"ignore",theta:"ignore"}),...fo(e,"x"),...fo(e,"y")})},Xye={vgMark:"rule",encodeEntry:e=>{const{markDef:t}=e,n=t.orient;return!e.encoding.x&&!e.encoding.y&&!e.encoding.latitude&&!e.encoding.longitude?{}:{...rr(e,{align:"ignore",baseline:"ignore",color:"include",orient:"ignore",size:"ignore",theta:"ignore"}),...lm("x",e,{defaultPos:n==="horizontal"?"zeroOrMax":"mid",defaultPos2:"zeroOrMin",range:n!=="vertical"}),...lm("y",e,{defaultPos:n==="vertical"?"zeroOrMax":"mid",defaultPos2:"zeroOrMin",range:n!=="horizontal"}),...vn("size",e,{vgChannel:"strokeWidth"})}}},Kye={vgMark:"text",encodeEntry:e=>{const{config:t,encoding:n}=e;return{...rr(e,{align:"include",baseline:"include",color:"include",size:"ignore",orient:"ignore",theta:"include"}),...ti("x",e,{defaultPos:"mid"}),...ti("y",e,{defaultPos:"mid"}),...$w(e),...vn("size",e,{vgChannel:"fontSize"}),...vn("angle",e),...OO("align",Zye(e.markDef,n,t)),...OO("baseline",Jye(e.markDef,n,t)),...ti("radius",e,{defaultPos:null}),...ti("theta",e,{defaultPos:null})}}};function Zye(e,t,n){if(gt("align",e,n)===void 0)return"center"}function Jye(e,t,n){if(gt("baseline",e,n)===void 0)return"middle"}const Im={arc:Iye,area:Pye,bar:zye,circle:Gye,geoshape:Bye,image:Uye,line:jye,point:Hye,rect:Yye,rule:Xye,square:Vye,text:Kye,tick:{vgMark:"rect",encodeEntry:e=>{const{config:t,markDef:n}=e,i=n.orient,r=i==="horizontal"?"x":"y",s=i==="horizontal"?"y":"x",o=i==="horizontal"?"height":"width";return{...rr(e,{align:"ignore",baseline:"ignore",color:"include",orient:"ignore",size:"ignore",theta:"ignore"}),...fo(e,r),...ti(s,e,{defaultPos:"mid",vgChannel:s==="y"?"yc":"xc"}),[o]:Et(gt("thickness",n,t))}}},trail:qye};function Qye(e){if(qe([P1,L1,r0e],e.mark)){const t=xR(e.mark,e.encoding);if(t.length>0)return ebe(e,t)}else if(e.mark===I1){const t=g_.some(n=>gt(n,e.markDef,e.config));if(e.stack&&!e.fieldDef("size")&&t)return tbe(e)}return g6(e)}const hI="faceted_path_";function ebe(e,t){return[{name:e.getName("pathgroup"),type:"group",from:{facet:{name:hI+e.requestDataName(Ft.Main),data:e.requestDataName(Ft.Main),groupby:t}},encode:{update:{width:{field:{group:"width"}},height:{field:{group:"height"}}}},marks:g6(e,{fromPrefix:hI})}]}const pI="stack_group_";function tbe(e){var l;const[t]=g6(e,{fromPrefix:pI}),n=e.scaleName(e.stack.fieldChannel),i=(c={})=>e.vgField(e.stack.fieldChannel,c),r=(c,f)=>{const d=[i({prefix:"min",suffix:"start",expr:f}),i({prefix:"max",suffix:"start",expr:f}),i({prefix:"min",suffix:"end",expr:f}),i({prefix:"max",suffix:"end",expr:f})];return`${c}(${d.map(h=>`scale('${n}',${h})`).join(",")})`};let s,o;e.stack.fieldChannel==="x"?(s={...uc(t.encode.update,["y","yc","y2","height",...g_]),x:{signal:r("min","datum")},x2:{signal:r("max","datum")},clip:{value:!0}},o={x:{field:{group:"x"},mult:-1},height:{field:{group:"height"}}},t.encode.update={...hi(t.encode.update,["y","yc","y2"]),height:{field:{group:"height"}}}):(s={...uc(t.encode.update,["x","xc","x2","width"]),y:{signal:r("min","datum")},y2:{signal:r("max","datum")},clip:{value:!0}},o={y:{field:{group:"y"},mult:-1},width:{field:{group:"width"}}},t.encode.update={...hi(t.encode.update,["x","xc","x2"]),width:{field:{group:"width"}}});for(const c of g_){const f=ys(c,e.markDef,e.config);t.encode.update[c]?(s[c]=t.encode.update[c],delete t.encode.update[c]):f&&(s[c]=Et(f)),f&&(t.encode.update[c]={value:0})}const a=[];if(((l=e.stack.groupbyChannels)==null?void 0:l.length)>0)for(const c of e.stack.groupbyChannels){const f=e.fieldDef(c),d=te(f);d&&a.push(d),(f!=null&&f.bin||f!=null&&f.timeUnit)&&a.push(te(f,{binSuffix:"end"}))}return s=["stroke","strokeWidth","strokeJoin","strokeCap","strokeDash","strokeDashOffset","strokeMiterLimit","strokeOpacity"].reduce((c,f)=>{if(t.encode.update[f])return{...c,[f]:t.encode.update[f]};{const d=ys(f,e.markDef,e.config);return d!==void 0?{...c,[f]:Et(d)}:c}},s),s.stroke&&(s.strokeForeground={value:!0},s.strokeOffset={value:0}),[{type:"group",from:{facet:{data:e.requestDataName(Ft.Main),name:pI+e.requestDataName(Ft.Main),groupby:a,aggregate:{fields:[i({suffix:"start"}),i({suffix:"start"}),i({suffix:"end"}),i({suffix:"end"})],ops:["min","max","min","max"]}}},encode:{update:s},marks:[{type:"group",encode:{update:o},marks:[t]}]}]}function nbe(e){const{encoding:t,stack:n,mark:i,markDef:r,config:s}=e,o=t.order;if(!(!W(o)&&Nr(o)&&J7(o.value)||!o&&J7(gt("order",r,s)))){if((W(o)||J(o))&&!n)return JM(o,{expr:"datum"});if(ha(i)){const a=r.orient==="horizontal"?"y":"x",u=t[a];if(J(u))return{field:a}}}}function g6(e,t={fromPrefix:""}){const{mark:n,markDef:i,encoding:r,config:s}=e,o=zt(i.clip,ibe(e),rbe(e)),a=KM(i),u=r.key,l=nbe(e),c=sbe(e),f=gt("aria",i,s),d=Im[n].postEncodingTransform?Im[n].postEncodingTransform(e):null;return[{name:e.getName("marks"),type:Im[n].vgMark,...o?{clip:o}:{},...a?{style:a}:{},...u?{key:u.field}:{},...l?{sort:l}:{},...c||{},...f===!1?{aria:f}:{},from:{data:t.fromPrefix+e.requestDataName(Ft.Main)},encode:{update:Im[n].encodeEntry(e)},...d?{transform:d}:{}}]}function ibe(e){const t=e.getScaleComponent("x"),n=e.getScaleComponent("y");return t!=null&&t.get("selectionExtent")||n!=null&&n.get("selectionExtent")?!0:void 0}function rbe(e){const t=e.component.projection;return t&&!t.isFit?!0:void 0}function sbe(e){if(!e.component.selection)return null;const t=Y(e.component.selection).length;let n=t,i=e.parent;for(;i&&n===0;)n=Y(i.component.selection).length,i=i.parent;return n?{interactive:t>0||e.mark==="geoshape"||!!e.encoding.tooltip||!!e.markDef.tooltip}:null}class gI extends aI{constructor(t,n,i,r={},s){super(t,"unit",n,i,s,void 0,PR(t)?t.view:void 0),this.specifiedScales={},this.specifiedAxes={},this.specifiedLegends={},this.specifiedProjection={},this.selection=[],this.children=[],this.correctDataNames=l=>{var c,f,d;return(c=l.from)!=null&&c.data&&(l.from.data=this.lookupDataSource(l.from.data),"time"in this.encoding&&(l.from.data=l.from.data+bO)),(d=(f=l.from)==null?void 0:f.facet)!=null&&d.data&&(l.from.facet.data=this.lookupDataSource(l.from.facet.data)),l};const o=xs(t.mark)?{...t.mark}:{type:t.mark},a=o.type;o.filled===void 0&&(o.filled=Rpe(o,s,{graticule:t.data&&vw(t.data)}));const u=this.encoding=U0e(t.encoding||{},a,o.filled,s);this.markDef=VR(o,u,s),this.size=Lye({encoding:u,size:PR(t)?{...r,...t.width?{width:t.width}:{},...t.height?{height:t.height}:{}}:r}),this.stack=GR(this.markDef,u),this.specifiedScales=this.initScales(a,u),this.specifiedAxes=this.initAxes(u),this.specifiedLegends=this.initLegends(u),this.specifiedProjection=t.projection,this.selection=(t.params??[]).filter(l=>aw(l))}get hasProjection(){const{encoding:t}=this,n=this.mark===zN,i=t&&Pfe.some(r=>Me(t[r]));return n||i}scaleDomain(t){const n=this.specifiedScales[t];return n?n.domain:void 0}axis(t){return this.specifiedAxes[t]}legend(t){return this.specifiedLegends[t]}initScales(t,n){return d_.reduce((i,r)=>{const s=Xt(n[r]);return s&&(i[r]=this.initScale(s.scale??{})),i},{})}initScale(t){const{domain:n,range:i}=t,r=yn(t);return W(n)&&(r.domain=n.map(Ni)),W(i)&&(r.range=i.map(Ni)),r}initAxes(t){return io.reduce((n,i)=>{const r=t[i];if(Me(r)||i===$t&&Me(t.x2)||i===rn&&Me(t.y2)){const s=Me(r)?r.axis:void 0;n[i]=s&&this.initAxis({...s})}return n},{})}initAxis(t){const n=Y(t),i={};for(const r of n){const s=t[r];i[r]=ah(s)?VM(s):Ni(s)}return i}initLegends(t){return Xfe.reduce((n,i)=>{const r=Xt(t[i]);if(r&&Zfe(i)){const s=r.legend;n[i]=s&&yn(s)}return n},{})}parseData(){this.component.data=Om(this)}parseLayoutSize(){vye(this)}parseSelections(){this.component.selection=lme(this,this.selection)}parseMarkGroup(){this.component.mark=Qye(this)}parseAxesAndHeaders(){this.component.axes=Sye(this)}assembleSelectionTopLevelSignals(t){return $ge(this,t)}assembleSignals(){return[...fL(this),...kge(this,[])]}assembleSelectionData(t){return Sge(this,t)}assembleLayout(){return null}assembleLayoutSignals(){return Vw(this)}assembleMarks(){let t=this.component.mark??[];return(!this.parent||!Lc(this.parent))&&(t=wO(this,t)),t.map(this.correctDataNames)}assembleGroupStyle(){const{style:t}=this.view||{};return t!==void 0?t:this.encoding.x||this.encoding.y?"cell":"view"}getMapping(){return this.encoding}get mark(){return this.markDef.type}channelHasField(t){return $u(this.encoding,t)}fieldDef(t){const n=this.encoding[t];return Rr(n)}typedFieldDef(t){const n=this.fieldDef(t);return ei(n)?n:null}}class m6 extends c6{constructor(t,n,i,r,s){super(t,"layer",n,i,s,t.resolve,t.view);const o={...r,...t.width?{width:t.width}:{},...t.height?{height:t.height}:{}};this.children=t.layer.map((a,u)=>{if(im(a))return new m6(a,this,this.getName(`layer_${u}`),o,s);if(ao(a))return new gI(a,this,this.getName(`layer_${u}`),o,s);throw new Error(y_(a))})}parseData(){this.component.data=Om(this);for(const t of this.children)t.parseData()}parseLayoutSize(){yye(this)}parseSelections(){this.component.selection={};for(const t of this.children){t.parseSelections();for(const n of Y(t.component.selection))this.component.selection[n]=t.component.selection[n]}Object.values(this.component.selection).some(t=>ks(t))&&w_(v_)}parseMarkGroup(){for(const t of this.children)t.parseMarkGroup()}parseAxesAndHeaders(){Dye(this)}assembleSelectionTopLevelSignals(t){return this.children.reduce((n,i)=>i.assembleSelectionTopLevelSignals(n),t)}assembleSignals(){return this.children.reduce((t,n)=>t.concat(n.assembleSignals()),fL(this))}assembleLayoutSignals(){return this.children.reduce((t,n)=>t.concat(n.assembleLayoutSignals()),Vw(this))}assembleSelectionData(t){return this.children.reduce((n,i)=>i.assembleSelectionData(n),t)}assembleGroupStyle(){const t=new Set;for(const i of this.children)for(const r of oe(i.assembleGroupStyle()))t.add(r);const n=Array.from(t);return n.length>1?n:n.length===1?n[0]:void 0}assembleTitle(){let t=super.assembleTitle();if(t)return t;for(const n of this.children)if(t=n.assembleTitle(),t)return t}assembleLayout(){return null}assembleMarks(){return Fge(this,this.children.flatMap(t=>t.assembleMarks()))}assembleLegends(){return this.children.reduce((t,n)=>t.concat(n.assembleLegends()),RL(this))}}function y6(e,t,n,i,r){if(q1(e))return new Ch(e,t,n,r);if(im(e))return new m6(e,t,n,i,r);if(ao(e))return new gI(e,t,n,i,r);if(cpe(e))return new Cye(e,t,n,r);throw new Error(y_(e))}function obe(e,t={}){t.logger&&bhe(t.logger),t.fieldTitle&&uR(t.fieldTitle);try{const n=WR(nl(t.config,e.config)),i=oO(e,n),r=y6(i,null,"",void 0,n);return r.parse(),T2e(r.component.data,r),{spec:ube(r,abe(e,i.autosize,n,r),e.datasets,e.usermeta),normalized:i}}finally{t.logger&&vhe(),t.fieldTitle&&N0e()}}function abe(e,t,n,i){const r=i.component.layoutSize.get("width"),s=i.component.layoutSize.get("height");if(t===void 0?(t={type:"pad"},i.hasAxisOrientSignalRef()&&(t.resize=!0)):se(t)&&(t={type:t}),r&&s&&gge(t.type)){if(r==="step"&&s==="step")Z(rN()),t.type="pad";else if(r==="step"||s==="step"){const o=r==="step"?"width":"height";Z(rN(F1(o)));const a=o==="width"?"height":"width";t.type=mge(a)}}return{...Y(t).length===1&&t.type?t.type==="pad"?{}:{autosize:t.type}:{autosize:t},...uO(n,!1),...uO(e,!0)}}function ube(e,t,n={},i){const r=e.config?wpe(e.config):void 0,s=hye(e.component.data,n),o=e.assembleSelectionData(s),a=e.assembleProjections(),u=e.assembleTitle(),l=e.assembleGroupStyle(),c=e.assembleGroupEncodeEntry(!0);let f=e.assembleLayoutSignals();f=f.filter(p=>(p.name==="width"||p.name==="height")&&p.value!==void 0?(t[p.name]=+p.value,!1):!0);const{params:d,...h}=t;return{$schema:"https://vega.github.io/schema/vega/v5.json",...e.description?{description:e.description}:{},...h,...u?{title:u}:{},...l?{style:l}:{},...c?{encode:{update:c}}:{},data:o,...a.length>0?{projections:a}:{},...e.assembleGroup([...f,...e.assembleSelectionTopLevelSignals([]),...LR(d)]),...r?{config:r}:{},...i?{usermeta:i}:{}}}const lbe=Mfe.version,cbe=Object.freeze(Object.defineProperty({__proto__:null,accessPathDepth:fc,accessPathWithDatum:i_,accessWithDatumToUnescapedPath:at,compile:obe,contains:qe,deepEqual:Mi,deleteNestedProperty:w1,duplicate:De,entries:ia,every:Q7,fieldIntersection:n_,flatAccessWithDatum:AM,getFirstDefined:zt,hasIntersection:e_,hasProperty:X,hash:We,internalField:FM,isBoolean:Hd,isEmpty:ht,isEqual:Rfe,isInternalField:DM,isNullOrFalse:J7,isNumeric:E1,keys:Y,logicalExpr:Gd,mergeDeep:CM,never:EM,normalize:oO,normalizeAngle:Yd,omit:hi,pick:uc,prefixGenerator:t_,removePathFromField:cc,replaceAll:du,replacePathInField:er,resetIdCounter:Lfe,setEqual:kM,some:lc,stringify:pt,titleCase:Vd,unique:fs,uniqueId:SM,vals:gn,varName:At,version:lbe},Symbol.toStringTag,{value:"Module"}));function mI(e){const[t,n]=/schema\/([\w-]+)\/([\w\.\-]+)\.json$/g.exec(e).slice(1,3);return{library:t,version:n}}var fbe="2.15.0",dbe={version:fbe};const Pc="#fff",yI="#888",hbe={background:"#333",view:{stroke:yI},title:{color:Pc,subtitleColor:Pc},style:{"guide-label":{fill:Pc},"guide-title":{fill:Pc}},axis:{domainColor:Pc,gridColor:yI,tickColor:Pc}},Pu="#4572a7",pbe={background:"#fff",arc:{fill:Pu},area:{fill:Pu},line:{stroke:Pu,strokeWidth:2},path:{stroke:Pu},rect:{fill:Pu},shape:{stroke:Pu},symbol:{fill:Pu,strokeWidth:1.5,size:50},axis:{bandPosition:.5,grid:!0,gridColor:"#000000",gridOpacity:1,gridWidth:.5,labelPadding:10,tickSize:5,tickWidth:.5},axisBand:{grid:!1,tickExtra:!0},legend:{labelBaseline:"middle",labelFontSize:11,symbolSize:50,symbolType:"square"},range:{category:["#4572a7","#aa4643","#8aa453","#71598e","#4598ae","#d98445","#94aace","#d09393","#b9cc98","#a99cbc"]}},zu="#30a2da",b6="#cbcbcb",gbe="#999",mbe="#333",bI="#f0f0f0",vI="#333",ybe={arc:{fill:zu},area:{fill:zu},axis:{domainColor:b6,grid:!0,gridColor:b6,gridWidth:1,labelColor:gbe,labelFontSize:10,titleColor:mbe,tickColor:b6,tickSize:10,titleFontSize:14,titlePadding:10,labelPadding:4},axisBand:{grid:!1},background:bI,group:{fill:bI},legend:{labelColor:vI,labelFontSize:11,padding:1,symbolSize:30,symbolType:"square",titleColor:vI,titleFontSize:14,titlePadding:10},line:{stroke:zu,strokeWidth:2},path:{stroke:zu,strokeWidth:.5},rect:{fill:zu},range:{category:["#30a2da","#fc4f30","#e5ae38","#6d904f","#8b8b8b","#b96db8","#ff9e27","#56cc60","#52d2ca","#52689e","#545454","#9fe4f8"],diverging:["#cc0020","#e77866","#f6e7e1","#d6e8ed","#91bfd9","#1d78b5"],heatmap:["#d6e8ed","#cee0e5","#91bfd9","#549cc6","#1d78b5"]},point:{filled:!0,shape:"circle"},shape:{stroke:zu},bar:{binSpacing:2,fill:zu,stroke:null},title:{anchor:"start",fontSize:24,fontWeight:600,offset:20}},Bu="#000",bbe={group:{fill:"#e5e5e5"},arc:{fill:Bu},area:{fill:Bu},line:{stroke:Bu},path:{stroke:Bu},rect:{fill:Bu},shape:{stroke:Bu},symbol:{fill:Bu,size:40},axis:{domain:!1,grid:!0,gridColor:"#FFFFFF",gridOpacity:1,labelColor:"#7F7F7F",labelPadding:4,tickColor:"#7F7F7F",tickSize:5.67,titleFontSize:16,titleFontWeight:"normal"},legend:{labelBaseline:"middle",labelFontSize:11,symbolSize:40},range:{category:["#000000","#7F7F7F","#1A1A1A","#999999","#333333","#B0B0B0","#4D4D4D","#C9C9C9","#666666","#DCDCDC"]}},vbe=22,xbe="normal",xI="Benton Gothic, sans-serif",_I=11.5,_be="normal",Uu="#82c6df",v6="Benton Gothic Bold, sans-serif",wI="normal",EI=13,kh={"category-6":["#ec8431","#829eb1","#c89d29","#3580b1","#adc839","#ab7fb4"],"fire-7":["#fbf2c7","#f9e39c","#f8d36e","#f4bb6a","#e68a4f","#d15a40","#ab4232"],"fireandice-6":["#e68a4f","#f4bb6a","#f9e39c","#dadfe2","#a6b7c6","#849eae"]},wbe={background:"#ffffff",title:{anchor:"start",color:"#000000",font:v6,fontSize:vbe,fontWeight:xbe},arc:{fill:Uu},area:{fill:Uu},line:{stroke:Uu,strokeWidth:2},path:{stroke:Uu},rect:{fill:Uu},shape:{stroke:Uu},symbol:{fill:Uu,size:30},axis:{labelFont:xI,labelFontSize:_I,labelFontWeight:_be,titleFont:v6,titleFontSize:EI,titleFontWeight:wI},axisX:{labelAngle:0,labelPadding:4,tickSize:3},axisY:{labelBaseline:"middle",maxExtent:45,minExtent:45,tickSize:2,titleAlign:"left",titleAngle:0,titleX:-45,titleY:-11},legend:{labelFont:xI,labelFontSize:_I,symbolType:"square",titleFont:v6,titleFontSize:EI,titleFontWeight:wI},range:{category:kh["category-6"],diverging:kh["fireandice-6"],heatmap:kh["fire-7"],ordinal:kh["fire-7"],ramp:kh["fire-7"]}},ju="#ab5787",Pm="#979797",Ebe={background:"#f9f9f9",arc:{fill:ju},area:{fill:ju},line:{stroke:ju},path:{stroke:ju},rect:{fill:ju},shape:{stroke:ju},symbol:{fill:ju,size:30},axis:{domainColor:Pm,domainWidth:.5,gridWidth:.2,labelColor:Pm,tickColor:Pm,tickWidth:.2,titleColor:Pm},axisBand:{grid:!1},axisX:{grid:!0,tickSize:10},axisY:{domain:!1,grid:!0,tickSize:0},legend:{labelFontSize:11,padding:1,symbolSize:30,symbolType:"square"},range:{category:["#ab5787","#51b2e5","#703c5c","#168dd9","#d190b6","#00609f","#d365ba","#154866","#666666","#c4c4c4"]}},qu="#3e5c69",Cbe={background:"#fff",arc:{fill:qu},area:{fill:qu},line:{stroke:qu},path:{stroke:qu},rect:{fill:qu},shape:{stroke:qu},symbol:{fill:qu},axis:{domainWidth:.5,grid:!0,labelPadding:2,tickSize:5,tickWidth:.5,titleFontWeight:"normal"},axisBand:{grid:!1},axisX:{gridWidth:.2},axisY:{gridDash:[3],gridWidth:.4},legend:{labelFontSize:11,padding:1,symbolType:"square"},range:{category:["#3e5c69","#6793a6","#182429","#0570b0","#3690c0","#74a9cf","#a6bddb","#e2ddf2"]}},sr="#1696d2",CI="#000000",kbe="#FFFFFF",zm="Lato",x6="Lato",Abe="Lato",$be="#DEDDDD",Sbe=18,Ah={"shades-blue":["#CFE8F3","#A2D4EC","#73BFE2","#46ABDB","#1696D2","#12719E","#0A4C6A","#062635"],"six-groups-cat-1":["#1696d2","#ec008b","#fdbf11","#000000","#d2d2d2","#55b748"],"six-groups-seq":["#cfe8f3","#a2d4ec","#73bfe2","#46abdb","#1696d2","#12719e"],"diverging-colors":["#ca5800","#fdbf11","#fdd870","#fff2cf","#cfe8f3","#73bfe2","#1696d2","#0a4c6a"]},Fbe={background:kbe,title:{anchor:"start",fontSize:Sbe,font:zm},axisX:{domain:!0,domainColor:CI,domainWidth:1,grid:!1,labelFontSize:12,labelFont:x6,labelAngle:0,tickColor:CI,tickSize:5,titleFontSize:12,titlePadding:10,titleFont:zm},axisY:{domain:!1,domainWidth:1,grid:!0,gridColor:$be,gridWidth:1,labelFontSize:12,labelFont:x6,labelPadding:8,ticks:!1,titleFontSize:12,titlePadding:10,titleFont:zm,titleAngle:0,titleY:-10,titleX:18},legend:{labelFontSize:12,labelFont:x6,symbolSize:100,titleFontSize:12,titlePadding:10,titleFont:zm,orient:"right",offset:10},view:{stroke:"transparent"},range:{category:Ah["six-groups-cat-1"],diverging:Ah["diverging-colors"],heatmap:Ah["diverging-colors"],ordinal:Ah["six-groups-seq"],ramp:Ah["shades-blue"]},area:{fill:sr},rect:{fill:sr},line:{color:sr,stroke:sr,strokeWidth:5},trail:{color:sr,stroke:sr,strokeWidth:0,size:1},path:{stroke:sr,strokeWidth:.5},point:{filled:!0},text:{font:Abe,color:sr,fontSize:11,align:"center",fontWeight:400,size:11},style:{bar:{fill:sr,stroke:null}},arc:{fill:sr},shape:{stroke:sr},symbol:{fill:sr,size:30}},Wu="#3366CC",kI="#ccc",Bm="Arial, sans-serif",Dbe={arc:{fill:Wu},area:{fill:Wu},path:{stroke:Wu},rect:{fill:Wu},shape:{stroke:Wu},symbol:{stroke:Wu},circle:{fill:Wu},background:"#fff",padding:{top:10,right:10,bottom:10,left:10},style:{"guide-label":{font:Bm,fontSize:12},"guide-title":{font:Bm,fontSize:12},"group-title":{font:Bm,fontSize:12}},title:{font:Bm,fontSize:14,fontWeight:"bold",dy:-3,anchor:"start"},axis:{gridColor:kI,tickColor:kI,domain:!1,grid:!0},range:{category:["#4285F4","#DB4437","#F4B400","#0F9D58","#AB47BC","#00ACC1","#FF7043","#9E9D24","#5C6BC0","#F06292","#00796B","#C2185B"],heatmap:["#c6dafc","#5e97f6","#2a56c6"]}},_6=e=>e*(1/3+1),AI=_6(9),$I=_6(10),SI=_6(12),$h="Segoe UI",FI="wf_standard-font, helvetica, arial, sans-serif",DI="#252423",Sh="#605E5C",TI="transparent",Tbe="#C8C6C4",zr="#118DFF",Mbe="#12239E",Nbe="#E66C37",Rbe="#6B007B",Obe="#E044A7",Lbe="#744EC2",Ibe="#D9B300",Pbe="#D64550",MI=zr,NI="#DEEFFF",RI=[NI,MI],zbe={view:{stroke:TI},background:TI,font:$h,header:{titleFont:FI,titleFontSize:SI,titleColor:DI,labelFont:$h,labelFontSize:$I,labelColor:Sh},axis:{ticks:!1,grid:!1,domain:!1,labelColor:Sh,labelFontSize:AI,titleFont:FI,titleColor:DI,titleFontSize:SI,titleFontWeight:"normal"},axisQuantitative:{tickCount:3,grid:!0,gridColor:Tbe,gridDash:[1,5],labelFlush:!1},axisBand:{tickExtra:!0},axisX:{labelPadding:5},axisY:{labelPadding:10},bar:{fill:zr},line:{stroke:zr,strokeWidth:3,strokeCap:"round",strokeJoin:"round"},text:{font:$h,fontSize:AI,fill:Sh},arc:{fill:zr},area:{fill:zr,line:!0,opacity:.6},path:{stroke:zr},rect:{fill:zr},point:{fill:zr,filled:!0,size:75},shape:{stroke:zr},symbol:{fill:zr,strokeWidth:1.5,size:50},legend:{titleFont:$h,titleFontWeight:"bold",titleColor:Sh,labelFont:$h,labelFontSize:$I,labelColor:Sh,symbolType:"circle",symbolSize:75},range:{category:[zr,Mbe,Nbe,Rbe,Obe,Lbe,Ibe,Pbe],diverging:RI,heatmap:RI,ordinal:[NI,"#c7e4ff","#b0d9ff","#9aceff","#83c3ff","#6cb9ff","#55aeff","#3fa3ff","#2898ff",MI]}},w6='IBM Plex Sans,system-ui,-apple-system,BlinkMacSystemFont,".sfnstext-regular",sans-serif',Bbe='IBM Plex Sans Condensed, system-ui, -apple-system, BlinkMacSystemFont, ".SFNSText-Regular", sans-serif',E6=400,Um={textPrimary:{g90:"#f4f4f4",g100:"#f4f4f4",white:"#161616",g10:"#161616"},textSecondary:{g90:"#c6c6c6",g100:"#c6c6c6",white:"#525252",g10:"#525252"},layerAccent01:{white:"#e0e0e0",g10:"#e0e0e0",g90:"#525252",g100:"#393939"},gridBg:{white:"#ffffff",g10:"#ffffff",g90:"#161616",g100:"#161616"}},Ube=["#8a3ffc","#33b1ff","#007d79","#ff7eb6","#fa4d56","#fff1f1","#6fdc8c","#4589ff","#d12771","#d2a106","#08bdba","#bae6ff","#ba4e00","#d4bbff"],jbe=["#6929c4","#1192e8","#005d5d","#9f1853","#fa4d56","#570408","#198038","#002d9c","#ee538b","#b28600","#009d9a","#012749","#8a3800","#a56eff"];function jm({theme:e,background:t}){const n=["white","g10"].includes(e)?"light":"dark",i=Um.gridBg[e],r=Um.textPrimary[e],s=Um.textSecondary[e],o=n==="dark"?Ube:jbe,a=n==="dark"?"#d4bbff":"#6929c4";return{background:t,arc:{fill:a},area:{fill:a},path:{stroke:a},rect:{fill:a},shape:{stroke:a},symbol:{stroke:a},circle:{fill:a},view:{fill:i,stroke:i},group:{fill:i},title:{color:r,anchor:"start",dy:-15,fontSize:16,font:w6,fontWeight:600},axis:{labelColor:s,labelFontSize:12,labelFont:Bbe,labelFontWeight:E6,titleColor:r,titleFontWeight:600,titleFontSize:12,grid:!0,gridColor:Um.layerAccent01[e],labelAngle:0},axisX:{titlePadding:10},axisY:{titlePadding:2.5},style:{"guide-label":{font:w6,fill:s,fontWeight:E6},"guide-title":{font:w6,fill:s,fontWeight:E6}},range:{category:o,diverging:["#750e13","#a2191f","#da1e28","#fa4d56","#ff8389","#ffb3b8","#ffd7d9","#fff1f1","#e5f6ff","#bae6ff","#82cfff","#33b1ff","#1192e8","#0072c3","#00539a","#003a6d"],heatmap:["#f6f2ff","#e8daff","#d4bbff","#be95ff","#a56eff","#8a3ffc","#6929c4","#491d8b","#31135e","#1c0f30"]}}}const qbe=jm({theme:"white",background:"#ffffff"}),Wbe=jm({theme:"g10",background:"#f4f4f4"}),Hbe=jm({theme:"g90",background:"#262626"}),Gbe=jm({theme:"g100",background:"#161616"}),Vbe=dbe.version,Ybe=Object.freeze(Object.defineProperty({__proto__:null,carbong10:Wbe,carbong100:Gbe,carbong90:Hbe,carbonwhite:qbe,dark:hbe,excel:pbe,fivethirtyeight:ybe,ggplot2:bbe,googlecharts:Dbe,latimes:wbe,powerbi:zbe,quartz:Ebe,urbaninstitute:Fbe,version:Vbe,vox:Cbe},Symbol.toStringTag,{value:"Module"}));function Xbe(e,t,n,i){if(W(e))return`[${e.map(r=>t(se(r)?r:OI(r,n))).join(", ")}]`;if(re(e)){let r="";const{title:s,image:o,...a}=e;s&&(r+=`

    ${t(s)}

    `),o&&(r+=``);const u=Object.keys(a);if(u.length>0){r+="";for(const l of u){let c=a[l];c!==void 0&&(re(c)&&(c=OI(c,n)),r+=``)}r+="
    ${t(l)}${t(c)}
    "}return r||"{}"}return t(e)}function Kbe(e){const t=[];return function(n,i){if(typeof i!="object"||i===null)return i;const r=t.indexOf(this)+1;return t.length=r,t.length>e?"[Object]":t.indexOf(i)>=0?"[Circular]":(t.push(i),i)}}function OI(e,t){return JSON.stringify(e,Kbe(t))}var Zbe=`#vg-tooltip-element { + visibility: hidden; + padding: 8px; + position: fixed; + z-index: 1000; + font-family: sans-serif; + font-size: 11px; + border-radius: 3px; + box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1); + /* The default theme is the light theme. */ + background-color: rgba(255, 255, 255, 0.95); + border: 1px solid #d9d9d9; + color: black; +} +#vg-tooltip-element.visible { + visibility: visible; +} +#vg-tooltip-element h2 { + margin-top: 0; + margin-bottom: 10px; + font-size: 13px; +} +#vg-tooltip-element table { + border-spacing: 0; +} +#vg-tooltip-element table tr { + border: none; +} +#vg-tooltip-element table tr td { + overflow: hidden; + text-overflow: ellipsis; + padding-top: 2px; + padding-bottom: 2px; +} +#vg-tooltip-element table tr td.key { + color: #808080; + max-width: 150px; + text-align: right; + padding-right: 4px; +} +#vg-tooltip-element table tr td.value { + display: block; + max-width: 300px; + max-height: 7em; + text-align: left; +} +#vg-tooltip-element.dark-theme { + background-color: rgba(32, 32, 32, 0.9); + border: 1px solid #f5f5f5; + color: white; +} +#vg-tooltip-element.dark-theme td.key { + color: #bfbfbf; +} +`;const LI="vg-tooltip-element",Jbe={offsetX:10,offsetY:10,id:LI,styleId:"vega-tooltip-style",theme:"light",disableDefaultStyle:!1,sanitize:Qbe,maxDepth:2,formatTooltip:Xbe,baseURL:"",anchor:"cursor",position:["top","bottom","left","right","top-left","top-right","bottom-left","bottom-right"]};function Qbe(e){return String(e).replace(/&/g,"&").replace(/=0&&e.y>=0&&e.x+t.width<=window.innerWidth&&e.y+t.height<=window.innerHeight}function i3e(e,t,n){return e.clientX>=t.x&&e.clientX<=t.x+n.width&&e.clientY>=t.y&&e.clientY<=t.y+n.height}class r3e{constructor(t){this.options={...Jbe,...t};const n=this.options.id;if(this.el=null,this.call=this.tooltipHandler.bind(this),!this.options.disableDefaultStyle&&!document.getElementById(this.options.styleId)){const i=document.createElement("style");i.setAttribute("id",this.options.styleId),i.innerHTML=e3e(n);const r=document.head;r.childNodes.length>0?r.insertBefore(i,r.childNodes[0]):r.appendChild(i)}}tooltipHandler(t,n,i,r){if(this.el=document.getElementById(this.options.id),this.el||(this.el=document.createElement("div"),this.el.setAttribute("id",this.options.id),this.el.classList.add("vg-tooltip"),(document.fullscreenElement??document.body).appendChild(this.el)),r==null||r===""){this.el.classList.remove("visible",`${this.options.theme}-theme`);return}this.el.innerHTML=this.options.formatTooltip(r,this.options.sanitize,this.options.maxDepth,this.options.baseURL),this.el.classList.add("visible",`${this.options.theme}-theme`);const{x:s,y:o}=this.options.anchor==="mark"?t3e(t,n,i,this.el.getBoundingClientRect(),this.options):II(n,this.el.getBoundingClientRect(),this.options);this.el.style.top=`${o}px`,this.el.style.left=`${s}px`}}/*! + * https://github.com/Starcounter-Jack/JSON-Patch + * (c) 2017-2022 Joachim Wester + * MIT licensed + */var s3e=function(){var e=function(t,n){return e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(i,r){i.__proto__=r}||function(i,r){for(var s in r)r.hasOwnProperty(s)&&(i[s]=r[s])},e(t,n)};return function(t,n){e(t,n);function i(){this.constructor=t}t.prototype=n===null?Object.create(n):(i.prototype=n.prototype,new i)}}(),o3e=Object.prototype.hasOwnProperty;function C6(e,t){return o3e.call(e,t)}function k6(e){if(Array.isArray(e)){for(var t=new Array(e.length),n=0;n=48&&i<=57){t++;continue}return!1}return!0}function Hu(e){return e.indexOf("/")===-1&&e.indexOf("~")===-1?e:e.replace(/~/g,"~0").replace(/\//g,"~1")}function BI(e){return e.replace(/~1/g,"/").replace(/~0/g,"~")}function $6(e){if(e===void 0)return!0;if(e){if(Array.isArray(e)){for(var t=0,n=e.length;t0&&u[c-1]=="constructor"))throw new TypeError("JSON-Patch: modifying `__proto__` or `constructor/prototype` prop is banned for security reasons, if this was on purpose, please set `banPrototypeModifications` flag false and pass it to this function. More info in fast-json-patch README");if(n&&d===void 0&&(l[h]===void 0?d=u.slice(0,c).join("/"):c==f-1&&(d=t.path),d!==void 0&&p(t,0,e,d)),c++,Array.isArray(l)){if(h==="-")h=l.length;else{if(n&&!A6(h))throw new Rt("Expected an unsigned base-10 integer value, making the new referenced value the array element with the zero-based index","OPERATION_PATH_ILLEGAL_ARRAY_INDEX",s,t,e);A6(h)&&(h=~~h)}if(c>=f){if(n&&t.op==="add"&&h>l.length)throw new Rt("The specified index MUST NOT be greater than the number of elements in the array","OPERATION_VALUE_OUT_OF_BOUNDS",s,t,e);var o=u3e[t.op].call(t,l,h,e);if(o.test===!1)throw new Rt("Test operation failed","TEST_OPERATION_FAILED",s,t,e);return o}}else if(c>=f){var o=zc[t.op].call(t,l,h,e);if(o.test===!1)throw new Rt("Test operation failed","TEST_OPERATION_FAILED",s,t,e);return o}if(l=l[h],n&&c0)throw new Rt('Operation `path` property must start with "/"',"OPERATION_PATH_INVALID",t,e,n);if((e.op==="move"||e.op==="copy")&&typeof e.from!="string")throw new Rt("Operation `from` property is not present (applicable in `move` and `copy` operations)","OPERATION_FROM_REQUIRED",t,e,n);if((e.op==="add"||e.op==="replace"||e.op==="test")&&e.value===void 0)throw new Rt("Operation `value` property is not present (applicable in `add`, `replace` and `test` operations)","OPERATION_VALUE_REQUIRED",t,e,n);if((e.op==="add"||e.op==="replace"||e.op==="test")&&$6(e.value))throw new Rt("Operation `value` property is not present (applicable in `add`, `replace` and `test` operations)","OPERATION_VALUE_CANNOT_CONTAIN_UNDEFINED",t,e,n);if(n){if(e.op=="add"){var r=e.path.split("/").length,s=i.split("/").length;if(r!==s+1&&r!==s)throw new Rt("Cannot perform an `add` operation at the desired path","OPERATION_PATH_CANNOT_ADD",t,e,n)}else if(e.op==="replace"||e.op==="remove"||e.op==="_get"){if(e.path!==i)throw new Rt("Cannot perform the operation at a path that does not exist","OPERATION_PATH_UNRESOLVABLE",t,e,n)}else if(e.op==="move"||e.op==="copy"){var o={op:"_get",path:e.from,value:void 0},a=qI([o],n);if(a&&a.name==="OPERATION_PATH_UNRESOLVABLE")throw new Rt("Cannot perform the operation from a path that does not exist","OPERATION_FROM_UNRESOLVABLE",t,e,n)}}}else throw new Rt("Operation `op` property is not one of operations defined in RFC-6902","OPERATION_OP_INVALID",t,e,n)}function qI(e,t,n){try{if(!Array.isArray(e))throw new Rt("Patch sequence must be an array","SEQUENCE_NOT_AN_ARRAY");if(t)Wm(Li(t),Li(e),n||!0);else{n=n||Hm;for(var i=0;i0&&(e.patches=[],e.callback&&e.callback(i)),i}function D6(e,t,n,i,r){if(t!==e){typeof t.toJSON=="function"&&(t=t.toJSON());for(var s=k6(t),o=k6(e),a=!1,u=o.length-1;u>=0;u--){var l=o[u],c=e[l];if(C6(t,l)&&!(t[l]===void 0&&c!==void 0&&Array.isArray(t)===!1)){var f=t[l];typeof c=="object"&&c!=null&&typeof f=="object"&&f!=null&&Array.isArray(c)===Array.isArray(f)?D6(c,f,n,i+"/"+Hu(l),r):c!==f&&(r&&n.push({op:"test",path:i+"/"+Hu(l),value:Li(c)}),n.push({op:"replace",path:i+"/"+Hu(l),value:Li(f)}))}else Array.isArray(e)===Array.isArray(t)?(r&&n.push({op:"test",path:i+"/"+Hu(l),value:Li(c)}),n.push({op:"remove",path:i+"/"+Hu(l)}),a=!0):(r&&n.push({op:"test",path:i,value:e}),n.push({op:"replace",path:i,value:t}))}if(!(!a&&s.length==o.length))for(var u=0;u=this.max){const s=this.map.keys().next().value;this.delete(s)}this.map.set(n,i)}return this}}return T6=e,T6}var M6,HI;function N6(){if(HI)return M6;HI=1;const e=Object.freeze({loose:!0}),t=Object.freeze({});return M6=i=>i?typeof i!="object"?e:i:t,M6}var Gm={exports:{}},R6,GI;function O6(){if(GI)return R6;GI=1;const e="2.0.0",t=256,n=Number.MAX_SAFE_INTEGER||9007199254740991,i=16,r=t-6;return R6={MAX_LENGTH:t,MAX_SAFE_COMPONENT_LENGTH:i,MAX_SAFE_BUILD_LENGTH:r,MAX_SAFE_INTEGER:n,RELEASE_TYPES:["major","premajor","minor","preminor","patch","prepatch","prerelease"],SEMVER_SPEC_VERSION:e,FLAG_INCLUDE_PRERELEASE:1,FLAG_LOOSE:2},R6}var L6,VI;function Vm(){return VI||(VI=1,L6=typeof process=="object"&&process.env&&process.env.NODE_DEBUG&&/\bsemver\b/i.test(process.env.NODE_DEBUG)?(...t)=>console.error("SEMVER",...t):()=>{}),L6}var YI;function I6(){return YI||(YI=1,function(e,t){const{MAX_SAFE_COMPONENT_LENGTH:n,MAX_SAFE_BUILD_LENGTH:i,MAX_LENGTH:r}=O6(),s=Vm();t=e.exports={};const o=t.re=[],a=t.safeRe=[],u=t.src=[],l=t.t={};let c=0;const f="[a-zA-Z0-9-]",d=[["\\s",1],["\\d",r],[f,i]],h=g=>{for(const[m,y]of d)g=g.split(`${m}*`).join(`${m}{0,${y}}`).split(`${m}+`).join(`${m}{1,${y}}`);return g},p=(g,m,y)=>{const b=h(m),v=c++;s(g,v,m),l[g]=v,u[v]=m,o[v]=new RegExp(m,y?"g":void 0),a[v]=new RegExp(b,y?"g":void 0)};p("NUMERICIDENTIFIER","0|[1-9]\\d*"),p("NUMERICIDENTIFIERLOOSE","\\d+"),p("NONNUMERICIDENTIFIER",`\\d*[a-zA-Z-]${f}*`),p("MAINVERSION",`(${u[l.NUMERICIDENTIFIER]})\\.(${u[l.NUMERICIDENTIFIER]})\\.(${u[l.NUMERICIDENTIFIER]})`),p("MAINVERSIONLOOSE",`(${u[l.NUMERICIDENTIFIERLOOSE]})\\.(${u[l.NUMERICIDENTIFIERLOOSE]})\\.(${u[l.NUMERICIDENTIFIERLOOSE]})`),p("PRERELEASEIDENTIFIER",`(?:${u[l.NUMERICIDENTIFIER]}|${u[l.NONNUMERICIDENTIFIER]})`),p("PRERELEASEIDENTIFIERLOOSE",`(?:${u[l.NUMERICIDENTIFIERLOOSE]}|${u[l.NONNUMERICIDENTIFIER]})`),p("PRERELEASE",`(?:-(${u[l.PRERELEASEIDENTIFIER]}(?:\\.${u[l.PRERELEASEIDENTIFIER]})*))`),p("PRERELEASELOOSE",`(?:-?(${u[l.PRERELEASEIDENTIFIERLOOSE]}(?:\\.${u[l.PRERELEASEIDENTIFIERLOOSE]})*))`),p("BUILDIDENTIFIER",`${f}+`),p("BUILD",`(?:\\+(${u[l.BUILDIDENTIFIER]}(?:\\.${u[l.BUILDIDENTIFIER]})*))`),p("FULLPLAIN",`v?${u[l.MAINVERSION]}${u[l.PRERELEASE]}?${u[l.BUILD]}?`),p("FULL",`^${u[l.FULLPLAIN]}$`),p("LOOSEPLAIN",`[v=\\s]*${u[l.MAINVERSIONLOOSE]}${u[l.PRERELEASELOOSE]}?${u[l.BUILD]}?`),p("LOOSE",`^${u[l.LOOSEPLAIN]}$`),p("GTLT","((?:<|>)?=?)"),p("XRANGEIDENTIFIERLOOSE",`${u[l.NUMERICIDENTIFIERLOOSE]}|x|X|\\*`),p("XRANGEIDENTIFIER",`${u[l.NUMERICIDENTIFIER]}|x|X|\\*`),p("XRANGEPLAIN",`[v=\\s]*(${u[l.XRANGEIDENTIFIER]})(?:\\.(${u[l.XRANGEIDENTIFIER]})(?:\\.(${u[l.XRANGEIDENTIFIER]})(?:${u[l.PRERELEASE]})?${u[l.BUILD]}?)?)?`),p("XRANGEPLAINLOOSE",`[v=\\s]*(${u[l.XRANGEIDENTIFIERLOOSE]})(?:\\.(${u[l.XRANGEIDENTIFIERLOOSE]})(?:\\.(${u[l.XRANGEIDENTIFIERLOOSE]})(?:${u[l.PRERELEASELOOSE]})?${u[l.BUILD]}?)?)?`),p("XRANGE",`^${u[l.GTLT]}\\s*${u[l.XRANGEPLAIN]}$`),p("XRANGELOOSE",`^${u[l.GTLT]}\\s*${u[l.XRANGEPLAINLOOSE]}$`),p("COERCEPLAIN",`(^|[^\\d])(\\d{1,${n}})(?:\\.(\\d{1,${n}}))?(?:\\.(\\d{1,${n}}))?`),p("COERCE",`${u[l.COERCEPLAIN]}(?:$|[^\\d])`),p("COERCEFULL",u[l.COERCEPLAIN]+`(?:${u[l.PRERELEASE]})?(?:${u[l.BUILD]})?(?:$|[^\\d])`),p("COERCERTL",u[l.COERCE],!0),p("COERCERTLFULL",u[l.COERCEFULL],!0),p("LONETILDE","(?:~>?)"),p("TILDETRIM",`(\\s*)${u[l.LONETILDE]}\\s+`,!0),t.tildeTrimReplace="$1~",p("TILDE",`^${u[l.LONETILDE]}${u[l.XRANGEPLAIN]}$`),p("TILDELOOSE",`^${u[l.LONETILDE]}${u[l.XRANGEPLAINLOOSE]}$`),p("LONECARET","(?:\\^)"),p("CARETTRIM",`(\\s*)${u[l.LONECARET]}\\s+`,!0),t.caretTrimReplace="$1^",p("CARET",`^${u[l.LONECARET]}${u[l.XRANGEPLAIN]}$`),p("CARETLOOSE",`^${u[l.LONECARET]}${u[l.XRANGEPLAINLOOSE]}$`),p("COMPARATORLOOSE",`^${u[l.GTLT]}\\s*(${u[l.LOOSEPLAIN]})$|^$`),p("COMPARATOR",`^${u[l.GTLT]}\\s*(${u[l.FULLPLAIN]})$|^$`),p("COMPARATORTRIM",`(\\s*)${u[l.GTLT]}\\s*(${u[l.LOOSEPLAIN]}|${u[l.XRANGEPLAIN]})`,!0),t.comparatorTrimReplace="$1$2$3",p("HYPHENRANGE",`^\\s*(${u[l.XRANGEPLAIN]})\\s+-\\s+(${u[l.XRANGEPLAIN]})\\s*$`),p("HYPHENRANGELOOSE",`^\\s*(${u[l.XRANGEPLAINLOOSE]})\\s+-\\s+(${u[l.XRANGEPLAINLOOSE]})\\s*$`),p("STAR","(<|>)?=?\\s*\\*"),p("GTE0","^\\s*>=\\s*0\\.0\\.0\\s*$"),p("GTE0PRE","^\\s*>=\\s*0\\.0\\.0-0\\s*$")}(Gm,Gm.exports)),Gm.exports}var P6,XI;function w3e(){if(XI)return P6;XI=1;const e=/^[0-9]+$/,t=(i,r)=>{const s=e.test(i),o=e.test(r);return s&&o&&(i=+i,r=+r),i===r?0:s&&!o?-1:o&&!s?1:it(r,i)},P6}var z6,KI;function B6(){if(KI)return z6;KI=1;const e=Vm(),{MAX_LENGTH:t,MAX_SAFE_INTEGER:n}=O6(),{safeRe:i,t:r}=I6(),s=N6(),{compareIdentifiers:o}=w3e();class a{constructor(l,c){if(c=s(c),l instanceof a){if(l.loose===!!c.loose&&l.includePrerelease===!!c.includePrerelease)return l;l=l.version}else if(typeof l!="string")throw new TypeError(`Invalid version. Must be a string. Got type "${typeof l}".`);if(l.length>t)throw new TypeError(`version is longer than ${t} characters`);e("SemVer",l,c),this.options=c,this.loose=!!c.loose,this.includePrerelease=!!c.includePrerelease;const f=l.trim().match(c.loose?i[r.LOOSE]:i[r.FULL]);if(!f)throw new TypeError(`Invalid Version: ${l}`);if(this.raw=l,this.major=+f[1],this.minor=+f[2],this.patch=+f[3],this.major>n||this.major<0)throw new TypeError("Invalid major version");if(this.minor>n||this.minor<0)throw new TypeError("Invalid minor version");if(this.patch>n||this.patch<0)throw new TypeError("Invalid patch version");f[4]?this.prerelease=f[4].split(".").map(d=>{if(/^[0-9]+$/.test(d)){const h=+d;if(h>=0&&h=0;)typeof this.prerelease[h]=="number"&&(this.prerelease[h]++,h=-2);if(h===-1){if(c===this.prerelease.join(".")&&f===!1)throw new Error("invalid increment argument: identifier already exists");this.prerelease.push(d)}}if(c){let h=[c,d];f===!1&&(h=[c]),o(this.prerelease[0],c)===0?isNaN(this.prerelease[1])&&(this.prerelease=h):this.prerelease=h}break}default:throw new Error(`invalid increment argument: ${l}`)}return this.raw=this.format(),this.build.length&&(this.raw+=`+${this.build.join(".")}`),this}}return z6=a,z6}var U6,ZI;function Bc(){if(ZI)return U6;ZI=1;const e=B6();return U6=(n,i,r)=>new e(n,r).compare(new e(i,r)),U6}var j6,JI;function E3e(){if(JI)return j6;JI=1;const e=Bc();return j6=(n,i,r)=>e(n,i,r)===0,j6}var q6,QI;function C3e(){if(QI)return q6;QI=1;const e=Bc();return q6=(n,i,r)=>e(n,i,r)!==0,q6}var W6,eP;function k3e(){if(eP)return W6;eP=1;const e=Bc();return W6=(n,i,r)=>e(n,i,r)>0,W6}var H6,tP;function A3e(){if(tP)return H6;tP=1;const e=Bc();return H6=(n,i,r)=>e(n,i,r)>=0,H6}var G6,nP;function $3e(){if(nP)return G6;nP=1;const e=Bc();return G6=(n,i,r)=>e(n,i,r)<0,G6}var V6,iP;function S3e(){if(iP)return V6;iP=1;const e=Bc();return V6=(n,i,r)=>e(n,i,r)<=0,V6}var Y6,rP;function F3e(){if(rP)return Y6;rP=1;const e=E3e(),t=C3e(),n=k3e(),i=A3e(),r=$3e(),s=S3e();return Y6=(a,u,l,c)=>{switch(u){case"===":return typeof a=="object"&&(a=a.version),typeof l=="object"&&(l=l.version),a===l;case"!==":return typeof a=="object"&&(a=a.version),typeof l=="object"&&(l=l.version),a!==l;case"":case"=":case"==":return e(a,l,c);case"!=":return t(a,l,c);case">":return n(a,l,c);case">=":return i(a,l,c);case"<":return r(a,l,c);case"<=":return s(a,l,c);default:throw new TypeError(`Invalid operator: ${u}`)}},Y6}var X6,sP;function D3e(){if(sP)return X6;sP=1;const e=Symbol("SemVer ANY");class t{static get ANY(){return e}constructor(c,f){if(f=n(f),c instanceof t){if(c.loose===!!f.loose)return c;c=c.value}c=c.trim().split(/\s+/).join(" "),o("comparator",c,f),this.options=f,this.loose=!!f.loose,this.parse(c),this.semver===e?this.value="":this.value=this.operator+this.semver.version,o("comp",this)}parse(c){const f=this.options.loose?i[r.COMPARATORLOOSE]:i[r.COMPARATOR],d=c.match(f);if(!d)throw new TypeError(`Invalid comparator: ${c}`);this.operator=d[1]!==void 0?d[1]:"",this.operator==="="&&(this.operator=""),d[2]?this.semver=new a(d[2],this.options.loose):this.semver=e}toString(){return this.value}test(c){if(o("Comparator.test",c,this.options.loose),this.semver===e||c===e)return!0;if(typeof c=="string")try{c=new a(c,this.options)}catch{return!1}return s(c,this.operator,this.semver,this.options)}intersects(c,f){if(!(c instanceof t))throw new TypeError("a Comparator is required");return this.operator===""?this.value===""?!0:new u(c.value,f).test(this.value):c.operator===""?c.value===""?!0:new u(this.value,f).test(c.semver):(f=n(f),f.includePrerelease&&(this.value==="<0.0.0-0"||c.value==="<0.0.0-0")||!f.includePrerelease&&(this.value.startsWith("<0.0.0")||c.value.startsWith("<0.0.0"))?!1:!!(this.operator.startsWith(">")&&c.operator.startsWith(">")||this.operator.startsWith("<")&&c.operator.startsWith("<")||this.semver.version===c.semver.version&&this.operator.includes("=")&&c.operator.includes("=")||s(this.semver,"<",c.semver,f)&&this.operator.startsWith(">")&&c.operator.startsWith("<")||s(this.semver,">",c.semver,f)&&this.operator.startsWith("<")&&c.operator.startsWith(">")))}}X6=t;const n=N6(),{safeRe:i,t:r}=I6(),s=F3e(),o=Vm(),a=B6(),u=aP();return X6}var K6,oP;function aP(){if(oP)return K6;oP=1;const e=/\s+/g;class t{constructor(A,T){if(T=r(T),A instanceof t)return A.loose===!!T.loose&&A.includePrerelease===!!T.includePrerelease?A:new t(A.raw,T);if(A instanceof s)return this.raw=A.value,this.set=[[A]],this.formatted=void 0,this;if(this.options=T,this.loose=!!T.loose,this.includePrerelease=!!T.includePrerelease,this.raw=A.trim().replace(e," "),this.set=this.raw.split("||").map(B=>this.parseRange(B.trim())).filter(B=>B.length),!this.set.length)throw new TypeError(`Invalid SemVer Range: ${this.raw}`);if(this.set.length>1){const B=this.set[0];if(this.set=this.set.filter(H=>!g(H[0])),this.set.length===0)this.set=[B];else if(this.set.length>1){for(const H of this.set)if(H.length===1&&m(H[0])){this.set=[H];break}}}this.formatted=void 0}get range(){if(this.formatted===void 0){this.formatted="";for(let A=0;A0&&(this.formatted+="||");const T=this.set[A];for(let B=0;B0&&(this.formatted+=" "),this.formatted+=T[B].toString().trim()}}return this.formatted}format(){return this.range}toString(){return this.range}parseRange(A){const B=((this.options.includePrerelease&&h)|(this.options.loose&&p))+":"+A,H=i.get(B);if(H)return H;const z=this.options.loose,ne=z?u[l.HYPHENRANGELOOSE]:u[l.HYPHENRANGE];A=A.replace(ne,O(this.options.includePrerelease)),o("hyphen replace",A),A=A.replace(u[l.COMPARATORTRIM],c),o("comparator trim",A),A=A.replace(u[l.TILDETRIM],f),o("tilde trim",A),A=A.replace(u[l.CARETTRIM],d),o("caret trim",A);let be=A.split(" ").map($e=>b($e,this.options)).join(" ").split(/\s+/).map($e=>$($e,this.options));z&&(be=be.filter($e=>(o("loose invalid filter",$e,this.options),!!$e.match(u[l.COMPARATORLOOSE])))),o("range list",be);const de=new Map,Te=be.map($e=>new s($e,this.options));for(const $e of Te){if(g($e))return[$e];de.set($e.value,$e)}de.size>1&&de.has("")&&de.delete("");const ct=[...de.values()];return i.set(B,ct),ct}intersects(A,T){if(!(A instanceof t))throw new TypeError("a Range is required");return this.set.some(B=>y(B,T)&&A.set.some(H=>y(H,T)&&B.every(z=>H.every(ne=>z.intersects(ne,T)))))}test(A){if(!A)return!1;if(typeof A=="string")try{A=new a(A,this.options)}catch{return!1}for(let T=0;TF.value==="<0.0.0-0",m=F=>F.value==="",y=(F,A)=>{let T=!0;const B=F.slice();let H=B.pop();for(;T&&B.length;)T=B.every(z=>H.intersects(z,A)),H=B.pop();return T},b=(F,A)=>(o("comp",F,A),F=E(F,A),o("caret",F),F=x(F,A),o("tildes",F),F=C(F,A),o("xrange",F),F=S(F,A),o("stars",F),F),v=F=>!F||F.toLowerCase()==="x"||F==="*",x=(F,A)=>F.trim().split(/\s+/).map(T=>_(T,A)).join(" "),_=(F,A)=>{const T=A.loose?u[l.TILDELOOSE]:u[l.TILDE];return F.replace(T,(B,H,z,ne,be)=>{o("tilde",F,B,H,z,ne,be);let de;return v(H)?de="":v(z)?de=`>=${H}.0.0 <${+H+1}.0.0-0`:v(ne)?de=`>=${H}.${z}.0 <${H}.${+z+1}.0-0`:be?(o("replaceTilde pr",be),de=`>=${H}.${z}.${ne}-${be} <${H}.${+z+1}.0-0`):de=`>=${H}.${z}.${ne} <${H}.${+z+1}.0-0`,o("tilde return",de),de})},E=(F,A)=>F.trim().split(/\s+/).map(T=>w(T,A)).join(" "),w=(F,A)=>{o("caret",F,A);const T=A.loose?u[l.CARETLOOSE]:u[l.CARET],B=A.includePrerelease?"-0":"";return F.replace(T,(H,z,ne,be,de)=>{o("caret",F,H,z,ne,be,de);let Te;return v(z)?Te="":v(ne)?Te=`>=${z}.0.0${B} <${+z+1}.0.0-0`:v(be)?z==="0"?Te=`>=${z}.${ne}.0${B} <${z}.${+ne+1}.0-0`:Te=`>=${z}.${ne}.0${B} <${+z+1}.0.0-0`:de?(o("replaceCaret pr",de),z==="0"?ne==="0"?Te=`>=${z}.${ne}.${be}-${de} <${z}.${ne}.${+be+1}-0`:Te=`>=${z}.${ne}.${be}-${de} <${z}.${+ne+1}.0-0`:Te=`>=${z}.${ne}.${be}-${de} <${+z+1}.0.0-0`):(o("no pr"),z==="0"?ne==="0"?Te=`>=${z}.${ne}.${be}${B} <${z}.${ne}.${+be+1}-0`:Te=`>=${z}.${ne}.${be}${B} <${z}.${+ne+1}.0-0`:Te=`>=${z}.${ne}.${be} <${+z+1}.0.0-0`),o("caret return",Te),Te})},C=(F,A)=>(o("replaceXRanges",F,A),F.split(/\s+/).map(T=>k(T,A)).join(" ")),k=(F,A)=>{F=F.trim();const T=A.loose?u[l.XRANGELOOSE]:u[l.XRANGE];return F.replace(T,(B,H,z,ne,be,de)=>{o("xRange",F,B,H,z,ne,be,de);const Te=v(z),ct=Te||v(ne),$e=ct||v(be),Ot=$e;return H==="="&&Ot&&(H=""),de=A.includePrerelease?"-0":"",Te?H===">"||H==="<"?B="<0.0.0-0":B="*":H&&Ot?(ct&&(ne=0),be=0,H===">"?(H=">=",ct?(z=+z+1,ne=0,be=0):(ne=+ne+1,be=0)):H==="<="&&(H="<",ct?z=+z+1:ne=+ne+1),H==="<"&&(de="-0"),B=`${H+z}.${ne}.${be}${de}`):ct?B=`>=${z}.0.0${de} <${+z+1}.0.0-0`:$e&&(B=`>=${z}.${ne}.0${de} <${z}.${+ne+1}.0-0`),o("xRange return",B),B})},S=(F,A)=>(o("replaceStars",F,A),F.trim().replace(u[l.STAR],"")),$=(F,A)=>(o("replaceGTE0",F,A),F.trim().replace(u[A.includePrerelease?l.GTE0PRE:l.GTE0],"")),O=F=>(A,T,B,H,z,ne,be,de,Te,ct,$e,Ot)=>(v(B)?T="":v(H)?T=`>=${B}.0.0${F?"-0":""}`:v(z)?T=`>=${B}.${H}.0${F?"-0":""}`:ne?T=`>=${T}`:T=`>=${T}${F?"-0":""}`,v(Te)?de="":v(ct)?de=`<${+Te+1}.0.0-0`:v($e)?de=`<${Te}.${+ct+1}.0-0`:Ot?de=`<=${Te}.${ct}.${$e}-${Ot}`:F?de=`<${Te}.${ct}.${+$e+1}-0`:de=`<=${de}`,`${T} ${de}`.trim()),R=(F,A,T)=>{for(let B=0;B0){const H=F[B].semver;if(H.major===A.major&&H.minor===A.minor&&H.patch===A.patch)return!0}return!1}return!0};return K6}var Z6,uP;function T3e(){if(uP)return Z6;uP=1;const e=aP();return Z6=(n,i,r)=>{try{i=new e(i,r)}catch{return!1}return i.test(n)},Z6}var M3e=T3e(),lP=x3e(M3e);function N3e(e,t,n){const i=e.open(t),r=250,{origin:s}=new URL(t);let o=40;function a(l){l.source===i&&(o=0,e.removeEventListener("message",a,!1))}e.addEventListener("message",a,!1);function u(){o<=0||(i.postMessage(n,s),setTimeout(u,r),o-=1)}setTimeout(u,r)}var R3e=`.vega-embed { + position: relative; + display: inline-block; + box-sizing: border-box; +} +.vega-embed.has-actions { + padding-right: 38px; +} +.vega-embed details:not([open]) > :not(summary) { + display: none !important; +} +.vega-embed summary { + list-style: none; + position: absolute; + top: 0; + right: 0; + padding: 6px; + z-index: 1000; + background: white; + box-shadow: 1px 1px 3px rgba(0, 0, 0, 0.1); + color: #1b1e23; + border: 1px solid #aaa; + border-radius: 999px; + opacity: 0.2; + transition: opacity 0.4s ease-in; + cursor: pointer; + line-height: 0px; +} +.vega-embed summary::-webkit-details-marker { + display: none; +} +.vega-embed summary:active { + box-shadow: #aaa 0px 0px 0px 1px inset; +} +.vega-embed summary svg { + width: 14px; + height: 14px; +} +.vega-embed details[open] summary { + opacity: 0.7; +} +.vega-embed:hover summary, .vega-embed:focus-within summary { + opacity: 1 !important; + transition: opacity 0.2s ease; +} +.vega-embed .vega-actions { + position: absolute; + z-index: 1001; + top: 35px; + right: -9px; + display: flex; + flex-direction: column; + padding-bottom: 8px; + padding-top: 8px; + border-radius: 4px; + box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.2); + border: 1px solid #d9d9d9; + background: white; + animation-duration: 0.15s; + animation-name: scale-in; + animation-timing-function: cubic-bezier(0.2, 0, 0.13, 1.5); + text-align: left; +} +.vega-embed .vega-actions a { + padding: 8px 16px; + font-family: sans-serif; + font-size: 14px; + font-weight: 600; + white-space: nowrap; + color: #434a56; + text-decoration: none; +} +.vega-embed .vega-actions a:hover, .vega-embed .vega-actions a:focus { + background-color: #f7f7f9; + color: black; +} +.vega-embed .vega-actions::before, .vega-embed .vega-actions::after { + content: ""; + display: inline-block; + position: absolute; +} +.vega-embed .vega-actions::before { + left: auto; + right: 14px; + top: -16px; + border: 8px solid rgba(0, 0, 0, 0); + border-bottom-color: #d9d9d9; +} +.vega-embed .vega-actions::after { + left: auto; + right: 15px; + top: -14px; + border: 7px solid rgba(0, 0, 0, 0); + border-bottom-color: #fff; +} +.vega-embed .chart-wrapper.fit-x { + width: 100%; +} +.vega-embed .chart-wrapper.fit-y { + height: 100%; +} + +.vega-embed-wrapper { + max-width: 100%; + overflow: auto; + padding-right: 14px; +} + +@keyframes scale-in { + from { + opacity: 0; + transform: scale(0.6); + } + to { + opacity: 1; + transform: scale(1); + } +} +`;function cP(e,...t){for(const n of t)O3e(e,n);return e}function O3e(e,t){for(const n of Object.keys(t))il(e,n,t[n],!0)}const Ss=_fe;let Dh=cbe;const Ym=typeof window<"u"?window:void 0;Dh===void 0&&((_z=Ym==null?void 0:Ym.vl)!=null&&_z.compile)&&(Dh=Ym.vl);const L3e={export:{svg:!0,png:!0},source:!0,compiled:!0,editor:!0},I3e={CLICK_TO_VIEW_ACTIONS:"Click to view actions",COMPILED_ACTION:"View Compiled Vega",EDITOR_ACTION:"Open in Vega Editor",PNG_ACTION:"Save as PNG",SOURCE_ACTION:"View Source",SVG_ACTION:"Save as SVG"},Th={vega:"Vega","vega-lite":"Vega-Lite"},Xm={vega:Ss.version,"vega-lite":Dh?Dh.version:"not available"},P3e={vega:e=>e,"vega-lite":(e,t)=>Dh.compile(e,{config:t}).spec},z3e=` + + + + +`,B3e="chart-wrapper";function U3e(e){return typeof e=="function"}function fP(e,t,n,i){const r=`${t}
    `,s=`
    ${n}`,o=window.open("");o.document.write(r+e+s),o.document.title=`${Th[i]} JSON Source`}function j3e(e,t){if(e.$schema){const n=mI(e.$schema);t&&t!==n.library&&console.warn(`The given visualization spec is written in ${Th[n.library]}, but mode argument sets ${Th[t]??t}.`);const i=n.library;return lP(Xm[i],`^${n.version.slice(1)}`)||console.warn(`The input spec uses ${Th[i]} ${n.version}, but the current version of ${Th[i]} is v${Xm[i]}.`),i}return"mark"in e||"encoding"in e||"layer"in e||"hconcat"in e||"vconcat"in e||"facet"in e||"repeat"in e?"vega-lite":"marks"in e||"signals"in e||"scales"in e||"axes"in e?"vega":t??"vega"}function dP(e){return!!(e&&"load"in e)}function hP(e){return dP(e)?e:Ss.loader(e)}function q3e(e){var n;const t=((n=e.usermeta)==null?void 0:n.embedOptions)??{};return se(t.defaultStyle)&&(t.defaultStyle=!1),t}async function W3e(e,t,n={}){let i,r;se(t)?(r=hP(n.loader),i=JSON.parse(await r.load(t))):i=t;const s=q3e(i),o=s.loader;(!r||o)&&(r=hP(n.loader??o));const a=await pP(s,r),u=await pP(n,r),l={...cP(u,a),config:nl(u.config??{},a.config??{})};return await G3e(e,i,l,r)}async function pP(e,t){const n=se(e.config)?JSON.parse(await t.load(e.config)):e.config??{},i=se(e.patch)?JSON.parse(await t.load(e.patch)):e.patch;return{...e,...i?{patch:i}:{},...n?{config:n}:{}}}function H3e(e){const t=e.getRootNode?e.getRootNode():document;return t instanceof ShadowRoot?{root:t,rootContainer:t}:{root:document,rootContainer:document.head??document.body}}async function G3e(e,t,n={},i){const r=n.theme?nl(Ybe[n.theme],n.config??{}):n.config,s=xo(n.actions)?n.actions:cP({},L3e,n.actions??{}),o={...I3e,...n.i18n},a=n.renderer??"canvas",u=n.logLevel??Ss.Warn,l=n.downloadFileName??"visualization",c=typeof e=="string"?document.querySelector(e):e;if(!c)throw new Error(`${e} does not exist`);if(n.defaultStyle!==!1){const _="vega-embed-style",{root:E,rootContainer:w}=H3e(c);if(!E.getElementById(_)){const C=document.createElement("style");C.id=_,C.innerHTML=n.defaultStyle===void 0||n.defaultStyle===!0?R3e.toString():n.defaultStyle,w.appendChild(C)}}const f=j3e(t,n.mode);let d=P3e[f](t,r);if(f==="vega-lite"&&d.$schema){const _=mI(d.$schema);lP(Xm.vega,`^${_.version.slice(1)}`)||console.warn(`The compiled spec uses Vega ${_.version}, but current version is v${Xm.vega}.`)}c.classList.add("vega-embed"),s&&c.classList.add("has-actions"),c.innerHTML="";let h=c;if(s){const _=document.createElement("div");_.classList.add(B3e),c.appendChild(_),h=_}const p=n.patch;if(p&&(d=p instanceof Function?p(d):Wm(d,p,!0,!1).newDocument),n.formatLocale&&Ss.formatLocale(n.formatLocale),n.timeFormatLocale&&Ss.timeFormatLocale(n.timeFormatLocale),n.expressionFunctions)for(const _ in n.expressionFunctions){const E=n.expressionFunctions[_];"fn"in E?Ss.expressionFunction(_,E.fn,E.visitor):E instanceof Function&&Ss.expressionFunction(_,E)}const{ast:g}=n,m=Ss.parse(d,f==="vega-lite"?{}:r,{ast:g}),y=new(n.viewClass||Ss.View)(m,{loader:i,logLevel:u,renderer:a,...g?{expr:Ss.expressionInterpreter??n.expr??Tfe}:{}});if(y.addSignalListener("autosize",(_,E)=>{const{type:w}=E;w=="fit-x"?(h.classList.add("fit-x"),h.classList.remove("fit-y")):w=="fit-y"?(h.classList.remove("fit-x"),h.classList.add("fit-y")):w=="fit"?h.classList.add("fit-x","fit-y"):h.classList.remove("fit-x","fit-y")}),n.tooltip!==!1){const{loader:_,tooltip:E}=n,w=_&&!dP(_)?_==null?void 0:_.baseURL:void 0,C=U3e(E)?E:new r3e({baseURL:w,...E===!0?{}:E}).call;y.tooltip(C)}let{hover:b}=n;if(b===void 0&&(b=f==="vega"),b){const{hoverSet:_,updateSet:E}=typeof b=="boolean"?{}:b;y.hover(_,E)}n&&(n.width!=null&&y.width(n.width),n.height!=null&&y.height(n.height),n.padding!=null&&y.padding(n.padding)),await y.initialize(h,n.bind).runAsync();let v;if(s!==!1){let _=c;if(n.defaultStyle!==!1||n.forceActionsMenu){const w=document.createElement("details");w.title=o.CLICK_TO_VIEW_ACTIONS,c.append(w),_=w;const C=document.createElement("summary");C.innerHTML=z3e,w.append(C),v=k=>{w.contains(k.target)||w.removeAttribute("open")},document.addEventListener("click",v)}const E=document.createElement("div");if(_.append(E),E.classList.add("vega-actions"),s===!0||s.export!==!1){for(const w of["svg","png"])if(s===!0||s.export===!0||s.export[w]){const C=o[`${w.toUpperCase()}_ACTION`],k=document.createElement("a"),S=re(n.scaleFactor)?n.scaleFactor[w]:n.scaleFactor;k.text=C,k.href="#",k.target="_blank",k.download=`${l}.${w}`,k.addEventListener("mousedown",async function($){$.preventDefault();const O=await y.toImageURL(w,S);this.href=O}),E.append(k)}}if(s===!0||s.source!==!1){const w=document.createElement("a");w.text=o.SOURCE_ACTION,w.href="#",w.addEventListener("click",function(C){fP(y2(t),n.sourceHeader??"",n.sourceFooter??"",f),C.preventDefault()}),E.append(w)}if(f==="vega-lite"&&(s===!0||s.compiled!==!1)){const w=document.createElement("a");w.text=o.COMPILED_ACTION,w.href="#",w.addEventListener("click",function(C){fP(y2(d),n.sourceHeader??"",n.sourceFooter??"","vega"),C.preventDefault()}),E.append(w)}if(s===!0||s.editor!==!1){const w=n.editorUrl??"https://vega.github.io/editor/",C=document.createElement("a");C.text=o.EDITOR_ACTION,C.href="#",C.addEventListener("click",function(k){N3e(window,w,{config:r,mode:p?"vega":f,renderer:a,spec:y2(p?d:t)}),k.preventDefault()}),E.append(C)}}function x(){v&&document.removeEventListener("click",v),y.finalize()}return{view:y,spec:t,vgSpec:d,finalize:x,embedOptions:n}}const V3e=new Set(["width","height"]);function Y3e(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var X3e=function e(t,n){if(t===n)return!0;if(t&&n&&typeof t=="object"&&typeof n=="object"){if(t.constructor!==n.constructor)return!1;var i,r,s;if(Array.isArray(t)){if(i=t.length,i!=n.length)return!1;for(r=i;r--!==0;)if(!e(t[r],n[r]))return!1;return!0}if(t.constructor===RegExp)return t.source===n.source&&t.flags===n.flags;if(t.valueOf!==Object.prototype.valueOf)return t.valueOf()===n.valueOf();if(t.toString!==Object.prototype.toString)return t.toString()===n.toString();if(s=Object.keys(t),i=s.length,i!==Object.keys(n).length)return!1;for(r=i;r--!==0;)if(!Object.prototype.hasOwnProperty.call(n,s[r]))return!1;for(r=i;r--!==0;){var o=s[r];if(!e(t[o],n[o]))return!1}return!0}return t!==t&&n!==n};const K3e=Y3e(X3e);function Z3e(e,t){for(const[n,i]of Object.entries(t))i&&(i&&{}.toString.call(i)==="[object Function]"?i(e.data(n)):e.change(n,Ao().remove(()=>!0).insert(i)))}function Km(e={},t={},n=new Set){const i=Object.keys(e),r=Object.keys(t);return e===t||i.length===r.length&&i.filter(s=>!n.has(s)).every(s=>e[s]===t[s])}function gP(e,t){const n=Object.keys(t);for(const i of n)try{e.removeSignalListener(i,t[i])}catch(r){console.warn("Cannot remove invalid signal listener.",r)}return n.length>0}function J6(e,t){const n=Object.keys(t);for(const i of n)try{e.addSignalListener(i,t[i])}catch(r){console.warn("Cannot add invalid signal listener.",r)}return n.length>0}function J3e(e){return new Set(e.flatMap(t=>Object.keys(t)))}function Q3e(e,t){if(e===t)return!1;const n={width:!1,height:!1,isExpensive:!1},i="width"in e||"width"in t,r="height"in e||"height"in t;return i&&(!("width"in e)||!("width"in t)||e.width!==t.width)&&("width"in e&&typeof e.width=="number"?n.width=e.width:n.isExpensive=!0),r&&(!("height"in e)||!("height"in t)||e.height!==t.height)&&("height"in e&&typeof e.height=="number"?n.height=e.height:n.isExpensive=!0),[...J3e([e,t])].filter(o=>o!=="width"&&o!=="height").some(o=>!(o in e)||!(o in t)||!K3e(e[o],t[o]))&&(n.isExpensive=!0),n.width!==!1||n.height!==!1||n.isExpensive?n:!1}function mP(e,t){const{width:n,height:i}=t;return typeof n<"u"&&typeof i<"u"?{...e,width:n,height:i}:typeof n<"u"?{...e,width:n}:typeof i<"u"?{...e,height:i}:e}function eve(e){let t;return{c(){t=G("div")},m(n,i){I(n,t,i),e[11](t)},p:ie,i:ie,o:ie,d(n){n&&L(t),e[11](null)}}}function tve(e,t,n){let{options:i}=t,{spec:r}=t,{view:s}=t,{signalListeners:o={}}=t,{data:a={}}=t;const u=a2();let l,c={},f={},d={},h={},p;w5(()=>{m()});async function g(){m();try{n(6,l=await W3e(p,r,i)),n(1,s=l.view),J6(s,o)&&s.runAsync(),b(s)}catch(_){y(_)}}function m(){l&&(l.finalize(),n(6,l=void 0),n(1,s=void 0))}function y(_){u("onError",{error:_}),console.warn(_)}function b(_){v(),u("onNewView",{view:_})}async function v(){a&&Object.keys(a).length>0&&l!==void 0&&(n(1,s=l.view),Z3e(s,a),await s.resize().runAsync())}function x(_){zi[_?"unshift":"push"](()=>{p=_,n(0,p)})}return e.$$set=_=>{"options"in _&&n(2,i=_.options),"spec"in _&&n(3,r=_.spec),"view"in _&&n(1,s=_.view),"signalListeners"in _&&n(4,o=_.signalListeners),"data"in _&&n(5,a=_.data)},e.$$.update=()=>{if(e.$$.dirty&1056&&(Km(a,h)||v(),n(10,h=a)),e.$$.dirty&991&&p!==void 0){if(!Km(i,c,V3e))g();else{const _=Q3e(mP(r,i),mP(d,c)),E=o,w=f;if(_){if(_.isExpensive)g();else if(l!==void 0){const C=!Km(E,w);n(1,s=l.view),_.width!==!1&&s.width(_.width),_.height!==!1&&s.height(_.height),C&&(w&&gP(s,w),E&&J6(s,E)),s.runAsync()}}else!Km(E,w)&&l!==void 0&&(n(1,s=l.view),w&&gP(s,w),E&&J6(s,E),s.runAsync())}n(7,c=i),n(8,f=o),n(9,d=r)}},[p,s,i,r,o,a,l,c,f,d,h,x]}class nve extends xe{constructor(t){super(),ve(this,t,tve,eve,ye,{options:2,spec:3,view:1,signalListeners:4,data:5})}}function ive(e){let t,n,i;function r(o){e[6](o)}let s={spec:e[1],data:e[2],signalListeners:e[3],options:e[4]};return e[0]!==void 0&&(s.view=e[0]),t=new nve({props:s}),zi.push(()=>h2(t,"view",r)),t.$on("onNewView",e[7]),t.$on("onError",e[8]),{c(){he(t.$$.fragment)},m(o,a){ce(t,o,a),i=!0},p(o,[a]){const u={};a&2&&(u.spec=o[1]),a&4&&(u.data=o[2]),a&8&&(u.signalListeners=o[3]),a&16&&(u.options=o[4]),!n&&a&1&&(n=!0,u.view=o[0],f2(()=>n=!1)),t.$set(u)},i(o){i||(D(t.$$.fragment,o),i=!0)},o(o){N(t.$$.fragment,o),i=!1},d(o){fe(t,o)}}}const rve="vega";function sve(e,t,n){let i,{spec:r}=t,{options:s={}}=t,{data:o={}}=t,{signalListeners:a={}}=t,{view:u=void 0}=t;function l(d){u=d,n(0,u)}function c(d){k5.call(this,e,d)}function f(d){k5.call(this,e,d)}return e.$$set=d=>{"spec"in d&&n(1,r=d.spec),"options"in d&&n(5,s=d.options),"data"in d&&n(2,o=d.data),"signalListeners"in d&&n(3,a=d.signalListeners),"view"in d&&n(0,u=d.view)},e.$$.update=()=>{e.$$.dirty&32&&n(4,i={...s,mode:rve})},[u,r,o,a,i,s,l,c,f]}class yP extends xe{constructor(t){super(),ve(this,t,sve,ive,ye,{spec:1,options:5,data:2,signalListeners:3,view:0})}}function ove(e){let t,n;return t=new yP({props:{spec:e[1],options:e[0]}}),{c(){he(t.$$.fragment)},m(i,r){ce(t,i,r),n=!0},p(i,r){const s={};r&2&&(s.spec=i[1]),r&1&&(s.options=i[0]),t.$set(s)},i(i){n||(D(t.$$.fragment,i),n=!0)},o(i){N(t.$$.fragment,i),n=!1},d(i){fe(t,i)}}}function ave(e){let t,n;return t=new yP({props:{data:e[2],spec:e[1],options:e[0]}}),{c(){he(t.$$.fragment)},m(i,r){ce(t,i,r),n=!0},p(i,r){const s={};r&4&&(s.data=i[2]),r&2&&(s.spec=i[1]),r&1&&(s.options=i[0]),t.$set(s)},i(i){n||(D(t.$$.fragment,i),n=!0)},o(i){N(t.$$.fragment,i),n=!1},d(i){fe(t,i)}}}function uve(e){let t,n,i,r;const s=[ave,ove],o=[];function a(u,l){return u[2]&&u[1]?0:1}return t=a(e),n=o[t]=s[t](e),{c(){n.c(),i=Ne()},m(u,l){o[t].m(u,l),I(u,i,l),r=!0},p(u,[l]){let c=t;t=a(u),t===c?o[t].p(u,l):(Se(),N(o[c],1,1,()=>{o[c]=null}),Fe(),n=o[t],n?n.p(u,l):(n=o[t]=s[t](u),n.c()),D(n,1),n.m(i.parentNode,i))},i(u){r||(D(n),r=!0)},o(u){N(n),r=!1},d(u){u&&L(i),o[t].d(u)}}}function lve(e,t,n){let i,r,s,{componentData:o}=t;return e.$$set=a=>{"componentData"in a&&n(3,o=a.componentData)},e.$$.update=()=>{e.$$.dirty&8&&n(2,{data:i,spec:r,options:s}=o,i,(n(1,r),n(3,o)),(n(0,s),n(3,o)))},[s,r,i,o]}class bP extends xe{constructor(t){super(),ve(this,t,lve,uve,ye,{componentData:3})}}function cve(e){var r;let t,n=(((r=e[0])==null?void 0:r.text)||"")+"",i;return{c(){t=G("p"),i=Be(n),M(t,"data-component","text")},m(s,o){I(s,t,o),V(t,i)},p(s,[o]){var a;o&1&&n!==(n=(((a=s[0])==null?void 0:a.text)||"")+"")&&ft(i,n)},i:ie,o:ie,d(s){s&&L(t)}}}function fve(e,t,n){let{componentData:i}=t;return e.$$set=r=>{"componentData"in r&&n(0,i=r.componentData)},[i]}class vP extends xe{constructor(t){super(),ve(this,t,fve,cve,ye,{componentData:0})}}function dve(e){let t,n,i=e[0].data+"",r,s,o;return{c(){t=G("pre"),n=G("code"),r=Be(i),s=Be(` + `),o=Be(` +`),M(n,"class","language-python"),M(t,"data-component","pythonCode")},m(a,u){I(a,t,u),V(t,n),V(n,r),V(n,s),e[2](n),V(t,o)},p(a,[u]){u&1&&i!==(i=a[0].data+"")&&ft(r,i)},i:ie,o:ie,d(a){a&&L(t),e[2](null)}}}function hve(e,t,n){let{componentData:i}=t,r;function s(){var a;r&&((a=window==null?void 0:window.Prism)==null||a.highlightElement(r))}function o(a){zi[a?"unshift":"push"](()=>{r=a,n(1,r)})}return e.$$set=a=>{"componentData"in a&&n(0,i=a.componentData)},e.$$.update=()=>{e.$$.dirty&2&&r&&s()},[i,r,o]}class xP extends xe{constructor(t){super(),ve(this,t,hve,dve,ye,{componentData:0})}}function pve(e){let t;return{c(){t=Be(e[0])},m(n,i){I(n,t,i)},p(n,i){i&1&&ft(t,n[0])},i:ie,o:ie,d(n){n&&L(t)}}}function gve(e){let t,n,i;var r=e[1];function s(o,a){return{props:{componentData:o[0]}}}return r&&(t=Ke(r,s(e))),{c(){t&&he(t.$$.fragment),n=Ne()},m(o,a){t&&ce(t,o,a),I(o,n,a),i=!0},p(o,a){if(a&2&&r!==(r=o[1])){if(t){Se();const u=t;N(u.$$.fragment,1,0,()=>{fe(u,1)}),Fe()}r?(t=Ke(r,s(o)),he(t.$$.fragment),D(t.$$.fragment,1),ce(t,n.parentNode,n)):t=null}else if(r){const u={};a&1&&(u.componentData=o[0]),t.$set(u)}},i(o){i||(t&&D(t.$$.fragment,o),i=!0)},o(o){t&&N(t.$$.fragment,o),i=!1},d(o){o&&L(n),t&&fe(t,o)}}}function mve(e){let t,n,i,r;const s=[gve,pve],o=[];function a(u,l){return u[1]?0:1}return t=a(e),n=o[t]=s[t](e),{c(){n.c(),i=Ne()},m(u,l){o[t].m(u,l),I(u,i,l),r=!0},p(u,[l]){let c=t;t=a(u),t===c?o[t].p(u,l):(Se(),N(o[c],1,1,()=>{o[c]=null}),Fe(),n=o[t],n?n.p(u,l):(n=o[t]=s[t](u),n.c()),D(n,1),n.m(i.parentNode,i))},i(u){r||(D(n),r=!0)},o(u){N(n),r=!1},d(u){u&&L(i),o[t].d(u)}}}function yve(e,t,n){let{componentData:i}=t,r;const s={artifacts:N5,dag:W5,heading:X5,image:J5,log:Q5,markdown:w4,progressBar:A4,text:vP,vegaChart:bP,pythonCode:xP},o=i==null?void 0:i.type;return o&&(r=s==null?void 0:s[o],r||console.error("Unknown component type: ",o)),e.$$set=a=>{"componentData"in a&&n(0,i=a.componentData)},[i,r]}class _P extends xe{constructor(t){super(),ve(this,t,yve,mve,ye,{componentData:0})}}function wP(e,t,n){const i=e.slice();return i[3]=t[n],i[5]=n,i}function EP(e,t,n){const i=e.slice();return i[6]=t[n],i}function CP(e){let t,n,i,r,s=Ue(e[1]),o=[];for(let u=0;uN(o[u],1,1,()=>{o[u]=null});return{c(){t=G("div"),n=G("table"),i=G("tbody");for(let u=0;uN(l[f],1,1,()=>{l[f]=null});return{c(){t=G("tr"),n=G("td"),r=Be(i),s=He();for(let f=0;f{i=null}),Fe())},i(r){n||(D(i),n=!0)},o(r){N(i),n=!1},d(r){r&&L(t),i&&i.d(r)}}}function vve(e,t,n){let i,r,{componentData:s}=t;return e.$$set=o=>{"componentData"in o&&n(2,s=o.componentData)},e.$$.update=()=>{e.$$.dirty&4&&n(1,{columns:i,data:r}=s,i,(n(0,r),n(2,s)))},[r,i,s]}class xve extends xe{constructor(t){super(),ve(this,t,vve,bve,ye,{componentData:2})}}function $P(e,t,n){const i=e.slice();return i[3]=t[n],i}function SP(e,t,n){const i=e.slice();return i[6]=t[n],i}function FP(e,t,n){const i=e.slice();return i[9]=t[n],i}function DP(e){let t,n,i,r,s,o,a,u=Ue(e[1]),l=[];for(let h=0;hN(f[h],1,1,()=>{f[h]=null});return{c(){t=G("div"),n=G("table"),i=G("thead"),r=G("tr");for(let h=0;hN(s[a],1,1,()=>{s[a]=null});return{c(){t=G("tr");for(let a=0;a{i=null}),Fe())},i(r){n||(D(i),n=!0)},o(r){N(i),n=!1},d(r){r&&L(t),i&&i.d(r)}}}function wve(e,t,n){let i,r,{componentData:s}=t;return e.$$set=o=>{"componentData"in o&&n(2,s=o.componentData)},e.$$.update=()=>{e.$$.dirty&4&&n(1,{columns:i,data:r}=s,i,(n(0,r),n(2,s)))},[r,i,s]}class Eve extends xe{constructor(t){super(),ve(this,t,wve,_ve,ye,{componentData:2})}}function RP(e){let t,n,i;var r=e[3];function s(o,a){return{props:{componentData:o[0]}}}return r&&(t=Ke(r,s(e))),{c(){t&&he(t.$$.fragment),n=Ne()},m(o,a){t&&ce(t,o,a),I(o,n,a),i=!0},p(o,a){if(r!==(r=o[3])){if(t){Se();const u=t;N(u.$$.fragment,1,0,()=>{fe(u,1)}),Fe()}r?(t=Ke(r,s(o)),he(t.$$.fragment),D(t.$$.fragment,1),ce(t,n.parentNode,n)):t=null}else if(r){const u={};a&1&&(u.componentData=o[0]),t.$set(u)}},i(o){i||(t&&D(t.$$.fragment,o),i=!0)},o(o){t&&N(t.$$.fragment,o),i=!1},d(o){o&&L(n),t&&fe(t,o)}}}function Cve(e){let t,n,i=e[2]&&e[1]&&RP(e);return{c(){i&&i.c(),t=Ne()},m(r,s){i&&i.m(r,s),I(r,t,s),n=!0},p(r,[s]){r[2]&&r[1]?i?(i.p(r,s),s&6&&D(i,1)):(i=RP(r),i.c(),D(i,1),i.m(t.parentNode,t)):i&&(Se(),N(i,1,1,()=>{i=null}),Fe())},i(r){n||(D(i),n=!0)},o(r){N(i),n=!1},d(r){r&&L(t),i&&i.d(r)}}}function kve(e,t,n){let i,r,s,{componentData:o}=t;const a=s?xve:Eve;return e.$$set=u=>{"componentData"in u&&n(0,o=u.componentData)},e.$$.update=()=>{e.$$.dirty&1&&n(2,{columns:i,data:r,vertical:s}=o,i,(n(1,r),n(0,o)))},[o,r,i,a]}class Ave extends xe{constructor(t){super(),ve(this,t,kve,Cve,ye,{componentData:0})}}function OP(e,t,n){const i=e.slice();return i[3]=t[n],i}function $ve(e){let t,n,i,r;const s=[Fve,Sve],o=[];function a(u,l){var c;return(u[0].type==="page"||u[0].type==="section")&&((c=u[0])!=null&&c.contents)?0:1}return t=a(e),n=o[t]=s[t](e),{c(){n.c(),i=Ne()},m(u,l){o[t].m(u,l),I(u,i,l),r=!0},p(u,l){let c=t;t=a(u),t===c?o[t].p(u,l):(Se(),N(o[c],1,1,()=>{o[c]=null}),Fe(),n=o[t],n?n.p(u,l):(n=o[t]=s[t](u),n.c()),D(n,1),n.m(i.parentNode,i))},i(u){r||(D(n),r=!0)},o(u){N(n),r=!1},d(u){u&&L(i),o[t].d(u)}}}function Sve(e){let t,n,i;var r=e[1];function s(o,a){return{props:{componentData:o[0]}}}return r&&(t=Ke(r,s(e))),{c(){t&&he(t.$$.fragment),n=Ne()},m(o,a){t&&ce(t,o,a),I(o,n,a),i=!0},p(o,a){if(r!==(r=o[1])){if(t){Se();const u=t;N(u.$$.fragment,1,0,()=>{fe(u,1)}),Fe()}r?(t=Ke(r,s(o)),he(t.$$.fragment),D(t.$$.fragment,1),ce(t,n.parentNode,n)):t=null}else if(r){const u={};a&1&&(u.componentData=o[0]),t.$set(u)}},i(o){i||(t&&D(t.$$.fragment,o),i=!0)},o(o){t&&N(t.$$.fragment,o),i=!1},d(o){o&&L(n),t&&fe(t,o)}}}function Fve(e){let t,n,i;var r=e[1];function s(o,a){return{props:{componentData:o[0],$$slots:{default:[Dve]},$$scope:{ctx:o}}}}return r&&(t=Ke(r,s(e))),{c(){t&&he(t.$$.fragment),n=Ne()},m(o,a){t&&ce(t,o,a),I(o,n,a),i=!0},p(o,a){if(r!==(r=o[1])){if(t){Se();const u=t;N(u.$$.fragment,1,0,()=>{fe(u,1)}),Fe()}r?(t=Ke(r,s(o)),he(t.$$.fragment),D(t.$$.fragment,1),ce(t,n.parentNode,n)):t=null}else if(r){const u={};a&1&&(u.componentData=o[0]),a&65&&(u.$$scope={dirty:a,ctx:o}),t.$set(u)}},i(o){i||(t&&D(t.$$.fragment,o),i=!0)},o(o){t&&N(t.$$.fragment,o),i=!1},d(o){o&&L(n),t&&fe(t,o)}}}function LP(e){let t,n;return t=new Q6({props:{componentData:e[3]}}),{c(){he(t.$$.fragment)},m(i,r){ce(t,i,r),n=!0},p(i,r){const s={};r&1&&(s.componentData=i[3]),t.$set(s)},i(i){n||(D(t.$$.fragment,i),n=!0)},o(i){N(t.$$.fragment,i),n=!1},d(i){fe(t,i)}}}function Dve(e){let t,n,i=Ue(e[0].contents),r=[];for(let o=0;oN(r[o],1,1,()=>{r[o]=null});return{c(){for(let o=0;o{"componentData"in o&&n(0,i=o.componentData)},[i,s]}class Q6 extends xe{constructor(t){super(),ve(this,t,Mve,Tve,ye,{componentData:0})}}function Nve(e){let t,n,i;const r=e[1].default,s=mt(r,e,e[0],null);return{c(){t=G("main"),n=G("div"),s&&s.c(),M(n,"class","mainContainer svelte-mqeomk"),M(t,"class","svelte-mqeomk")},m(o,a){I(o,t,a),V(t,n),s&&s.m(n,null),i=!0},p(o,[a]){s&&s.p&&(!i||a&1)&&bt(s,r,o,o[0],i?yt(r,o[0],a,null):vt(o[0]),null)},i(o){i||(D(s,o),i=!0)},o(o){N(s,o),i=!1},d(o){o&&L(t),s&&s.d(o)}}}function Rve(e,t,n){let{$$slots:i={},$$scope:r}=t;return e.$$set=s=>{"$$scope"in s&&n(0,r=s.$$scope)},[r,i]}class Ove extends xe{constructor(t){super(),ve(this,t,Rve,Nve,ye,{})}}const Mh=/^[a-z0-9]+(-[a-z0-9]+)*$/,Zm=(e,t,n,i="")=>{const r=e.split(":");if(e.slice(0,1)==="@"){if(r.length<2||r.length>3)return null;i=r.shift().slice(1)}if(r.length>3||!r.length)return null;if(r.length>1){const a=r.pop(),u=r.pop(),l={provider:r.length>0?r[0]:i,prefix:u,name:a};return t&&!Jm(l)?null:l}const s=r[0],o=s.split("-");if(o.length>1){const a={provider:i,prefix:o.shift(),name:o.join("-")};return t&&!Jm(a)?null:a}if(n&&i===""){const a={provider:i,prefix:"",name:s};return t&&!Jm(a,n)?null:a}return null},Jm=(e,t)=>e?!!((e.provider===""||e.provider.match(Mh))&&(t&&e.prefix===""||e.prefix.match(Mh))&&e.name.match(Mh)):!1,IP=Object.freeze({left:0,top:0,width:16,height:16}),Qm=Object.freeze({rotate:0,vFlip:!1,hFlip:!1}),e2=Object.freeze({...IP,...Qm}),e5=Object.freeze({...e2,body:"",hidden:!1});function Lve(e,t){const n={};!e.hFlip!=!t.hFlip&&(n.hFlip=!0),!e.vFlip!=!t.vFlip&&(n.vFlip=!0);const i=((e.rotate||0)+(t.rotate||0))%4;return i&&(n.rotate=i),n}function PP(e,t){const n=Lve(e,t);for(const i in e5)i in Qm?i in e&&!(i in n)&&(n[i]=Qm[i]):i in t?n[i]=t[i]:i in e&&(n[i]=e[i]);return n}function Ive(e,t){const n=e.icons,i=e.aliases||Object.create(null),r=Object.create(null);function s(o){if(n[o])return r[o]=[];if(!(o in r)){r[o]=null;const a=i[o]&&i[o].parent,u=a&&s(a);u&&(r[o]=[a].concat(u))}return r[o]}return Object.keys(n).concat(Object.keys(i)).forEach(s),r}function Pve(e,t,n){const i=e.icons,r=e.aliases||Object.create(null);let s={};function o(a){s=PP(i[a]||r[a],s)}return o(t),n.forEach(o),PP(e,s)}function zP(e,t){const n=[];if(typeof e!="object"||typeof e.icons!="object")return n;e.not_found instanceof Array&&e.not_found.forEach(r=>{t(r,null),n.push(r)});const i=Ive(e);for(const r in i){const s=i[r];s&&(t(r,Pve(e,r,s)),n.push(r))}return n}const zve={provider:"",aliases:{},not_found:{},...IP};function t5(e,t){for(const n in t)if(n in e&&typeof e[n]!=typeof t[n])return!1;return!0}function BP(e){if(typeof e!="object"||e===null)return null;const t=e;if(typeof t.prefix!="string"||!e.icons||typeof e.icons!="object"||!t5(e,zve))return null;const n=t.icons;for(const r in n){const s=n[r];if(!r.match(Mh)||typeof s.body!="string"||!t5(s,e5))return null}const i=t.aliases||Object.create(null);for(const r in i){const s=i[r],o=s.parent;if(!r.match(Mh)||typeof o!="string"||!n[o]&&!i[o]||!t5(s,e5))return null}return t}const UP=Object.create(null);function Bve(e,t){return{provider:e,prefix:t,icons:Object.create(null),missing:new Set}}function Vu(e,t){const n=UP[e]||(UP[e]=Object.create(null));return n[t]||(n[t]=Bve(e,t))}function n5(e,t){return BP(t)?zP(t,(n,i)=>{i?e.icons[n]=i:e.missing.add(n)}):[]}function Uve(e,t,n){try{if(typeof n.body=="string")return e.icons[t]={...n},!0}catch{}return!1}let Nh=!1;function jP(e){return typeof e=="boolean"&&(Nh=e),Nh}function jve(e){const t=typeof e=="string"?Zm(e,!0,Nh):e;if(t){const n=Vu(t.provider,t.prefix),i=t.name;return n.icons[i]||(n.missing.has(i)?null:void 0)}}function qve(e,t){const n=Zm(e,!0,Nh);if(!n)return!1;const i=Vu(n.provider,n.prefix);return Uve(i,n.name,t)}function Wve(e,t){if(typeof e!="object")return!1;if(typeof t!="string"&&(t=e.provider||""),Nh&&!t&&!e.prefix){let r=!1;return BP(e)&&(e.prefix="",zP(e,(s,o)=>{o&&qve(s,o)&&(r=!0)})),r}const n=e.prefix;if(!Jm({provider:t,prefix:n,name:"a"}))return!1;const i=Vu(t,n);return!!n5(i,e)}const qP=Object.freeze({width:null,height:null}),WP=Object.freeze({...qP,...Qm}),Hve=/(-?[0-9.]*[0-9]+[0-9.]*)/g,Gve=/^-?[0-9.]*[0-9]+[0-9.]*$/g;function HP(e,t,n){if(t===1)return e;if(n=n||100,typeof e=="number")return Math.ceil(e*t*n)/n;if(typeof e!="string")return e;const i=e.split(Hve);if(i===null||!i.length)return e;const r=[];let s=i.shift(),o=Gve.test(s);for(;;){if(o){const a=parseFloat(s);isNaN(a)?r.push(s):r.push(Math.ceil(a*t*n)/n)}else r.push(s);if(s=i.shift(),s===void 0)return r.join("");o=!o}}function Vve(e,t="defs"){let n="";const i=e.indexOf("<"+t);for(;i>=0;){const r=e.indexOf(">",i),s=e.indexOf("",s);if(o===-1)break;n+=e.slice(r+1,s).trim(),e=e.slice(0,i).trim()+e.slice(o+1)}return{defs:n,content:e}}function Yve(e,t){return e?""+e+""+t:t}function Xve(e,t,n){const i=Vve(e);return Yve(i.defs,t+i.content+n)}const Kve=e=>e==="unset"||e==="undefined"||e==="none";function Zve(e,t){const n={...e2,...e},i={...WP,...t},r={left:n.left,top:n.top,width:n.width,height:n.height};let s=n.body;[n,i].forEach(g=>{const m=[],y=g.hFlip,b=g.vFlip;let v=g.rotate;y?b?v+=2:(m.push("translate("+(r.width+r.left).toString()+" "+(0-r.top).toString()+")"),m.push("scale(-1 1)"),r.top=r.left=0):b&&(m.push("translate("+(0-r.left).toString()+" "+(r.height+r.top).toString()+")"),m.push("scale(1 -1)"),r.top=r.left=0);let x;switch(v<0&&(v-=Math.floor(v/4)*4),v=v%4,v){case 1:x=r.height/2+r.top,m.unshift("rotate(90 "+x.toString()+" "+x.toString()+")");break;case 2:m.unshift("rotate(180 "+(r.width/2+r.left).toString()+" "+(r.height/2+r.top).toString()+")");break;case 3:x=r.width/2+r.left,m.unshift("rotate(-90 "+x.toString()+" "+x.toString()+")");break}v%2===1&&(r.left!==r.top&&(x=r.left,r.left=r.top,r.top=x),r.width!==r.height&&(x=r.width,r.width=r.height,r.height=x)),m.length&&(s=Xve(s,'',""))});const o=i.width,a=i.height,u=r.width,l=r.height;let c,f;o===null?(f=a===null?"1em":a==="auto"?l:a,c=HP(f,u/l)):(c=o==="auto"?u:o,f=a===null?HP(c,l/u):a==="auto"?l:a);const d={},h=(g,m)=>{Kve(m)||(d[g]=m.toString())};h("width",c),h("height",f);const p=[r.left,r.top,u,l];return d.viewBox=p.join(" "),{attributes:d,viewBox:p,body:s}}const Jve=/\sid="(\S+)"/g,Qve="IconifyId"+Date.now().toString(16)+(Math.random()*16777216|0).toString(16);let exe=0;function txe(e,t=Qve){const n=[];let i;for(;i=Jve.exec(e);)n.push(i[1]);if(!n.length)return e;const r="suffix"+(Math.random()*16777216|Date.now()).toString(16);return n.forEach(s=>{const o=typeof t=="function"?t(s):t+(exe++).toString(),a=s.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");e=e.replace(new RegExp('([#;"])('+a+')([")]|\\.[a-z])',"g"),"$1"+o+r+"$3")}),e=e.replace(new RegExp(r,"g"),""),e}const i5=Object.create(null);function nxe(e,t){i5[e]=t}function r5(e){return i5[e]||i5[""]}function s5(e){let t;if(typeof e.resources=="string")t=[e.resources];else if(t=e.resources,!(t instanceof Array)||!t.length)return null;return{resources:t,path:e.path||"/",maxURL:e.maxURL||500,rotate:e.rotate||750,timeout:e.timeout||5e3,random:e.random===!0,index:e.index||0,dataAfterTimeout:e.dataAfterTimeout!==!1}}const o5=Object.create(null),Rh=["https://api.simplesvg.com","https://api.unisvg.com"],t2=[];for(;Rh.length>0;)Rh.length===1||Math.random()>.5?t2.push(Rh.shift()):t2.push(Rh.pop());o5[""]=s5({resources:["https://api.iconify.design"].concat(t2)});function ixe(e,t){const n=s5(t);return n===null?!1:(o5[e]=n,!0)}function a5(e){return o5[e]}let GP=(()=>{let e;try{if(e=fetch,typeof e=="function")return e}catch{}})();function rxe(e,t){const n=a5(e);if(!n)return 0;let i;if(!n.maxURL)i=0;else{let r=0;n.resources.forEach(o=>{r=Math.max(r,o.length)});const s=t+".json?icons=";i=n.maxURL-r-n.path.length-s.length}return i}function sxe(e){return e===404}const oxe=(e,t,n)=>{const i=[],r=rxe(e,t),s="icons";let o={type:s,provider:e,prefix:t,icons:[]},a=0;return n.forEach((u,l)=>{a+=u.length+1,a>=r&&l>0&&(i.push(o),o={type:s,provider:e,prefix:t,icons:[]},a=u.length),o.icons.push(u)}),i.push(o),i};function axe(e){if(typeof e=="string"){const t=a5(e);if(t)return t.path}return"/"}const uxe={prepare:oxe,send:(e,t,n)=>{if(!GP){n("abort",424);return}let i=axe(t.provider);switch(t.type){case"icons":{const s=t.prefix,a=t.icons.join(","),u=new URLSearchParams({icons:a});i+=s+".json?"+u.toString();break}case"custom":{const s=t.uri;i+=s.slice(0,1)==="/"?s.slice(1):s;break}default:n("abort",400);return}let r=503;GP(e+i).then(s=>{const o=s.status;if(o!==200){setTimeout(()=>{n(sxe(o)?"abort":"next",o)});return}return r=501,s.json()}).then(s=>{if(typeof s!="object"||s===null){setTimeout(()=>{s===404?n("abort",s):n("next",r)});return}setTimeout(()=>{n("success",s)})}).catch(()=>{n("next",r)})}};function lxe(e){const t={loaded:[],missing:[],pending:[]},n=Object.create(null);e.sort((r,s)=>r.provider!==s.provider?r.provider.localeCompare(s.provider):r.prefix!==s.prefix?r.prefix.localeCompare(s.prefix):r.name.localeCompare(s.name));let i={provider:"",prefix:"",name:""};return e.forEach(r=>{if(i.name===r.name&&i.prefix===r.prefix&&i.provider===r.provider)return;i=r;const s=r.provider,o=r.prefix,a=r.name,u=n[s]||(n[s]=Object.create(null)),l=u[o]||(u[o]=Vu(s,o));let c;a in l.icons?c=t.loaded:o===""||l.missing.has(a)?c=t.missing:c=t.pending;const f={provider:s,prefix:o,name:a};c.push(f)}),t}function VP(e,t){e.forEach(n=>{const i=n.loaderCallbacks;i&&(n.loaderCallbacks=i.filter(r=>r.id!==t))})}function cxe(e){e.pendingCallbacksFlag||(e.pendingCallbacksFlag=!0,setTimeout(()=>{e.pendingCallbacksFlag=!1;const t=e.loaderCallbacks?e.loaderCallbacks.slice(0):[];if(!t.length)return;let n=!1;const i=e.provider,r=e.prefix;t.forEach(s=>{const o=s.icons,a=o.pending.length;o.pending=o.pending.filter(u=>{if(u.prefix!==r)return!0;const l=u.name;if(e.icons[l])o.loaded.push({provider:i,prefix:r,name:l});else if(e.missing.has(l))o.missing.push({provider:i,prefix:r,name:l});else return n=!0,!0;return!1}),o.pending.length!==a&&(n||VP([e],s.id),s.callback(o.loaded.slice(0),o.missing.slice(0),o.pending.slice(0),s.abort))})}))}let fxe=0;function dxe(e,t,n){const i=fxe++,r=VP.bind(null,n,i);if(!t.pending.length)return r;const s={id:i,icons:t,callback:e,abort:r};return n.forEach(o=>{(o.loaderCallbacks||(o.loaderCallbacks=[])).push(s)}),r}function hxe(e,t=!0,n=!1){const i=[];return e.forEach(r=>{const s=typeof r=="string"?Zm(r,t,n):r;s&&i.push(s)}),i}var pxe={resources:[],index:0,timeout:2e3,rotate:750,random:!1,dataAfterTimeout:!1};function gxe(e,t,n,i){const r=e.resources.length,s=e.random?Math.floor(Math.random()*r):e.index;let o;if(e.random){let E=e.resources.slice(0);for(o=[];E.length>1;){const w=Math.floor(Math.random()*E.length);o.push(E[w]),E=E.slice(0,w).concat(E.slice(w+1))}o=o.concat(E)}else o=e.resources.slice(s).concat(e.resources.slice(0,s));const a=Date.now();let u="pending",l=0,c,f=null,d=[],h=[];typeof i=="function"&&h.push(i);function p(){f&&(clearTimeout(f),f=null)}function g(){u==="pending"&&(u="aborted"),p(),d.forEach(E=>{E.status==="pending"&&(E.status="aborted")}),d=[]}function m(E,w){w&&(h=[]),typeof E=="function"&&h.push(E)}function y(){return{startTime:a,payload:t,status:u,queriesSent:l,queriesPending:d.length,subscribe:m,abort:g}}function b(){u="failed",h.forEach(E=>{E(void 0,c)})}function v(){d.forEach(E=>{E.status==="pending"&&(E.status="aborted")}),d=[]}function x(E,w,C){const k=w!=="success";switch(d=d.filter(S=>S!==E),u){case"pending":break;case"failed":if(k||!e.dataAfterTimeout)return;break;default:return}if(w==="abort"){c=C,b();return}if(k){c=C,d.length||(o.length?_():b());return}if(p(),v(),!e.random){const S=e.resources.indexOf(E.resource);S!==-1&&S!==e.index&&(e.index=S)}u="completed",h.forEach(S=>{S(C)})}function _(){if(u!=="pending")return;p();const E=o.shift();if(E===void 0){if(d.length){f=setTimeout(()=>{p(),u==="pending"&&(v(),b())},e.timeout);return}b();return}const w={status:"pending",resource:E,callback:(C,k)=>{x(w,C,k)}};d.push(w),l++,f=setTimeout(_,e.rotate),n(E,t,w.callback)}return setTimeout(_),y}function YP(e){const t={...pxe,...e};let n=[];function i(){n=n.filter(a=>a().status==="pending")}function r(a,u,l){const c=gxe(t,a,u,(f,d)=>{i(),l&&l(f,d)});return n.push(c),c}function s(a){return n.find(u=>a(u))||null}return{query:r,find:s,setIndex:a=>{t.index=a},getIndex:()=>t.index,cleanup:i}}function XP(){}const u5=Object.create(null);function mxe(e){if(!u5[e]){const t=a5(e);if(!t)return;const n=YP(t),i={config:t,redundancy:n};u5[e]=i}return u5[e]}function yxe(e,t,n){let i,r;if(typeof e=="string"){const s=r5(e);if(!s)return n(void 0,424),XP;r=s.send;const o=mxe(e);o&&(i=o.redundancy)}else{const s=s5(e);if(s){i=YP(s);const o=e.resources?e.resources[0]:"",a=r5(o);a&&(r=a.send)}}return!i||!r?(n(void 0,424),XP):i.query(t,r,n)().abort}const KP="iconify2",Oh="iconify",ZP=Oh+"-count",JP=Oh+"-version",QP=36e5,bxe=168,vxe=50;function l5(e,t){try{return e.getItem(t)}catch{}}function c5(e,t,n){try{return e.setItem(t,n),!0}catch{}}function ez(e,t){try{e.removeItem(t)}catch{}}function f5(e,t){return c5(e,ZP,t.toString())}function d5(e){return parseInt(l5(e,ZP))||0}const n2={local:!0,session:!0},tz={local:new Set,session:new Set};let h5=!1;function xxe(e){h5=e}let i2=typeof window>"u"?{}:window;function nz(e){const t=e+"Storage";try{if(i2&&i2[t]&&typeof i2[t].length=="number")return i2[t]}catch{}n2[e]=!1}function iz(e,t){const n=nz(e);if(!n)return;const i=l5(n,JP);if(i!==KP){if(i){const a=d5(n);for(let u=0;u{const u=Oh+a.toString(),l=l5(n,u);if(typeof l=="string"){try{const c=JSON.parse(l);if(typeof c=="object"&&typeof c.cached=="number"&&c.cached>r&&typeof c.provider=="string"&&typeof c.data=="object"&&typeof c.data.prefix=="string"&&t(c,a))return!0}catch{}ez(n,u)}};let o=d5(n);for(let a=o-1;a>=0;a--)s(a)||(a===o-1?(o--,f5(n,o)):tz[e].add(a))}function rz(){if(!h5){xxe(!0);for(const e in n2)iz(e,t=>{const n=t.data,i=t.provider,r=n.prefix,s=Vu(i,r);if(!n5(s,n).length)return!1;const o=n.lastModified||-1;return s.lastModifiedCached=s.lastModifiedCached?Math.min(s.lastModifiedCached,o):o,!0})}}function _xe(e,t){const n=e.lastModifiedCached;if(n&&n>=t)return n===t;if(e.lastModifiedCached=t,n)for(const i in n2)iz(i,r=>{const s=r.data;return r.provider!==e.provider||s.prefix!==e.prefix||s.lastModified===t});return!0}function wxe(e,t){h5||rz();function n(i){let r;if(!n2[i]||!(r=nz(i)))return;const s=tz[i];let o;if(s.size)s.delete(o=Array.from(s).shift());else if(o=d5(r),o>=vxe||!f5(r,o+1))return;const a={cached:Math.floor(Date.now()/QP),provider:e.provider,data:t};return c5(r,Oh+o.toString(),JSON.stringify(a))}t.lastModified&&!_xe(e,t.lastModified)||Object.keys(t.icons).length&&(t.not_found&&(t=Object.assign({},t),delete t.not_found),n("local")||n("session"))}function sz(){}function Exe(e){e.iconsLoaderFlag||(e.iconsLoaderFlag=!0,setTimeout(()=>{e.iconsLoaderFlag=!1,cxe(e)}))}function Cxe(e,t){e.iconsToLoad?e.iconsToLoad=e.iconsToLoad.concat(t).sort():e.iconsToLoad=t,e.iconsQueueFlag||(e.iconsQueueFlag=!0,setTimeout(()=>{e.iconsQueueFlag=!1;const{provider:n,prefix:i}=e,r=e.iconsToLoad;delete e.iconsToLoad;let s;if(!r||!(s=r5(n)))return;s.prepare(n,i,r).forEach(a=>{yxe(n,a,u=>{if(typeof u!="object")a.icons.forEach(l=>{e.missing.add(l)});else try{const l=n5(e,u);if(!l.length)return;const c=e.pendingIcons;c&&l.forEach(f=>{c.delete(f)}),wxe(e,u)}catch(l){console.error(l)}Exe(e)})})}))}const kxe=(e,t)=>{const n=hxe(e,!0,jP()),i=lxe(n);if(!i.pending.length){let u=!0;return t&&setTimeout(()=>{u&&t(i.loaded,i.missing,i.pending,sz)}),()=>{u=!1}}const r=Object.create(null),s=[];let o,a;return i.pending.forEach(u=>{const{provider:l,prefix:c}=u;if(c===a&&l===o)return;o=l,a=c,s.push(Vu(l,c));const f=r[l]||(r[l]=Object.create(null));f[c]||(f[c]=[])}),i.pending.forEach(u=>{const{provider:l,prefix:c,name:f}=u,d=Vu(l,c),h=d.pendingIcons||(d.pendingIcons=new Set);h.has(f)||(h.add(f),r[l][c].push(f))}),s.forEach(u=>{const{provider:l,prefix:c}=u;r[l][c].length&&Cxe(u,r[l][c])}),t?dxe(t,i,s):sz};function Axe(e,t){const n={...e};for(const i in t){const r=t[i],s=typeof r;i in qP?(r===null||r&&(s==="string"||s==="number"))&&(n[i]=r):s===typeof n[i]&&(n[i]=i==="rotate"?r%4:r)}return n}const $xe=/[\s,]+/;function Sxe(e,t){t.split($xe).forEach(n=>{switch(n.trim()){case"horizontal":e.hFlip=!0;break;case"vertical":e.vFlip=!0;break}})}function Fxe(e,t=0){const n=e.replace(/^-?[0-9.]*/,"");function i(r){for(;r<0;)r+=4;return r%4}if(n===""){const r=parseInt(e);return isNaN(r)?0:i(r)}else if(n!==e){let r=0;switch(n){case"%":r=25;break;case"deg":r=90}if(r){let s=parseFloat(e.slice(0,e.length-n.length));return isNaN(s)?0:(s=s/r,s%1===0?i(s):0)}}return t}function Dxe(e,t){let n=e.indexOf("xlink:")===-1?"":' xmlns:xlink="http://www.w3.org/1999/xlink"';for(const i in t)n+=" "+i+'="'+t[i]+'"';return'"+e+""}function Txe(e){return e.replace(/"/g,"'").replace(/%/g,"%25").replace(/#/g,"%23").replace(//g,"%3E").replace(/\s+/g," ")}function Mxe(e){return"data:image/svg+xml,"+Txe(e)}function Nxe(e){return'url("'+Mxe(e)+'")'}const oz={...WP,inline:!1},Rxe={xmlns:"http://www.w3.org/2000/svg","xmlns:xlink":"http://www.w3.org/1999/xlink","aria-hidden":!0,role:"img"},Oxe={display:"inline-block"},p5={"background-color":"currentColor"},az={"background-color":"transparent"},uz={image:"var(--svg)",repeat:"no-repeat",size:"100% 100%"},lz={"-webkit-mask":p5,mask:p5,background:az};for(const e in lz){const t=lz[e];for(const n in uz)t[e+"-"+n]=uz[n]}function Lxe(e){return e+(e.match(/^[-0-9.]+$/)?"px":"")}function Ixe(e,t){const n=Axe(oz,t),i=t.mode||"svg",r=i==="svg"?{...Rxe}:{};e.body.indexOf("xlink:")===-1&&delete r["xmlns:xlink"];let s=typeof t.style=="string"?t.style:"";for(let y in t){const b=t[y];if(b!==void 0)switch(y){case"icon":case"style":case"onLoad":case"mode":break;case"inline":case"hFlip":case"vFlip":n[y]=b===!0||b==="true"||b===1;break;case"flip":typeof b=="string"&&Sxe(n,b);break;case"color":s=s+(s.length>0&&s.trim().slice(-1)!==";"?";":"")+"color: "+b+"; ";break;case"rotate":typeof b=="string"?n[y]=Fxe(b):typeof b=="number"&&(n[y]=b);break;case"ariaHidden":case"aria-hidden":b!==!0&&b!=="true"&&delete r["aria-hidden"];break;default:if(y.slice(0,3)==="on:")break;oz[y]===void 0&&(r[y]=b)}}const o=Zve(e,n),a=o.attributes;if(n.inline&&(s="vertical-align: -0.125em; "+s),i==="svg"){Object.assign(r,a),s!==""&&(r.style=s);let y=0,b=t.id;return typeof b=="string"&&(b=b.replace(/-/g,"_")),{svg:!0,attributes:r,body:txe(o.body,b?()=>b+"ID"+y++:"iconifySvelte")}}const{body:u,width:l,height:c}=e,f=i==="mask"||(i==="bg"?!1:u.indexOf("currentColor")!==-1),d=Dxe(u,{...a,width:l+"",height:c+""}),p={"--svg":Nxe(d)},g=y=>{const b=a[y];b&&(p[y]=Lxe(b))};g("width"),g("height"),Object.assign(p,Oxe,f?p5:az);let m="";for(const y in p)m+=y+": "+p[y]+";";return r.style=m+s,{svg:!1,attributes:r}}if(jP(!0),nxe("",uxe),typeof document<"u"&&typeof window<"u"){rz();const e=window;if(e.IconifyPreload!==void 0){const t=e.IconifyPreload,n="Invalid IconifyPreload syntax.";typeof t=="object"&&t!==null&&(t instanceof Array?t:[t]).forEach(i=>{try{(typeof i!="object"||i===null||i instanceof Array||typeof i.icons!="object"||typeof i.prefix!="string"||!Wve(i))&&console.error(n)}catch{console.error(n)}})}if(e.IconifyProviders!==void 0){const t=e.IconifyProviders;if(typeof t=="object"&&t!==null)for(let n in t){const i="IconifyProviders["+n+"] is invalid.";try{const r=t[n];if(typeof r!="object"||!r||r.resources===void 0)continue;ixe(n,r)||console.error(i)}catch{console.error(i)}}}}function Pxe(e,t,n,i,r){function s(){t.loading&&(t.loading.abort(),t.loading=null)}if(typeof e=="object"&&e!==null&&typeof e.body=="string")return t.name="",s(),{data:{...e2,...e}};let o;if(typeof e!="string"||(o=Zm(e,!1,!0))===null)return s(),null;const a=jve(o);if(!a)return n&&(!t.loading||t.loading.name!==e)&&(s(),t.name="",t.loading={name:e,abort:kxe([o],i)}),null;s(),t.name!==e&&(t.name=e,r&&!t.destroyed&&r(e));const u=["iconify"];return o.prefix!==""&&u.push("iconify--"+o.prefix),o.provider!==""&&u.push("iconify--"+o.provider),{data:a,classes:u}}function zxe(e,t){return e?Ixe({...e2,...e},t):null}function cz(e){let t;function n(s,o){return s[0].svg?Uxe:Bxe}let i=n(e),r=i(e);return{c(){r.c(),t=Ne()},m(s,o){r.m(s,o),I(s,t,o)},p(s,o){i===(i=n(s))&&r?r.p(s,o):(r.d(1),r=i(s),r&&(r.c(),r.m(t.parentNode,t)))},d(s){s&&L(t),r.d(s)}}}function Bxe(e){let t,n=[e[0].attributes],i={};for(let r=0;r{typeof t.onLoad=="function"&&t.onLoad(l),a2()("load",{icon:l})};function u(){n(3,s++,s)}return Wc(()=>{n(2,r=!0)}),w5(()=>{n(1,i.destroyed=!0,i),i.loading&&(i.loading.abort(),n(1,i.loading=null,i))}),e.$$set=l=>{n(6,t=Pe(Pe({},t),o2(l)))},e.$$.update=()=>{{const l=Pxe(t.icon,i,r,u,a);n(0,o=l?zxe(l.data,t):null),o&&l.classes&&n(0,o.attributes.class=(typeof t.class=="string"?t.class+" ":"")+l.classes.join(" "),o)}},t=o2(t),[o,i,r,s]}class Wxe extends xe{constructor(t){super(),ve(this,t,qxe,jxe,ye,{})}}function fz(e){let t,n,i,r,s,o,a,u,l;return i=new Wxe({props:{icon:"mdi:close"}}),o=new Q6({props:{componentData:e[1]}}),{c(){t=G("div"),n=G("span"),he(i.$$.fragment),r=He(),s=G("div"),he(o.$$.fragment),M(n,"class","cancelButton svelte-1hhf5ym"),M(s,"class","modalContainer"),M(t,"class","modal svelte-1hhf5ym"),M(t,"data-component","modal")},m(c,f){I(c,t,f),V(t,n),ce(i,n,null),V(t,r),V(t,s),ce(o,s,null),a=!0,u||(l=[Ku(s,"click",Gxe),Ku(t,"click",e[3])],u=!0)},p(c,f){const d={};f&2&&(d.componentData=c[1]),o.$set(d)},i(c){a||(D(i.$$.fragment,c),D(o.$$.fragment,c),a=!0)},o(c){N(i.$$.fragment,c),N(o.$$.fragment,c),a=!1},d(c){c&&L(t),fe(i),fe(o),u=!1,Xu(l)}}}function Hxe(e){let t,n,i,r,s=e[0]&&e[1]&&fz(e);return{c(){s&&s.c(),t=Ne()},m(o,a){s&&s.m(o,a),I(o,t,a),n=!0,i||(r=Ku(window,"keyup",e[2]),i=!0)},p(o,[a]){o[0]&&o[1]?s?(s.p(o,a),a&3&&D(s,1)):(s=fz(o),s.c(),D(s,1),s.m(t.parentNode,t)):s&&(Se(),N(s,1,1,()=>{s=null}),Fe())},i(o){n||(D(s),n=!0)},o(o){N(s),n=!1},d(o){o&&L(t),s&&s.d(o),i=!1,r()}}}const Gxe=e=>{e==null||e.stopImmediatePropagation()};function Vxe(e,t,n){let i;Ph(e,Hc,a=>n(1,i=a));let{componentData:r}=t;function s(a){a.code==="Escape"&&Hc.set(void 0)}function o(a){a.stopImmediatePropagation(),Hc.set(void 0)}return e.$$set=a=>{"componentData"in a&&n(0,r=a.componentData)},[r,i,s,o]}class Yxe extends xe{constructor(t){super(),ve(this,t,Vxe,Hxe,ye,{componentData:0})}}function dz(e,t,n){const i=e.slice();return i[2]=t[n][0],i[3]=t[n][1],i}function hz(e,t,n){const i=e.slice();return i[6]=t[n],i}function pz(e){let t,n=e[2]+"",i;return{c(){t=G("span"),i=Be(n),M(t,"class","pageId svelte-1kdpgko")},m(r,s){I(r,t,s),V(t,i)},p(r,s){s&1&&n!==(n=r[2]+"")&&ft(i,n)},d(r){r&&L(t)}}}function gz(e){let t,n,i=e[6]+"",r,s,o,a;function u(){return e[1](e[6])}return{c(){t=G("li"),n=G("button"),r=Be(i),s=He(),M(n,"class","textButton"),M(t,"class","sectionLink svelte-1kdpgko")},m(l,c){I(l,t,c),V(t,n),V(n,r),V(t,s),o||(a=Ku(n,"click",u),o=!0)},p(l,c){e=l,c&1&&i!==(i=e[6]+"")&&ft(r,i)},d(l){l&&L(t),o=!1,a()}}}function mz(e){let t,n,i,r,s=e[2]&&pz(e),o=Ue(e[3]||[]),a=[];for(let u=0;uKxe(s);return e.$$set=s=>{"pageHierarchy"in s&&n(0,i=s.pageHierarchy)},[i,r]}class Jxe extends xe{constructor(t){super(),ve(this,t,Zxe,Xxe,ye,{pageHierarchy:0})}}const{Boolean:Qxe}=Sz;function yz(e,t,n){const i=e.slice();return i[5]=t[n],i}function e7e(e){var i;let t,n;return t=new Jxe({props:{pageHierarchy:F5((i=e[0])==null?void 0:i.components)}}),{c(){he(t.$$.fragment)},m(r,s){ce(t,r,s),n=!0},p(r,s){var a;const o={};s&1&&(o.pageHierarchy=F5((a=r[0])==null?void 0:a.components)),t.$set(o)},i(r){n||(D(t.$$.fragment,r),n=!0)},o(r){N(t.$$.fragment,r),n=!1},d(r){fe(t,r)}}}function bz(e){let t,n;return t=new Q6({props:{componentData:e[5]}}),{c(){he(t.$$.fragment)},m(i,r){ce(t,i,r),n=!0},p(i,r){const s={};r&1&&(s.componentData=i[5]),t.$set(s)},i(i){n||(D(t.$$.fragment,i),n=!0)},o(i){N(t.$$.fragment,i),n=!1},d(i){fe(t,i)}}}function t7e(e){var o;let t,n,i=Ue(((o=e[0])==null?void 0:o.components)||[]),r=[];for(let a=0;aN(r[a],1,1,()=>{r[a]=null});return{c(){for(let a=0;a{u=null}),Fe())},i(l){a||(D(n.$$.fragment,l),D(r.$$.fragment,l),D(u),a=!0)},o(l){N(n.$$.fragment,l),N(r.$$.fragment,l),N(u),a=!1},d(l){l&&(L(t),L(s),L(o)),fe(n),fe(r),u&&u.d(l)}}}function i7e(e,t,n){let i,r;Ph(e,wa,u=>n(0,i=u)),Ph(e,Hc,u=>n(1,r=u));let{cardDataId:s}=t;Uz(s);let a=!!new URLSearchParams(window==null?void 0:window.location.search).get("embed");return e.$$set=u=>{"cardDataId"in u&&n(3,s=u.cardDataId)},[i,r,a,s]}class r7e extends xe{constructor(t){super(),ve(this,t,i7e,n7e,ye,{cardDataId:3})}}let xz;try{const e=window.mfCardDataId,t=window.mfContainerId,n=(wz=document.querySelector(`[data-container="${t}"]`))==null?void 0:wz.querySelector(".card_app");xz=new r7e({target:n??document.querySelector(".card_app"),props:{cardDataId:e}})}catch(e){throw new Error(e)}return xz}); diff --git a/metaflow/plugins/cards/card_modules/renderer_tools.py b/metaflow/plugins/cards/card_modules/renderer_tools.py index 6746e8fee02..16adf799024 100644 --- a/metaflow/plugins/cards/card_modules/renderer_tools.py +++ b/metaflow/plugins/cards/card_modules/renderer_tools.py @@ -40,6 +40,7 @@ def render_safely(func): This is a decorator that can be added to any `MetaflowCardComponent.render` The goal is to render subcomponents safely and ensure that they are JSON serializable. """ + # expects a renderer func def ret_func(self, *args, **kwargs): return _render_component_safely(self, func, True, *args, **kwargs) diff --git a/metaflow/plugins/cards/card_modules/test_cards.py b/metaflow/plugins/cards/card_modules/test_cards.py index b756fb37dbd..67a2ee533da 100644 --- a/metaflow/plugins/cards/card_modules/test_cards.py +++ b/metaflow/plugins/cards/card_modules/test_cards.py @@ -1,13 +1,20 @@ +import json from .card import MetaflowCard, MetaflowCardComponent +from .renderer_tools import render_safely class TestStringComponent(MetaflowCardComponent): + REALTIME_UPDATABLE = True + def __init__(self, text): self._text = text def render(self): return str(self._text) + def update(self, text): + self._text = text + class TestPathSpecCard(MetaflowCard): type = "test_pathspec_card" @@ -27,41 +34,41 @@ def render(self, task): class TestEditableCard(MetaflowCard): type = "test_editable_card" - seperator = "$&#!!@*" + separator = "$&#!!@*" ALLOW_USER_COMPONENTS = True - def __init__(self, options={}, components=[], graph=None): + def __init__(self, components=[], **kwargs): self._components = components def render(self, task): - return self.seperator.join([str(comp) for comp in self._components]) + return self.separator.join([str(comp) for comp in self._components]) class TestEditableCard2(MetaflowCard): type = "test_editable_card_2" - seperator = "$&#!!@*" + separator = "$&#!!@*" ALLOW_USER_COMPONENTS = True - def __init__(self, options={}, components=[], graph=None): + def __init__(self, components=[], **kwargs): self._components = components def render(self, task): - return self.seperator.join([str(comp) for comp in self._components]) + return self.separator.join([str(comp) for comp in self._components]) class TestNonEditableCard(MetaflowCard): type = "test_non_editable_card" - seperator = "$&#!!@*" + separator = "$&#!!@*" - def __init__(self, options={}, components=[], graph=None): + def __init__(self, components=[], **kwargs): self._components = components def render(self, task): - return self.seperator.join([str(comp) for comp in self._components]) + return self.separator.join([str(comp) for comp in self._components]) class TestMockCard(MetaflowCard): @@ -98,3 +105,127 @@ def render(self, task): time.sleep(self._timeout) return "%s" % task.pathspec + + +REFRESHABLE_HTML_TEMPLATE = """ + + +

    [PATHSPEC]

    +

    [REPLACE_CONTENT_HERE]

    + +""" + + +class TestJSONComponent(MetaflowCardComponent): + + REALTIME_UPDATABLE = True + + def __init__(self, data): + self._data = data + + @render_safely + def render(self): + return self._data + + def update(self, data): + self._data = data + + +class TestRefreshCard(MetaflowCard): + """ + This card takes no components and helps test the `current.card.refresh(data)` interface. + """ + + HTML_TEMPLATE = REFRESHABLE_HTML_TEMPLATE + + RUNTIME_UPDATABLE = True + + ALLOW_USER_COMPONENTS = True + + # Not implementing Reload Policy here since the reload Policy is set to always + RELOAD_POLICY = MetaflowCard.RELOAD_POLICY_ALWAYS + + type = "test_refresh_card" + + def render(self, task) -> str: + return self._render_func(task, self.runtime_data) + + def _render_func(self, task, data): + return self.HTML_TEMPLATE.replace( + "[REPLACE_CONTENT_HERE]", json.dumps(data["user"]) + ).replace("[PATHSPEC]", task.pathspec) + + def render_runtime(self, task, data): + return self._render_func(task, data) + + def refresh(self, task, data): + return data + + +import hashlib + + +def _component_values_to_hash(components): + comma_str = ",".join(["".join(x) for v in components.values() for x in v]) + return hashlib.sha256(comma_str.encode("utf-8")).hexdigest() + + +class TestRefreshComponentCard(MetaflowCard): + """ + This card takes components and helps test the `current.card.components["A"].update()` + interface + """ + + HTML_TEMPLATE = REFRESHABLE_HTML_TEMPLATE + + RUNTIME_UPDATABLE = True + + ALLOW_USER_COMPONENTS = True + + # Not implementing Reload Policy here since the reload Policy is set to always + RELOAD_POLICY = MetaflowCard.RELOAD_POLICY_ONCHANGE + + type = "test_component_refresh_card" + + def __init__(self, components=[], **kwargs): + self._components = components + + def render(self, task) -> str: + # Calling `render`/`render_runtime` wont require the `data` object + return self.HTML_TEMPLATE.replace( + "[REPLACE_CONTENT_HERE]", json.dumps(self._components) + ).replace("[PATHSPEC]", task.pathspec) + + def render_runtime(self, task, data): + return self.render(task) + + def refresh(self, task, data): + # Govers the information passed in the data update + return data["components"] + + def reload_content_token(self, task, data): + if task.finished: + return "final" + return "runtime-%s" % _component_values_to_hash(data["components"]) + + +class TestImageCard(MetaflowCard): + """Card that renders a tiny PNG using ``TaskToDict.parse_image``.""" + + type = "test_image_card" + + def render(self, task): + from .convert_to_native_type import TaskToDict + import base64 + + png_bytes = base64.b64decode( + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR4nGNgYGBgAAAABQABRDE8UwAAAABJRU5ErkJggg==" + ) + img_src = TaskToDict().parse_image(png_bytes) + return f"" diff --git a/metaflow/plugins/cards/card_resolver.py b/metaflow/plugins/cards/card_resolver.py index d8366990d2f..e717005ff28 100644 --- a/metaflow/plugins/cards/card_resolver.py +++ b/metaflow/plugins/cards/card_resolver.py @@ -1,5 +1,3 @@ -from collections import namedtuple - from .card_datastore import CardDatastore diff --git a/metaflow/plugins/cards/card_server.py b/metaflow/plugins/cards/card_server.py new file mode 100644 index 00000000000..d0050c6559e --- /dev/null +++ b/metaflow/plugins/cards/card_server.py @@ -0,0 +1,386 @@ +import os +import json +from http.server import BaseHTTPRequestHandler +from threading import Thread +from multiprocessing import Pipe +from multiprocessing.connection import Connection +from urllib.parse import urlparse +import time + +try: + from http.server import ThreadingHTTPServer +except ImportError: + from socketserver import ThreadingMixIn + from http.server import HTTPServer + + class ThreadingHTTPServer(ThreadingMixIn, HTTPServer): + daemon_threads = True + + +from .card_client import CardContainer +from .exception import CardNotPresentException +from .card_resolver import resolve_paths_from_task +from metaflow import namespace +from metaflow.exception import MetaflowNotFound +from metaflow.plugins.datastores.local_storage import LocalStorage + +VIEWER_PATH = os.path.join( + os.path.dirname(os.path.abspath(__file__)), "card_viewer", "viewer.html" +) + +CARD_VIEWER_HTML = open(VIEWER_PATH).read() + +TASK_CACHE = {} + +_ClickLogger = None + + +class RunWatcher(Thread): + """ + A thread that watches for new runs and sends the run_id to the + card server when a new run is detected. It observes the `latest_run` + file in the `.metaflow/` directory. + """ + + def __init__(self, flow_name, connection: Connection): + super().__init__() + + self.daemon = True + self._connection = connection + self._flow_name = flow_name + + self._watch_file = self._initialize_watch_file() + if self._watch_file is None: + _ClickLogger( + "Warning: Could not initialize watch file location.", fg="yellow" + ) + + self._current_run_id = self.get_run_id() + + def _initialize_watch_file(self): + local_root = LocalStorage.datastore_root + if local_root is None: + local_root = LocalStorage.get_datastore_root_from_config( + lambda _: None, create_on_absent=False + ) + + return ( + os.path.join(local_root, self._flow_name, "latest_run") + if local_root + else None + ) + + def get_run_id(self): + # Try to reinitialize watch file if needed + if not self._watch_file: + self._watch_file = self._initialize_watch_file() + + # Early return if watch file is still None or doesn't exist + if not (self._watch_file and os.path.exists(self._watch_file)): + return None + + try: + with open(self._watch_file, "r") as f: + return f.read().strip() + except (IOError, OSError) as e: + _ClickLogger( + "Warning: Could not read run ID from watch file: %s" % e, fg="yellow" + ) + return None + + def watch(self): + while True: + run_id = self.get_run_id() + if run_id != self._current_run_id: + self._current_run_id = run_id + self._connection.send(run_id) + time.sleep(2) + + def run(self): + self.watch() + + +class CardServerOptions: + def __init__( + self, + flow_name, + run_object, + only_running, + follow_resumed, + flow_datastore, + follow_new_runs, + max_cards=20, + poll_interval=5, + ): + from metaflow import Run + + self.RunClass = Run + self.run_object = run_object + + self.flow_name = flow_name + self.only_running = only_running + self.follow_resumed = follow_resumed + self.flow_datastore = flow_datastore + self.max_cards = max_cards + self.follow_new_runs = follow_new_runs + self.poll_interval = poll_interval + + self._parent_conn, self._child_conn = Pipe() + + def refresh_run(self): + if not self.follow_new_runs: + return False + if not self.parent_conn.poll(): + return False + run_id = self.parent_conn.recv() + if run_id is None: + return False + namespace(None) + try: + self.run_object = self.RunClass(f"{self.flow_name}/{run_id}") + return True + except MetaflowNotFound: + return False + + @property + def parent_conn(self): + return self._parent_conn + + @property + def child_conn(self): + return self._child_conn + + +def cards_for_task( + flow_datastore, task_pathspec, card_type=None, card_hash=None, card_id=None +): + try: + paths, card_ds = resolve_paths_from_task( + flow_datastore, + task_pathspec, + type=card_type, + hash=card_hash, + card_id=card_id, + ) + except CardNotPresentException: + return None + for card in CardContainer(paths, card_ds, origin_pathspec=None): + yield card + + +def cards_for_run( + flow_datastore, + run_object, + only_running, + card_type=None, + card_hash=None, + card_id=None, + max_cards=20, +): + curr_idx = 0 + for step in run_object.steps(): + for task in step.tasks(): + if only_running and task.finished: + continue + card_generator = cards_for_task( + flow_datastore, + task.pathspec, + card_type=card_type, + card_hash=card_hash, + card_id=card_id, + ) + if card_generator is None: + continue + for card in card_generator: + curr_idx += 1 + if curr_idx >= max_cards: + raise StopIteration + yield task.pathspec, card + + +class CardViewerRoutes(BaseHTTPRequestHandler): + + card_options: CardServerOptions = None + + run_watcher: RunWatcher = None + + def do_GET(self): + try: + _, path = self.path.split("/", 1) + try: + prefix, suffix = path.split("/", 1) + except: + prefix = path + suffix = None + except: + prefix = None + if prefix in self.ROUTES: + self.ROUTES[prefix](self, suffix) + else: + self._response(open(VIEWER_PATH).read().encode("utf-8")) + + def get_runinfo(self, suffix): + run_id_changed = self.card_options.refresh_run() + if run_id_changed: + self.log_message( + "RunID changed in the background to %s" + % self.card_options.run_object.pathspec + ) + _ClickLogger( + "RunID changed in the background to %s" + % self.card_options.run_object.pathspec, + fg="blue", + ) + + if self.card_options.run_object is None: + self._response( + {"status": "No Run Found", "flow": self.card_options.flow_name}, + code=404, + is_json=True, + ) + return + + task_card_generator = cards_for_run( + self.card_options.flow_datastore, + self.card_options.run_object, + self.card_options.only_running, + max_cards=self.card_options.max_cards, + ) + flow_name = self.card_options.run_object.parent.id + run_id = self.card_options.run_object.id + cards = [] + for pathspec, card in task_card_generator: + step, task = pathspec.split("/")[-2:] + _task = self.card_options.run_object[step][task] + task_finished = True if _task.finished else False + cards.append( + dict( + task=pathspec, + label="%s/%s %s" % (step, task, card.hash), + card_object=dict( + hash=card.hash, + type=card.type, + path=card.path, + id=card.id, + ), + finished=task_finished, + card="%s/%s" % (pathspec, card.hash), + ) + ) + resp = { + "status": "ok", + "flow": flow_name, + "run_id": run_id, + "cards": cards, + "poll_interval": self.card_options.poll_interval, + } + self._response(resp, is_json=True) + + def get_card(self, suffix): + _suffix = urlparse(self.path).path + _, flow, run_id, step, task_id, card_hash = _suffix.strip("/").split("/") + + pathspec = "/".join([flow, run_id, step, task_id]) + cards = list( + cards_for_task( + self.card_options.flow_datastore, pathspec, card_hash=card_hash + ) + ) + if len(cards) == 0: + self._response({"status": "Card Not Found"}, code=404) + return + selected_card = cards[0] + self._response(selected_card.get().encode("utf-8")) + + def get_data(self, suffix): + _suffix = urlparse(self.path).path + _, flow, run_id, step, task_id, card_hash = _suffix.strip("/").split("/") + pathspec = "/".join([flow, run_id, step, task_id]) + cards = list( + cards_for_task( + self.card_options.flow_datastore, pathspec, card_hash=card_hash + ) + ) + if len(cards) == 0: + self._response( + { + "status": "Card Not Found", + }, + is_json=True, + code=404, + ) + return + + status = "ok" + try: + task_object = self.card_options.run_object[step][task_id] + except KeyError: + return self._response( + {"status": "Task Not Found", "is_complete": False}, + is_json=True, + code=404, + ) + + is_complete = task_object.finished + selected_card = cards[0] + card_data = selected_card.get_data() + if card_data is not None: + self.log_message( + "Task Success: %s, Task Finished: %s" + % (task_object.successful, is_complete) + ) + if not task_object.successful and is_complete: + status = "Task Failed" + self._response( + {"status": status, "payload": card_data, "is_complete": is_complete}, + is_json=True, + ) + else: + self._response( + {"status": "ok", "is_complete": is_complete}, + is_json=True, + code=404, + ) + + def _response(self, body, is_json=False, code=200): + self.send_response(code) + mime = "application/json" if is_json else "text/html" + self.send_header("Content-type", mime) + self.end_headers() + if is_json: + self.wfile.write(json.dumps(body).encode("utf-8")) + else: + self.wfile.write(body) + + ROUTES = {"runinfo": get_runinfo, "card": get_card, "data": get_data} + + +def _is_debug_mode(): + debug_flag = os.environ.get("METAFLOW_DEBUG_CARD_SERVER") + if debug_flag is None: + return False + return debug_flag.lower() in ["true", "1"] + + +def create_card_server(card_options: CardServerOptions, port, ctx_obj): + CardViewerRoutes.card_options = card_options + global _ClickLogger + _ClickLogger = ctx_obj.echo + if card_options.follow_new_runs: + CardViewerRoutes.run_watcher = RunWatcher( + card_options.flow_name, card_options.child_conn + ) + CardViewerRoutes.run_watcher.start() + server_addr = ("", port) + ctx_obj.echo( + "Starting card server on port %d " % (port), + fg="green", + bold=True, + ) + # Disable logging if not in debug mode + if not _is_debug_mode(): + CardViewerRoutes.log_request = lambda *args, **kwargs: None + CardViewerRoutes.log_message = lambda *args, **kwargs: None + + server = ThreadingHTTPServer(server_addr, CardViewerRoutes) + server.serve_forever() diff --git a/metaflow/plugins/cards/card_viewer/viewer.html b/metaflow/plugins/cards/card_viewer/viewer.html new file mode 100644 index 00000000000..00a794facdc --- /dev/null +++ b/metaflow/plugins/cards/card_viewer/viewer.html @@ -0,0 +1,344 @@ + + + + + + Metaflow Run + + + +
    +

    Metaflow Run

    +
    + +
    +
    +
    Initializing
    + +
    +
    +
    + +
    + + + + diff --git a/metaflow/plugins/cards/component_serializer.py b/metaflow/plugins/cards/component_serializer.py index 9451cd67406..697934099d5 100644 --- a/metaflow/plugins/cards/component_serializer.py +++ b/metaflow/plugins/cards/component_serializer.py @@ -1,8 +1,17 @@ from .card_modules import MetaflowCardComponent +from .card_modules.card import create_component_id from .card_modules.basic import ErrorComponent, SectionComponent -from .card_modules.components import UserComponent +from .card_modules.components import ( + UserComponent, + StubComponent, +) +from .exception import ComponentOverwriteNotSupportedException +from metaflow.metaflow_config import RUNTIME_CARD_RENDER_INTERVAL import uuid import json +import platform +from collections import OrderedDict +import time _TYPE = type @@ -16,11 +25,367 @@ def get_card_class(card_type): return filtered_cards[0] +def _component_is_valid(component): + """ + Validates if the component is of the correct class. + """ + return issubclass(type(component), MetaflowCardComponent) + + +def warning_message(message, logger=None, ts=False): + if logger: + msg = "[@card WARNING] %s" % message + logger(msg, timestamp=ts, bad=True) + + class WarningComponent(ErrorComponent): def __init__(self, warning_message): super().__init__("@card WARNING", warning_message) +class ComponentStore: + """ + The `ComponentStore` object helps store the components for a single card in memory. + This class has combination of a array/dictionary like interfaces to access/change the stored components. + + It exposes the `append` /`extend` methods (like an array) to add components. + It also exposes the `__getitem__`/`__setitem__` methods (like a dictionary) to access the components by their Ids. + """ + + def _set_component_map(self): + """ + The `_component_map` attribute is supposed to be a dictionary so that we can access the components by their ids. + But we also want to maintain order in which components are inserted since all of these components are going to be visible on a UI. + Since python3.6 dictionaries are ordered by default so we can use the default python `dict`. + """ + self._component_map = {} + + def __init__(self, logger, card_type=None, components=None, user_set_id=None): + self._logger = logger + self._card_type = card_type + self._user_set_id = user_set_id + self._layout_last_changed_on = time.time() + self._set_component_map() + if components is not None: + for c in list(components): + self._store_component(c, component_id=None) + + @property + def layout_last_changed_on(self): + """This property helps the CardComponentManager identify when the layout of the card has changed so that it can trigger a re-render of the card.""" + return self._layout_last_changed_on + + def _realtime_updateable_components(self): + for c in self._component_map.values(): + if c.REALTIME_UPDATABLE: + yield c + + def _store_component(self, component, component_id=None): + if not _component_is_valid(component): + warning_message( + "Component (%s) is not a valid MetaflowCardComponent. It will not be stored." + % str(component), + self._logger, + ) + return + if component_id is not None: + component.component_id = component_id + elif component.component_id is None: + component.component_id = create_component_id(component) + setattr(component, "_logger", self._logger) + self._component_map[component.component_id] = component + self._layout_last_changed_on = time.time() + + def _remove_component(self, component_id): + del self._component_map[component_id] + self._layout_last_changed_on = time.time() + + def __iter__(self): + return iter(self._component_map.values()) + + def __setitem__(self, key, value): + if self._component_map.get(key) is not None: + # This is the equivalent of calling `current.card.components["mycomponent"] = Markdown("## New Component")` + # We don't support the replacement of individual components in the card. + # Instead we support rewriting the entire component array instead. + # So users can run `current.card[ID] = [FirstComponent, SecondComponent]` which will instantiate an entirely + # new ComponentStore. + # So we should throw an error over here, since it is clearly an operation which is not supported. + raise ComponentOverwriteNotSupportedException( + key, self._user_set_id, self._card_type + ) + else: + self._store_component(value, component_id=key) + + def __getitem__(self, key): + if key not in self._component_map: + # Store a stub-component in place since `key` doesnt exist. + # If the user does a `current.card.append(component, id=key)` + # then the stub component will be replaced by the actual component. + self._store_component(StubComponent(key), component_id=key) + return self._component_map[key] + + def __delitem__(self, key): + if key not in self._component_map: + raise KeyError( + "MetaflowCardComponent with id `%s` not found. Available components for the cards include : %s" + % (key, ", ".join(self.keys())) + ) + self._remove_component(key) + + def __contains__(self, key): + return key in self._component_map + + def append(self, component, id=None): + self._store_component(component, component_id=id) + + def extend(self, components): + for c in components: + self._store_component(c, component_id=None) + + def clear(self): + self._component_map.clear() + + def keys(self): + return list(self._component_map.keys()) + + def values(self): + return self._component_map.values() + + def __str__(self): + return "Card components present in the card: `%s` " % ("`, `".join(self.keys())) + + def __len__(self): + return len(self._component_map) + + +def _object_is_json_serializable(obj): + try: + json.dumps(obj) + return True + except TypeError as e: + return False + + +class CardComponentManager: + """ + This class manages the card's state for a single card. + - It uses the `ComponentStore` to manage the storage of the components + - It exposes methods to add, remove and access the components. + - It exposes a `refresh` method that will allow refreshing a card with new data + for realtime(ish) updates. + - The `CardComponentCollector` exposes convenience methods similar to this class for a default + editable card. These methods include : + - `append` + - `extend` + - `clear` + - `refresh` + - `components` + - `__iter__` + + ## Usage Patterns : + + ```python + current.card["mycardid"].append(component, id="comp123") + current.card["mycardid"].extend([component]) + current.card["mycardid"].refresh(data) # refreshes the card with new data + current.card["mycardid"].components["comp123"] # returns the component with id "comp123" + current.card["mycardid"].components["comp123"].update() + current.card["mycardid"].components.clear() # Wipe all the components + del current.card["mycardid"].components["mycomponentid"] # Delete a component + ``` + """ + + def __init__( + self, + card_uuid, + decorator_attributes, + card_creator, + components=None, + logger=None, + no_warnings=False, + user_set_card_id=None, + runtime_card=False, + card_options=None, + refresh_interval=5, + ): + self._card_creator_args = dict( + card_uuid=card_uuid, + user_set_card_id=user_set_card_id, + runtime_card=runtime_card, + decorator_attributes=decorator_attributes, + card_options=card_options, + logger=logger, + ) + self._card_creator = card_creator + self._refresh_interval = refresh_interval + self._last_layout_change = None + self._latest_user_data = None + self._last_refresh = 0 + self._last_render = 0 + self._render_seq = 0 + self._logger = logger + self._no_warnings = no_warnings + self._warn_once = { + "update": {}, + "not_implemented": {}, + } + card_type = decorator_attributes["type"] + + if components is None: + self._components = ComponentStore( + logger=self._logger, + card_type=card_type, + user_set_id=user_set_card_id, + components=None, + ) + else: + self._components = ComponentStore( + logger=self._logger, + card_type=card_type, + user_set_id=user_set_card_id, + components=list(components), + ) + + def append(self, component, id=None): + self._components.append(component, id=id) + + def extend(self, components): + self._components.extend(components) + + def clear(self): + self._components.clear() + + def _card_proc(self, mode, sync=False): + self._card_creator.create(**self._card_creator_args, mode=mode, sync=sync) + + def refresh(self, data=None, force=False): + self._latest_user_data = data + nu = time.time() + first_render = True if self._last_render == 0 else False + + if nu - self._last_refresh < self._refresh_interval: + # rate limit refreshes: silently ignore requests that + # happen too frequently + return + self._last_refresh = nu + + # This block of code will render the card in `render_runtime` mode when: + # 1. refresh is called with `force=True` + # 2. Layout of the components in the card has changed. i.e. The actual elements in the component array have changed. + # 3. The last time the card was rendered was more the minimum interval after which they should be rendered. + last_rendered_before_minimum_interval = ( + nu - self._last_refresh + ) > RUNTIME_CARD_RENDER_INTERVAL + layout_has_changed = ( + self._last_layout_change != self.components.layout_last_changed_on + or self._last_layout_change is None + ) + if force or last_rendered_before_minimum_interval or layout_has_changed: + self._render_seq += 1 + self._last_render = nu + self._card_proc("render_runtime") + # The below `if not first_render` condition is a special case for the following scenario: + # Lets assume the case that the user is only doing `current.card.append` followed by `refresh`. + # In this case, there will be no process executed in `refresh` mode since `layout_has_changed` + # will always be true and as a result there will be no data update that informs the UI of the RELOAD_TOKEN change. + # This will cause the UI to seek for the data update object but will constantly find None. So if it is not + # the first render then we should also have a `refresh` call followed by a `render_runtime` call so + # that the UI can always be updated with the latest data. + if not first_render: + # For the general case, the CardCreator's ProcessManager run's the `refresh` / `render_runtime` in a asynchronous manner. + # Due to this when the `render_runtime` call is happening, an immediately subsequent call to `refresh` will not be able to + # execute since the card-process manager will be busy executing the `render_runtime` call and ignore the `refresh` call. + # Hence we need to pass the `sync=True` argument to the `refresh` call so that the `refresh` call is executed synchronously and waits for the + # `render_runtime` call to finish. + self._card_proc("refresh", sync=True) + # We set self._last_layout_change so that when self._last_layout_change is not the same + # as `self.components.layout_last_changed_on`, then the component array itself + # has been modified. So we should force a re-render of the card. + self._last_layout_change = self.components.layout_last_changed_on + else: + self._card_proc("refresh") + + @property + def components(self): + return self._components + + def _warning(self, message): + msg = "[@card WARNING] %s" % message + self._logger(msg, timestamp=False, bad=True) + + def _get_latest_data(self, final=False, mode=None): + """ + This function returns the data object that is passed down to : + - `MetaflowCard.render_runtime` + - `MetaflowCard.refresh` + - `MetaflowCard.reload_content_token` + + The return value of this function contains all the necessary state information for Metaflow Cards to make decisions on the following: + 1. What components are rendered + 2. Should the card be reloaded on the UI + 3. What data to pass down to the card. + + Parameters + ---------- + final : bool, optional + If True, it implies that the final "rendering" sequence is taking place (which involves calling a `render` and a `refresh` function.) + When final is set the `render_seq` is set to "final" so that the reload token in the card is set to final + and the card is not reloaded again on the user interface. + mode : str + This parameter is passed down to the object returned by this function. Can be one of `render_runtime` / `refresh` / `render` + + Returns + ------- + dict + A dictionary of the form : + ```python + { + "user": user_data, # any passed to `current.card.refresh` function + "components": component_dict, # all rendered REALTIME_UPDATABLE components + "render_seq": seq, + # `render_seq` is a counter that is incremented every time `render_runtime` is called. + # If a metaflow card has a RELOAD_POLICY_ALWAYS set then the reload token will be set to this value + # so that the card reload on the UI everytime `render_runtime` is called. + "component_update_ts": self.components.layout_last_changed_on, + # `component_update_ts` is the timestamp of the last time the component array was modified. + # `component_update_ts` can get used by the `reload_content_token` to make decisions on weather to + # reload the card on the UI when component array has changed. + "mode": mode, + } + ``` + """ + seq = "final" if final else self._render_seq + # Extract all the runtime-updatable components as a dictionary + component_dict = {} + for component in self._components._realtime_updateable_components(): + rendered_comp = _render_card_component(component) + if rendered_comp is not None: + component_dict.update({component.component_id: rendered_comp}) + + # Verify _latest_user_data is json serializable + user_data = {} + if self._latest_user_data is not None and not _object_is_json_serializable( + self._latest_user_data + ): + self._warning( + "Data provided to `refresh` is not JSON serializable. It will be ignored." + ) + else: + user_data = self._latest_user_data + + return { + "user": user_data, + "components": component_dict, + "render_seq": seq, + "component_update_ts": self.components.layout_last_changed_on, + "mode": mode, + } + + def __iter__(self): + return iter(self._components) + + class CardComponentCollector: """ This class helps collect `MetaflowCardComponent`s during runtime execution @@ -42,25 +407,34 @@ class CardComponentCollector: - [x] by looking it up by its type, e.g. `current.card.get(type='pytorch')`. """ - def __init__(self, logger=None): + def __init__(self, logger=None, card_creator=None): from metaflow.metaflow_config import CARD_NO_WARNING - self._cards_components = ( + self._card_component_store = ( + # Each key in the dictionary is the UUID of an individual card. + # value is of type `CardComponentManager`, holding a list of MetaflowCardComponents for that particular card {} - ) # a dict with key as uuid and value as a list of MetaflowCardComponent. + ) self._cards_meta = ( {} ) # a `dict` of (card_uuid, `dict)` holding all metadata about all @card decorators on the `current` @step. self._card_id_map = {} # card_id to uuid map for all cards with ids self._logger = logger + self._card_creator = card_creator # `self._default_editable_card` holds the uuid of the card that is default editable. This card has access to `append`/`extend` methods of `self` self._default_editable_card = None - self._warned_once = {"__getitem__": {}, "append": False, "extend": False} + self._warned_once = { + "__getitem__": {}, + "append": False, + "extend": False, + "update": False, + "update_no_id": False, + } self._no_warnings = True if CARD_NO_WARNING else False @staticmethod def create_uuid(): - return str(uuid.uuid4()) + return str(uuid.uuid4()).replace("-", "") def _log(self, *args, **kwargs): if self._logger: @@ -70,9 +444,13 @@ def _add_card( self, card_type, card_id, + decorator_attributes, + card_options, editable=False, customize=False, suppress_warnings=False, + runtime_card=False, + refresh_interval=5, ): """ This function helps collect cards from all the card decorators. @@ -95,9 +473,24 @@ def _add_card( editable=editable, customize=customize, suppress_warnings=suppress_warnings, + runtime_card=runtime_card, + decorator_attributes=decorator_attributes, + card_options=card_options, + refresh_interval=refresh_interval, ) self._cards_meta[card_uuid] = card_metadata - self._cards_components[card_uuid] = [] + self._card_component_store[card_uuid] = CardComponentManager( + card_uuid, + decorator_attributes, + self._card_creator, + components=None, + logger=self._logger, + no_warnings=self._no_warnings, + user_set_card_id=card_id, + runtime_card=runtime_card, + card_options=card_options, + refresh_interval=refresh_interval, + ) return card_metadata def _warning(self, message): @@ -107,9 +500,9 @@ def _warning(self, message): def _add_warning_to_cards(self, warn_msg): if self._no_warnings: return - for card_id in self._cards_components: + for card_id in self._card_component_store: if not self._cards_meta[card_id]["suppress_warnings"]: - self._cards_components[card_id].append(WarningComponent(warn_msg)) + self._card_component_store[card_id].append(WarningComponent(warn_msg)) def get(self, type=None): """`get` @@ -128,7 +521,7 @@ def get(self, type=None): for card_meta in self._cards_meta.values() if card_meta["type"] == card_type ] - return [self._cards_components[uuid] for uuid in card_uuids] + return [self._card_component_store[uuid] for uuid in card_uuids] def _finalize(self): """ @@ -229,13 +622,13 @@ def __getitem__(self, key): Returns ------- - CardComponentCollector + CardComponentManager An object with `append` and `extend` calls which allow you to add components to the chosen card. """ if key in self._card_id_map: card_uuid = self._card_id_map[key] - return self._cards_components[card_uuid] + return self._card_component_store[card_uuid] if key not in self._warned_once["__getitem__"]: _warn_msg = [ "`current.card['%s']` is not present. Please set the `id` argument in @card to '%s' to access `current.card['%s']`." @@ -246,6 +639,7 @@ def __getitem__(self, key): self._warning(" ".join(_warn_msg)) self._add_warning_to_cards("\n".join(_warn_msg)) self._warned_once["__getitem__"][key] = True + return [] def __setitem__(self, key, value): @@ -263,7 +657,7 @@ def __setitem__(self, key, value): key: str Card ID. - value: List[CardComponent] + value: List[MetaflowCardComponent] List of card components to assign to this card. """ if key in self._card_id_map: @@ -275,7 +669,18 @@ def __setitem__(self, key, value): ) self._warning(_warning_msg) return - self._cards_components[card_uuid] = value + self._card_component_store[card_uuid] = CardComponentManager( + card_uuid, + self._cards_meta[card_uuid]["decorator_attributes"], + self._card_creator, + components=value, + logger=self._logger, + no_warnings=self._no_warnings, + user_set_card_id=key, + card_options=self._cards_meta[card_uuid]["card_options"], + runtime_card=self._cards_meta[card_uuid]["runtime_card"], + refresh_interval=self._cards_meta[card_uuid]["refresh_interval"], + ) return self._warning( @@ -283,18 +688,18 @@ def __setitem__(self, key, value): % (key, key, key) ) - def append(self, component): + def append(self, component, id=None): """ Appends a component to the current card. Parameters ---------- - component : CardComponent + component : MetaflowCardComponent Card component to add to this card. """ if self._default_editable_card is None: if ( - len(self._cards_components) == 1 + len(self._card_component_store) == 1 ): # if there is one card which is not the _default_editable_card then the card is not editable card_type = list(self._cards_meta.values())[0]["type"] if list(self._cards_meta.values())[0]["exists"]: @@ -324,7 +729,7 @@ def append(self, component): self._warned_once["append"] = True return - self._cards_components[self._default_editable_card].append(component) + self._card_component_store[self._default_editable_card].append(component, id=id) def extend(self, components): """ @@ -332,12 +737,12 @@ def extend(self, components): Parameters ---------- - component : Iterator[CardComponent] + component : Iterator[MetaflowCardComponent] Card components to add to this card. """ if self._default_editable_card is None: # if there is one card which is not the _default_editable_card then the card is not editable - if len(self._cards_components) == 1: + if len(self._card_component_store) == 1: card_type = list(self._cards_meta.values())[0]["type"] _warning_msg = [ "Card of type `%s` is not an editable card." % card_type, @@ -357,7 +762,52 @@ def extend(self, components): return - self._cards_components[self._default_editable_card].extend(components) + self._card_component_store[self._default_editable_card].extend(components) + + @property + def components(self): + # FIXME: document + if self._default_editable_card is None: + if len(self._card_component_store) == 1: + card_type = list(self._cards_meta.values())[0]["type"] + _warning_msg = [ + "Card of type `%s` is not an editable card." % card_type, + "Components list will not be updated and `current.card.components` will not work for any call during this runtime execution.", + "Please use an editable card", # todo : link to documentation + ] + else: + _warning_msg = [ + "`current.card.components` cannot disambiguate between multiple @card decorators.", + "Components list will not be accessible and `current.card.components` will not work for any call during this runtime execution.", + "To fix this set the `id` argument in all @card when using multiple @card decorators over a single @step and reference `current.card[ID].components`", # todo : Add Link to documentation + "to update/access the appropriate card component.", + ] + if not self._warned_once["components"]: + self._warning(" ".join(_warning_msg)) + self._warned_once["components"] = True + return + + return self._card_component_store[self._default_editable_card].components + + def clear(self): + # FIXME: document + if self._default_editable_card is not None: + self._card_component_store[self._default_editable_card].clear() + + def refresh(self, *args, **kwargs): + # FIXME: document + if self._default_editable_card is not None: + self._card_component_store[self._default_editable_card].refresh( + *args, **kwargs + ) + + def _get_latest_data(self, card_uuid, final=False, mode=None): + """ + Returns latest data so it can be used in the final render() call + """ + return self._card_component_store[card_uuid]._get_latest_data( + final=final, mode=mode + ) def _serialize_components(self, card_uuid): """ @@ -366,36 +816,43 @@ def _serialize_components(self, card_uuid): don't render safely then we don't add them to the final list of serialized functions """ serialized_components = [] - if card_uuid not in self._cards_components: + if card_uuid not in self._card_component_store: return [] has_user_components = any( [ issubclass(type(component), UserComponent) - for component in self._cards_components[card_uuid] + for component in self._card_component_store[card_uuid] ] ) - for component in self._cards_components[card_uuid]: - if not issubclass(type(component), MetaflowCardComponent): - continue - try: - rendered_obj = component.render() - except: + for component in self._card_component_store[card_uuid]: + rendered_obj = _render_card_component(component) + if rendered_obj is None: continue - else: - if not (type(rendered_obj) == str or type(rendered_obj) == dict): - continue - else: - # Since `UserComponent`s are safely_rendered using render_tools.py - # we don't need to check JSON serialization as @render_tools.render_safely - # decorator ensures this check so there is no need to re-serialize - if not issubclass(type(component), UserComponent): - try: # check if rendered object is json serializable. - json.dumps(rendered_obj) - except (TypeError, OverflowError) as e: - continue - serialized_components.append(rendered_obj) + serialized_components.append(rendered_obj) if has_user_components and len(serialized_components) > 0: serialized_components = [ SectionComponent(contents=serialized_components).render() ] return serialized_components + + +def _render_card_component(component): + if not _component_is_valid(component): + return None + try: + rendered_obj = component.render() + except: + return None + else: + if not (type(rendered_obj) == str or type(rendered_obj) == dict): + return None + else: + # Since `UserComponent`s are safely_rendered using render_tools.py + # we don't need to check JSON serialization as @render_tools.render_safely + # decorator ensures this check so there is no need to re-serialize + if not issubclass(type(component), UserComponent): + try: # check if rendered object is json serializable. + json.dumps(rendered_obj) + except (TypeError, OverflowError) as e: + return None + return rendered_obj diff --git a/metaflow/plugins/cards/exception.py b/metaflow/plugins/cards/exception.py index 0b9188b4475..fdc96f86e2f 100644 --- a/metaflow/plugins/cards/exception.py +++ b/metaflow/plugins/cards/exception.py @@ -116,7 +116,7 @@ def __init__(self, task): super(UnresolvableDatastoreException, self).__init__(msg) -class IncorrectArguementException(MetaflowException): +class IncorrectArgumentException(MetaflowException): headline = ( "`get_cards` function requires a `Task` object or pathspec as an argument" ) @@ -138,3 +138,22 @@ def __init__(self, pthspec): % pthspec ) super().__init__(msg=msg, lineno=None) + + +class ComponentOverwriteNotSupportedException(MetaflowException): + headline = "Component overwrite is not supported" + + def __init__(self, component_id, card_id, card_type): + id_str = "" + if card_id is not None: + id_str = "id='%s'" % card_id + msg = ( + "Card component overwrite is not supported. " + "Component with id %s already exists in the @card(type='%s', %s). \n" + "Instead of calling `current.card.components[ID] = MyComponent`. " + "You can overwrite the entire component Array by calling " + "`current.card.components = [MyComponent]`" + ) % (component_id, card_type, id_str) + super().__init__( + msg=msg, + ) diff --git a/metaflow/plugins/cards/metadata.py b/metaflow/plugins/cards/metadata.py new file mode 100644 index 00000000000..4b6d29fd277 --- /dev/null +++ b/metaflow/plugins/cards/metadata.py @@ -0,0 +1,22 @@ +import json +from metaflow.metadata_provider import MetaDatum + + +def _save_metadata( + metadata_provider, + run_id, + step_name, + task_id, + attempt_id, + card_uuid, + save_metadata, +): + entries = [ + MetaDatum( + field=card_uuid, + value=json.dumps(save_metadata), + type="card-info", + tags=["attempt_id:{0}".format(attempt_id)], + ) + ] + metadata_provider.register_metadata(run_id, step_name, task_id, entries) diff --git a/metaflow/plugins/cards/ui/.eslintrc.cjs b/metaflow/plugins/cards/ui/.eslintrc.cjs new file mode 100644 index 00000000000..a1c19c48e6b --- /dev/null +++ b/metaflow/plugins/cards/ui/.eslintrc.cjs @@ -0,0 +1,35 @@ +// TypeScript Project + +module.exports = { + root: true, + extends: [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:svelte/recommended", + ], + parser: "@typescript-eslint/parser", + plugins: ["@typescript-eslint", "prettier"], + parserOptions: { + sourceType: "module", + ecmaVersion: 2020, + extraFileExtensions: [".svelte"], + }, + env: { + browser: true, + es2017: true, + node: true, + }, + overrides: [ + { + files: ["*.svelte"], + parser: "svelte-eslint-parser", + parserOptions: { + parser: "@typescript-eslint/parser", + }, + }, + ], + rules: { + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-unsafe-assignment": "off", + }, +}; diff --git a/metaflow/plugins/cards/ui/.eslintrc.js b/metaflow/plugins/cards/ui/.eslintrc.js deleted file mode 100644 index ab5386457eb..00000000000 --- a/metaflow/plugins/cards/ui/.eslintrc.js +++ /dev/null @@ -1,36 +0,0 @@ -module.exports = { - parser: "@typescript-eslint/parser", // add the TypeScript parser - parserOptions: { - tsconfigRootDir: __dirname, - project: ["./tsconfig.json"], - extraFileExtensions: [".svelte"], - }, - env: { - browser: true, - }, - extends: [ - "eslint:recommended", - "plugin:@typescript-eslint/recommended", - "plugin:@typescript-eslint/recommended-requiring-type-checking", - ], - plugins: [ - "svelte3", - "@typescript-eslint", // add the TypeScript plugin - ], - ignorePatterns: ["**/*.js"], - overrides: [ - { - files: ["*.svelte"], - processor: "svelte3/svelte3", - }, - ], - rules: { - "@typescript-eslint/no-unsafe-member-access": 0, - "@typescript-eslint/no-explicit-any": 0, - "@typescript-eslint/no-unsafe-argument": 0, - "@typescript-eslint/no-unsafe-call": 0, - }, - settings: { - "svelte3/typescript": () => require("typescript"), - }, -}; diff --git a/metaflow/plugins/cards/ui/.gitignore b/metaflow/plugins/cards/ui/.gitignore new file mode 100644 index 00000000000..31d93b23fc2 --- /dev/null +++ b/metaflow/plugins/cards/ui/.gitignore @@ -0,0 +1,581 @@ +.yarn + +# If you prefer the allow list template instead of the deny list, see community template: +# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore +# +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + +# Go workspace file +go.work +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Tes# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencit files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml +es or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ diff --git a/metaflow/plugins/cards/ui/public/card-example.json b/metaflow/plugins/cards/ui/demo/card-example.json similarity index 95% rename from metaflow/plugins/cards/ui/public/card-example.json rename to metaflow/plugins/cards/ui/demo/card-example.json index 91c7bdc609a..52c2d2b51ff 100644 --- a/metaflow/plugins/cards/ui/public/card-example.json +++ b/metaflow/plugins/cards/ui/demo/card-example.json @@ -19,7 +19,21 @@ "contents": [ { "type": "section", - "title": "Vertical Table", + "title": "Progress Bar", + "contents": [ + { + "type": "progressBar", + "id": "progress1", + "max": 100, + "value": 55, + "label": "Epochs", + "details": "elapsed 00:35 left: 01:05, 1.00 iters/sec" + } + ] + }, + { + "type": "section", + "title": "Table with Progress Bar", "contents": [ { "type": "table", @@ -36,7 +50,29 @@ [ 2, "start", - "completed", + { + "type": "progressBar", + "id": "progress1", + "max": 100, + "value": 55, + "label": "Epochs", + "details": "elapsed 00:35 left: 01:05, 1.00 iters/sec" + }, + "10-20-2021 05:18:34", + "10-20-2021 05:18:34", + "0.5s" + ], + [ + 3, + "middle", + { + "type": "progressBar", + "id": "progress1", + "max": 100, + "value": 35, + "label": "Epochs", + "details": "elapsed 00:35 left: 01:05, 1.00 iters/sec" + }, "10-20-2021 05:18:34", "10-20-2021 05:18:34", "0.5s" @@ -52,120 +88,120 @@ "contents": [ { "type": "artifacts", - + "data": [ { - "name":null, - "type":"tuple", - "data":"(1,2,3)" + "name": null, + "type": "tuple", + "data": "(1,2,3)" }, { - "name":"file_param", + "name": "file_param", "type": "NoneType", "data": "None" }, { - "name":"py_set", + "name": "py_set", "type": "set", "data": "{1, 2, 3}" }, { - "name":"img_jpg", + "name": "img_jpg", "type": "bytes", "data": "b'\\xff\\xd8\\xff\\xe0\\x00\\x10JFIF\\x00\\x01\\x02\\x01\\x...\\x80\\x80\\x80\\x80\\x80\\x80\\x80\\x80\\x80\\x83\\xff\\xd9'" }, { - "name":"py_frozenset", + "name": "py_frozenset", "type": "frozenset", "data": "frozenset({4, 5, 6})" }, { - "name":"py_bytearray", + "name": "py_bytearray", "type": "bytearray", "data": "bytearray(b'\\xf0\\xf1\\xf2')" }, { - "name":"custom_class", + "name": "custom_class", "type": "__main__.CustomClass", "data": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa...aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" }, { - "name":"py_dict", + "name": "py_dict", "type": "dict", "data": "{'a': 1, 'null': None, True: False}" }, { - "name":"py_float", + "name": "py_float", "type": "float", "data": "3.141592653589793" }, { - "name":"large_dict_large_val", + "name": "large_dict_large_val", "type": "dict", "data": "{'constitution': 'Provided by USConstitution.net\\n---------------...ion of Representatives shall\\nhave intervened.\\n'}" }, { - "name":"name", + "name": "name", "type": "str", "data": "'DefaultCardFlow'" }, { - "name":"large_dict", + "name": "large_dict", "type": "dict", "data": "{'clubs': ['ace', 2, 3, 4, 5, 6, 7, 8, 9, 'jack', 'queen', 'king'], 'diamonds': ['ace', 2, 3, 4, 5, 6, 7, 8, 9, 'jack', 'queen', 'king'], 'hearts': ['ace', 2, 3, 4, 5, 6, 7, 8, 9, 'jack', 'queen', 'king'], 'spades': ['ace', 2, 3, 4, 5, 6, 7, 8, 9, 'jack', 'queen', 'king']}" }, { - "name":"np_array", + "name": "np_array", "type": "numpy.ndarray", "data": "array([ 0, 1, 2, ..., 9999997, 9999998, 9999999],\n dtype=uint64)" }, { - "name":"large_dict_many_keys", + "name": "large_dict_many_keys", "type": "dict", "data": "{'00001cdb-f3cf-4a80-9d50-a5685697a9c3': 1636352431.135456, '00007105-19d0-4c25-bd18-c4b9f0f72326': 1636352431.09861, '00007fe7-87f5-499a-84a0-95bc7829350f': 1636352430.95102, '00009603-ec57-42ac-8ae1-d190d75ec8fd': 1636352431.879671, ...}" }, { - "name":"py_str", + "name": "py_str", "type": "str", "data": "'εˆΊθΊ«γ―ηΎŽε‘³γ—γ„'" }, { - "name":"float_param", + "name": "float_param", "type": "float", "data": "3.141592653589793" }, { - "name":"py_complex", + "name": "py_complex", "type": "complex", "data": "(1+2j)" }, { - "name":"str_param", + "name": "str_param", "type": "str", "data": "'εˆΊθΊ«γ―ηΎŽε‘³γ—γ„'" }, { - "name":"json_param", + "name": "json_param", "type": "str", "data": "'{\"states\": {[{\"CA\", 0}, {\"NY\", 1}]}'" }, { - "name":"large_int", + "name": "large_int", "type": "int", "data": "36893488147419103232" }, { - "name":"large_str", + "name": "large_str", "type": "str", "data": "'Provided by USConstitution.net\\n---------------...ion of Representatives shall\\nhave intervened.\\n'" }, { - "name":"exception", + "name": "exception", "type": "Exception", "data": "Exception('This is an exception!')" }, { - "name":"py_list", + "name": "py_list", "type": "list", "data": "[1, 2, 3]" } @@ -1051,7 +1087,7 @@ "contents": [ { "type": "log", - "data": "Card of type default is unable to be rendered with arguements {'only_repr': True}.\nStack trace : Traceback (most recent call last):\n File \"/Users/valay/Documents/Outerbounds-Code-Workspace/metaflow/metaflow/plugins/cards/card_cli.py\", line 407, in create\n rendered_info = render_card(mf_card, task, timeout_value=timeout)\n File \"/Users/valay/Documents/Outerbounds-Code-Workspace/metaflow/metaflow/plugins/cards/card_cli.py\", line 283, in render_card\n rendered_info = mf_card.render(task)\n File \"/Users/valay/Documents/Outerbounds-Code-Workspace/metaflow/metaflow/plugins/cards/card_modules/basic.py\", line 461, in render\n raise Exception(\"Hellow There\")\nException: Hellow There\n" + "data": "Card of type default is unable to be rendered with arguments {'only_repr': True}.\nStack trace : Traceback (most recent call last):\n File \"/Users/valay/Documents/Outerbounds-Code-Workspace/metaflow/metaflow/plugins/cards/card_cli.py\", line 407, in create\n rendered_info = render_card(mf_card, task, timeout_value=timeout)\n File \"/Users/valay/Documents/Outerbounds-Code-Workspace/metaflow/metaflow/plugins/cards/card_cli.py\", line 283, in render_card\n rendered_info = mf_card.render(task)\n File \"/Users/valay/Documents/Outerbounds-Code-Workspace/metaflow/metaflow/plugins/cards/card_modules/basic.py\", line 461, in render\n raise Exception(\"Hellow There\")\nException: Hellow There\n" } ] }, diff --git a/metaflow/plugins/cards/ui/demo/index.html b/metaflow/plugins/cards/ui/demo/index.html new file mode 100644 index 00000000000..3d651629232 --- /dev/null +++ b/metaflow/plugins/cards/ui/demo/index.html @@ -0,0 +1,38 @@ + + + + + + + Metaflow Card Example + + + + + + +
    + + + + diff --git a/metaflow/plugins/cards/ui/package-lock.json b/metaflow/plugins/cards/ui/package-lock.json new file mode 100644 index 00000000000..f376b99ef55 --- /dev/null +++ b/metaflow/plugins/cards/ui/package-lock.json @@ -0,0 +1,6070 @@ +{ + "name": "svelte-app", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "svelte-app", + "version": "1.0.0", + "license": "UNLICENSED", + "dependencies": { + "@iconify/svelte": "^3.1.4", + "svelte-markdown": "^0.4.0", + "svelte-vega": "^2.1.0", + "vega": "^5.26.1", + "vega-lite": "^5.16.3" + }, + "devDependencies": { + "@sveltejs/vite-plugin-svelte": "^3.0.1", + "@tsconfig/svelte": "^5.0.2", + "@types/node": "^20.10.4", + "@typescript-eslint/eslint-plugin": "^6.14.0", + "@typescript-eslint/parser": "^6.14.0", + "cypress": "^13.6.1", + "cypress-svelte-unit-test": "^3.3.4", + "eslint": "^8.55.0", + "eslint-plugin-prettier": "^5.0.1", + "eslint-plugin-svelte": "^2.35.1", + "postcss": "^8.4.32", + "prettier": "^3.1.1", + "svelte": "^4.2.19", + "svelte-check": "^3.6.2", + "svelte-preprocess": "^5.1.2", + "tslib": "^2.6.2", + "typescript": "^5.3.3", + "vite": "^5.4.19" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@bahmutov/cy-rollup": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@bahmutov/cy-rollup/-/cy-rollup-2.0.0.tgz", + "integrity": "sha512-KJlvqB3U1nvKSq7djkXcvM4WpBLP6HEWKKzChI8s/aw0pYm0gVc+sphpyz5CmGpsNE9+/1QB6yoDfTe9Q97SJw==", + "dev": true, + "dependencies": { + "debug": "4.1.1" + }, + "peerDependencies": { + "rollup": "2" + } + }, + "node_modules/@bahmutov/cy-rollup/node_modules/debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@cypress/request": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.8.tgz", + "integrity": "sha512-h0NFgh1mJmm1nr4jCwkGHwKneVYKghUyWe6TMNrk0B9zsjAJxpg8C4/+BAcmLgCPa1vj1V8rNUaILl+zYRUWBQ==", + "dev": true, + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~4.0.0", + "http-signature": "~1.4.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "performance-now": "^2.1.0", + "qs": "6.14.0", + "safe-buffer": "^5.1.2", + "tough-cookie": "^5.0.0", + "tunnel-agent": "^0.6.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@cypress/xvfb": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz", + "integrity": "sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==", + "dev": true, + "dependencies": { + "debug": "^3.1.0", + "lodash.once": "^4.1.1" + } + }, + "node_modules/@cypress/xvfb/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", + "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/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/@eslint/eslintrc/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/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/config-array/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/@humanwhocodes/config-array/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/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true + }, + "node_modules/@iconify/svelte": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@iconify/svelte/-/svelte-3.1.6.tgz", + "integrity": "sha512-yLSrlkOx5J6xXU5GDLPBV/MdVBVEZhd36onfqSbxQobp1XBoWQbMPLNZyCAmTKCPnmzXSowGy79agl8FQ3kj6A==", + "dependencies": { + "@iconify/types": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/cyberalien" + }, + "peerDependencies": { + "svelte": "*" + } + }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==" + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgr/core": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", + "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.9.tgz", + "integrity": "sha512-qZdlImWXur0CFakn2BJ2znJOdqYZKiedEPEVNTBrpfPjc/YuTGcaYZcdmNFTkUj3DU0ZM/AElcM8Ybww3xVLzA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.9.tgz", + "integrity": "sha512-4KW7P53h6HtJf5Y608T1ISKvNIYLWRKMvfnG0c44M6In4DQVU58HZFEVhWINDZKp7FZps98G3gxwC1sb0wXUUg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.9.tgz", + "integrity": "sha512-0CY3/K54slrzLDjOA7TOjN1NuLKERBgk9nY5V34mhmuu673YNb+7ghaDUs6N0ujXR7fz5XaS5Aa6d2TNxZd0OQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.9.tgz", + "integrity": "sha512-eOojSEAi/acnsJVYRxnMkPFqcxSMFfrw7r2iD9Q32SGkb/Q9FpUY1UlAu1DH9T7j++gZ0lHjnm4OyH2vCI7l7Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.9.tgz", + "integrity": "sha512-2lzjQPJbN5UnHm7bHIUKFMulGTQwdvOkouJDpPysJS+QFBGDJqcfh+CxxtG23Ik/9tEvnebQiylYoazFMAgrYw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.9.tgz", + "integrity": "sha512-SLl0hi2Ah2H7xQYd6Qaiu01kFPzQ+hqvdYSoOtHYg/zCIFs6t8sV95kaoqjzjFwuYQLtOI0RZre/Ke0nPaQV+g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.9.tgz", + "integrity": "sha512-88I+D3TeKItrw+Y/2ud4Tw0+3CxQ2kLgu3QvrogZ0OfkmX/DEppehus7L3TS2Q4lpB+hYyxhkQiYPJ6Mf5/dPg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.9.tgz", + "integrity": "sha512-3qyfWljSFHi9zH0KgtEPG4cBXHDFhwD8kwg6xLfHQ0IWuH9crp005GfoUUh/6w9/FWGBwEHg3lxK1iHRN1MFlA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.9.tgz", + "integrity": "sha512-6TZjPHjKZUQKmVKMUowF3ewHxctrRR09eYyvT5eFv8w/fXarEra83A2mHTVJLA5xU91aCNOUnM+DWFMSbQ0Nxw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.9.tgz", + "integrity": "sha512-LD2fytxZJZ6xzOKnMbIpgzFOuIKlxVOpiMAXawsAZ2mHBPEYOnLRK5TTEsID6z4eM23DuO88X0Tq1mErHMVq0A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.9.tgz", + "integrity": "sha512-dRAgTfDsn0TE0HI6cmo13hemKpVHOEyeciGtvlBTkpx/F65kTvShtY/EVyZEIfxFkV5JJTuQ9tP5HGBS0hfxIg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.9.tgz", + "integrity": "sha512-PHcNOAEhkoMSQtMf+rJofwisZqaU8iQ8EaSps58f5HYll9EAY5BSErCZ8qBDMVbq88h4UxaNPlbrKqfWP8RfJA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.9.tgz", + "integrity": "sha512-Z2i0Uy5G96KBYKjeQFKbbsB54xFOL5/y1P5wNBsbXB8yE+At3oh0DVMjQVzCJRJSfReiB2tX8T6HUFZ2k8iaKg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.9.tgz", + "integrity": "sha512-U+5SwTMoeYXoDzJX5dhDTxRltSrIax8KWwfaaYcynuJw8mT33W7oOgz0a+AaXtGuvhzTr2tVKh5UO8GVANTxyQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.9.tgz", + "integrity": "sha512-FwBHNSOjUTQLP4MG7y6rR6qbGw4MFeQnIBrMe161QGaQoBQLqSUEKlHIiVgF3g/mb3lxlxzJOpIBhaP+C+KP2A==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.9.tgz", + "integrity": "sha512-cYRpV4650z2I3/s6+5/LONkjIz8MBeqrk+vPXV10ORBnshpn8S32bPqQ2Utv39jCiDcO2eJTuSlPXpnvmaIgRA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.9.tgz", + "integrity": "sha512-z4mQK9dAN6byRA/vsSgQiPeuO63wdiDxZ9yg9iyX2QTzKuQM7T4xlBoeUP/J8uiFkqxkcWndWi+W7bXdPbt27Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.9.tgz", + "integrity": "sha512-KB48mPtaoHy1AwDNkAJfHXvHp24H0ryZog28spEs0V48l3H1fr4i37tiyHsgKZJnCmvxsbATdZGBpbmxTE3a9w==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.9.tgz", + "integrity": "sha512-AyleYRPU7+rgkMWbEh71fQlrzRfeP6SyMnRf9XX4fCdDPAJumdSBqYEcWPMzVQ4ScAl7E4oFfK0GUVn77xSwbw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@sveltejs/vite-plugin-svelte": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-3.1.2.tgz", + "integrity": "sha512-Txsm1tJvtiYeLUVRNqxZGKR/mI+CzuIQuc2gn+YCs9rMTowpNZ2Nqt53JdL8KF9bLhAf2ruR/dr9eZCwdTriRA==", + "dev": true, + "dependencies": { + "@sveltejs/vite-plugin-svelte-inspector": "^2.1.0", + "debug": "^4.3.4", + "deepmerge": "^4.3.1", + "kleur": "^4.1.5", + "magic-string": "^0.30.10", + "svelte-hmr": "^0.16.0", + "vitefu": "^0.2.5" + }, + "engines": { + "node": "^18.0.0 || >=20" + }, + "peerDependencies": { + "svelte": "^4.0.0 || ^5.0.0-next.0", + "vite": "^5.0.0" + } + }, + "node_modules/@sveltejs/vite-plugin-svelte-inspector": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-2.1.0.tgz", + "integrity": "sha512-9QX28IymvBlSCqsCll5t0kQVxipsfhFFL+L2t3nTWfXnddYwxBuAEtTtlaVQpRz9c37BhJjltSeY4AJSC03SSg==", + "dev": true, + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.0.0 || >=20" + }, + "peerDependencies": { + "@sveltejs/vite-plugin-svelte": "^3.0.0", + "svelte": "^4.0.0 || ^5.0.0-next.0", + "vite": "^5.0.0" + } + }, + "node_modules/@tsconfig/svelte": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/svelte/-/svelte-5.0.4.tgz", + "integrity": "sha512-BV9NplVgLmSi4mwKzD8BD/NQ8erOY/nUE/GpgWe2ckx+wIQF5RyRirn/QsSSCPeulVpc3RA/iJt6DpfTIZps0Q==", + "dev": true + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==" + }, + "node_modules/@types/geojson": { + "version": "7946.0.4", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.4.tgz", + "integrity": "sha512-MHmwBtCb7OCv1DSivz2UNJXPGU/1btAWRKlqJ2saEhVJkpkvqHMMaOpKg0v4sAbDWSQekHGvPVMM8nQ+Jen03Q==" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/marked": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@types/marked/-/marked-5.0.2.tgz", + "integrity": "sha512-OucS4KMHhFzhz27KxmWg7J+kIYqyqoW5kdIEI319hqARQQUTqhao3M/F+uFnDXD0Rg72iDDZxZNxq5gvctmLlg==" + }, + "node_modules/@types/node": { + "version": "20.17.23", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.23.tgz", + "integrity": "sha512-8PCGZ1ZJbEZuYNTMqywO+Sj4vSKjSjT6Ua+6RFOYlEvIvKQABPtrNkoVSLSKDb4obYcMhspVKmsw8Cm10NFRUg==", + "dev": true, + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/@types/pug": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.10.tgz", + "integrity": "sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==", + "dev": true + }, + "node_modules/@types/semver": { + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", + "dev": true + }, + "node_modules/@types/sinonjs__fake-timers": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz", + "integrity": "sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==", + "dev": true + }, + "node_modules/@types/sizzle": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.9.tgz", + "integrity": "sha512-xzLEyKB50yqCUPUJkIsrVvoWNfFUbIZI+RspLWt8u+tIW/BetMBZtgV2LY/2o+tYH8dRvQ+eoPf3NdhQCcLE2w==", + "dev": true + }, + "node_modules/@types/yauzl": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "dev": true, + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", + "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/type-utils": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", + "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", + "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", + "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", + "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", + "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", + "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", + "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true + }, + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "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-escapes/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/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/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/arch": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", + "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "dev": true, + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/aws4": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz", + "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==", + "dev": true + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "engines": { + "node": ">= 0.4" + } + }, + "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/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "dev": true, + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/blob-util": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/blob-util/-/blob-util-2.0.2.tgz", + "integrity": "sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==", + "dev": true + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", + "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/cachedir": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.4.0.tgz", + "integrity": "sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", + "dev": true + }, + "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/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/check-more-types": { + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", + "integrity": "sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/ci-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.1.0.tgz", + "integrity": "sha512-HutrvTNsF48wnxkzERIXOe5/mlcfFcbfCmwcg6CJnizbSue78AbDt+1cgl26zwn61WFxhcPykPfZrbqjGmBb4A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-table3": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", + "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "node_modules/cli-truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "dev": true, + "dependencies": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/code-red": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/code-red/-/code-red-1.0.4.tgz", + "integrity": "sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15", + "@types/estree": "^1.0.1", + "acorn": "^8.10.0", + "estree-walker": "^3.0.3", + "periscopic": "^3.1.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "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==" + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "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/commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/common-tags": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", + "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, + "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/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-tree": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "dependencies": { + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cypress": { + "version": "13.17.0", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.17.0.tgz", + "integrity": "sha512-5xWkaPurwkIljojFidhw8lFScyxhtiFHl/i/3zov+1Z5CmY4t9tjIdvSXfu82Y3w7wt0uR9KkucbhkVvJZLQSA==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@cypress/request": "^3.0.6", + "@cypress/xvfb": "^1.2.4", + "@types/sinonjs__fake-timers": "8.1.1", + "@types/sizzle": "^2.3.2", + "arch": "^2.2.0", + "blob-util": "^2.0.2", + "bluebird": "^3.7.2", + "buffer": "^5.7.1", + "cachedir": "^2.3.0", + "chalk": "^4.1.0", + "check-more-types": "^2.24.0", + "ci-info": "^4.0.0", + "cli-cursor": "^3.1.0", + "cli-table3": "~0.6.1", + "commander": "^6.2.1", + "common-tags": "^1.8.0", + "dayjs": "^1.10.4", + "debug": "^4.3.4", + "enquirer": "^2.3.6", + "eventemitter2": "6.4.7", + "execa": "4.1.0", + "executable": "^4.1.1", + "extract-zip": "2.0.1", + "figures": "^3.2.0", + "fs-extra": "^9.1.0", + "getos": "^3.2.1", + "is-installed-globally": "~0.4.0", + "lazy-ass": "^1.6.0", + "listr2": "^3.8.3", + "lodash": "^4.17.21", + "log-symbols": "^4.0.0", + "minimist": "^1.2.8", + "ospath": "^1.2.2", + "pretty-bytes": "^5.6.0", + "process": "^0.11.10", + "proxy-from-env": "1.0.0", + "request-progress": "^3.0.0", + "semver": "^7.5.3", + "supports-color": "^8.1.1", + "tmp": "~0.2.3", + "tree-kill": "1.2.2", + "untildify": "^4.0.0", + "yauzl": "^2.10.0" + }, + "bin": { + "cypress": "bin/cypress" + }, + "engines": { + "node": "^16.0.0 || ^18.0.0 || >=20.0.0" + } + }, + "node_modules/cypress-svelte-unit-test": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/cypress-svelte-unit-test/-/cypress-svelte-unit-test-3.3.4.tgz", + "integrity": "sha512-Ce93Tz7YuqnQnyIlOSoieL3tzorUg+/R/sFxNU2v4t1rHI5oCyQ4Q1P2nydUGyJaGYrNXDwTP1CaGpw8yIR++g==", + "dev": true, + "dependencies": { + "@bahmutov/cy-rollup": "2.0.0", + "unfetch": "4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", + "dependencies": { + "delaunator": "5" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "dependencies": { + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + }, + "bin": { + "csv2json": "bin/dsv2json.js", + "csv2tsv": "bin/dsv2dsv.js", + "dsv2dsv": "bin/dsv2dsv.js", + "dsv2json": "bin/dsv2json.js", + "json2csv": "bin/json2dsv.js", + "json2dsv": "bin/json2dsv.js", + "json2tsv": "bin/json2dsv.js", + "tsv2csv": "bin/dsv2dsv.js", + "tsv2json": "bin/dsv2json.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", + "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", + "dependencies": { + "d3-array": "2.5.0 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo-projection": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/d3-geo-projection/-/d3-geo-projection-4.0.0.tgz", + "integrity": "sha512-p0bK60CEzph1iqmnxut7d/1kyTmm3UWtPlwdkM31AU+LW+BXazd5zJdoCn7VFxNCHXRngPHRnsNn5uGjLRGndg==", + "dependencies": { + "commander": "7", + "d3-array": "1 - 3", + "d3-geo": "1.12.0 - 3" + }, + "bin": { + "geo2svg": "bin/geo2svg.js", + "geograticule": "bin/geograticule.js", + "geoproject": "bin/geoproject.js", + "geoquantize": "bin/geoquantize.js", + "geostitch": "bin/geostitch.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo-projection/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/d3-hierarchy": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", + "dependencies": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "dev": true, + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", + "dev": true + }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/delaunator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz", + "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==", + "dependencies": { + "robust-predicates": "^3.0.2" + } + }, + "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-indent": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", + "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "dev": true, + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/enquirer": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", + "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.1", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es6-promise": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", + "integrity": "sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==", + "dev": true + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-compat-utils": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.5.1.tgz", + "integrity": "sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==", + "dev": true, + "dependencies": { + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "eslint": ">=6.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.3.tgz", + "integrity": "sha512-qJ+y0FfCp/mQYQ/vWQ3s7eUlFEL4PyKfAJxsnYTJ4YT73nsJBWqmEpFryxV9OeUiqmsTsYJ5Y+KDNaeP31wrRw==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.9.1" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": "*", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-svelte": { + "version": "2.46.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-svelte/-/eslint-plugin-svelte-2.46.1.tgz", + "integrity": "sha512-7xYr2o4NID/f9OEYMqxsEQsCsj4KaMy4q5sANaKkAb6/QeCjYFxRmDm2S3YC3A3pl1kyPZ/syOx/i7LcWYSbIw==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@jridgewell/sourcemap-codec": "^1.4.15", + "eslint-compat-utils": "^0.5.1", + "esutils": "^2.0.3", + "known-css-properties": "^0.35.0", + "postcss": "^8.4.38", + "postcss-load-config": "^3.1.4", + "postcss-safe-parser": "^6.0.0", + "postcss-selector-parser": "^6.1.0", + "semver": "^7.6.2", + "svelte-eslint-parser": "^0.43.0" + }, + "engines": { + "node": "^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ota-meshi" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0-0 || ^9.0.0-0", + "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "svelte": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/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/eslint/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/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/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/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dependencies": { + "@types/estree": "^1.0.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/eventemitter2": { + "version": "6.4.7", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.7.tgz", + "integrity": "sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==", + "dev": true + }, + "node_modules/execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/executable": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz", + "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", + "dev": true, + "dependencies": { + "pify": "^2.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", + "dev": true, + "engines": [ + "node >=0.6.0" + ] + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-patch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-3.1.1.tgz", + "integrity": "sha512-vf6IHUX2SBcA+5/+4883dsIjpBTqmfBjmYiWK1savxQmFk4JfBMLa7ynTYOs1Rolp/T1betJxHiGD3g1Mn8lUQ==", + "peer": true + }, + "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/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/figures/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/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true + }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/form-data": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "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.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/getos": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/getos/-/getos-3.2.1.tgz", + "integrity": "sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==", + "dev": true, + "dependencies": { + "async": "^3.2.0" + } + }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "dev": true, + "dependencies": { + "assert-plus": "^1.0.0" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "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/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob/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/glob/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/global-dirs": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", + "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==", + "dev": true, + "dependencies": { + "ini": "2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/has-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/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-signature": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.4.0.tgz", + "integrity": "sha512-G5akfn7eKbpDN+8nPS/cb57YeA1jLTVxjpCj7tmm3QKPdyDy7T+qSC40e9ptydSWvkwjSXw1VbkpyEm39ukeAg==", + "dev": true, + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^2.0.2", + "sshpk": "^1.18.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true, + "engines": { + "node": ">=8.12.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==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "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/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "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/ini": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-installed-globally": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", + "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", + "dev": true, + "dependencies": { + "global-dirs": "^3.0.0", + "is-path-inside": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-reference": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz", + "integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==", + "dependencies": { + "@types/estree": "^1.0.6" + } + }, + "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/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "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/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", + "dev": true + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json-stringify-pretty-compact": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-4.0.0.tgz", + "integrity": "sha512-3CNZ2DnrpByG9Nqj6Xo8vqbjT4F6N+tb4Gb28ESAZjYZ5yqvmc56J+/kuIwkaAMOyblTQhUW7PxMkUb8Q36N3Q==" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsprim": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz", + "integrity": "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/known-css-properties": { + "version": "0.35.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.35.0.tgz", + "integrity": "sha512-a/RAk2BfKk+WFGhhOCAYqSiFLc34k8Mt/6NWRI4joER0EYUzXIcFivjjnoD3+XU1DggLn/tZc3DOAgke7l8a4A==", + "dev": true + }, + "node_modules/lazy-ass": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz", + "integrity": "sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==", + "dev": true, + "engines": { + "node": "> 0.8" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/listr2": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.14.0.tgz", + "integrity": "sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g==", + "dev": true, + "dependencies": { + "cli-truncate": "^2.1.0", + "colorette": "^2.0.16", + "log-update": "^4.0.0", + "p-map": "^4.0.0", + "rfdc": "^1.3.0", + "rxjs": "^7.5.1", + "through": "^2.3.8", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "enquirer": ">= 2.3.0 < 3" + }, + "peerDependenciesMeta": { + "enquirer": { + "optional": true + } + } + }, + "node_modules/locate-character": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", + "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==" + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "dev": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", + "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.3.0", + "cli-cursor": "^3.1.0", + "slice-ansi": "^4.0.0", + "wrap-ansi": "^6.2.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/marked": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-5.1.2.tgz", + "integrity": "sha512-ahRPGXJpjMjwSOlBoTMZAK7ATXkli5qCPxZ21TG44rx1KEo44bii4ekgTDQPNRQ4Kh7JMb9Ub1PVk1NxRSsorg==", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 16" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mdn-data": { + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==" + }, + "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/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "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/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/nanoid": { + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "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-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": 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/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ospath": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz", + "integrity": "sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==", + "dev": true + }, + "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": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "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-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true + }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", + "dev": true + }, + "node_modules/periscopic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz", + "integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^3.0.0", + "is-reference": "^3.0.0" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "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/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-load-config": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", + "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", + "dev": true, + "dependencies": { + "lilconfig": "^2.0.5", + "yaml": "^1.10.2" + }, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-safe-parser": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz", + "integrity": "sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==", + "dev": true, + "engines": { + "node": ">=12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.3.3" + } + }, + "node_modules/postcss-scss": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-4.0.9.tgz", + "integrity": "sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss-scss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.4.29" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", + "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/pretty-bytes": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/proxy-from-env": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", + "integrity": "sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A==", + "dev": true + }, + "node_modules/pump": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "dev": true, + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/request-progress": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz", + "integrity": "sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg==", + "dev": true, + "dependencies": { + "throttleit": "^1.0.0" + } + }, + "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==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/robust-predicates": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", + "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==" + }, + "node_modules/rollup": { + "version": "2.79.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz", + "integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==", + "dev": true, + "peer": true, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=10.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==" + }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/sade": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", + "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", + "dev": true, + "dependencies": { + "mri": "^1.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/sander": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/sander/-/sander-0.5.1.tgz", + "integrity": "sha512-3lVqBir7WuKDHGrKRDn/1Ye3kwpXaDOMsiRP1wd6wpZW56gJhsbp5RqQpA6JG/P+pkXizygnr1dKR8vzWaVsfA==", + "dev": true, + "dependencies": { + "es6-promise": "^3.1.2", + "graceful-fs": "^4.1.3", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.2" + } + }, + "node_modules/sander/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "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/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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/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/slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/sorcery": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/sorcery/-/sorcery-0.11.1.tgz", + "integrity": "sha512-o7npfeJE6wi6J9l0/5LKshFzZ2rMatRiCDwYeDQaOzqdzRJwALhX7mk/A/ecg6wjMu7wdZbmXfD2S/vpOg0bdQ==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.14", + "buffer-crc32": "^1.0.0", + "minimist": "^1.2.0", + "sander": "^0.5.0" + }, + "bin": { + "sorcery": "bin/sorcery" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sshpk": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", + "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", + "dev": true, + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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==", + "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==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "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-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/svelte": { + "version": "4.2.19", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.19.tgz", + "integrity": "sha512-IY1rnGr6izd10B0A8LqsBfmlT5OILVuZ7XsI0vdGPEvuonFV7NYEUK4dAkm9Zg2q0Um92kYjTpS1CAP3Nh/KWw==", + "dependencies": { + "@ampproject/remapping": "^2.2.1", + "@jridgewell/sourcemap-codec": "^1.4.15", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/estree": "^1.0.1", + "acorn": "^8.9.0", + "aria-query": "^5.3.0", + "axobject-query": "^4.0.0", + "code-red": "^1.0.3", + "css-tree": "^2.3.1", + "estree-walker": "^3.0.3", + "is-reference": "^3.0.1", + "locate-character": "^3.0.0", + "magic-string": "^0.30.4", + "periscopic": "^3.1.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/svelte-check": { + "version": "3.8.6", + "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-3.8.6.tgz", + "integrity": "sha512-ij0u4Lw/sOTREP13BdWZjiXD/BlHE6/e2e34XzmVmsp5IN4kVa3PWP65NM32JAgwjZlwBg/+JtiNV1MM8khu0Q==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.17", + "chokidar": "^3.4.1", + "picocolors": "^1.0.0", + "sade": "^1.7.4", + "svelte-preprocess": "^5.1.3", + "typescript": "^5.0.3" + }, + "bin": { + "svelte-check": "bin/svelte-check" + }, + "peerDependencies": { + "svelte": "^3.55.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0" + } + }, + "node_modules/svelte-eslint-parser": { + "version": "0.43.0", + "resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-0.43.0.tgz", + "integrity": "sha512-GpU52uPKKcVnh8tKN5P4UZpJ/fUDndmq7wfsvoVXsyP+aY0anol7Yqo01fyrlaWGMFfm4av5DyrjlaXdLRJvGA==", + "dev": true, + "dependencies": { + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "postcss": "^8.4.39", + "postcss-scss": "^4.0.9" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ota-meshi" + }, + "peerDependencies": { + "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "svelte": { + "optional": true + } + } + }, + "node_modules/svelte-hmr": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.16.0.tgz", + "integrity": "sha512-Gyc7cOS3VJzLlfj7wKS0ZnzDVdv3Pn2IuVeJPk9m2skfhcu5bq3wtIZyQGggr7/Iim5rH5cncyQft/kRLupcnA==", + "dev": true, + "engines": { + "node": "^12.20 || ^14.13.1 || >= 16" + }, + "peerDependencies": { + "svelte": "^3.19.0 || ^4.0.0" + } + }, + "node_modules/svelte-markdown": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/svelte-markdown/-/svelte-markdown-0.4.1.tgz", + "integrity": "sha512-pOlLY6EruKJaWI9my/2bKX8PdTeP5CM0s4VMmwmC2prlOkjAf+AOmTM4wW/l19Y6WZ87YmP8+ZCJCCwBChWjYw==", + "dependencies": { + "@types/marked": "^5.0.1", + "marked": "^5.1.2" + }, + "peerDependencies": { + "svelte": "^4.0.0" + } + }, + "node_modules/svelte-preprocess": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-5.1.4.tgz", + "integrity": "sha512-IvnbQ6D6Ao3Gg6ftiM5tdbR6aAETwjhHV+UKGf5bHGYR69RQvF1ho0JKPcbUON4vy4R7zom13jPjgdOWCQ5hDA==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@types/pug": "^2.0.6", + "detect-indent": "^6.1.0", + "magic-string": "^0.30.5", + "sorcery": "^0.11.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">= 16.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.10.2", + "coffeescript": "^2.5.1", + "less": "^3.11.3 || ^4.0.0", + "postcss": "^7 || ^8", + "postcss-load-config": "^2.1.0 || ^3.0.0 || ^4.0.0 || ^5.0.0", + "pug": "^3.0.0", + "sass": "^1.26.8", + "stylus": "^0.55.0", + "sugarss": "^2.0.0 || ^3.0.0 || ^4.0.0", + "svelte": "^3.23.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0", + "typescript": ">=3.9.5 || ^4.0.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "coffeescript": { + "optional": true + }, + "less": { + "optional": true + }, + "postcss": { + "optional": true + }, + "postcss-load-config": { + "optional": true + }, + "pug": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/svelte-vega": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/svelte-vega/-/svelte-vega-2.3.0.tgz", + "integrity": "sha512-5EhvJNoJ1Dc6FP/lMZsn59Ieapj6o/APCj4N1AueEIqfoXT2kgOWHIcKnKxcnccNmD+EHwA1YPZhgcV0PRwvPA==", + "peerDependencies": { + "svelte": "^3.54.0 || ^4.0.0 || ^5.0.0", + "vega": "*", + "vega-embed": "*", + "vega-lite": "*" + } + }, + "node_modules/synckit": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz", + "integrity": "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==", + "dev": true, + "dependencies": { + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/throttleit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.1.tgz", + "integrity": "sha512-vDZpf9Chs9mAdfY046mcPt8fg5QSZr37hEH4TXYBnDF+izxgrbRGUAAaBvIk/fJm9aOFCGFd1EsNg5AZCbnQCQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true + }, + "node_modules/tldts": { + "version": "6.1.82", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.82.tgz", + "integrity": "sha512-KCTjNL9F7j8MzxgfTgjT+v21oYH38OidFty7dH00maWANAI2IsLw2AnThtTJi9HKALHZKQQWnNebYheadacD+g==", + "dev": true, + "dependencies": { + "tldts-core": "^6.1.82" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "6.1.82", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.82.tgz", + "integrity": "sha512-Jabl32m21tt/d/PbDO88R43F8aY98Piiz6BVH9ShUlOAiiAELhEqwrAmBocjAqnCfoUeIsRU+h3IEzZd318F3w==", + "dev": true + }, + "node_modules/tmp": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "dev": true, + "engines": { + "node": ">=14.14" + } + }, + "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/topojson-client": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/topojson-client/-/topojson-client-3.1.0.tgz", + "integrity": "sha512-605uxS6bcYxGXw9qi62XyrV6Q3xwbndjachmNxu8HWTtVPxZfEJN9fd/SZS1Q54Sn2y0TMyMxFj/cJINqGHrKw==", + "dependencies": { + "commander": "2" + }, + "bin": { + "topo2geo": "bin/topo2geo", + "topomerge": "bin/topomerge", + "topoquantize": "bin/topoquantize" + } + }, + "node_modules/topojson-client/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "node_modules/tough-cookie": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", + "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", + "dev": true, + "dependencies": { + "tldts": "^6.1.32" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/ts-api-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", + "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", + "dev": true, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", + "dev": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", + "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true + }, + "node_modules/unfetch": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/unfetch/-/unfetch-4.1.0.tgz", + "integrity": "sha512-crP/n3eAPUJxZXM9T80/yv0YhkTEx2K1D3h7D1AJM6fzsWZrxdyRuLN0JH/dkZh1LNH8LxCnBzoPFCPbb2iGpg==", + "dev": true + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/vega": { + "version": "5.32.0", + "resolved": "https://registry.npmjs.org/vega/-/vega-5.32.0.tgz", + "integrity": "sha512-jANt/5+SpV7b7owB5u8+M1TZ/TrF1fK6WlcvKDW38tH3Gb6hM1nzIhv10E41w3GBmwF29BU/qH2ruNkaYKjI5g==", + "dependencies": { + "vega-crossfilter": "~4.1.3", + "vega-dataflow": "~5.7.7", + "vega-encode": "~4.10.2", + "vega-event-selector": "~3.0.1", + "vega-expression": "~5.2.0", + "vega-force": "~4.2.2", + "vega-format": "~1.1.3", + "vega-functions": "~5.17.0", + "vega-geo": "~4.4.3", + "vega-hierarchy": "~4.1.3", + "vega-label": "~1.3.1", + "vega-loader": "~4.5.3", + "vega-parser": "~6.5.0", + "vega-projection": "~1.6.2", + "vega-regression": "~1.3.1", + "vega-runtime": "~6.2.1", + "vega-scale": "~7.4.2", + "vega-scenegraph": "~4.13.1", + "vega-statistics": "~1.9.0", + "vega-time": "~2.1.3", + "vega-transforms": "~4.12.1", + "vega-typings": "~1.5.0", + "vega-util": "~1.17.2", + "vega-view": "~5.15.0", + "vega-view-transforms": "~4.6.1", + "vega-voronoi": "~4.2.4", + "vega-wordcloud": "~4.1.6" + } + }, + "node_modules/vega-canvas": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/vega-canvas/-/vega-canvas-1.2.7.tgz", + "integrity": "sha512-OkJ9CACVcN9R5Pi9uF6MZBF06pO6qFpDYHWSKBJsdHP5o724KrsgR6UvbnXFH82FdsiTOff/HqjuaG8C7FL+9Q==" + }, + "node_modules/vega-crossfilter": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/vega-crossfilter/-/vega-crossfilter-4.1.3.tgz", + "integrity": "sha512-nyPJAXAUABc3EocUXvAL1J/IWotZVsApIcvOeZaUdEQEtZ7bt8VtP2nj3CLbHBA8FZZVV+K6SmdwvCOaAD4wFQ==", + "dependencies": { + "d3-array": "^3.2.2", + "vega-dataflow": "^5.7.7", + "vega-util": "^1.17.3" + } + }, + "node_modules/vega-dataflow": { + "version": "5.7.7", + "resolved": "https://registry.npmjs.org/vega-dataflow/-/vega-dataflow-5.7.7.tgz", + "integrity": "sha512-R2NX2HvgXL+u4E6u+L5lKvvRiCtnE6N6l+umgojfi53suhhkFP+zB+2UAQo4syxuZ4763H1csfkKc4xpqLzKnw==", + "dependencies": { + "vega-format": "^1.1.3", + "vega-loader": "^4.5.3", + "vega-util": "^1.17.3" + } + }, + "node_modules/vega-embed": { + "version": "6.29.0", + "resolved": "https://registry.npmjs.org/vega-embed/-/vega-embed-6.29.0.tgz", + "integrity": "sha512-PmlshTLtLFLgWtF/b23T1OwX53AugJ9RZ3qPE2c01VFAbgt3/GSNI/etzA/GzdrkceXFma+FDHNXUppKuM0U6Q==", + "peer": true, + "dependencies": { + "fast-json-patch": "^3.1.1", + "json-stringify-pretty-compact": "^4.0.0", + "semver": "^7.6.3", + "tslib": "^2.8.1", + "vega-interpreter": "^1.0.5", + "vega-schema-url-parser": "^2.2.0", + "vega-themes": "^2.15.0", + "vega-tooltip": "^0.35.2" + }, + "peerDependencies": { + "vega": "^5.21.0", + "vega-lite": "*" + } + }, + "node_modules/vega-encode": { + "version": "4.10.2", + "resolved": "https://registry.npmjs.org/vega-encode/-/vega-encode-4.10.2.tgz", + "integrity": "sha512-fsjEY1VaBAmqwt7Jlpz0dpPtfQFiBdP9igEefvumSpy7XUxOJmDQcRDnT3Qh9ctkv3itfPfI9g8FSnGcv2b4jQ==", + "dependencies": { + "d3-array": "^3.2.2", + "d3-interpolate": "^3.0.1", + "vega-dataflow": "^5.7.7", + "vega-scale": "^7.4.2", + "vega-util": "^1.17.3" + } + }, + "node_modules/vega-event-selector": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/vega-event-selector/-/vega-event-selector-3.0.1.tgz", + "integrity": "sha512-K5zd7s5tjr1LiOOkjGpcVls8GsH/f2CWCrWcpKy74gTCp+llCdwz0Enqo013ZlGaRNjfgD/o1caJRt3GSaec4A==" + }, + "node_modules/vega-expression": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/vega-expression/-/vega-expression-5.2.0.tgz", + "integrity": "sha512-WRMa4ny3iZIVAzDlBh3ipY2QUuLk2hnJJbfbncPgvTF7BUgbIbKq947z+JicWksYbokl8n1JHXJoqi3XvpG0Zw==", + "dependencies": { + "@types/estree": "^1.0.0", + "vega-util": "^1.17.3" + } + }, + "node_modules/vega-force": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/vega-force/-/vega-force-4.2.2.tgz", + "integrity": "sha512-cHZVaY2VNNIG2RyihhSiWniPd2W9R9kJq0znxzV602CgUVgxEfTKtx/lxnVCn8nNrdKAYrGiqIsBzIeKG1GWHw==", + "dependencies": { + "d3-force": "^3.0.0", + "vega-dataflow": "^5.7.7", + "vega-util": "^1.17.3" + } + }, + "node_modules/vega-format": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/vega-format/-/vega-format-1.1.3.tgz", + "integrity": "sha512-wQhw7KR46wKJAip28FF/CicW+oiJaPAwMKdrxlnTA0Nv8Bf7bloRlc+O3kON4b4H1iALLr9KgRcYTOeXNs2MOA==", + "dependencies": { + "d3-array": "^3.2.2", + "d3-format": "^3.1.0", + "d3-time-format": "^4.1.0", + "vega-time": "^2.1.3", + "vega-util": "^1.17.3" + } + }, + "node_modules/vega-functions": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/vega-functions/-/vega-functions-5.17.0.tgz", + "integrity": "sha512-EoGvdCtv1Y4M/hLy83Kf0HTs4qInUfrBoanrnhbguzRl00rx7orjcv+bNZFHbCe4HkfVpbOnTrYmz3K2ivaOLw==", + "dependencies": { + "d3-array": "^3.2.2", + "d3-color": "^3.1.0", + "d3-geo": "^3.1.0", + "vega-dataflow": "^5.7.7", + "vega-expression": "^5.2.0", + "vega-scale": "^7.4.2", + "vega-scenegraph": "^4.13.1", + "vega-selections": "^5.6.0", + "vega-statistics": "^1.9.0", + "vega-time": "^2.1.3", + "vega-util": "^1.17.3" + } + }, + "node_modules/vega-geo": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/vega-geo/-/vega-geo-4.4.3.tgz", + "integrity": "sha512-+WnnzEPKIU1/xTFUK3EMu2htN35gp9usNZcC0ZFg2up1/Vqu6JyZsX0PIO51oXSIeXn9bwk6VgzlOmJUcx92tA==", + "dependencies": { + "d3-array": "^3.2.2", + "d3-color": "^3.1.0", + "d3-geo": "^3.1.0", + "vega-canvas": "^1.2.7", + "vega-dataflow": "^5.7.7", + "vega-projection": "^1.6.2", + "vega-statistics": "^1.9.0", + "vega-util": "^1.17.3" + } + }, + "node_modules/vega-hierarchy": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/vega-hierarchy/-/vega-hierarchy-4.1.3.tgz", + "integrity": "sha512-0Z+TYKRgOEo8XYXnJc2HWg1EGpcbNAhJ9Wpi9ubIbEyEHqIgjCIyFVN8d4nSfsJOcWDzsSmRqohBztxAhOCSaw==", + "dependencies": { + "d3-hierarchy": "^3.1.2", + "vega-dataflow": "^5.7.7", + "vega-util": "^1.17.3" + } + }, + "node_modules/vega-interpreter": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/vega-interpreter/-/vega-interpreter-1.2.0.tgz", + "integrity": "sha512-p408/0IPevyR/bIKdXGNzOixkTYCkH83zNhGypRqDxd/qVrdJVrh9RcECOYx1MwEc6JTB1BeK2lArHiGGuG7Hw==", + "peer": true, + "dependencies": { + "vega-util": "^1.17.3" + } + }, + "node_modules/vega-label": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/vega-label/-/vega-label-1.3.1.tgz", + "integrity": "sha512-Emx4b5s7pvuRj3fBkAJ/E2snCoZACfKAwxVId7f/4kYVlAYLb5Swq6W8KZHrH4M9Qds1XJRUYW9/Y3cceqzEFA==", + "dependencies": { + "vega-canvas": "^1.2.7", + "vega-dataflow": "^5.7.7", + "vega-scenegraph": "^4.13.1", + "vega-util": "^1.17.3" + } + }, + "node_modules/vega-lite": { + "version": "5.23.0", + "resolved": "https://registry.npmjs.org/vega-lite/-/vega-lite-5.23.0.tgz", + "integrity": "sha512-l4J6+AWE3DIjvovEoHl2LdtCUkfm4zs8Xxx7INwZEAv+XVb6kR6vIN1gt3t2gN2gs/y4DYTs/RPoTeYAuEg6mA==", + "dependencies": { + "json-stringify-pretty-compact": "~4.0.0", + "tslib": "~2.8.1", + "vega-event-selector": "~3.0.1", + "vega-expression": "~5.1.1", + "vega-util": "~1.17.2", + "yargs": "~17.7.2" + }, + "bin": { + "vl2pdf": "bin/vl2pdf", + "vl2png": "bin/vl2png", + "vl2svg": "bin/vl2svg", + "vl2vg": "bin/vl2vg" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "vega": "^5.24.0" + } + }, + "node_modules/vega-lite/node_modules/vega-expression": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/vega-expression/-/vega-expression-5.1.2.tgz", + "integrity": "sha512-fFeDTh4UtOxlZWL54jf1ZqJHinyerWq/ROiqrQxqLkNJRJ86RmxYTgXwt65UoZ/l4VUv9eAd2qoJeDEf610Umw==", + "dependencies": { + "@types/estree": "^1.0.0", + "vega-util": "^1.17.3" + } + }, + "node_modules/vega-loader": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/vega-loader/-/vega-loader-4.5.3.tgz", + "integrity": "sha512-dUfIpxTLF2magoMaur+jXGvwMxjtdlDZaIS8lFj6N7IhUST6nIvBzuUlRM+zLYepI5GHtCLOnqdKU4XV0NggCA==", + "dependencies": { + "d3-dsv": "^3.0.1", + "node-fetch": "^2.6.7", + "topojson-client": "^3.1.0", + "vega-format": "^1.1.3", + "vega-util": "^1.17.3" + } + }, + "node_modules/vega-parser": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/vega-parser/-/vega-parser-6.5.0.tgz", + "integrity": "sha512-dPxFKn6IlDyWi6CgHGGv8htSPBAyLHWlJNNGD17eMXh+Kjn4hupSNOIboRcYb8gL5HYt1tYwS6oYZXK84Bc4tg==", + "dependencies": { + "vega-dataflow": "^5.7.7", + "vega-event-selector": "^3.0.1", + "vega-functions": "^5.17.0", + "vega-scale": "^7.4.2", + "vega-util": "^1.17.3" + } + }, + "node_modules/vega-projection": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/vega-projection/-/vega-projection-1.6.2.tgz", + "integrity": "sha512-3pcVaQL9R3Zfk6PzopLX6awzrQUeYOXJzlfLGP2Xd93mqUepBa6m/reVrTUoSFXA3v9lfK4W/PS2AcVzD/MIcQ==", + "dependencies": { + "d3-geo": "^3.1.0", + "d3-geo-projection": "^4.0.0", + "vega-scale": "^7.4.2" + } + }, + "node_modules/vega-regression": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/vega-regression/-/vega-regression-1.3.1.tgz", + "integrity": "sha512-AmccF++Z9uw4HNZC/gmkQGe6JsRxTG/R4QpbcSepyMvQN1Rj5KtVqMcmVFP1r3ivM4dYGFuPlzMWvuqp0iKMkQ==", + "dependencies": { + "d3-array": "^3.2.2", + "vega-dataflow": "^5.7.7", + "vega-statistics": "^1.9.0", + "vega-util": "^1.17.3" + } + }, + "node_modules/vega-runtime": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/vega-runtime/-/vega-runtime-6.2.1.tgz", + "integrity": "sha512-b4eot3tWKCk++INWqot+6sLn3wDTj/HE+tRSbiaf8aecuniPMlwJEK7wWuhVGeW2Ae5n8fI/8TeTViaC94bNHA==", + "dependencies": { + "vega-dataflow": "^5.7.7", + "vega-util": "^1.17.3" + } + }, + "node_modules/vega-scale": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/vega-scale/-/vega-scale-7.4.2.tgz", + "integrity": "sha512-o6Hl76aU1jlCK7Q8DPYZ8OGsp4PtzLdzI6nGpLt8rxoE78QuB3GBGEwGAQJitp4IF7Lb2rL5oAXEl3ZP6xf9jg==", + "dependencies": { + "d3-array": "^3.2.2", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-scale-chromatic": "^3.1.0", + "vega-time": "^2.1.3", + "vega-util": "^1.17.3" + } + }, + "node_modules/vega-scenegraph": { + "version": "4.13.1", + "resolved": "https://registry.npmjs.org/vega-scenegraph/-/vega-scenegraph-4.13.1.tgz", + "integrity": "sha512-LFY9+sLIxRfdDI9ZTKjLoijMkIAzPLBWHpPkwv4NPYgdyx+0qFmv+puBpAUGUY9VZqAZ736Uj5NJY9zw+/M3yQ==", + "dependencies": { + "d3-path": "^3.1.0", + "d3-shape": "^3.2.0", + "vega-canvas": "^1.2.7", + "vega-loader": "^4.5.3", + "vega-scale": "^7.4.2", + "vega-util": "^1.17.3" + } + }, + "node_modules/vega-schema-url-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/vega-schema-url-parser/-/vega-schema-url-parser-2.2.0.tgz", + "integrity": "sha512-yAtdBnfYOhECv9YC70H2gEiqfIbVkq09aaE4y/9V/ovEFmH9gPKaEgzIZqgT7PSPQjKhsNkb6jk6XvSoboxOBw==", + "peer": true + }, + "node_modules/vega-selections": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/vega-selections/-/vega-selections-5.6.0.tgz", + "integrity": "sha512-UE2w78rUUbaV3Ph+vQbQDwh8eywIJYRxBiZdxEG/Tr/KtFMLdy2BDgNZuuDO1Nv8jImPJwONmqjNhNDYwM0VJQ==", + "dependencies": { + "d3-array": "3.2.4", + "vega-expression": "^5.2.0", + "vega-util": "^1.17.3" + } + }, + "node_modules/vega-statistics": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/vega-statistics/-/vega-statistics-1.9.0.tgz", + "integrity": "sha512-GAqS7mkatpXcMCQKWtFu1eMUKLUymjInU0O8kXshWaQrVWjPIO2lllZ1VNhdgE0qGj4oOIRRS11kzuijLshGXQ==", + "dependencies": { + "d3-array": "^3.2.2" + } + }, + "node_modules/vega-themes": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/vega-themes/-/vega-themes-2.15.0.tgz", + "integrity": "sha512-DicRAKG9z+23A+rH/3w3QjJvKnlGhSbbUXGjBvYGseZ1lvj9KQ0BXZ2NS/+MKns59LNpFNHGi9us/wMlci4TOA==", + "peer": true, + "peerDependencies": { + "vega": "*", + "vega-lite": "*" + } + }, + "node_modules/vega-time": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/vega-time/-/vega-time-2.1.3.tgz", + "integrity": "sha512-hFcWPdTV844IiY0m97+WUoMLADCp+8yUQR1NStWhzBzwDDA7QEGGwYGxALhdMOaDTwkyoNj3V/nox2rQAJD/vQ==", + "dependencies": { + "d3-array": "^3.2.2", + "d3-time": "^3.1.0", + "vega-util": "^1.17.3" + } + }, + "node_modules/vega-tooltip": { + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/vega-tooltip/-/vega-tooltip-0.35.2.tgz", + "integrity": "sha512-kuYcsAAKYn39ye5wKf2fq1BAxVcjoz0alvKp/G+7BWfIb94J0PHmwrJ5+okGefeStZnbXxINZEOKo7INHaj9GA==", + "peer": true, + "dependencies": { + "vega-util": "^1.17.2" + }, + "optionalDependencies": { + "@rollup/rollup-linux-x64-gnu": "^4.24.4" + } + }, + "node_modules/vega-transforms": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/vega-transforms/-/vega-transforms-4.12.1.tgz", + "integrity": "sha512-Qxo+xeEEftY1jYyKgzOGc9NuW4/MqGm1YPZ5WrL9eXg2G0410Ne+xL/MFIjHF4hRX+3mgFF4Io2hPpfy/thjLg==", + "dependencies": { + "d3-array": "^3.2.2", + "vega-dataflow": "^5.7.7", + "vega-statistics": "^1.9.0", + "vega-time": "^2.1.3", + "vega-util": "^1.17.3" + } + }, + "node_modules/vega-typings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/vega-typings/-/vega-typings-1.5.0.tgz", + "integrity": "sha512-tcZ2HwmiQEOXIGyBMP8sdCnoFoVqHn4KQ4H0MQiHwzFU1hb1EXURhfc+Uamthewk4h/9BICtAM3AFQMjBGpjQA==", + "dependencies": { + "@types/geojson": "7946.0.4", + "vega-event-selector": "^3.0.1", + "vega-expression": "^5.2.0", + "vega-util": "^1.17.3" + } + }, + "node_modules/vega-util": { + "version": "1.17.3", + "resolved": "https://registry.npmjs.org/vega-util/-/vega-util-1.17.3.tgz", + "integrity": "sha512-nSNpZLUrRvFo46M5OK4O6x6f08WD1yOcEzHNlqivF+sDLSsVpstaF6fdJYwrbf/debFi2L9Tkp4gZQtssup9iQ==" + }, + "node_modules/vega-view": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/vega-view/-/vega-view-5.15.0.tgz", + "integrity": "sha512-bm8STHPsI8BjVu2gYlWU8KEVOA2JyTzdtb9cJj8NW6HpN72UxTYsg5y22u9vfcLYjzjmolrlr0756VXR0uI1Cg==", + "dependencies": { + "d3-array": "^3.2.2", + "d3-timer": "^3.0.1", + "vega-dataflow": "^5.7.7", + "vega-format": "^1.1.3", + "vega-functions": "^5.17.0", + "vega-runtime": "^6.2.1", + "vega-scenegraph": "^4.13.1", + "vega-util": "^1.17.3" + } + }, + "node_modules/vega-view-transforms": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/vega-view-transforms/-/vega-view-transforms-4.6.1.tgz", + "integrity": "sha512-RYlyMJu5kZV4XXjmyTQKADJWDB25SMHsiF+B1rbE1p+pmdQPlp5tGdPl9r5dUJOp3p8mSt/NGI8GPGucmPMxtw==", + "dependencies": { + "vega-dataflow": "^5.7.7", + "vega-scenegraph": "^4.13.1", + "vega-util": "^1.17.3" + } + }, + "node_modules/vega-voronoi": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/vega-voronoi/-/vega-voronoi-4.2.4.tgz", + "integrity": "sha512-lWNimgJAXGeRFu2Pz8axOUqVf1moYhD+5yhBzDSmckE9I5jLOyZc/XvgFTXwFnsVkMd1QW1vxJa+y9yfUblzYw==", + "dependencies": { + "d3-delaunay": "^6.0.2", + "vega-dataflow": "^5.7.7", + "vega-util": "^1.17.3" + } + }, + "node_modules/vega-wordcloud": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/vega-wordcloud/-/vega-wordcloud-4.1.6.tgz", + "integrity": "sha512-lFmF3u9/ozU0P+WqPjeThQfZm0PigdbXDwpIUCxczrCXKYJLYFmZuZLZR7cxtmpZ0/yuvRvAJ4g123LXbSZF8A==", + "dependencies": { + "vega-canvas": "^1.2.7", + "vega-dataflow": "^5.7.7", + "vega-scale": "^7.4.2", + "vega-statistics": "^1.9.0", + "vega-util": "^1.17.3" + } + }, + "node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "node_modules/vite": { + "version": "5.4.19", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz", + "integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/rollup": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.34.9.tgz", + "integrity": "sha512-nF5XYqWWp9hx/LrpC8sZvvvmq0TeTjQgaZHYmAgwysT9nh8sWnZhBnM8ZyVbbJFIQBLwHDNoMqsBZBbUo4U8sQ==", + "dev": true, + "dependencies": { + "@types/estree": "1.0.6" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.34.9", + "@rollup/rollup-android-arm64": "4.34.9", + "@rollup/rollup-darwin-arm64": "4.34.9", + "@rollup/rollup-darwin-x64": "4.34.9", + "@rollup/rollup-freebsd-arm64": "4.34.9", + "@rollup/rollup-freebsd-x64": "4.34.9", + "@rollup/rollup-linux-arm-gnueabihf": "4.34.9", + "@rollup/rollup-linux-arm-musleabihf": "4.34.9", + "@rollup/rollup-linux-arm64-gnu": "4.34.9", + "@rollup/rollup-linux-arm64-musl": "4.34.9", + "@rollup/rollup-linux-loongarch64-gnu": "4.34.9", + "@rollup/rollup-linux-powerpc64le-gnu": "4.34.9", + "@rollup/rollup-linux-riscv64-gnu": "4.34.9", + "@rollup/rollup-linux-s390x-gnu": "4.34.9", + "@rollup/rollup-linux-x64-gnu": "4.34.9", + "@rollup/rollup-linux-x64-musl": "4.34.9", + "@rollup/rollup-win32-arm64-msvc": "4.34.9", + "@rollup/rollup-win32-ia32-msvc": "4.34.9", + "@rollup/rollup-win32-x64-msvc": "4.34.9", + "fsevents": "~2.3.2" + } + }, + "node_modules/vitefu": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-0.2.5.tgz", + "integrity": "sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==", + "dev": true, + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "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/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "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==", + "engines": { + "node": ">=12" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/yauzl/node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "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/metaflow/plugins/cards/ui/package.json b/metaflow/plugins/cards/ui/package.json index e251a2974a1..c80a9518692 100644 --- a/metaflow/plugins/cards/ui/package.json +++ b/metaflow/plugins/cards/ui/package.json @@ -2,51 +2,45 @@ "name": "svelte-app", "version": "1.0.0", "private": true, + "type": "module", "scripts": { - "build": "cross-env OUTPUT_DIR=../card_modules rollup -c", - "dev": "rollup -c -w", - "start": "sirv public --no-clear", - "check": "svelte-check --workspace src", - "lint": "eslint src --ext .ts,.svelte", - "lint:fix": "yarn run lint --fix", - "prebuild": "yarn run check && yarn run lint", + "dev": "vite build --watch", + "build": "vite build", + "preview": "vite preview", + "check": "svelte-check --tsconfig ./tsconfig.node.json", + "lint": "eslint src --ext .ts,.svelte --fix", + "prebuild": "npm run check && npm run lint", "cypress:open": "wait-on http://localhost:8080 && cypress open", - "cypress:open-dev": "yarn dev & yarn run cypress:open", + "cypress:open-dev": "npm run dev & npm run cypress:open", "cypress:run": "wait-on http://localhost:8080 && cypress run", - "cypress:run-dev": "yarn dev & yarn run cypress:run" + "cypress:run-dev": "npm run dev & npm run cypress:run" }, "devDependencies": { - "@rollup/plugin-commonjs": "^21.0.1", - "@rollup/plugin-node-resolve": "^13.1.3", - "@rollup/plugin-typescript": "^8.3.0", - "@tsconfig/svelte": "^3.0.0", - "@types/node": "^17.0.8", - "@typescript-eslint/eslint-plugin": "^5.9.1", - "@typescript-eslint/parser": "^5.9.1", - "cross-env": "^7.0.3", - "cypress": "9.2.1", + "@sveltejs/vite-plugin-svelte": "^3.0.1", + "@tsconfig/svelte": "^5.0.2", + "@types/node": "^20.10.4", + "@typescript-eslint/eslint-plugin": "^6.14.0", + "@typescript-eslint/parser": "^6.14.0", + "cypress": "^13.6.1", "cypress-svelte-unit-test": "^3.3.4", - "eslint": "^8.6.0", - "eslint-plugin-svelte3": "^3.4.0", - "postcss": "^8.4.5", - "rollup": "^2.63.0", - "rollup-plugin-css-only": "^3.1.0", - "rollup-plugin-livereload": "^2.0.5", - "rollup-plugin-postcss": "^4.0.2", - "rollup-plugin-svelte": "^7.1.0", - "rollup-plugin-terser": "^7.0.2", - "sirv-cli": "^2.0.1", - "svelte": "^3.49.0", - "svelte-check": "^2.3.0", - "svelte-preprocess": "^4.10.1", - "tslib": "^2.3.1", - "typescript": "^4.5.4", - "wait-on": "^6.0.0" + "eslint": "^8.55.0", + "eslint-plugin-prettier": "^5.0.1", + "eslint-plugin-svelte": "^2.35.1", + "postcss": "^8.4.32", + "prettier": "^3.1.1", + "svelte": "^4.2.19", + "svelte-check": "^3.6.2", + "svelte-preprocess": "^5.1.2", + "tslib": "^2.6.2", + "typescript": "^5.3.3", + "vite": "^5.4.19" }, "license": "UNLICENSED", "dependencies": { - "@iconify/svelte": "^2.1.1", - "chart.js": "^3.7.0", - "svelte-markdown": "^0.2.1" + "@iconify/svelte": "^3.1.4", + "svelte-markdown": "^0.4.0", + "svelte-vega": "^2.1.0", + "vega": "^5.26.1", + "vega-lite": "^5.16.3" } } diff --git a/metaflow/plugins/cards/ui/src/prism.css b/metaflow/plugins/cards/ui/prism.css similarity index 100% rename from metaflow/plugins/cards/ui/src/prism.css rename to metaflow/plugins/cards/ui/prism.css diff --git a/metaflow/plugins/cards/ui/src/prism.js b/metaflow/plugins/cards/ui/prism.js similarity index 100% rename from metaflow/plugins/cards/ui/src/prism.js rename to metaflow/plugins/cards/ui/prism.js diff --git a/metaflow/plugins/cards/ui/public/index.html b/metaflow/plugins/cards/ui/public/index.html deleted file mode 100644 index 9b18320f76f..00000000000 --- a/metaflow/plugins/cards/ui/public/index.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - Metaflow Card Example - - - - - - -
    - - diff --git a/metaflow/plugins/cards/ui/rollup.config.js b/metaflow/plugins/cards/ui/rollup.config.jsBACKUP similarity index 96% rename from metaflow/plugins/cards/ui/rollup.config.js rename to metaflow/plugins/cards/ui/rollup.config.jsBACKUP index 5d4b7502aef..aea515ce6a3 100644 --- a/metaflow/plugins/cards/ui/rollup.config.js +++ b/metaflow/plugins/cards/ui/rollup.config.jsBACKUP @@ -5,6 +5,7 @@ import livereload from "rollup-plugin-livereload"; import { terser } from "rollup-plugin-terser"; import sveltePreprocess from "svelte-preprocess"; import typescript from "@rollup/plugin-typescript"; +import json from "@rollup/plugin-json" /** * @NOTE: we are currently commenting out postcss, * it will be added back in a separate PR @@ -43,7 +44,7 @@ export default { input: "src/main.ts", output: { dir: process.env.OUTPUT_DIR ?? "public/build", - sourcemap: true, + sourcemap: !production, format: "iife", name: "app", }, @@ -77,7 +78,7 @@ export default { sourceMap: !production, inlineSources: !production, }), - + json(), // In dev mode, call `npm run start` once // the bundle has been generated !production && serve(), diff --git a/metaflow/plugins/cards/ui/src/App.svelte b/metaflow/plugins/cards/ui/src/App.svelte index bbb390ebbca..d8a980650c4 100644 --- a/metaflow/plugins/cards/ui/src/App.svelte +++ b/metaflow/plugins/cards/ui/src/App.svelte @@ -1,9 +1,9 @@
    diff --git a/metaflow/plugins/cards/ui/src/components/artifacts.svelte b/metaflow/plugins/cards/ui/src/components/artifacts.svelte index 72309d41225..3e4de16f06c 100644 --- a/metaflow/plugins/cards/ui/src/components/artifacts.svelte +++ b/metaflow/plugins/cards/ui/src/components/artifacts.svelte @@ -4,10 +4,9 @@ import ArtifactRow from "./artifact-row.svelte"; export let componentData: types.ArtifactsComponent; - const { data } = componentData; // we can't guarantee the data is sorted from the source, so we sort before render - const sortedData = data.sort((a, b) => { + const sortedData = componentData?.data.sort((a, b) => { // nulls first if (a.name && b.name) { if (a.name > b.name) { @@ -24,7 +23,7 @@ {#each sortedData as artifact} - + {/each}
    diff --git a/metaflow/plugins/cards/ui/src/components/bar-chart.svelte b/metaflow/plugins/cards/ui/src/components/bar-chart.svelte deleted file mode 100644 index 7db0febe9a7..00000000000 --- a/metaflow/plugins/cards/ui/src/components/bar-chart.svelte +++ /dev/null @@ -1,56 +0,0 @@ - - - -
    - -
    - - diff --git a/metaflow/plugins/cards/ui/src/components/card-component-renderer.svelte b/metaflow/plugins/cards/ui/src/components/card-component-renderer.svelte index 6d9e05f9a14..dbeb3026f83 100644 --- a/metaflow/plugins/cards/ui/src/components/card-component-renderer.svelte +++ b/metaflow/plugins/cards/ui/src/components/card-component-renderer.svelte @@ -1,46 +1,48 @@ + {#if component} {#if (componentData.type === "page" || componentData.type === "section") && componentData?.contents} diff --git a/metaflow/plugins/cards/ui/src/components/dag/connector.svelte b/metaflow/plugins/cards/ui/src/components/dag/connector.svelte index be41248568a..77def1f790b 100644 --- a/metaflow/plugins/cards/ui/src/components/dag/connector.svelte +++ b/metaflow/plugins/cards/ui/src/components/dag/connector.svelte @@ -1,5 +1,5 @@ -
    diff --git a/metaflow/plugins/cards/ui/src/components/image.svelte b/metaflow/plugins/cards/ui/src/components/image.svelte index 3a247455907..d4a81e393cb 100644 --- a/metaflow/plugins/cards/ui/src/components/image.svelte +++ b/metaflow/plugins/cards/ui/src/components/image.svelte @@ -4,9 +4,10 @@ import { modal } from "../store"; export let componentData: types.ImageComponent; - const { src, label, description } = componentData; + $: ({ src, label, description } = componentData); +
    modal.set(componentData)} data-component="image">
    {label diff --git a/metaflow/plugins/cards/ui/src/components/line-chart.svelte b/metaflow/plugins/cards/ui/src/components/line-chart.svelte deleted file mode 100644 index 1defb02e457..00000000000 --- a/metaflow/plugins/cards/ui/src/components/line-chart.svelte +++ /dev/null @@ -1,56 +0,0 @@ - - - -
    - -
    - - diff --git a/metaflow/plugins/cards/ui/src/components/main.svelte b/metaflow/plugins/cards/ui/src/components/main.svelte index 08e50f08e64..4f38cb14d2d 100644 --- a/metaflow/plugins/cards/ui/src/components/main.svelte +++ b/metaflow/plugins/cards/ui/src/components/main.svelte @@ -25,5 +25,6 @@ /* if the embed class is present, we hide the aside, and we should center the main */ :global(.embed main) { margin: 0 auto; + min-width: 80% } diff --git a/metaflow/plugins/cards/ui/src/components/modal.svelte b/metaflow/plugins/cards/ui/src/components/modal.svelte index 3dff91f914d..e475bb71187 100644 --- a/metaflow/plugins/cards/ui/src/components/modal.svelte +++ b/metaflow/plugins/cards/ui/src/components/modal.svelte @@ -20,6 +20,7 @@ {#if componentData && $modal} +