From 81788b06cfb3e54f4fa82b8d08e4fd68d6e808c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Gro=C3=9Fer?= Date: Fri, 30 Jan 2026 10:08:26 +0100 Subject: [PATCH] ci: add docs deployment workflow and remove site/ from git - Add GitHub Actions workflow to build and deploy docs to GitHub Pages - Remove site/ build artifacts from git tracking (70 files) - Add site/ to .gitignore Co-Authored-By: Claude Opus 4.5 --- .github/workflows/docs.yml | 50 + .gitignore | 1 + site/404.html | 929 -- site/api/chunking/index.html | 3516 -------- site/api/filters/index.html | 8016 ----------------- site/api/index.html | 1788 ---- site/api/ops/column/index.html | 2505 ------ site/api/ops/datetime/index.html | 3218 ------- site/api/ops/map/index.html | 2450 ----- site/api/ops/math/index.html | 2652 ------ site/api/ops/rows/index.html | 2849 ------ site/api/ops/string/index.html | 2723 ------ site/api/plan/index.html | 1414 --- site/api/protocol/index.html | 2928 ------ site/api/validation/index.html | 2588 ------ site/assets/_mkdocstrings.css | 237 - site/assets/images/favicon.png | Bin 1870 -> 0 bytes site/assets/images/logo.png | Bin 33938 -> 0 bytes site/assets/images/logo_black.png | Bin 33665 -> 0 bytes site/assets/images/logo_blue.png | Bin 40870 -> 0 bytes site/assets/images/logo_white.png | Bin 33938 -> 0 bytes site/assets/images/logo_wordmark_black.png | Bin 55838 -> 0 bytes .../assets/javascripts/bundle.79ae519e.min.js | 16 - .../javascripts/bundle.79ae519e.min.js.map | 7 - .../javascripts/lunr/min/lunr.ar.min.js | 1 - .../javascripts/lunr/min/lunr.da.min.js | 18 - .../javascripts/lunr/min/lunr.de.min.js | 18 - .../javascripts/lunr/min/lunr.du.min.js | 18 - .../javascripts/lunr/min/lunr.el.min.js | 1 - .../javascripts/lunr/min/lunr.es.min.js | 18 - .../javascripts/lunr/min/lunr.fi.min.js | 18 - .../javascripts/lunr/min/lunr.fr.min.js | 18 - .../javascripts/lunr/min/lunr.he.min.js | 1 - .../javascripts/lunr/min/lunr.hi.min.js | 1 - .../javascripts/lunr/min/lunr.hu.min.js | 18 - .../javascripts/lunr/min/lunr.hy.min.js | 1 - .../javascripts/lunr/min/lunr.it.min.js | 18 - .../javascripts/lunr/min/lunr.ja.min.js | 1 - .../javascripts/lunr/min/lunr.jp.min.js | 1 - .../javascripts/lunr/min/lunr.kn.min.js | 1 - .../javascripts/lunr/min/lunr.ko.min.js | 1 - .../javascripts/lunr/min/lunr.multi.min.js | 1 - .../javascripts/lunr/min/lunr.nl.min.js | 18 - .../javascripts/lunr/min/lunr.no.min.js | 18 - .../javascripts/lunr/min/lunr.pt.min.js | 18 - .../javascripts/lunr/min/lunr.ro.min.js | 18 - .../javascripts/lunr/min/lunr.ru.min.js | 18 - .../javascripts/lunr/min/lunr.sa.min.js | 1 - .../lunr/min/lunr.stemmer.support.min.js | 1 - .../javascripts/lunr/min/lunr.sv.min.js | 18 - .../javascripts/lunr/min/lunr.ta.min.js | 1 - .../javascripts/lunr/min/lunr.te.min.js | 1 - .../javascripts/lunr/min/lunr.th.min.js | 1 - .../javascripts/lunr/min/lunr.tr.min.js | 18 - .../javascripts/lunr/min/lunr.vi.min.js | 1 - .../javascripts/lunr/min/lunr.zh.min.js | 1 - site/assets/javascripts/lunr/tinyseg.js | 206 - site/assets/javascripts/lunr/wordcut.js | 6708 -------------- .../workers/search.2c215733.min.js | 42 - .../workers/search.2c215733.min.js.map | 7 - site/assets/stylesheets/custom.css | 8 - site/assets/stylesheets/main.484c7ddc.min.css | 1 - .../stylesheets/main.484c7ddc.min.css.map | 1 - .../stylesheets/palette.ab4e12ef.min.css | 1 - .../stylesheets/palette.ab4e12ef.min.css.map | 1 - site/getting-started/installation/index.html | 1158 --- site/getting-started/quickstart/index.html | 1353 --- site/index.html | 1264 --- site/objects.inv | Bin 2590 -> 0 bytes site/search/search_index.json | 1 - site/sitemap.xml | 63 - site/sitemap.xml.gz | Bin 323 -> 0 bytes 72 files changed, 51 insertions(+), 48937 deletions(-) create mode 100644 .github/workflows/docs.yml delete mode 100644 site/404.html delete mode 100644 site/api/chunking/index.html delete mode 100644 site/api/filters/index.html delete mode 100644 site/api/index.html delete mode 100644 site/api/ops/column/index.html delete mode 100644 site/api/ops/datetime/index.html delete mode 100644 site/api/ops/map/index.html delete mode 100644 site/api/ops/math/index.html delete mode 100644 site/api/ops/rows/index.html delete mode 100644 site/api/ops/string/index.html delete mode 100644 site/api/plan/index.html delete mode 100644 site/api/protocol/index.html delete mode 100644 site/api/validation/index.html delete mode 100644 site/assets/_mkdocstrings.css delete mode 100644 site/assets/images/favicon.png delete mode 100644 site/assets/images/logo.png delete mode 100644 site/assets/images/logo_black.png delete mode 100644 site/assets/images/logo_blue.png delete mode 100644 site/assets/images/logo_white.png delete mode 100644 site/assets/images/logo_wordmark_black.png delete mode 100644 site/assets/javascripts/bundle.79ae519e.min.js delete mode 100644 site/assets/javascripts/bundle.79ae519e.min.js.map delete mode 100644 site/assets/javascripts/lunr/min/lunr.ar.min.js delete mode 100644 site/assets/javascripts/lunr/min/lunr.da.min.js delete mode 100644 site/assets/javascripts/lunr/min/lunr.de.min.js delete mode 100644 site/assets/javascripts/lunr/min/lunr.du.min.js delete mode 100644 site/assets/javascripts/lunr/min/lunr.el.min.js delete mode 100644 site/assets/javascripts/lunr/min/lunr.es.min.js delete mode 100644 site/assets/javascripts/lunr/min/lunr.fi.min.js delete mode 100644 site/assets/javascripts/lunr/min/lunr.fr.min.js delete mode 100644 site/assets/javascripts/lunr/min/lunr.he.min.js delete mode 100644 site/assets/javascripts/lunr/min/lunr.hi.min.js delete mode 100644 site/assets/javascripts/lunr/min/lunr.hu.min.js delete mode 100644 site/assets/javascripts/lunr/min/lunr.hy.min.js delete mode 100644 site/assets/javascripts/lunr/min/lunr.it.min.js delete mode 100644 site/assets/javascripts/lunr/min/lunr.ja.min.js delete mode 100644 site/assets/javascripts/lunr/min/lunr.jp.min.js delete mode 100644 site/assets/javascripts/lunr/min/lunr.kn.min.js delete mode 100644 site/assets/javascripts/lunr/min/lunr.ko.min.js delete mode 100644 site/assets/javascripts/lunr/min/lunr.multi.min.js delete mode 100644 site/assets/javascripts/lunr/min/lunr.nl.min.js delete mode 100644 site/assets/javascripts/lunr/min/lunr.no.min.js delete mode 100644 site/assets/javascripts/lunr/min/lunr.pt.min.js delete mode 100644 site/assets/javascripts/lunr/min/lunr.ro.min.js delete mode 100644 site/assets/javascripts/lunr/min/lunr.ru.min.js delete mode 100644 site/assets/javascripts/lunr/min/lunr.sa.min.js delete mode 100644 site/assets/javascripts/lunr/min/lunr.stemmer.support.min.js delete mode 100644 site/assets/javascripts/lunr/min/lunr.sv.min.js delete mode 100644 site/assets/javascripts/lunr/min/lunr.ta.min.js delete mode 100644 site/assets/javascripts/lunr/min/lunr.te.min.js delete mode 100644 site/assets/javascripts/lunr/min/lunr.th.min.js delete mode 100644 site/assets/javascripts/lunr/min/lunr.tr.min.js delete mode 100644 site/assets/javascripts/lunr/min/lunr.vi.min.js delete mode 100644 site/assets/javascripts/lunr/min/lunr.zh.min.js delete mode 100644 site/assets/javascripts/lunr/tinyseg.js delete mode 100644 site/assets/javascripts/lunr/wordcut.js delete mode 100644 site/assets/javascripts/workers/search.2c215733.min.js delete mode 100644 site/assets/javascripts/workers/search.2c215733.min.js.map delete mode 100644 site/assets/stylesheets/custom.css delete mode 100644 site/assets/stylesheets/main.484c7ddc.min.css delete mode 100644 site/assets/stylesheets/main.484c7ddc.min.css.map delete mode 100644 site/assets/stylesheets/palette.ab4e12ef.min.css delete mode 100644 site/assets/stylesheets/palette.ab4e12ef.min.css.map delete mode 100644 site/getting-started/installation/index.html delete mode 100644 site/getting-started/quickstart/index.html delete mode 100644 site/index.html delete mode 100644 site/objects.inv delete mode 100644 site/search/search_index.json delete mode 100644 site/sitemap.xml delete mode 100644 site/sitemap.xml.gz diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..b458216 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,50 @@ +name: Docs + +on: + push: + branches: [main] + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + build: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v5 + + - name: Install uv + uses: astral-sh/setup-uv@v5 + with: + enable-cache: true + cache-dependency-glob: pyproject.toml + python-version: "3.12" + + - name: Install dependencies + run: uv sync --group dev + + - name: Build docs + run: uv run mkdocs build + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: site/ + + deploy: + needs: build + runs-on: ubuntu-22.04 + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore index 2dd2c76..887ef2e 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ sandbox/ __pycache__ *.pyc .coverage +site/ diff --git a/site/404.html b/site/404.html deleted file mode 100644 index ea35fd1..0000000 --- a/site/404.html +++ /dev/null @@ -1,929 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - TransformPlan - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
- -
- - - - - - -
- - -
- -
- - - - - - -
-
- - - -
-
-
- - - - - -
-
-
- - - -
-
-
- - - -
-
-
- - - -
- -
- -

404 - Not found

- -
-
- - - -
- - - -
- - - -
-
-
-
- - - - - - - - - - - - - \ No newline at end of file diff --git a/site/api/chunking/index.html b/site/api/chunking/index.html deleted file mode 100644 index abf80d5..0000000 --- a/site/api/chunking/index.html +++ /dev/null @@ -1,3516 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Chunked Processing - TransformPlan - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - Skip to content - - -
-
- -
- - - - - - -
- - -
- -
- - - - - - -
-
- - - -
-
-
- - - - - -
-
-
- - - - - - - -
- -
- - - - - - - - -

Chunked Processing

-

Process large Parquet files that exceed available RAM by reading and transforming data in chunks.

-

Overview

-

When working with files larger than available memory, use process_chunked() instead of process(). This method:

-
    -
  • Reads Parquet files in configurable chunks using lazy evaluation
  • -
  • Optionally keeps related rows together using a partition key
  • -
  • Validates that operations are compatible with chunked processing
  • -
  • Returns a ChunkedProtocol with per-chunk statistics
  • -
-
from transformplan import TransformPlan, Col
-
-plan = (
-    TransformPlan()
-    .col_rename(column="PatientID", new_name="patient_id")
-    .rows_filter(Col("age") >= 18)
-    .rows_unique(columns=["patient_id", "visit_date"])
-)
-
-# Process a large file in chunks
-result, protocol = plan.process_chunked(
-    source="patients_10gb.parquet",
-    partition_key="patient_id",  # Keep patient rows together
-    chunk_size=100_000,
-)
-
-protocol.print()
-
-

Operation Compatibility

-

Not all operations can be used with chunked processing. Operations are classified into three categories:

-

Chunkable Operations

-

These operations process each row independently and work with any chunk:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CategoryOperations
Columncol_drop, col_rename, col_cast, col_reorder, col_select, col_duplicate, col_fill_null, col_drop_null, col_drop_zero, col_add, col_add_uuid, col_hash, col_coalesce
Mathmath_add, math_subtract, math_multiply, math_divide, math_clamp, math_abs, math_round, math_set_min, math_set_max, math_add_columns, math_subtract_columns, math_multiply_columns, math_divide_columns, math_percent_of
Stringstr_replace, str_slice, str_truncate, str_lower, str_upper, str_strip, str_pad, str_split, str_concat, str_extract
Datetimedt_year, dt_month, dt_day, dt_week, dt_quarter, dt_year_month, dt_quarter_year, dt_calendar_week, dt_parse, dt_format, dt_diff_days, dt_age_years, dt_is_between, dt_truncate
Mapmap_values, map_discretize, map_bool_to_int, map_null_to_value, map_value_to_null, map_case, map_from_column
Rowsrows_filter, rows_drop, rows_flag, rows_explode, rows_drop_nulls, rows_melt
-

Group-Dependent Operations

-

These operations need all rows for a group together. They work with chunked processing only when partition_key includes their grouping columns:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
OperationGroup ParameterRequirement
rows_uniquecolumnspartition_key must contain columns
rows_deduplicatecolumnspartition_key must contain columns
math_cumsumgroup_bypartition_key must contain group_by
math_rankgroup_bypartition_key must contain group_by
-

Example: To use rows_unique(columns=["patient_id"]), you must set partition_key="patient_id" (or a list containing it).

-

Global Operations (Blocked)

-

These operations require the full dataset and cannot be used with chunked processing:

-
    -
  • rows_sort - Requires global ordering
  • -
  • rows_pivot - Needs all values to determine output columns
  • -
  • rows_sample - Random sampling requires full dataset
  • -
  • rows_head - Requires global ordering
  • -
  • rows_tail - Requires global ordering
  • -
-

Attempting to use these operations will raise a ChunkingError.

-

Validation

-

Before processing, validate that your pipeline is compatible with chunked processing:

-
# Validate without processing
-validation = plan.validate_chunked(
-    schema={"patient_id": pl.Utf8, "age": pl.Int64, "visit_date": pl.Date},
-    partition_key="patient_id"
-)
-
-print(validation)
-# Pipeline is compatible with chunked processing.
-
-# Or validate with a sample DataFrame
-validation = plan.validate_chunked(data=sample_df, partition_key="patient_id")
-
-if not validation.is_valid:
-    for error in validation.errors:
-        print(f"Error: {error}")
-
-

ChunkedProtocol Class

- - -
- - - -

- ChunkedProtocol - - -

-
ChunkedProtocol()
-
- -
- - - -

Protocol for tracking chunked processing with per-chunk information.

-

Tracks the overall processing as well as individual chunk statistics.

- - -

Attributes:

- - - - - - - - - - - - - - - -
NameTypeDescription
VERSION - -
-

Protocol version string.

-
-
- -

Initialize an empty ChunkedProtocol.

- - - - - - - - -
- Source code in transformplan/chunking.py -
def __init__(self) -> None:
-    """Initialize an empty ChunkedProtocol."""
-    self._chunks: list[ChunkInfo] = []
-    self._source_path: str | None = None
-    self._partition_key: list[str] | None = None
-    self._chunk_size: int | None = None
-    self._created_at: str = datetime.now(timezone.utc).isoformat()
-    self._metadata: dict[str, Any] = {}
-    self._operations: list[dict[str, Any]] = []
-
-
- - - -
- - - - - - - -
- - - -

- chunks - - - - property - - -

-
chunks: list[ChunkInfo]
-
- -
- -

List of chunk information.

- -
- -
- -
- - - -

- total_input_rows - - - - property - - -

-
total_input_rows: int
-
- -
- -

Total rows across all input chunks.

- -
- -
- -
- - - -

- total_output_rows - - - - property - - -

-
total_output_rows: int
-
- -
- -

Total rows across all output chunks.

- -
- -
- -
- - - -

- total_elapsed_seconds - - - - property - - -

-
total_elapsed_seconds: float
-
- -
- -

Total processing time across all chunks.

- -
- -
- -
- - - -

- num_chunks - - - - property - - -

-
num_chunks: int
-
- -
- -

Number of chunks processed.

- -
- -
- -
- - - -

- metadata - - - - property - - -

-
metadata: dict[str, Any]
-
- -
- -

Protocol metadata.

- -
- -
- - - - -
- - -

- set_source - - -

-
set_source(path: str, partition_key: list[str] | None, chunk_size: int) -> None
-
- -
- -

Set source file information.

- - -
- Source code in transformplan/chunking.py -
def set_source(
-    self,
-    path: str,
-    partition_key: list[str] | None,
-    chunk_size: int,
-) -> None:
-    """Set source file information."""
-    self._source_path = path
-    self._partition_key = partition_key
-    self._chunk_size = chunk_size
-
-
-
- -
- -
- - -

- set_operations - - -

-
set_operations(operations: list[dict[str, Any]]) -> None
-
- -
- -

Record the operations that were applied.

- - -
- Source code in transformplan/chunking.py -
def set_operations(self, operations: list[dict[str, Any]]) -> None:
-    """Record the operations that were applied."""
-    self._operations = operations
-
-
-
- -
- -
- - -

- set_metadata - - -

-
set_metadata(**kwargs: Any) -> None
-
- -
- -

Set arbitrary metadata on the protocol.

- - -
- Source code in transformplan/chunking.py -
def set_metadata(self, **kwargs: Any) -> None:
-    """Set arbitrary metadata on the protocol."""
-    self._metadata.update(kwargs)
-
-
-
- -
- -
- - -

- add_chunk - - -

-
add_chunk(chunk_info: ChunkInfo) -> None
-
- -
- -

Add information about a processed chunk.

- - -
- Source code in transformplan/chunking.py -
def add_chunk(self, chunk_info: ChunkInfo) -> None:
-    """Add information about a processed chunk."""
-    self._chunks.append(chunk_info)
-
-
-
- -
- -
- - -

- output_hash - - -

-
output_hash() -> str
-
- -
- -

Compute a combined hash of all output chunk hashes.

- - -

Returns:

- - - - - - - - - - - - - -
TypeDescription
- str - -
-

A 16-character hex hash of all chunk output hashes combined.

-
-
- - -
- Source code in transformplan/chunking.py -
def output_hash(self) -> str:
-    """Compute a combined hash of all output chunk hashes.
-
-    Returns:
-        A 16-character hex hash of all chunk output hashes combined.
-    """
-    if not self._chunks:
-        return ""
-    combined = "|".join(c.output_hash for c in self._chunks)
-    return hashlib.sha256(combined.encode()).hexdigest()[:16]
-
-
-
- -
- -
- - -

- to_dict - - -

-
to_dict() -> dict[str, Any]
-
- -
- -

Serialize protocol to a dictionary.

- - -

Returns:

- - - - - - - - - - - - - -
TypeDescription
- dict[str, Any] - -
-

Dictionary representation of the protocol.

-
-
- - -
- Source code in transformplan/chunking.py -
def to_dict(self) -> dict[str, Any]:
-    """Serialize protocol to a dictionary.
-
-    Returns:
-        Dictionary representation of the protocol.
-    """
-    return {
-        "version": self.VERSION,
-        "created_at": self._created_at,
-        "metadata": self._metadata,
-        "source": {
-            "path": self._source_path,
-            "partition_key": self._partition_key,
-            "chunk_size": self._chunk_size,
-        },
-        "operations": self._operations,
-        "summary": {
-            "num_chunks": self.num_chunks,
-            "total_input_rows": self.total_input_rows,
-            "total_output_rows": self.total_output_rows,
-            "total_elapsed_seconds": round(self.total_elapsed_seconds, 4),
-            "output_hash": self.output_hash(),
-        },
-        "chunks": [
-            {
-                "chunk_index": c.chunk_index,
-                "input_rows": c.input_rows,
-                "output_rows": c.output_rows,
-                "input_hash": c.input_hash,
-                "output_hash": c.output_hash,
-                "elapsed_seconds": round(c.elapsed_seconds, 4),
-            }
-            for c in self._chunks
-        ],
-    }
-
-
-
- -
- -
- - -

- from_dict - - - - classmethod - - -

-
from_dict(data: dict[str, Any]) -> ChunkedProtocol
-
- -
- -

Deserialize protocol from a dictionary.

- - -

Returns:

- - - - - - - - - - - - - -
TypeDescription
- ChunkedProtocol - -
-

ChunkedProtocol instance.

-
-
- - -
- Source code in transformplan/chunking.py -
@classmethod
-def from_dict(cls, data: dict[str, Any]) -> ChunkedProtocol:
-    """Deserialize protocol from a dictionary.
-
-    Returns:
-        ChunkedProtocol instance.
-    """
-    protocol = cls()
-    protocol._created_at = data.get("created_at", protocol._created_at)
-    protocol._metadata = data.get("metadata", {})
-
-    source = data.get("source", {})
-    protocol._source_path = source.get("path")
-    protocol._partition_key = source.get("partition_key")
-    protocol._chunk_size = source.get("chunk_size")
-
-    protocol._operations = data.get("operations", [])
-
-    for chunk_data in data.get("chunks", []):
-        protocol._chunks.append(
-            ChunkInfo(
-                chunk_index=chunk_data["chunk_index"],
-                input_rows=chunk_data["input_rows"],
-                output_rows=chunk_data["output_rows"],
-                input_hash=chunk_data["input_hash"],
-                output_hash=chunk_data["output_hash"],
-                elapsed_seconds=chunk_data["elapsed_seconds"],
-            )
-        )
-
-    return protocol
-
-
-
- -
- -
- - -

- to_json - - -

-
to_json(path: str | Path | None = None, indent: int = 2) -> str
-
- -
- -

Serialize protocol to JSON.

- - -

Parameters:

- - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- path - - str | Path | None - -
-

Optional file path to write to.

-
-
- None -
- indent - - int - -
-

JSON indentation level.

-
-
- 2 -
- - -

Returns:

- - - - - - - - - - - - - -
TypeDescription
- str - -
-

JSON string.

-
-
- - -
- Source code in transformplan/chunking.py -
def to_json(self, path: str | Path | None = None, indent: int = 2) -> str:
-    """Serialize protocol to JSON.
-
-    Args:
-        path: Optional file path to write to.
-        indent: JSON indentation level.
-
-    Returns:
-        JSON string.
-    """
-    json_str = json.dumps(self.to_dict(), indent=indent)
-
-    if path is not None:
-        Path(path).write_text(json_str)
-
-    return json_str
-
-
-
- -
- -
- - -

- from_json - - - - classmethod - - -

-
from_json(source: str | Path) -> ChunkedProtocol
-
- -
- -

Deserialize protocol from JSON.

- - -

Parameters:

- - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- source - - str | Path - -
-

Either a JSON string or a path to a JSON file.

-
-
- required -
- - -

Returns:

- - - - - - - - - - - - - -
TypeDescription
- ChunkedProtocol - -
-

ChunkedProtocol instance.

-
-
- - -
- Source code in transformplan/chunking.py -
@classmethod
-def from_json(cls, source: str | Path) -> ChunkedProtocol:
-    """Deserialize protocol from JSON.
-
-    Args:
-        source: Either a JSON string or a path to a JSON file.
-
-    Returns:
-        ChunkedProtocol instance.
-    """
-    if isinstance(source, Path) or (
-        isinstance(source, str) and not source.strip().startswith("{")
-    ):
-        content = Path(source).read_text()
-    else:
-        content = source
-
-    return cls.from_dict(json.loads(content))
-
-
-
- -
- -
- - -

- summary - - -

-
summary() -> str
-
- -
- -

Generate a human-readable summary of the chunked processing.

- - -

Returns:

- - - - - - - - - - - - - -
TypeDescription
- str - -
-

Formatted string summary of the protocol.

-
-
- - -
- Source code in transformplan/chunking.py -
def summary(self) -> str:
-    """Generate a human-readable summary of the chunked processing.
-
-    Returns:
-        Formatted string summary of the protocol.
-    """
-    lines = [
-        "=" * 70,
-        "CHUNKED PROCESSING PROTOCOL",
-        "=" * 70,
-    ]
-
-    if self._metadata:
-        for key, value in self._metadata.items():
-            lines.append(f"{key}: {value}")
-        lines.append("-" * 70)
-
-    # Source info
-    if self._source_path:
-        lines.append(f"Source: {self._source_path}")
-    if self._partition_key:
-        lines.append(f"Partition key: {self._partition_key}")
-    if self._chunk_size:
-        lines.append(f"Target chunk size: {self._chunk_size:,}")
-    lines.append("-" * 70)
-
-    # Summary stats
-    lines.append(f"Chunks processed: {self.num_chunks}")
-    lines.append(f"Total input rows: {self.total_input_rows:,}")
-    lines.append(f"Total output rows: {self.total_output_rows:,}")
-    rows_diff = self.total_output_rows - self.total_input_rows
-    if rows_diff != 0:
-        lines.append(f"Row change: {rows_diff:+,}")
-    lines.append(f"Total time: {self.total_elapsed_seconds:.4f}s")
-    if self.num_chunks > 0:
-        avg_time = self.total_elapsed_seconds / self.num_chunks
-        lines.append(f"Avg time per chunk: {avg_time:.4f}s")
-    lines.append(f"Output hash: {self.output_hash()}")
-    lines.append("-" * 70)
-
-    # Per-chunk details
-    if self._chunks:
-        lines.append("")
-        lines.append(
-            f"{'#':<6} {'Input':<12} {'Output':<12} {'Change':<10} {'Time':<10} {'Hash':<16}"
-        )
-        lines.append("-" * 70)
-
-        for chunk in self._chunks:
-            idx = str(chunk.chunk_index)
-            input_rows = f"{chunk.input_rows:,}"
-            output_rows = f"{chunk.output_rows:,}"
-            change = chunk.output_rows - chunk.input_rows
-            change_str = f"{change:+,}" if change != 0 else "-"
-            time_str = f"{chunk.elapsed_seconds:.4f}s"
-            hash_str = chunk.output_hash
-
-            lines.append(
-                f"{idx:<6} {input_rows:<12} {output_rows:<12} {change_str:<10} {time_str:<10} {hash_str:<16}"
-            )
-
-    lines.append("=" * 70)
-    return "\n".join(lines)
-
-
-
- -
- -
- - -

- print - - -

-
print() -> None
-
- -
- -

Print the protocol summary to stdout.

- - -
- Source code in transformplan/chunking.py -
def print(self) -> None:
-    """Print the protocol summary to stdout."""
-    print(self.summary())
-
-
-
- -
- - - -
- -
- -

ChunkValidationResult Class

- - -
- - - -

- ChunkValidationResult - - - - dataclass - - -

-
ChunkValidationResult(
-    is_valid: bool,
-    errors: list[str] = list(),
-    warnings: list[str] = list(),
-    global_operations: list[str] = list(),
-    group_dependent_ops: list[tuple[str, list[str] | None]] = list(),
-)
-
- -
- - - -

Result of validating a pipeline for chunked processing.

- - -

Attributes:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
is_valid - bool - -
-

Whether the pipeline can be processed in chunks.

-
-
errors - list[str] - -
-

List of error messages explaining incompatibilities.

-
-
warnings - list[str] - -
-

List of warning messages (non-blocking).

-
-
global_operations - list[str] - -
-

Names of operations that require full dataset.

-
-
group_dependent_ops - list[tuple[str, list[str] | None]] - -
-

List of (operation, columns) for group-dependent ops.

-
-
- - - - - - - - - - - -
- - - - - - - -
- - - -

- is_valid - - - - instance-attribute - - -

-
is_valid: bool
-
- -
- -
- -
- -
- - - -

- errors - - - - class-attribute - instance-attribute - - -

-
errors: list[str] = field(default_factory=list)
-
- -
- -
- -
- -
- - - -

- warnings - - - - class-attribute - instance-attribute - - -

-
warnings: list[str] = field(default_factory=list)
-
- -
- -
- -
- -
- - - -

- global_operations - - - - class-attribute - instance-attribute - - -

-
global_operations: list[str] = field(default_factory=list)
-
- -
- -
- -
- -
- - - -

- group_dependent_ops - - - - class-attribute - instance-attribute - - -

-
group_dependent_ops: list[tuple[str, list[str] | None]] = field(
-    default_factory=list
-)
-
- -
- -
- -
- - - - - - -
- -
- -

ChunkingError Exception

- - -
- - - -

- ChunkingError - - -

-
ChunkingError(
-    message: str, validation_result: ChunkValidationResult | None = None
-)
-
- -
-

- Bases: Exception

- - - -

Raised when a pipeline is incompatible with chunked processing.

- - -

Attributes:

- - - - - - - - - - - - - - - -
NameTypeDescription
validation_result - -
-

The validation result containing error details.

-
-
- -

Initialize ChunkingError with message and optional validation result.

- - - - - - - - -
- Source code in transformplan/chunking.py -
def __init__(
-    self, message: str, validation_result: ChunkValidationResult | None = None
-) -> None:
-    """Initialize ChunkingError with message and optional validation result."""
-    super().__init__(message)
-    self.validation_result = validation_result
-
-
- - - -
- - - - - - - - - - - - -
- -
- -

Example: Processing Patient Records

-
import polars as pl
-from transformplan import TransformPlan, Col, ChunkingError
-
-# Build pipeline with group-dependent operation
-plan = (
-    TransformPlan()
-    .col_rename(column="PatientID", new_name="patient_id")
-    .dt_age_years(birth_column="date_of_birth", new_column="age")
-    .rows_filter(Col("age") >= 18)
-    .rows_unique(columns=["patient_id", "visit_date"])  # Needs partition key
-    .col_drop("date_of_birth")
-)
-
-# Validate first
-validation = plan.validate_chunked(
-    schema={
-        "PatientID": pl.Utf8,
-        "date_of_birth": pl.Date,
-        "visit_date": pl.Date,
-        "diagnosis": pl.Utf8,
-    },
-    partition_key="PatientID"
-)
-
-if validation.is_valid:
-    # Process the large file
-    result, protocol = plan.process_chunked(
-        source="patients_archive.parquet",
-        partition_key="PatientID",
-        chunk_size=50_000,
-    )
-
-    # View processing summary
-    protocol.print()
-
-    # Save audit trail
-    protocol.to_json("chunked_audit.json")
-else:
-    print("Pipeline incompatible with chunking:")
-    for error in validation.errors:
-        print(f"  - {error}")
-
-

Example Output

-
======================================================================
-CHUNKED PROCESSING PROTOCOL
-======================================================================
-Source: patients_archive.parquet
-Partition key: ['PatientID']
-Target chunk size: 50,000
-----------------------------------------------------------------------
-Chunks processed: 24
-Total input rows: 1,187,432
-Total output rows: 892,156
-Row change: -295,276
-Total time: 12.4523s
-Avg time per chunk: 0.5188s
-Output hash: 7a3b2c1d4e5f6789
-----------------------------------------------------------------------
-
-#      Input        Output       Change     Time       Hash
-----------------------------------------------------------------------
-0      49,832       37,291       -12,541    0.4821s    a1b2c3d4e5f67890
-1      50,127       38,456       -11,671    0.5123s    b2c3d4e5f6789012
-2      49,956       37,892       -12,064    0.4956s    c3d4e5f678901234
-...
-======================================================================
-
- - - - - - - - - - - - - -
-
- - - -
- - - -
- - - -
-
-
-
- - - - - - - - - - - - - \ No newline at end of file diff --git a/site/api/filters/index.html b/site/api/filters/index.html deleted file mode 100644 index 751797b..0000000 --- a/site/api/filters/index.html +++ /dev/null @@ -1,8016 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Filters - TransformPlan - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - Skip to content - - -
-
- -
- - - - - - -
- - -
- -
- - - - - - -
-
- - - -
-
-
- - - - - -
-
-
- - - -
-
-
- - - -
-
-
- - - -
- -
- - - - - - - - -

Filters

-

Serializable filter expressions for row filtering operations.

-

Overview

-

The filter system provides a way to build complex filter conditions that can be serialized to JSON and deserialized back. This enables reproducible pipelines that can be saved and shared.

-
from transformplan import Col, Filter
-
-# Build a filter
-filter_expr = (Col("age") >= 18) & (Col("status") == "active")
-
-# Use in pipeline
-plan = TransformPlan().rows_filter(filter_expr)
-
-# Serialize
-filter_dict = filter_expr.to_dict()
-
-# Deserialize
-restored = Filter.from_dict(filter_dict)
-
-

Col Class

- - -
- - - -

- Col - - -

-
Col(name: str)
-
- -
- - - -

Column reference for building filter expressions.

-

Col provides a fluent interface for creating filter conditions on DataFrame -columns. Use comparison operators and methods to build filters that can be -combined using & (and), | (or), and ~ (not).

- - -

Parameters:

- - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- name - - str - -
-

The name of the column to reference.

-
-
- required -
- - -
- Example -
-
-
-

Comparison operators

-

Col("age") >= 18 -Col("status") == "active" -Col("price") < 100

-

String methods

-

Col("email").str_contains("@company.com") -Col("name").str_starts_with("A")

-

Null checks

-

Col("optional").is_null() -Col("required").is_not_null()

-

Membership

-

Col("country").is_in(["US", "CA", "MX"]) -Col("age").between(18, 65)

-

Combining conditions

-

(Col("age") >= 18) & (Col("status") == "active") -(Col("role") == "admin") | (Col("role") == "moderator")

-
-
-
-
-

Initialize a column reference.

- - -

Parameters:

- - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- name - - str - -
-

The name of the column to reference.

-
-
- required -
- - - - - - - - -
- Source code in transformplan/filters.py -
def __init__(self, name: str) -> None:
-    """Initialize a column reference.
-
-    Args:
-        name: The name of the column to reference.
-    """
-    self.name = name
-
-
- - - -
- - - - - - - - - - -
- - -

- __eq__ - - -

-
__eq__(value: Any) -> Eq
-
- -
- -

Create an equality filter (column == value).

- - -

Parameters:

- - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- value - - Any - -
-

Value to compare against.

-
-
- required -
- - -

Returns:

- - - - - - - - - - - - - -
TypeDescription
- Eq - -
-

Eq filter for column equals value.

-
-
- - -
- Source code in transformplan/filters.py -
def __eq__(self, value: Any) -> Eq:  # type: ignore[override]
-    """Create an equality filter (column == value).
-
-    Args:
-        value: Value to compare against.
-
-    Returns:
-        Eq filter for column equals value.
-    """
-    return Eq(self.name, value)
-
-
-
- -
- -
- - -

- __ne__ - - -

-
__ne__(value: Any) -> Ne
-
- -
- -

Create an inequality filter (column != value).

- - -

Parameters:

- - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- value - - Any - -
-

Value to compare against.

-
-
- required -
- - -

Returns:

- - - - - - - - - - - - - -
TypeDescription
- Ne - -
-

Ne filter for column not equals value.

-
-
- - -
- Source code in transformplan/filters.py -
def __ne__(self, value: Any) -> Ne:  # type: ignore[override]
-    """Create an inequality filter (column != value).
-
-    Args:
-        value: Value to compare against.
-
-    Returns:
-        Ne filter for column not equals value.
-    """
-    return Ne(self.name, value)
-
-
-
- -
- -
- - -

- __gt__ - - -

-
__gt__(value: Any) -> Gt
-
- -
- -

Create a greater-than filter (column > value).

- - -

Parameters:

- - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- value - - Any - -
-

Value to compare against.

-
-
- required -
- - -

Returns:

- - - - - - - - - - - - - -
TypeDescription
- Gt - -
-

Gt filter for column greater than value.

-
-
- - -
- Source code in transformplan/filters.py -
def __gt__(self, value: Any) -> Gt:
-    """Create a greater-than filter (column > value).
-
-    Args:
-        value: Value to compare against.
-
-    Returns:
-        Gt filter for column greater than value.
-    """
-    return Gt(self.name, value)
-
-
-
- -
- -
- - -

- __ge__ - - -

-
__ge__(value: Any) -> Ge
-
- -
- -

Create a greater-or-equal filter (column >= value).

- - -

Parameters:

- - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- value - - Any - -
-

Value to compare against.

-
-
- required -
- - -

Returns:

- - - - - - - - - - - - - -
TypeDescription
- Ge - -
-

Ge filter for column greater than or equal to value.

-
-
- - -
- Source code in transformplan/filters.py -
def __ge__(self, value: Any) -> Ge:
-    """Create a greater-or-equal filter (column >= value).
-
-    Args:
-        value: Value to compare against.
-
-    Returns:
-        Ge filter for column greater than or equal to value.
-    """
-    return Ge(self.name, value)
-
-
-
- -
- -
- - -

- __lt__ - - -

-
__lt__(value: Any) -> Lt
-
- -
- -

Create a less-than filter (column < value).

- - -

Parameters:

- - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- value - - Any - -
-

Value to compare against.

-
-
- required -
- - -

Returns:

- - - - - - - - - - - - - -
TypeDescription
- Lt - -
-

Lt filter for column less than value.

-
-
- - -
- Source code in transformplan/filters.py -
def __lt__(self, value: Any) -> Lt:
-    """Create a less-than filter (column < value).
-
-    Args:
-        value: Value to compare against.
-
-    Returns:
-        Lt filter for column less than value.
-    """
-    return Lt(self.name, value)
-
-
-
- -
- -
- - -

- __le__ - - -

-
__le__(value: Any) -> Le
-
- -
- -

Create a less-or-equal filter (column <= value).

- - -

Parameters:

- - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- value - - Any - -
-

Value to compare against.

-
-
- required -
- - -

Returns:

- - - - - - - - - - - - - -
TypeDescription
- Le - -
-

Le filter for column less than or equal to value.

-
-
- - -
- Source code in transformplan/filters.py -
def __le__(self, value: Any) -> Le:
-    """Create a less-or-equal filter (column <= value).
-
-    Args:
-        value: Value to compare against.
-
-    Returns:
-        Le filter for column less than or equal to value.
-    """
-    return Le(self.name, value)
-
-
-
- -
- -
- - -

- is_in - - -

-
is_in(values: Sequence[Any]) -> IsIn
-
- -
- -

Create a membership filter (column in values).

- - -

Parameters:

- - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- values - - Sequence[Any] - -
-

Sequence of values to check membership against.

-
-
- required -
- - -

Returns:

- - - - - - - - - - - - - -
TypeDescription
- IsIn - -
-

IsIn filter for column value in the given sequence.

-
-
- - -
- Example -
-
-
-

Col("status").is_in(["active", "pending"])

-
-
-
-
- -
- Source code in transformplan/filters.py -
def is_in(self, values: Sequence[Any]) -> IsIn:
-    """Create a membership filter (column in values).
-
-    Args:
-        values: Sequence of values to check membership against.
-
-    Returns:
-        IsIn filter for column value in the given sequence.
-
-    Example:
-        >>> Col("status").is_in(["active", "pending"])
-    """
-    return IsIn(self.name, values)
-
-
-
- -
- -
- - -

- is_null - - -

-
is_null() -> IsNull
-
- -
- -

Create a null check filter (column is null).

- - -

Returns:

- - - - - - - - - - - - - -
TypeDescription
- IsNull - -
-

IsNull filter for column is null.

-
-
- - -
- Example -
-
-
-

Col("optional_field").is_null()

-
-
-
-
- -
- Source code in transformplan/filters.py -
def is_null(self) -> IsNull:
-    """Create a null check filter (column is null).
-
-    Returns:
-        IsNull filter for column is null.
-
-    Example:
-        >>> Col("optional_field").is_null()
-    """
-    return IsNull(self.name)
-
-
-
- -
- -
- - -

- is_not_null - - -

-
is_not_null() -> IsNotNull
-
- -
- -

Create a not-null check filter (column is not null).

- - -

Returns:

- - - - - - - - - - - - - -
TypeDescription
- IsNotNull - -
-

IsNotNull filter for column is not null.

-
-
- - -
- Example -
-
-
-

Col("required_field").is_not_null()

-
-
-
-
- -
- Source code in transformplan/filters.py -
def is_not_null(self) -> IsNotNull:
-    """Create a not-null check filter (column is not null).
-
-    Returns:
-        IsNotNull filter for column is not null.
-
-    Example:
-        >>> Col("required_field").is_not_null()
-    """
-    return IsNotNull(self.name)
-
-
-
- -
- -
- - -

- str_contains - - -

-
str_contains(pattern: str, literal: bool = True) -> StrContains
-
- -
- -

Create a string contains filter.

- - -

Parameters:

- - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- pattern - - str - -
-

Substring or regex pattern to search for.

-
-
- required -
- literal - - bool - -
-

If True, treat pattern as literal string. If False, as regex.

-
-
- True -
- - -

Returns:

- - - - - - - - - - - - - -
TypeDescription
- StrContains - -
-

StrContains filter for column containing pattern.

-
-
- - -
- Example -
-
-
-

Col("email").str_contains("@company.com") -Col("description").str_contains(r"\d+", literal=False)

-
-
-
-
- -
- Source code in transformplan/filters.py -
def str_contains(self, pattern: str, literal: bool = True) -> StrContains:
-    """Create a string contains filter.
-
-    Args:
-        pattern: Substring or regex pattern to search for.
-        literal: If True, treat pattern as literal string. If False, as regex.
-
-    Returns:
-        StrContains filter for column containing pattern.
-
-    Example:
-        >>> Col("email").str_contains("@company.com")
-        >>> Col("description").str_contains(r"\\d+", literal=False)
-    """
-    return StrContains(self.name, pattern, literal)
-
-
-
- -
- -
- - -

- str_starts_with - - -

-
str_starts_with(prefix: str) -> StrStartsWith
-
- -
- -

Create a string starts-with filter.

- - -

Parameters:

- - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- prefix - - str - -
-

Prefix to check for.

-
-
- required -
- - -

Returns:

- - - - - - - - - - - - - -
TypeDescription
- StrStartsWith - -
-

StrStartsWith filter for column starting with prefix.

-
-
- - -
- Example -
-
-
-

Col("code").str_starts_with("PRD-")

-
-
-
-
- -
- Source code in transformplan/filters.py -
def str_starts_with(self, prefix: str) -> StrStartsWith:
-    """Create a string starts-with filter.
-
-    Args:
-        prefix: Prefix to check for.
-
-    Returns:
-        StrStartsWith filter for column starting with prefix.
-
-    Example:
-        >>> Col("code").str_starts_with("PRD-")
-    """
-    return StrStartsWith(self.name, prefix)
-
-
-
- -
- -
- - -

- str_ends_with - - -

-
str_ends_with(suffix: str) -> StrEndsWith
-
- -
- -

Create a string ends-with filter.

- - -

Parameters:

- - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- suffix - - str - -
-

Suffix to check for.

-
-
- required -
- - -

Returns:

- - - - - - - - - - - - - -
TypeDescription
- StrEndsWith - -
-

StrEndsWith filter for column ending with suffix.

-
-
- - -
- Example -
-
-
-

Col("filename").str_ends_with(".csv")

-
-
-
-
- -
- Source code in transformplan/filters.py -
def str_ends_with(self, suffix: str) -> StrEndsWith:
-    """Create a string ends-with filter.
-
-    Args:
-        suffix: Suffix to check for.
-
-    Returns:
-        StrEndsWith filter for column ending with suffix.
-
-    Example:
-        >>> Col("filename").str_ends_with(".csv")
-    """
-    return StrEndsWith(self.name, suffix)
-
-
-
- -
- -
- - -

- between - - -

-
between(lower: Any, upper: Any) -> Between
-
- -
- -

Create a range filter (lower <= column <= upper).

- - -

Parameters:

- - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- lower - - Any - -
-

Lower bound (inclusive).

-
-
- required -
- upper - - Any - -
-

Upper bound (inclusive).

-
-
- required -
- - -

Returns:

- - - - - - - - - - - - - -
TypeDescription
- Between - -
-

Between filter for column within range.

-
-
- - -
- Example -
-
-
-

Col("age").between(18, 65) -Col("date").between("2024-01-01", "2024-12-31")

-
-
-
-
- -
- Source code in transformplan/filters.py -
def between(self, lower: Any, upper: Any) -> Between:
-    """Create a range filter (lower <= column <= upper).
-
-    Args:
-        lower: Lower bound (inclusive).
-        upper: Upper bound (inclusive).
-
-    Returns:
-        Between filter for column within range.
-
-    Example:
-        >>> Col("age").between(18, 65)
-        >>> Col("date").between("2024-01-01", "2024-12-31")
-    """
-    return Between(self.name, lower, upper)
-
-
-
- -
- - - -
- -
- -

Filter Base Class

- - -
- - - -

- Filter - - -

- - -
-

- Bases: ABC

- - - -

Abstract base class for all filter expressions.

-

Filters are composable, serializable expressions that define row selection -criteria. They can be combined using logical operators (&, |, ~) and -serialized to dictionaries for storage and transmission.

- - -
- Subclasses must implement -
    -
  • to_expr(): Convert to a Polars expression
  • -
  • to_dict(): Serialize to a dictionary
  • -
  • _from_dict(): Deserialize from a dictionary (classmethod)
  • -
-
- -
- Example -
-
-
-

filter1 = Col("age") >= 18 -filter2 = Col("status") == "active" -combined = filter1 & filter2 # And filter -inverted = ~filter1 # Not filter

-
-
-
-
- - - - - - - - - - -
- - - - - - - - - - -
- - -

- to_expr - - - - abstractmethod - - -

-
to_expr() -> Expr
-
- -
- -

Convert to a Polars expression.

- - -

Returns:

- - - - - - - - - - - - - -
TypeDescription
- Expr - -
-

A Polars expression that can be used with DataFrame.filter().

-
-
- - -
- Source code in transformplan/filters.py -
58
-59
-60
-61
-62
-63
-64
-65
@abstractmethod
-def to_expr(self) -> pl.Expr:
-    """Convert to a Polars expression.
-
-    Returns:
-        A Polars expression that can be used with DataFrame.filter().
-    """
-    ...
-
-
-
- -
- -
- - -

- to_dict - - - - abstractmethod - - -

-
to_dict() -> dict[str, Any]
-
- -
- -

Serialize to a dictionary for JSON storage.

-

The dictionary includes a 'type' key identifying the filter class, -plus any parameters needed to reconstruct the filter.

- - -

Returns:

- - - - - - - - - - - - - -
TypeDescription
- dict[str, Any] - -
-

Dictionary representation of the filter.

-
-
- - -
- Source code in transformplan/filters.py -
67
-68
-69
-70
-71
-72
-73
-74
-75
-76
-77
@abstractmethod
-def to_dict(self) -> dict[str, Any]:
-    """Serialize to a dictionary for JSON storage.
-
-    The dictionary includes a 'type' key identifying the filter class,
-    plus any parameters needed to reconstruct the filter.
-
-    Returns:
-        Dictionary representation of the filter.
-    """
-    ...
-
-
-
- -
- -
- - -

- from_dict - - - - classmethod - - -

-
from_dict(data: dict[str, Any]) -> Filter
-
- -
- -

Deserialize a filter from a dictionary.

-

Uses the 'type' key to determine which filter class to instantiate.

- - -

Parameters:

- - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- data - - dict[str, Any] - -
-

Dictionary with 'type' key and filter parameters.

-
-
- required -
- - -

Returns:

- - - - - - - - - - - - - -
TypeDescription
- Filter - -
-

Reconstructed Filter instance.

-
-
- - -

Raises:

- - - - - - - - - - - - - -
TypeDescription
- ValueError - -
-

If 'type' is missing or unknown.

-
-
- - -
- Example -
-
-
-

data = {"type": "eq", "column": "status", "value": "active"} -filter_obj = Filter.from_dict(data)

-
-
-
-
- -
- Source code in transformplan/filters.py -
@classmethod
-def from_dict(cls, data: dict[str, Any]) -> Filter:
-    """Deserialize a filter from a dictionary.
-
-    Uses the 'type' key to determine which filter class to instantiate.
-
-    Args:
-        data: Dictionary with 'type' key and filter parameters.
-
-    Returns:
-        Reconstructed Filter instance.
-
-    Raises:
-        ValueError: If 'type' is missing or unknown.
-
-    Example:
-        >>> data = {"type": "eq", "column": "status", "value": "active"}
-        >>> filter_obj = Filter.from_dict(data)
-    """
-    filter_type = data.get("type")
-    if filter_type is None:
-        raise ValueError("Missing 'type' in filter dict")
-
-    filter_cls = _FILTER_REGISTRY.get(filter_type)
-    if filter_cls is None:
-        raise ValueError(f"Unknown filter type: {filter_type}")
-
-    return filter_cls._from_dict(data)
-
-
-
- -
- - - -
- -
- -

Comparison Filters

-

Eq (Equal)

- - -
- - - -

- Eq - - - - dataclass - - -

-
Eq(column: str, value: Any)
-
- -
-

- Bases: Filter

- - - -

Equality filter: column == value.

- - -

Attributes:

- - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
column - str - -
-

Name of the column to compare.

-
-
value - Any - -
-

Value to compare against.

-
-
- - - - - - - - - - - -
- - - - - - - - - - -
- - -

- to_expr - - -

-
to_expr() -> Expr
-
- -
- -

Convert to Polars equality expression.

- - -
- Source code in transformplan/filters.py -
def to_expr(self) -> pl.Expr:
-    """Convert to Polars equality expression."""
-    return pl.col(self.column) == self.value
-
-
-
- -
- -
- - -

- to_dict - - -

-
to_dict() -> dict[str, Any]
-
- -
- -

Serialize to dictionary.

- - -
- Source code in transformplan/filters.py -
def to_dict(self) -> dict[str, Any]:
-    """Serialize to dictionary."""
-    return {"type": "eq", "column": self.column, "value": self.value}
-
-
-
- -
- - - -
- -
- -

Ne (Not Equal)

- - -
- - - -

- Ne - - - - dataclass - - -

-
Ne(column: str, value: Any)
-
- -
-

- Bases: Filter

- - - -

Inequality filter: column != value.

- - -

Attributes:

- - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
column - str - -
-

Name of the column to compare.

-
-
value - Any - -
-

Value to compare against.

-
-
- - - - - - - - - - - -
- - - - - - - - - - -
- - -

- to_expr - - -

-
to_expr() -> Expr
-
- -
- -

Convert to Polars inequality expression.

- - -
- Source code in transformplan/filters.py -
def to_expr(self) -> pl.Expr:
-    """Convert to Polars inequality expression."""
-    return pl.col(self.column) != self.value
-
-
-
- -
- -
- - -

- to_dict - - -

-
to_dict() -> dict[str, Any]
-
- -
- -

Serialize to dictionary.

- - -
- Source code in transformplan/filters.py -
def to_dict(self) -> dict[str, Any]:
-    """Serialize to dictionary."""
-    return {"type": "ne", "column": self.column, "value": self.value}
-
-
-
- -
- - - -
- -
- -

Gt (Greater Than)

- - -
- - - -

- Gt - - - - dataclass - - -

-
Gt(column: str, value: Any)
-
- -
-

- Bases: Filter

- - - -

Greater-than filter: column > value.

- - -

Attributes:

- - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
column - str - -
-

Name of the column to compare.

-
-
value - Any - -
-

Value to compare against.

-
-
- - - - - - - - - - - -
- - - - - - - - - - -
- - -

- to_expr - - -

-
to_expr() -> Expr
-
- -
- -

Convert to Polars greater-than expression.

- - -
- Source code in transformplan/filters.py -
def to_expr(self) -> pl.Expr:
-    """Convert to Polars greater-than expression."""
-    return pl.col(self.column) > self.value
-
-
-
- -
- -
- - -

- to_dict - - -

-
to_dict() -> dict[str, Any]
-
- -
- -

Serialize to dictionary.

- - -
- Source code in transformplan/filters.py -
def to_dict(self) -> dict[str, Any]:
-    """Serialize to dictionary."""
-    return {"type": "gt", "column": self.column, "value": self.value}
-
-
-
- -
- - - -
- -
- -

Ge (Greater Than or Equal)

- - -
- - - -

- Ge - - - - dataclass - - -

-
Ge(column: str, value: Any)
-
- -
-

- Bases: Filter

- - - -

Greater-or-equal filter: column >= value.

- - -

Attributes:

- - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
column - str - -
-

Name of the column to compare.

-
-
value - Any - -
-

Value to compare against.

-
-
- - - - - - - - - - - -
- - - - - - - - - - -
- - -

- to_expr - - -

-
to_expr() -> Expr
-
- -
- -

Convert to Polars greater-or-equal expression.

- - -
- Source code in transformplan/filters.py -
def to_expr(self) -> pl.Expr:
-    """Convert to Polars greater-or-equal expression."""
-    return pl.col(self.column) >= self.value
-
-
-
- -
- -
- - -

- to_dict - - -

-
to_dict() -> dict[str, Any]
-
- -
- -

Serialize to dictionary.

- - -
- Source code in transformplan/filters.py -
def to_dict(self) -> dict[str, Any]:
-    """Serialize to dictionary."""
-    return {"type": "ge", "column": self.column, "value": self.value}
-
-
-
- -
- - - -
- -
- -

Lt (Less Than)

- - -
- - - -

- Lt - - - - dataclass - - -

-
Lt(column: str, value: Any)
-
- -
-

- Bases: Filter

- - - -

Less-than filter: column < value.

- - -

Attributes:

- - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
column - str - -
-

Name of the column to compare.

-
-
value - Any - -
-

Value to compare against.

-
-
- - - - - - - - - - - -
- - - - - - - - - - -
- - -

- to_expr - - -

-
to_expr() -> Expr
-
- -
- -

Convert to Polars less-than expression.

- - -
- Source code in transformplan/filters.py -
def to_expr(self) -> pl.Expr:
-    """Convert to Polars less-than expression."""
-    return pl.col(self.column) < self.value
-
-
-
- -
- -
- - -

- to_dict - - -

-
to_dict() -> dict[str, Any]
-
- -
- -

Serialize to dictionary.

- - -
- Source code in transformplan/filters.py -
def to_dict(self) -> dict[str, Any]:
-    """Serialize to dictionary."""
-    return {"type": "lt", "column": self.column, "value": self.value}
-
-
-
- -
- - - -
- -
- -

Le (Less Than or Equal)

- - -
- - - -

- Le - - - - dataclass - - -

-
Le(column: str, value: Any)
-
- -
-

- Bases: Filter

- - - -

Less-or-equal filter: column <= value.

- - -

Attributes:

- - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
column - str - -
-

Name of the column to compare.

-
-
value - Any - -
-

Value to compare against.

-
-
- - - - - - - - - - - -
- - - - - - - - - - -
- - -

- to_expr - - -

-
to_expr() -> Expr
-
- -
- -

Convert to Polars less-or-equal expression.

- - -
- Source code in transformplan/filters.py -
def to_expr(self) -> pl.Expr:
-    """Convert to Polars less-or-equal expression."""
-    return pl.col(self.column) <= self.value
-
-
-
- -
- -
- - -

- to_dict - - -

-
to_dict() -> dict[str, Any]
-
- -
- -

Serialize to dictionary.

- - -
- Source code in transformplan/filters.py -
def to_dict(self) -> dict[str, Any]:
-    """Serialize to dictionary."""
-    return {"type": "le", "column": self.column, "value": self.value}
-
-
-
- -
- - - -
- -
- -

IsIn

- - -
- - - -

- IsIn - - - - dataclass - - -

-
IsIn(column: str, values: Sequence[Any])
-
- -
-

- Bases: Filter

- - - -

Membership filter: column value in list of values.

- - -

Attributes:

- - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
column - str - -
-

Name of the column to check.

-
-
values - Sequence[Any] - -
-

Sequence of values to check membership against.

-
-
- - - - - - - - - - - -
- - - - - - - - - - -
- - -

- to_expr - - -

-
to_expr() -> Expr
-
- -
- -

Convert to Polars is_in expression.

- - -
- Source code in transformplan/filters.py -
def to_expr(self) -> pl.Expr:
-    """Convert to Polars is_in expression."""
-    return pl.col(self.column).is_in(self.values)
-
-
-
- -
- -
- - -

- to_dict - - -

-
to_dict() -> dict[str, Any]
-
- -
- -

Serialize to dictionary.

- - -
- Source code in transformplan/filters.py -
def to_dict(self) -> dict[str, Any]:
-    """Serialize to dictionary."""
-    return {"type": "is_in", "column": self.column, "values": list(self.values)}
-
-
-
- -
- - - -
- -
- -

Between

- - -
- - - -

- Between - - - - dataclass - - -

-
Between(column: str, lower: Any, upper: Any)
-
- -
-

- Bases: Filter

- - - -

Range filter: lower <= column <= upper.

- - -

Attributes:

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
column - str - -
-

Name of the column to check.

-
-
lower - Any - -
-

Lower bound (inclusive).

-
-
upper - Any - -
-

Upper bound (inclusive).

-
-
- - - - - - - - - - - -
- - - - - - - - - - -
- - -

- to_expr - - -

-
to_expr() -> Expr
-
- -
- -

Convert to Polars is_between expression.

- - -
- Source code in transformplan/filters.py -
def to_expr(self) -> pl.Expr:
-    """Convert to Polars is_between expression."""
-    return pl.col(self.column).is_between(self.lower, self.upper)
-
-
-
- -
- -
- - -

- to_dict - - -

-
to_dict() -> dict[str, Any]
-
- -
- -

Serialize to dictionary.

- - -
- Source code in transformplan/filters.py -
def to_dict(self) -> dict[str, Any]:
-    """Serialize to dictionary."""
-    return {
-        "type": "between",
-        "column": self.column,
-        "lower": self.lower,
-        "upper": self.upper,
-    }
-
-
-
- -
- - - -
- -
- -

Null Filters

-

IsNull

- - -
- - - -

- IsNull - - - - dataclass - - -

-
IsNull(column: str)
-
- -
-

- Bases: Filter

- - - -

Null check filter: column is null.

- - -

Attributes:

- - - - - - - - - - - - - - - -
NameTypeDescription
column - str - -
-

Name of the column to check.

-
-
- - - - - - - - - - - -
- - - - - - - - - - -
- - -

- to_expr - - -

-
to_expr() -> Expr
-
- -
- -

Convert to Polars is_null expression.

- - -
- Source code in transformplan/filters.py -
def to_expr(self) -> pl.Expr:
-    """Convert to Polars is_null expression."""
-    return pl.col(self.column).is_null()
-
-
-
- -
- -
- - -

- to_dict - - -

-
to_dict() -> dict[str, Any]
-
- -
- -

Serialize to dictionary.

- - -
- Source code in transformplan/filters.py -
def to_dict(self) -> dict[str, Any]:
-    """Serialize to dictionary."""
-    return {"type": "is_null", "column": self.column}
-
-
-
- -
- - - -
- -
- -

IsNotNull

- - -
- - - -

- IsNotNull - - - - dataclass - - -

-
IsNotNull(column: str)
-
- -
-

- Bases: Filter

- - - -

Not-null check filter: column is not null.

- - -

Attributes:

- - - - - - - - - - - - - - - -
NameTypeDescription
column - str - -
-

Name of the column to check.

-
-
- - - - - - - - - - - -
- - - - - - - - - - -
- - -

- to_expr - - -

-
to_expr() -> Expr
-
- -
- -

Convert to Polars is_not_null expression.

- - -
- Source code in transformplan/filters.py -
def to_expr(self) -> pl.Expr:
-    """Convert to Polars is_not_null expression."""
-    return pl.col(self.column).is_not_null()
-
-
-
- -
- -
- - -

- to_dict - - -

-
to_dict() -> dict[str, Any]
-
- -
- -

Serialize to dictionary.

- - -
- Source code in transformplan/filters.py -
def to_dict(self) -> dict[str, Any]:
-    """Serialize to dictionary."""
-    return {"type": "is_not_null", "column": self.column}
-
-
-
- -
- - - -
- -
- -

String Filters

-

StrContains

- - -
- - - -

- StrContains - - - - dataclass - - -

-
StrContains(column: str, pattern: str, literal: bool = True)
-
- -
-

- Bases: Filter

- - - -

String contains filter: column contains pattern.

- - -

Attributes:

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
column - str - -
-

Name of the string column to search.

-
-
pattern - str - -
-

Substring or regex pattern to find.

-
-
literal - bool - -
-

If True, treat pattern as literal. If False, as regex.

-
-
- - - - - - - - - - - -
- - - - - - - - - - -
- - -

- to_expr - - -

-
to_expr() -> Expr
-
- -
- -

Convert to Polars str.contains expression.

- - -
- Source code in transformplan/filters.py -
def to_expr(self) -> pl.Expr:
-    """Convert to Polars str.contains expression."""
-    return pl.col(self.column).str.contains(self.pattern, literal=self.literal)
-
-
-
- -
- -
- - -

- to_dict - - -

-
to_dict() -> dict[str, Any]
-
- -
- -

Serialize to dictionary.

- - -
- Source code in transformplan/filters.py -
def to_dict(self) -> dict[str, Any]:
-    """Serialize to dictionary."""
-    return {
-        "type": "str_contains",
-        "column": self.column,
-        "pattern": self.pattern,
-        "literal": self.literal,
-    }
-
-
-
- -
- - - -
- -
- -

StrStartsWith

- - -
- - - -

- StrStartsWith - - - - dataclass - - -

-
StrStartsWith(column: str, prefix: str)
-
- -
-

- Bases: Filter

- - - -

String starts-with filter: column starts with prefix.

- - -

Attributes:

- - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
column - str - -
-

Name of the string column to check.

-
-
prefix - str - -
-

Prefix to match at the start.

-
-
- - - - - - - - - - - -
- - - - - - - - - - -
- - -

- to_expr - - -

-
to_expr() -> Expr
-
- -
- -

Convert to Polars str.starts_with expression.

- - -
- Source code in transformplan/filters.py -
def to_expr(self) -> pl.Expr:
-    """Convert to Polars str.starts_with expression."""
-    return pl.col(self.column).str.starts_with(self.prefix)
-
-
-
- -
- -
- - -

- to_dict - - -

-
to_dict() -> dict[str, Any]
-
- -
- -

Serialize to dictionary.

- - -
- Source code in transformplan/filters.py -
def to_dict(self) -> dict[str, Any]:
-    """Serialize to dictionary."""
-    return {"type": "str_starts_with", "column": self.column, "prefix": self.prefix}
-
-
-
- -
- - - -
- -
- -

StrEndsWith

- - -
- - - -

- StrEndsWith - - - - dataclass - - -

-
StrEndsWith(column: str, suffix: str)
-
- -
-

- Bases: Filter

- - - -

String ends-with filter: column ends with suffix.

- - -

Attributes:

- - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
column - str - -
-

Name of the string column to check.

-
-
suffix - str - -
-

Suffix to match at the end.

-
-
- - - - - - - - - - - -
- - - - - - - - - - -
- - -

- to_expr - - -

-
to_expr() -> Expr
-
- -
- -

Convert to Polars str.ends_with expression.

- - -
- Source code in transformplan/filters.py -
def to_expr(self) -> pl.Expr:
-    """Convert to Polars str.ends_with expression."""
-    return pl.col(self.column).str.ends_with(self.suffix)
-
-
-
- -
- -
- - -

- to_dict - - -

-
to_dict() -> dict[str, Any]
-
- -
- -

Serialize to dictionary.

- - -
- Source code in transformplan/filters.py -
def to_dict(self) -> dict[str, Any]:
-    """Serialize to dictionary."""
-    return {"type": "str_ends_with", "column": self.column, "suffix": self.suffix}
-
-
-
- -
- - - -
- -
- -

Logical Combinators

-

And

- - -
- - - -

- And - - - - dataclass - - -

-
And(left: Filter, right: Filter)
-
- -
-

- Bases: Filter

- - - -

Logical AND filter: both conditions must be true.

-

Typically created using the & operator between filters.

- - -

Attributes:

- - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
left - Filter - -
-

First filter condition.

-
-
right - Filter - -
-

Second filter condition.

-
-
- - -
- Example -
-
-
-

(Col("age") >= 18) & (Col("status") == "active")

-
-
-
-
- - - - - - - - - - -
- - - - - - - - - - -
- - -

- to_expr - - -

-
to_expr() -> Expr
-
- -
- -

Convert to Polars AND expression.

- - -
- Source code in transformplan/filters.py -
def to_expr(self) -> pl.Expr:
-    """Convert to Polars AND expression."""
-    return self.left.to_expr() & self.right.to_expr()
-
-
-
- -
- -
- - -

- to_dict - - -

-
to_dict() -> dict[str, Any]
-
- -
- -

Serialize to dictionary with nested filter dicts.

- - -
- Source code in transformplan/filters.py -
def to_dict(self) -> dict[str, Any]:
-    """Serialize to dictionary with nested filter dicts."""
-    return {
-        "type": "and",
-        "left": self.left.to_dict(),
-        "right": self.right.to_dict(),
-    }
-
-
-
- -
- - - -
- -
- -

Or

- - -
- - - -

- Or - - - - dataclass - - -

-
Or(left: Filter, right: Filter)
-
- -
-

- Bases: Filter

- - - -

Logical OR filter: at least one condition must be true.

-

Typically created using the | operator between filters.

- - -

Attributes:

- - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
left - Filter - -
-

First filter condition.

-
-
right - Filter - -
-

Second filter condition.

-
-
- - -
- Example -
-
-
-

(Col("role") == "admin") | (Col("role") == "moderator")

-
-
-
-
- - - - - - - - - - -
- - - - - - - - - - -
- - -

- to_expr - - -

-
to_expr() -> Expr
-
- -
- -

Convert to Polars OR expression.

- - -
- Source code in transformplan/filters.py -
def to_expr(self) -> pl.Expr:
-    """Convert to Polars OR expression."""
-    return self.left.to_expr() | self.right.to_expr()
-
-
-
- -
- -
- - -

- to_dict - - -

-
to_dict() -> dict[str, Any]
-
- -
- -

Serialize to dictionary with nested filter dicts.

- - -
- Source code in transformplan/filters.py -
def to_dict(self) -> dict[str, Any]:
-    """Serialize to dictionary with nested filter dicts."""
-    return {
-        "type": "or",
-        "left": self.left.to_dict(),
-        "right": self.right.to_dict(),
-    }
-
-
-
- -
- - - -
- -
- -

Not

- - -
- - - -

- Not - - - - dataclass - - -

-
Not(operand: Filter)
-
- -
-

- Bases: Filter

- - - -

Logical NOT filter: inverts the condition.

-

Typically created using the ~ operator on a filter.

- - -

Attributes:

- - - - - - - - - - - - - - - -
NameTypeDescription
operand - Filter - -
-

Filter condition to invert.

-
-
- - -
- Example -
-
-
-

~(Col("deleted") == True)

-
-
-
-
- - - - - - - - - - -
- - - - - - - - - - -
- - -

- to_expr - - -

-
to_expr() -> Expr
-
- -
- -

Convert to Polars NOT expression.

- - -
- Source code in transformplan/filters.py -
def to_expr(self) -> pl.Expr:
-    """Convert to Polars NOT expression."""
-    return ~self.operand.to_expr()
-
-
-
- -
- -
- - -

- to_dict - - -

-
to_dict() -> dict[str, Any]
-
- -
- -

Serialize to dictionary with nested filter dict.

- - -
- Source code in transformplan/filters.py -
def to_dict(self) -> dict[str, Any]:
-    """Serialize to dictionary with nested filter dict."""
-    return {"type": "not", "operand": self.operand.to_dict()}
-
-
-
- -
- - - -
- -
- -

Examples

-

Simple Comparisons

-
from transformplan import Col
-
-# Numeric comparisons
-Col("age") >= 18
-Col("price") < 100
-Col("quantity") == 0
-
-# String equality
-Col("status") == "active"
-Col("country") != "US"
-
-

String Matching

-
# Contains substring
-Col("email").str_contains("@company.com")
-
-# Starts/ends with
-Col("code").str_starts_with("PRD-")
-Col("filename").str_ends_with(".csv")
-
-

Membership Tests

-
# Check if value is in list
-Col("status").is_in(["active", "pending"])
-
-# Range check
-Col("age").between(18, 65)
-
-

Null Checks

-
# Filter nulls
-Col("email").is_not_null()
-Col("optional_field").is_null()
-
-

Combining Conditions

-
# AND: both conditions must be true
-(Col("age") >= 18) & (Col("status") == "active")
-
-# OR: at least one condition must be true
-(Col("role") == "admin") | (Col("role") == "moderator")
-
-# NOT: invert condition
-~(Col("deleted") == True)
-
-# Complex combinations
-(
-    (Col("age") >= 18) &
-    (Col("country").is_in(["US", "CA"])) &
-    ~(Col("status") == "banned")
-)
-
- - - - - - - - - - - - - -
-
- - - -
- - - -
- - - -
-
-
-
- - - - - - - - - - - - - \ No newline at end of file diff --git a/site/api/index.html b/site/api/index.html deleted file mode 100644 index 4c66834..0000000 --- a/site/api/index.html +++ /dev/null @@ -1,1788 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Overview - TransformPlan - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - Skip to content - - -
-
- -
- - - - - - -
- - -
- -
- - - - - - -
-
- - - -
-
-
- - - - - -
-
-
- - - - - - - -
- -
- - - - - - - - -

API Reference

-

This section provides detailed API documentation for all TransformPlan classes and functions.

-

Core Classes

- - - - - - - - - - - - - - - - - - - - - - - - - -
ClassDescription
TransformPlanMain class for building transformation pipelines
ProtocolAudit trail capturing transformation history
ColColumn reference for building filter expressions
FilterBase class for serializable filter expressions
-

Validation Classes

- - - - - - - - - - - - - - - - - - - - - -
ClassDescription
ValidationResultResult of schema validation
DryRunResultPreview of pipeline execution
SchemaValidationErrorException raised on validation failure
-

Chunked Processing Classes

- - - - - - - - - - - - - - - - - - - - - -
ClassDescription
ChunkedProtocolProtocol for tracking chunked file processing
ChunkValidationResultResult of validating pipeline for chunked processing
ChunkingErrorException raised when pipeline is incompatible with chunking
-

Operation Categories

-

TransformPlan provides operations organized by category:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CategoryDescriptionExamples
Column OperationsAdd, drop, rename, cast columnscol_drop, col_rename, col_cast
Math OperationsArithmetic on numeric columnsmath_add, math_multiply, math_round
Row OperationsFilter, sort, deduplicate rowsrows_filter, rows_sort, rows_unique
String OperationsText manipulationstr_replace, str_lower, str_split
Datetime OperationsDate and time extractiondt_year, dt_month, dt_parse
Map OperationsValue mapping and discretizationmap_values, map_discretize
-

Complete Method Reference

-

All TransformPlan operations at a glance. Click method names for detailed documentation.

-

Column Operations

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MethodDescription
col_dropDrop a column from the DataFrame
col_renameRename a column
col_castCast a column to a different dtype
col_reorderReorder columns (drops unlisted)
col_selectKeep only the specified columns
col_duplicateDuplicate a column under a new name
col_fill_nullFill null values in a column
col_drop_nullDrop rows with null values in specified columns
col_drop_zeroDrop rows where the specified column is zero
col_addAdd a new column with a constant value or expression
col_add_uuidAdd a column with unique random identifiers
col_hashHash one or more columns into a new column
col_coalesceTake the first non-null value across multiple columns
-

Math Operations

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MethodDescription
math_addAdd a scalar value to a column
math_subtractSubtract a scalar value from a column
math_multiplyMultiply a column by a scalar value
math_divideDivide a column by a scalar value
math_clampClamp column values to a range
math_absTake absolute value of a column
math_roundRound a column to specified decimal places
math_set_minSet a minimum value for a column
math_set_maxSet a maximum value for a column
math_add_columnsAdd two columns together into a new column
math_subtract_columnsSubtract one column from another
math_multiply_columnsMultiply two columns together
math_divide_columnsDivide one column by another
math_percent_ofCalculate percentage of one column relative to another
math_cumsumCalculate cumulative sum (optionally grouped)
math_rankCalculate rank of values
-

Row Operations

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MethodDescription
rows_filterFilter rows using a Filter expression
rows_dropDrop rows matching a filter
rows_drop_nullsDrop rows with null values
rows_flagAdd a flag column based on a filter condition
rows_uniqueKeep unique rows based on specified columns
rows_deduplicateDeduplicate by keeping first/last based on sort order
rows_sortSort rows by one or more columns
rows_headKeep only the first n rows
rows_tailKeep only the last n rows
rows_sampleSample rows from the DataFrame
rows_explodeExplode a list column into multiple rows
rows_meltUnpivot from wide to long format
rows_pivotPivot from long to wide format
-

String Operations

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MethodDescription
str_lowerConvert string column to lowercase
str_upperConvert string column to uppercase
str_stripStrip leading and trailing characters
str_padPad a string column to a specified length
str_sliceExtract a substring from a string column
str_truncateTruncate strings to a maximum length
str_replaceReplace occurrences of a pattern
str_extractExtract substring using regex capture group
str_splitSplit a string column by separator
str_concatConcatenate multiple string columns
-

Datetime Operations

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MethodDescription
dt_yearExtract year from a datetime column
dt_monthExtract month from a datetime column
dt_dayExtract day from a datetime column
dt_weekExtract ISO week number
dt_quarterExtract quarter (1-4)
dt_year_monthCreate a year-month string
dt_quarter_yearCreate a quarter-year string (e.g., 'Q1-2024')
dt_calendar_weekCreate a year-week string (e.g., '2024-W05')
dt_formatFormat a datetime column as a string
dt_parseParse a string column into a datetime
dt_diff_daysCalculate difference in days between two dates
dt_age_yearsCalculate age in years from a birth date
dt_truncateTruncate datetime to a specified precision
dt_is_betweenCheck if date falls within a range
-

Map Operations

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MethodDescription
map_valuesMap values in a column using a dictionary
map_caseApply case-when logic to a column
map_from_columnMap values using another column as lookup
map_discretizeDiscretize a numeric column into bins
map_bool_to_intConvert boolean to integer (True=1, False=0)
map_null_to_valueReplace null values with a specific value
map_value_to_nullReplace a specific value with null
-

Utility Functions

- - - - - - - - - - - - - - - - - -
FunctionDescription
frame_hashCompute deterministic hash of a DataFrame
validate_chunked_pipelineValidate pipeline compatibility with chunked processing
- - - - - - - - - - - - - -
-
- - - -
- - - -
- - - -
-
-
-
- - - - - - - - - - - - - \ No newline at end of file diff --git a/site/api/ops/column/index.html b/site/api/ops/column/index.html deleted file mode 100644 index 67f738a..0000000 --- a/site/api/ops/column/index.html +++ /dev/null @@ -1,2505 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Column Operations - TransformPlan - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - Skip to content - - -
-
- -
- - - - - - -
- - -
- -
- - - - - - -
-
- - - -
-
-
- - - - - -
-
-
- - - - - - - -
- -
- - - - - - - - -

Column Operations

-

Operations for adding, dropping, renaming, and transforming columns.

-

Overview

-

Column operations modify the structure of a DataFrame by adding, removing, or transforming columns. All operations return the TransformPlan instance for method chaining.

-
from transformplan import TransformPlan
-
-plan = (
-    TransformPlan()
-    .col_rename("old_name", "new_name")
-    .col_drop("temp_column")
-    .col_cast("price", pl.Float64)
-    .col_add("status", value="active")
-)
-
-

Class Reference

- - -
- - - -

- ColumnOps - - -

- - -
- - - -

Mixin providing column-level operations.

- - - - - - - - - - - -
- - - - - - - - - - -
- - -

- col_drop - - -

-
col_drop(column: str) -> Self
-
- -
- -

Drop a column from the DataFrame.

- - -
- Source code in transformplan/ops/column.py -
55
-56
-57
def col_drop(self, column: str) -> Self:
-    """Drop a column from the DataFrame."""
-    return self._register(self._col_drop, {"column": column})
-
-
-
- -
- -
- - -

- col_rename - - -

-
col_rename(column: str, new_name: str) -> Self
-
- -
- -

Rename a column.

- - -
- Source code in transformplan/ops/column.py -
62
-63
-64
-65
-66
def col_rename(self, column: str, new_name: str) -> Self:
-    """Rename a column."""
-    return self._register(
-        self._col_rename, {"column": column, "new_name": new_name}
-    )
-
-
-
- -
- -
- - -

- col_cast - - -

-
col_cast(column: str, dtype: type) -> Self
-
- -
- -

Cast a column to a different dtype.

- - -
- Source code in transformplan/ops/column.py -
73
-74
-75
def col_cast(self, column: str, dtype: type) -> Self:
-    """Cast a column to a different dtype."""
-    return self._register(self._col_cast, {"column": column, "dtype": dtype})
-
-
-
- -
- -
- - -

- col_reorder - - -

-
col_reorder(columns: Sequence[str]) -> Self
-
- -
- -

Reorder columns. Unlisted columns are dropped.

- - -
- Source code in transformplan/ops/column.py -
80
-81
-82
def col_reorder(self, columns: Sequence[str]) -> Self:
-    """Reorder columns. Unlisted columns are dropped."""
-    return self._register(self._col_reorder, {"columns": list(columns)})
-
-
-
- -
- -
- - -

- col_select - - -

-
col_select(columns: Sequence[str]) -> Self
-
- -
- -

Keep only the specified columns (order preserved).

- - -

Parameters:

- - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- columns - - Sequence[str] - -
-

Columns to keep.

-
-
- required -
- - -
- Source code in transformplan/ops/column.py -
def col_select(self, columns: Sequence[str]) -> Self:
-    """Keep only the specified columns (order preserved).
-
-    Args:
-        columns: Columns to keep.
-    """
-    return self._register(self._col_select, {"columns": list(columns)})
-
-
-
- -
- -
- - -

- col_duplicate - - -

-
col_duplicate(column: str, new_name: str) -> Self
-
- -
- -

Duplicate a column under a new name.

- - -
- Source code in transformplan/ops/column.py -
87
-88
-89
-90
-91
def col_duplicate(self, column: str, new_name: str) -> Self:
-    """Duplicate a column under a new name."""
-    return self._register(
-        self._col_duplicate, {"column": column, "new_name": new_name}
-    )
-
-
-
- -
- -
- - -

- col_fill_null - - -

-
col_fill_null(
-    column: str, value: Any = None, strategy: str | None = None
-) -> Self
-
- -
- -

Fill null values in a column.

- - -

Parameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- column - - str - -
-

Column to fill.

-
-
- required -
- value - - Any - -
-

Value to fill nulls with (if strategy is None).

-
-
- None -
- strategy - - str | None - -
-

Fill strategy - 'forward', 'backward', 'mean', 'min', 'max', 'zero', 'one'.

-
-
- None -
- - -
- Source code in transformplan/ops/column.py -
def col_fill_null(
-    self, column: str, value: Any = None, strategy: str | None = None
-) -> Self:
-    """Fill null values in a column.
-
-    Args:
-        column: Column to fill.
-        value: Value to fill nulls with (if strategy is None).
-        strategy: Fill strategy - 'forward', 'backward', 'mean', 'min', 'max', 'zero', 'one'.
-    """
-    return self._register(
-        self._col_fill_null,
-        {"column": column, "value": value, "strategy": strategy},
-    )
-
-
-
- -
- -
- - -

- col_drop_null - - -

-
col_drop_null(columns: str | Sequence[str] | None = None) -> Self
-
- -
- -

Drop rows with null values in specified columns.

- - -

Parameters:

- - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- columns - - str | Sequence[str] | None - -
-

Column(s) to check for nulls. If None, checks all columns.

-
-
- None -
- - -
- Source code in transformplan/ops/column.py -
def col_drop_null(self, columns: str | Sequence[str] | None = None) -> Self:
-    """Drop rows with null values in specified columns.
-
-    Args:
-        columns: Column(s) to check for nulls. If None, checks all columns.
-    """
-    if isinstance(columns, str):
-        columns = [columns]
-    return self._register(self._col_drop_null, {"columns": columns})
-
-
-
- -
- -
- - -

- col_drop_zero - - -

-
col_drop_zero(column: str) -> Self
-
- -
- -

Drop rows where the specified column is zero.

- - -
- Source code in transformplan/ops/column.py -
def col_drop_zero(self, column: str) -> Self:
-    """Drop rows where the specified column is zero."""
-    return self._register(self._col_drop_zero, {"column": column})
-
-
-
- -
- -
- - -

- col_add - - -

-
col_add(
-    new_column: str, expr: str | int | float | None = None, value: Any = None
-) -> Self
-
- -
- -

Add a new column with a constant value or expression.

- - -

Parameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- new_column - - str - -
-

Name of the new column.

-
-
- required -
- expr - - str | int | float | None - -
-

Column name to copy from, or None for constant value.

-
-
- None -
- value - - Any - -
-

Constant value to fill the column with.

-
-
- None -
- - -
- Source code in transformplan/ops/column.py -
def col_add(
-    self,
-    new_column: str,
-    expr: str | int | float | None = None,
-    value: Any = None,
-) -> Self:
-    """Add a new column with a constant value or expression.
-
-    Args:
-        new_column: Name of the new column.
-        expr: Column name to copy from, or None for constant value.
-        value: Constant value to fill the column with.
-    """
-    return self._register(
-        self._col_add, {"new_column": new_column, "expr": expr, "value": value}
-    )
-
-
-
- -
- -
- - -

- col_add_uuid - - -

-
col_add_uuid(column: str, length: int = 16) -> Self
-
- -
- -

Add a column with unique random identifiers.

- - -

Parameters:

- - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- column - - str - -
-

Name of the new column.

-
-
- required -
- length - - int - -
-

Length of the identifier string.

-
-
- 16 -
- - -
- Source code in transformplan/ops/column.py -
def col_add_uuid(self, column: str, length: int = 16) -> Self:
-    """Add a column with unique random identifiers.
-
-    Args:
-        column: Name of the new column.
-        length: Length of the identifier string.
-    """
-    return self._register(self._col_add_uuid, {"column": column, "length": length})
-
-
-
- -
- -
- - -

- col_hash - - -

-
col_hash(columns: str | Sequence[str], new_column: str, salt: str = '') -> Self
-
- -
- -

Hash one or more columns into a new column.

- - -

Parameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- columns - - str | Sequence[str] - -
-

Column(s) to hash.

-
-
- required -
- new_column - - str - -
-

Name for the hash column.

-
-
- required -
- salt - - str - -
-

Optional salt to add to the hash.

-
-
- '' -
- - -
- Source code in transformplan/ops/column.py -
def col_hash(
-    self,
-    columns: str | Sequence[str],
-    new_column: str,
-    salt: str = "",
-) -> Self:
-    """Hash one or more columns into a new column.
-
-    Args:
-        columns: Column(s) to hash.
-        new_column: Name for the hash column.
-        salt: Optional salt to add to the hash.
-    """
-    if isinstance(columns, str):
-        columns = [columns]
-    return self._register(
-        self._col_hash,
-        {"columns": list(columns), "new_column": new_column, "salt": salt},
-    )
-
-
-
- -
- -
- - -

- col_coalesce - - -

-
col_coalesce(columns: Sequence[str], new_column: str) -> Self
-
- -
- -

Take the first non-null value across multiple columns.

- - -

Parameters:

- - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- columns - - Sequence[str] - -
-

Columns to coalesce (in priority order).

-
-
- required -
- new_column - - str - -
-

Name for the result column.

-
-
- required -
- - -
- Source code in transformplan/ops/column.py -
def col_coalesce(
-    self,
-    columns: Sequence[str],
-    new_column: str,
-) -> Self:
-    """Take the first non-null value across multiple columns.
-
-    Args:
-        columns: Columns to coalesce (in priority order).
-        new_column: Name for the result column.
-    """
-    return self._register(
-        self._col_coalesce, {"columns": list(columns), "new_column": new_column}
-    )
-
-
-
- -
- - - -
- -
- -

Examples

-

Basic Column Operations

-
# Drop a column
-plan = TransformPlan().col_drop("temp")
-
-# Rename a column
-plan = TransformPlan().col_rename("old", "new")
-
-# Cast to a different type
-plan = TransformPlan().col_cast("price", pl.Float64)
-
-

Column Selection

-
# Keep only specific columns (in order)
-plan = TransformPlan().col_select(["id", "name", "value"])
-
-# Reorder columns (drops unlisted columns)
-plan = TransformPlan().col_reorder(["value", "name", "id"])
-
-

Adding Columns

-
# Add column with constant value
-plan = TransformPlan().col_add("status", value="pending")
-
-# Copy from existing column
-plan = TransformPlan().col_add("price_backup", expr="price")
-
-# Add unique identifiers
-plan = TransformPlan().col_add_uuid("row_id", length=16)
-
-

Handling Null Values

-
# Fill nulls with a value
-plan = TransformPlan().col_fill_null("score", value=0)
-
-# Fill with strategy
-plan = TransformPlan().col_fill_null("value", strategy="forward")
-
-# Drop rows with nulls
-plan = TransformPlan().col_drop_null(columns=["required_field"])
-
-

Advanced Operations

-
# Create hash from multiple columns
-plan = TransformPlan().col_hash(
-    columns=["first_name", "last_name", "email"],
-    new_column="user_hash",
-    salt="my_salt"
-)
-
-# Take first non-null from multiple columns
-plan = TransformPlan().col_coalesce(
-    columns=["primary_email", "secondary_email", "backup_email"],
-    new_column="contact_email"
-)
-
-# Duplicate a column
-plan = TransformPlan().col_duplicate("original", "copy")
-
- - - - - - - - - - - - - -
-
- - - -
- - - -
- - - -
-
-
-
- - - - - - - - - - - - - \ No newline at end of file diff --git a/site/api/ops/datetime/index.html b/site/api/ops/datetime/index.html deleted file mode 100644 index b5e723d..0000000 --- a/site/api/ops/datetime/index.html +++ /dev/null @@ -1,3218 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Datetime Operations - TransformPlan - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - Skip to content - - -
-
- -
- - - - - - -
- - -
- -
- - - - - - -
-
- - - -
-
-
- - - - - -
-
-
- - - - - - - -
- -
- - - - - - - - -

Datetime Operations

-

Date and time extraction and manipulation operations.

-

Overview

-

Datetime operations allow you to extract components from date/datetime columns, parse date strings, and perform date arithmetic.

-
from transformplan import TransformPlan
-
-plan = (
-    TransformPlan()
-    .dt_parse("date_string", fmt="%Y-%m-%d")
-    .dt_year("order_date", new_column="order_year")
-    .dt_diff_days("end_date", "start_date", new_column="duration")
-)
-
-

Class Reference

- - -
- - - -

- DatetimeOps - - -

- - -
- - - -

Mixin providing datetime operations on columns.

- - - - - - - - - - - -
- - - - - - - - - - -
- - -

- dt_year - - -

-
dt_year(column: str, new_column: str | None = None) -> Self
-
- -
- -

Extract year from a datetime column.

- - -

Parameters:

- - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- column - - str - -
-

Source datetime column.

-
-
- required -
- new_column - - str | None - -
-

Name for result column (None = modify in place).

-
-
- None -
- - -
- Source code in transformplan/ops/datetime.py -
61
-62
-63
-64
-65
-66
-67
-68
-69
-70
def dt_year(self, column: str, new_column: str | None = None) -> Self:
-    """Extract year from a datetime column.
-
-    Args:
-        column: Source datetime column.
-        new_column: Name for result column (None = modify in place).
-    """
-    return self._register(
-        self._dt_year, {"column": column, "new_column": new_column or column}
-    )
-
-
-
- -
- -
- - -

- dt_month - - -

-
dt_month(column: str, new_column: str | None = None) -> Self
-
- -
- -

Extract month from a datetime column.

- - -

Parameters:

- - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- column - - str - -
-

Source datetime column.

-
-
- required -
- new_column - - str | None - -
-

Name for result column (None = modify in place).

-
-
- None -
- - -
- Source code in transformplan/ops/datetime.py -
77
-78
-79
-80
-81
-82
-83
-84
-85
-86
def dt_month(self, column: str, new_column: str | None = None) -> Self:
-    """Extract month from a datetime column.
-
-    Args:
-        column: Source datetime column.
-        new_column: Name for result column (None = modify in place).
-    """
-    return self._register(
-        self._dt_month, {"column": column, "new_column": new_column or column}
-    )
-
-
-
- -
- -
- - -

- dt_day - - -

-
dt_day(column: str, new_column: str | None = None) -> Self
-
- -
- -

Extract day from a datetime column.

- - -

Parameters:

- - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- column - - str - -
-

Source datetime column.

-
-
- required -
- new_column - - str | None - -
-

Name for result column (None = modify in place).

-
-
- None -
- - -
- Source code in transformplan/ops/datetime.py -
def dt_day(self, column: str, new_column: str | None = None) -> Self:
-    """Extract day from a datetime column.
-
-    Args:
-        column: Source datetime column.
-        new_column: Name for result column (None = modify in place).
-    """
-    return self._register(
-        self._dt_day, {"column": column, "new_column": new_column or column}
-    )
-
-
-
- -
- -
- - -

- dt_week - - -

-
dt_week(column: str, new_column: str | None = None) -> Self
-
- -
- -

Extract ISO week number from a datetime column.

- - -

Parameters:

- - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- column - - str - -
-

Source datetime column.

-
-
- required -
- new_column - - str | None - -
-

Name for result column (None = modify in place).

-
-
- None -
- - -
- Source code in transformplan/ops/datetime.py -
def dt_week(self, column: str, new_column: str | None = None) -> Self:
-    """Extract ISO week number from a datetime column.
-
-    Args:
-        column: Source datetime column.
-        new_column: Name for result column (None = modify in place).
-    """
-    return self._register(
-        self._dt_week, {"column": column, "new_column": new_column or column}
-    )
-
-
-
- -
- -
- - -

- dt_quarter - - -

-
dt_quarter(column: str, new_column: str | None = None) -> Self
-
- -
- -

Extract quarter (1-4) from a datetime column.

- - -

Parameters:

- - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- column - - str - -
-

Source datetime column.

-
-
- required -
- new_column - - str | None - -
-

Name for result column (None = modify in place).

-
-
- None -
- - -
- Source code in transformplan/ops/datetime.py -
def dt_quarter(self, column: str, new_column: str | None = None) -> Self:
-    """Extract quarter (1-4) from a datetime column.
-
-    Args:
-        column: Source datetime column.
-        new_column: Name for result column (None = modify in place).
-    """
-    return self._register(
-        self._dt_quarter, {"column": column, "new_column": new_column or column}
-    )
-
-
-
- -
- -
- - -

- dt_year_month - - -

-
dt_year_month(column: str, new_column: str, fmt: str = '%Y-%m') -> Self
-
- -
- -

Create a year-month string from a datetime column.

- - -

Parameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- column - - str - -
-

Source datetime column.

-
-
- required -
- new_column - - str - -
-

Name for result column.

-
-
- required -
- fmt - - str - -
-

Output format string.

-
-
- '%Y-%m' -
- - -
- Source code in transformplan/ops/datetime.py -
def dt_year_month(self, column: str, new_column: str, fmt: str = "%Y-%m") -> Self:
-    """Create a year-month string from a datetime column.
-
-    Args:
-        column: Source datetime column.
-        new_column: Name for result column.
-        fmt: Output format string.
-    """
-    return self._register(
-        self._dt_year_month,
-        {"column": column, "new_column": new_column, "fmt": fmt},
-    )
-
-
-
- -
- -
- - -

- dt_quarter_year - - -

-
dt_quarter_year(column: str, new_column: str) -> Self
-
- -
- -

Create a quarter-year string (e.g., 'Q1-2024') from a datetime column.

- - -

Parameters:

- - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- column - - str - -
-

Source datetime column.

-
-
- required -
- new_column - - str - -
-

Name for result column.

-
-
- required -
- - -
- Source code in transformplan/ops/datetime.py -
def dt_quarter_year(self, column: str, new_column: str) -> Self:
-    """Create a quarter-year string (e.g., 'Q1-2024') from a datetime column.
-
-    Args:
-        column: Source datetime column.
-        new_column: Name for result column.
-    """
-    return self._register(
-        self._dt_quarter_year, {"column": column, "new_column": new_column}
-    )
-
-
-
- -
- -
- - -

- dt_calendar_week - - -

-
dt_calendar_week(column: str, new_column: str) -> Self
-
- -
- -

Create a year-week string (e.g., '2024-W05') from a datetime column.

- - -

Parameters:

- - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- column - - str - -
-

Source datetime column.

-
-
- required -
- new_column - - str - -
-

Name for result column.

-
-
- required -
- - -
- Source code in transformplan/ops/datetime.py -
def dt_calendar_week(self, column: str, new_column: str) -> Self:
-    """Create a year-week string (e.g., '2024-W05') from a datetime column.
-
-    Args:
-        column: Source datetime column.
-        new_column: Name for result column.
-    """
-    return self._register(
-        self._dt_calendar_week, {"column": column, "new_column": new_column}
-    )
-
-
-
- -
- -
- - -

- dt_parse - - -

-
dt_parse(
-    column: str, fmt: str = "%Y-%m-%d", new_column: str | None = None
-) -> Self
-
- -
- -

Parse a string column into a datetime.

- - -

Parameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- column - - str - -
-

Source string column.

-
-
- required -
- fmt - - str - -
-

Date format string.

-
-
- '%Y-%m-%d' -
- new_column - - str | None - -
-

Name for result column (None = modify in place).

-
-
- None -
- - -
- Source code in transformplan/ops/datetime.py -
def dt_parse(
-    self,
-    column: str,
-    fmt: str = "%Y-%m-%d",
-    new_column: str | None = None,
-) -> Self:
-    """Parse a string column into a datetime.
-
-    Args:
-        column: Source string column.
-        fmt: Date format string.
-        new_column: Name for result column (None = modify in place).
-    """
-    return self._register(
-        self._dt_parse,
-        {"column": column, "fmt": fmt, "new_column": new_column or column},
-    )
-
-
-
- -
- -
- - -

- dt_format - - -

-
dt_format(column: str, fmt: str, new_column: str | None = None) -> Self
-
- -
- -

Format a datetime column as a string.

- - -

Parameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- column - - str - -
-

Source datetime column.

-
-
- required -
- fmt - - str - -
-

Output format string.

-
-
- required -
- new_column - - str | None - -
-

Name for result column (None = modify in place).

-
-
- None -
- - -
- Source code in transformplan/ops/datetime.py -
def dt_format(self, column: str, fmt: str, new_column: str | None = None) -> Self:
-    """Format a datetime column as a string.
-
-    Args:
-        column: Source datetime column.
-        fmt: Output format string.
-        new_column: Name for result column (None = modify in place).
-    """
-    return self._register(
-        self._dt_format,
-        {"column": column, "fmt": fmt, "new_column": new_column or column},
-    )
-
-
-
- -
- -
- - -

- dt_diff_days - - -

-
dt_diff_days(column_a: str, column_b: str, new_column: str) -> Self
-
- -
- -

Calculate difference in days between two date columns (a - b).

- - -

Parameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- column_a - - str - -
-

First date column.

-
-
- required -
- column_b - - str - -
-

Second date column.

-
-
- required -
- new_column - - str - -
-

Name for result column.

-
-
- required -
- - -
- Source code in transformplan/ops/datetime.py -
def dt_diff_days(self, column_a: str, column_b: str, new_column: str) -> Self:
-    """Calculate difference in days between two date columns (a - b).
-
-    Args:
-        column_a: First date column.
-        column_b: Second date column.
-        new_column: Name for result column.
-    """
-    return self._register(
-        self._dt_diff_days,
-        {"column_a": column_a, "column_b": column_b, "new_column": new_column},
-    )
-
-
-
- -
- -
- - -

- dt_age_years - - -

-
dt_age_years(
-    birth_column: str,
-    reference_column: str | None = None,
-    new_column: str = "age",
-) -> Self
-
- -
- -

Calculate age in years from a birth date.

- - -

Parameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- birth_column - - str - -
-

Column containing birth dates.

-
-
- required -
- reference_column - - str | None - -
-

Column containing reference dates (None = today).

-
-
- None -
- new_column - - str - -
-

Name for result column.

-
-
- 'age' -
- - -
- Source code in transformplan/ops/datetime.py -
def dt_age_years(
-    self,
-    birth_column: str,
-    reference_column: str | None = None,
-    new_column: str = "age",
-) -> Self:
-    """Calculate age in years from a birth date.
-
-    Args:
-        birth_column: Column containing birth dates.
-        reference_column: Column containing reference dates (None = today).
-        new_column: Name for result column.
-    """
-    return self._register(
-        self._dt_age_years,
-        {
-            "birth_column": birth_column,
-            "reference_column": reference_column,
-            "new_column": new_column,
-        },
-    )
-
-
-
- -
- -
- - -

- dt_truncate - - -

-
dt_truncate(column: str, every: str, new_column: str | None = None) -> Self
-
- -
- -

Truncate datetime to a specified precision.

- - -

Parameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- column - - str - -
-

Source datetime column.

-
-
- required -
- every - - str - -
-

Truncation interval ('1d', '1mo', '1y', '1h', etc.).

-
-
- required -
- new_column - - str | None - -
-

Name for result column (None = modify in place).

-
-
- None -
- - -
- Source code in transformplan/ops/datetime.py -
def dt_truncate(
-    self,
-    column: str,
-    every: str,
-    new_column: str | None = None,
-) -> Self:
-    """Truncate datetime to a specified precision.
-
-    Args:
-        column: Source datetime column.
-        every: Truncation interval ('1d', '1mo', '1y', '1h', etc.).
-        new_column: Name for result column (None = modify in place).
-    """
-    return self._register(
-        self._dt_truncate,
-        {"column": column, "every": every, "new_column": new_column or column},
-    )
-
-
-
- -
- -
- - -

- dt_is_between - - -

-
dt_is_between(
-    column: str, start: str, end: str, new_column: str, closed: str = "both"
-) -> Self
-
- -
- -

Check if date falls within a range.

- - -

Parameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- column - - str - -
-

Source datetime column.

-
-
- required -
- start - - str - -
-

Start date (string, will be parsed).

-
-
- required -
- end - - str - -
-

End date (string, will be parsed).

-
-
- required -
- new_column - - str - -
-

Name for boolean result column.

-
-
- required -
- closed - - str - -
-

Which endpoints to include ('both', 'left', 'right', 'none').

-
-
- 'both' -
- - -
- Source code in transformplan/ops/datetime.py -
def dt_is_between(
-    self,
-    column: str,
-    start: str,
-    end: str,
-    new_column: str,
-    closed: str = "both",
-) -> Self:
-    """Check if date falls within a range.
-
-    Args:
-        column: Source datetime column.
-        start: Start date (string, will be parsed).
-        end: End date (string, will be parsed).
-        new_column: Name for boolean result column.
-        closed: Which endpoints to include ('both', 'left', 'right', 'none').
-    """
-    return self._register(
-        self._dt_is_between,
-        {
-            "column": column,
-            "start": start,
-            "end": end,
-            "new_column": new_column,
-            "closed": closed,
-        },
-    )
-
-
-
- -
- - - -
- -
- -

Examples

-

Extracting Date Components

-
# Extract year
-plan = TransformPlan().dt_year("date", new_column="year")
-
-# Extract month
-plan = TransformPlan().dt_month("date", new_column="month")
-
-# Extract day
-plan = TransformPlan().dt_day("date", new_column="day")
-
-# Extract week number
-plan = TransformPlan().dt_week("date", new_column="week")
-
-# Extract quarter
-plan = TransformPlan().dt_quarter("date", new_column="quarter")
-
-

Formatted Date Strings

-
# Year-month string (e.g., "2024-01")
-plan = TransformPlan().dt_year_month("date", new_column="year_month")
-
-# Quarter-year string (e.g., "Q1-2024")
-plan = TransformPlan().dt_quarter_year("date", new_column="quarter_year")
-
-# Calendar week string (e.g., "2024-W05")
-plan = TransformPlan().dt_calendar_week("date", new_column="calendar_week")
-
-

Parsing and Formatting

-
# Parse string to date
-plan = TransformPlan().dt_parse(
-    column="date_string",
-    fmt="%Y-%m-%d",
-    new_column="date"
-)
-
-# Format date to string
-plan = TransformPlan().dt_format(
-    column="date",
-    fmt="%B %d, %Y",
-    new_column="formatted_date"
-)
-
-

Date Arithmetic

-
# Calculate difference in days
-plan = TransformPlan().dt_diff_days(
-    column_a="end_date",
-    column_b="start_date",
-    new_column="duration_days"
-)
-
-# Calculate age in years
-plan = TransformPlan().dt_age_years(
-    birth_column="birth_date",
-    new_column="age"
-)
-
-# Age relative to reference column
-plan = TransformPlan().dt_age_years(
-    birth_column="birth_date",
-    reference_column="event_date",
-    new_column="age_at_event"
-)
-
-

Truncation

-
# Truncate to month start
-plan = TransformPlan().dt_truncate("timestamp", every="1mo")
-
-# Truncate to day
-plan = TransformPlan().dt_truncate("timestamp", every="1d")
-
-# Truncate to year
-plan = TransformPlan().dt_truncate("timestamp", every="1y")
-
-

Range Checks

-
# Check if date is within range
-plan = TransformPlan().dt_is_between(
-    column="order_date",
-    start="2024-01-01",
-    end="2024-12-31",
-    new_column="is_2024_order",
-    closed="both"  # or "left", "right", "none"
-)
-
- - - - - - - - - - - - - -
-
- - - -
- - - -
- - - -
-
-
-
- - - - - - - - - - - - - \ No newline at end of file diff --git a/site/api/ops/map/index.html b/site/api/ops/map/index.html deleted file mode 100644 index dafebfe..0000000 --- a/site/api/ops/map/index.html +++ /dev/null @@ -1,2450 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - Map Operations - TransformPlan - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - Skip to content - - -
-
- -
- - - - - - -
- - -
- -
- - - - - - -
-
- - - -
-
-
- - - - - -
-
-
- - - - - - - -
- -
- - - - - - - - -

Map Operations

-

Value mapping, discretization, and transformation operations.

-

Overview

-

Map operations transform column values using dictionaries, bins, or other columns. They're useful for categorization, value replacement, and data normalization.

-
from transformplan import TransformPlan
-
-plan = (
-    TransformPlan()
-    .map_values("status", {"A": "Active", "I": "Inactive"})
-    .map_discretize("age", bins=[18, 35, 55], labels=["Young", "Adult", "Senior"])
-)
-
-

Class Reference

- - -
- - - -

- MapOps - - -

- - -
- - - -

Mixin providing value mapping and transformation operations.

- - - - - - - - - - - -
- - - - - - - - - - -
- - -

- map_values - - -

-
map_values(
-    column: str,
-    mapping: dict[Any, Any],
-    default: Any = None,
-    keep_unmapped: bool = True,
-) -> Self
-
- -
- -

Map values in a column using a dictionary.

- - -

Parameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- column - - str - -
-

Column to transform.

-
-
- required -
- mapping - - dict[Any, Any] - -
-

Dictionary mapping old values to new values.

-
-
- required -
- default - - Any - -
-

Default value for unmapped values (if keep_unmapped=False).

-
-
- None -
- keep_unmapped - - bool - -
-

If True, keep original value when not in mapping.

-
-
- True -
- - -
- Source code in transformplan/ops/map.py -
52
-53
-54
-55
-56
-57
-58
-59
-60
-61
-62
-63
-64
-65
-66
-67
-68
-69
-70
-71
-72
-73
-74
-75
def map_values(
-    self,
-    column: str,
-    mapping: dict[Any, Any],
-    default: Any = None,
-    keep_unmapped: bool = True,
-) -> Self:
-    """Map values in a column using a dictionary.
-
-    Args:
-        column: Column to transform.
-        mapping: Dictionary mapping old values to new values.
-        default: Default value for unmapped values (if keep_unmapped=False).
-        keep_unmapped: If True, keep original value when not in mapping.
-    """
-    return self._register(
-        self._map_values,
-        {
-            "column": column,
-            "mapping": mapping,
-            "default": default,
-            "keep_unmapped": keep_unmapped,
-        },
-    )
-
-
-
- -
- -
- - -

- map_discretize - - -

-
map_discretize(
-    column: str,
-    bins: Sequence[float],
-    labels: Sequence[str] | None = None,
-    new_column: str | None = None,
-    right: bool = True,
-) -> Self
-
- -
- -

Discretize a numeric column into bins/categories.

- - -

Parameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- column - - str - -
-

Column to discretize.

-
-
- required -
- bins - - Sequence[float] - -
-

Bin edges (e.g., [0, 18, 65, 100] creates 4 bins).

-
-
- required -
- labels - - Sequence[str] | None - -
-

Labels for each bin (must be len(bins)+1 if provided).

-
-
- None -
- new_column - - str | None - -
-

Name for result column (None = modify in place).

-
-
- None -
- right - - bool - -
-

If True, bins are (left, right]. If False, [left, right).

-
-
- True -
- - -
- Source code in transformplan/ops/map.py -
def map_discretize(
-    self,
-    column: str,
-    bins: Sequence[float],
-    labels: Sequence[str] | None = None,
-    new_column: str | None = None,
-    right: bool = True,
-) -> Self:
-    """Discretize a numeric column into bins/categories.
-
-    Args:
-        column: Column to discretize.
-        bins: Bin edges (e.g., [0, 18, 65, 100] creates 4 bins).
-        labels: Labels for each bin (must be len(bins)+1 if provided).
-        new_column: Name for result column (None = modify in place).
-        right: If True, bins are (left, right]. If False, [left, right).
-    """
-    return self._register(
-        self._map_discretize,
-        {
-            "column": column,
-            "bins": list(bins),
-            "labels": list(labels) if labels else None,
-            "new_column": new_column or column,
-            "right": right,
-        },
-    )
-
-
-
- -
- -
- - -

- map_case - - -

-
map_case(
-    column: str,
-    cases: list[tuple[Any, Any]],
-    default: Any = None,
-    new_column: str | None = None,
-) -> Self
-
- -
- -

Apply case-when logic to a column.

- - -

Parameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- column - - str - -
-

Column to evaluate.

-
-
- required -
- cases - - list[tuple[Any, Any]] - -
-

List of (condition_value, result_value) tuples.

-
-
- required -
- default - - Any - -
-

Default value if no case matches.

-
-
- None -
- new_column - - str | None - -
-

Name for result column (None = modify in place).

-
-
- None -
- - -
- Example -

.map_case('grade', [(90, 'A'), (80, 'B'), (70, 'C')], default='F') -Maps: >= 90 -> A, >= 80 -> B, >= 70 -> C, else F

-
- -
- Source code in transformplan/ops/map.py -
def map_case(
-    self,
-    column: str,
-    cases: list[tuple[Any, Any]],
-    default: Any = None,
-    new_column: str | None = None,
-) -> Self:
-    """Apply case-when logic to a column.
-
-    Args:
-        column: Column to evaluate.
-        cases: List of (condition_value, result_value) tuples.
-        default: Default value if no case matches.
-        new_column: Name for result column (None = modify in place).
-
-    Example:
-        .map_case('grade', [(90, 'A'), (80, 'B'), (70, 'C')], default='F')
-        Maps: >= 90 -> A, >= 80 -> B, >= 70 -> C, else F
-    """
-    return self._register(
-        self._map_case,
-        {
-            "column": column,
-            "cases": cases,
-            "default": default,
-            "new_column": new_column or column,
-        },
-    )
-
-
-
- -
- -
- - -

- map_bool_to_int - - -

-
map_bool_to_int(column: str) -> Self
-
- -
- -

Convert a boolean column to integer (True=1, False=0).

- - -
- Source code in transformplan/ops/map.py -
def map_bool_to_int(self, column: str) -> Self:
-    """Convert a boolean column to integer (True=1, False=0)."""
-    return self._register(self._map_bool_to_int, {"column": column})
-
-
-
- -
- -
- - -

- map_null_to_value - - -

-
map_null_to_value(column: str, value: Any) -> Self
-
- -
- -

Replace null values with a specific value.

- - -
- Source code in transformplan/ops/map.py -
def map_null_to_value(self, column: str, value: Any) -> Self:
-    """Replace null values with a specific value."""
-    return self._register(
-        self._map_null_to_value, {"column": column, "value": value}
-    )
-
-
-
- -
- -
- - -

- map_value_to_null - - -

-
map_value_to_null(column: str, value: Any) -> Self
-
- -
- -

Replace a specific value with null.

- - -
- Source code in transformplan/ops/map.py -
def map_value_to_null(self, column: str, value: Any) -> Self:
-    """Replace a specific value with null."""
-    return self._register(
-        self._map_value_to_null, {"column": column, "value": value}
-    )
-
-
-
- -
- -
- - -

- map_from_column - - -

-
map_from_column(
-    column: str,
-    lookup_column: str,
-    value_column: str,
-    new_column: str | None = None,
-    default: Any = None,
-) -> Self
-
- -
- -

Map values using another column as lookup (like vlookup).

-

This maps values from column using lookup_column -> value_column mapping -from the same DataFrame. Useful for denormalization.

- - -

Parameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- column - - str - -
-

Column containing keys to look up.

-
-
- required -
- lookup_column - - str - -
-

Column containing lookup keys.

-
-
- required -
- value_column - - str - -
-

Column containing values to map to.

-
-
- required -
- new_column - - str | None - -
-

Name for result column (None = modify in place).

-
-
- None -
- default - - Any - -
-

Default value if lookup fails.

-
-
- None -
- - -
- Source code in transformplan/ops/map.py -
def map_from_column(
-    self,
-    column: str,
-    lookup_column: str,
-    value_column: str,
-    new_column: str | None = None,
-    default: Any = None,
-) -> Self:
-    """Map values using another column as lookup (like vlookup).
-
-    This maps values from `column` using `lookup_column` -> `value_column` mapping
-    from the same DataFrame. Useful for denormalization.
-
-    Args:
-        column: Column containing keys to look up.
-        lookup_column: Column containing lookup keys.
-        value_column: Column containing values to map to.
-        new_column: Name for result column (None = modify in place).
-        default: Default value if lookup fails.
-    """
-    return self._register(
-        self._map_from_column,
-        {
-            "column": column,
-            "lookup_column": lookup_column,
-            "value_column": value_column,
-            "new_column": new_column or column,
-            "default": default,
-        },
-    )
-
-
-
- -
- - - -
- -
- -

Examples

-

Dictionary Mapping

-
# Map values using a dictionary
-plan = TransformPlan().map_values(
-    column="country_code",
-    mapping={"US": "United States", "CA": "Canada", "MX": "Mexico"}
-)
-
-# With default for unmapped values
-plan = TransformPlan().map_values(
-    column="status",
-    mapping={"A": "Active", "I": "Inactive"},
-    default="Unknown",
-    keep_unmapped=False
-)
-
-

Discretization (Binning)

-
# Discretize numeric values into categories
-plan = TransformPlan().map_discretize(
-    column="age",
-    bins=[0, 18, 35, 55, 100],
-    labels=["Child", "Young Adult", "Adult", "Senior"],
-    new_column="age_group"
-)
-
-# Auto-generated labels
-plan = TransformPlan().map_discretize(
-    column="score",
-    bins=[0, 50, 75, 100],
-    new_column="score_band"
-)
-
-

Case-When Logic

-
# Apply case-when transformations
-plan = TransformPlan().map_case(
-    column="score",
-    cases=[
-        (90, "A"),
-        (80, "B"),
-        (70, "C"),
-        (60, "D"),
-    ],
-    default="F",
-    new_column="grade"
-)
-
-

Null Handling

-
# Replace null with a value
-plan = TransformPlan().map_null_to_value("status", "Unknown")
-
-# Replace a value with null
-plan = TransformPlan().map_value_to_null("status", "N/A")
-
-

Type Conversion

-
# Convert boolean to integer
-plan = TransformPlan().map_bool_to_int("is_active")
-# True -> 1, False -> 0
-
-

Column-based Lookup

-
# Map using values from other columns (vlookup-style)
-plan = TransformPlan().map_from_column(
-    column="category_id",
-    lookup_column="category_id",
-    value_column="category_name",
-    new_column="category_label",
-    default="Unknown"
-)
-
-

Use Cases

-

Categorizing Continuous Data

-
# Income brackets
-plan = TransformPlan().map_discretize(
-    column="income",
-    bins=[0, 30000, 60000, 100000, 200000],
-    labels=["Low", "Lower-Middle", "Middle", "Upper-Middle", "High"],
-    new_column="income_bracket"
-)
-
-

Standardizing Codes

-
# Standardize department codes
-plan = TransformPlan().map_values(
-    column="dept",
-    mapping={
-        "ENG": "Engineering",
-        "MKT": "Marketing",
-        "SAL": "Sales",
-        "HR": "Human Resources"
-    }
-)
-
-

Data Cleaning

-
# Replace sentinel values with null
-plan = TransformPlan().map_value_to_null("score", -999)
-
-# Replace null with default
-plan = TransformPlan().map_null_to_value("category", "Uncategorized")
-
- - - - - - - - - - - - - -
-
- - - -
- - - -
- - - -
-
-
-
- - - - - - - - - - - - - \ No newline at end of file diff --git a/site/api/ops/math/index.html b/site/api/ops/math/index.html deleted file mode 100644 index 078ce02..0000000 --- a/site/api/ops/math/index.html +++ /dev/null @@ -1,2652 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Math Operations - TransformPlan - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - Skip to content - - -
-
- -
- - - - - - -
- - -
- -
- - - - - - -
-
- - - -
-
-
- - - - - -
-
-
- - - - - - - -
- -
- - - - - - - - -

Math Operations

-

Arithmetic and numeric operations on DataFrame columns.

-

Overview

-

Math operations perform arithmetic on numeric columns. They support both scalar operations (column with a constant) and column-wise operations (column with column).

-
from transformplan import TransformPlan
-
-plan = (
-    TransformPlan()
-    .math_multiply("price", 1.1)  # 10% increase
-    .math_round("price", decimals=2)
-    .math_add_columns("subtotal", "tax", "total")
-)
-
-

Class Reference

- - -
- - - -

- MathOps - - -

- - -
- - - -

Mixin providing mathematical operations on columns.

- - - - - - - - - - - -
- - - - - - - - - - -
- - -

- math_add - - -

-
math_add(column: str, value: Numeric) -> Self
-
- -
- -

Add a scalar value to a column.

- - -
- Source code in transformplan/ops/math.py -
61
-62
-63
def math_add(self, column: str, value: Numeric) -> Self:
-    """Add a scalar value to a column."""
-    return self._register(self._math_add, {"column": column, "value": value})
-
-
-
- -
- -
- - -

- math_subtract - - -

-
math_subtract(column: str, value: Numeric) -> Self
-
- -
- -

Subtract a scalar value from a column.

- - -
- Source code in transformplan/ops/math.py -
70
-71
-72
def math_subtract(self, column: str, value: Numeric) -> Self:
-    """Subtract a scalar value from a column."""
-    return self._register(self._math_subtract, {"column": column, "value": value})
-
-
-
- -
- -
- - -

- math_multiply - - -

-
math_multiply(column: str, value: Numeric) -> Self
-
- -
- -

Multiply a column by a scalar value.

- - -
- Source code in transformplan/ops/math.py -
79
-80
-81
def math_multiply(self, column: str, value: Numeric) -> Self:
-    """Multiply a column by a scalar value."""
-    return self._register(self._math_multiply, {"column": column, "value": value})
-
-
-
- -
- -
- - -

- math_divide - - -

-
math_divide(column: str, value: Numeric) -> Self
-
- -
- -

Divide a column by a scalar value.

- - -
- Source code in transformplan/ops/math.py -
88
-89
-90
def math_divide(self, column: str, value: Numeric) -> Self:
-    """Divide a column by a scalar value."""
-    return self._register(self._math_divide, {"column": column, "value": value})
-
-
-
- -
- -
- - -

- math_clamp - - -

-
math_clamp(
-    column: str, lower: Numeric | None = None, upper: Numeric | None = None
-) -> Self
-
- -
- -

Clamp column values to a range.

- - -
- Source code in transformplan/ops/math.py -
def math_clamp(
-    self,
-    column: str,
-    lower: Numeric | None = None,
-    upper: Numeric | None = None,
-) -> Self:
-    """Clamp column values to a range."""
-    return self._register(
-        self._math_clamp, {"column": column, "lower": lower, "upper": upper}
-    )
-
-
-
- -
- -
- - -

- math_set_min - - -

-
math_set_min(column: str, min_value: Numeric) -> Self
-
- -
- -

Set a minimum value for a column (values below are raised to min).

- - -
- Source code in transformplan/ops/math.py -
def math_set_min(self, column: str, min_value: Numeric) -> Self:
-    """Set a minimum value for a column (values below are raised to min)."""
-    return self._register(
-        self._math_set_min, {"column": column, "min_value": min_value}
-    )
-
-
-
- -
- -
- - -

- math_set_max - - -

-
math_set_max(column: str, max_value: Numeric) -> Self
-
- -
- -

Set a maximum value for a column (values above are lowered to max).

- - -
- Source code in transformplan/ops/math.py -
def math_set_max(self, column: str, max_value: Numeric) -> Self:
-    """Set a maximum value for a column (values above are lowered to max)."""
-    return self._register(
-        self._math_set_max, {"column": column, "max_value": max_value}
-    )
-
-
-
- -
- -
- - -

- math_abs - - -

-
math_abs(column: str) -> Self
-
- -
- -

Take absolute value of a column.

- - -
- Source code in transformplan/ops/math.py -
def math_abs(self, column: str) -> Self:
-    """Take absolute value of a column."""
-    return self._register(self._math_abs, {"column": column})
-
-
-
- -
- -
- - -

- math_round - - -

-
math_round(column: str, decimals: int = 0) -> Self
-
- -
- -

Round a column to specified decimal places.

- - -
- Source code in transformplan/ops/math.py -
def math_round(self, column: str, decimals: int = 0) -> Self:
-    """Round a column to specified decimal places."""
-    return self._register(
-        self._math_round, {"column": column, "decimals": decimals}
-    )
-
-
-
- -
- -
- - -

- math_add_columns - - -

-
math_add_columns(column_a: str, column_b: str, new_column: str) -> Self
-
- -
- -

Add two columns together into a new column.

- - -
- Source code in transformplan/ops/math.py -
def math_add_columns(self, column_a: str, column_b: str, new_column: str) -> Self:
-    """Add two columns together into a new column."""
-    return self._register(
-        self._math_add_columns,
-        {"column_a": column_a, "column_b": column_b, "new_column": new_column},
-    )
-
-
-
- -
- -
- - -

- math_subtract_columns - - -

-
math_subtract_columns(column_a: str, column_b: str, new_column: str) -> Self
-
- -
- -

Subtract column_b from column_a into a new column.

- - -
- Source code in transformplan/ops/math.py -
def math_subtract_columns(
-    self, column_a: str, column_b: str, new_column: str
-) -> Self:
-    """Subtract column_b from column_a into a new column."""
-    return self._register(
-        self._math_subtract_columns,
-        {"column_a": column_a, "column_b": column_b, "new_column": new_column},
-    )
-
-
-
- -
- -
- - -

- math_multiply_columns - - -

-
math_multiply_columns(column_a: str, column_b: str, new_column: str) -> Self
-
- -
- -

Multiply two columns together into a new column.

- - -
- Source code in transformplan/ops/math.py -
def math_multiply_columns(
-    self, column_a: str, column_b: str, new_column: str
-) -> Self:
-    """Multiply two columns together into a new column."""
-    return self._register(
-        self._math_multiply_columns,
-        {"column_a": column_a, "column_b": column_b, "new_column": new_column},
-    )
-
-
-
- -
- -
- - -

- math_divide_columns - - -

-
math_divide_columns(column_a: str, column_b: str, new_column: str) -> Self
-
- -
- -

Divide column_a by column_b into a new column.

- - -
- Source code in transformplan/ops/math.py -
def math_divide_columns(
-    self, column_a: str, column_b: str, new_column: str
-) -> Self:
-    """Divide column_a by column_b into a new column."""
-    return self._register(
-        self._math_divide_columns,
-        {"column_a": column_a, "column_b": column_b, "new_column": new_column},
-    )
-
-
-
- -
- -
- - -

- math_percent_of - - -

-
math_percent_of(
-    column: str, total_column: str, new_column: str, multiply_by: float = 100.0
-) -> Self
-
- -
- -

Calculate percentage of one column relative to another.

- - -

Parameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- column - - str - -
-

Numerator column.

-
-
- required -
- total_column - - str - -
-

Denominator column.

-
-
- required -
- new_column - - str - -
-

Name for result column.

-
-
- required -
- multiply_by - - float - -
-

Multiplier (default 100 for percentage).

-
-
- 100.0 -
- - -
- Source code in transformplan/ops/math.py -
def math_percent_of(
-    self,
-    column: str,
-    total_column: str,
-    new_column: str,
-    multiply_by: float = 100.0,
-) -> Self:
-    """Calculate percentage of one column relative to another.
-
-    Args:
-        column: Numerator column.
-        total_column: Denominator column.
-        new_column: Name for result column.
-        multiply_by: Multiplier (default 100 for percentage).
-    """
-    return self._register(
-        self._math_percent_of,
-        {
-            "column": column,
-            "total_column": total_column,
-            "new_column": new_column,
-            "multiply_by": multiply_by,
-        },
-    )
-
-
-
- -
- -
- - -

- math_cumsum - - -

-
math_cumsum(
-    column: str,
-    new_column: str | None = None,
-    group_by: str | list[str] | None = None,
-) -> Self
-
- -
- -

Calculate cumulative sum.

- - -

Parameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- column - - str - -
-

Column to sum.

-
-
- required -
- new_column - - str | None - -
-

Name for result column (None = modify in place).

-
-
- None -
- group_by - - str | list[str] | None - -
-

Optional column(s) to group by.

-
-
- None -
- - -
- Source code in transformplan/ops/math.py -
def math_cumsum(
-    self,
-    column: str,
-    new_column: str | None = None,
-    group_by: str | list[str] | None = None,
-) -> Self:
-    """Calculate cumulative sum.
-
-    Args:
-        column: Column to sum.
-        new_column: Name for result column (None = modify in place).
-        group_by: Optional column(s) to group by.
-    """
-    if isinstance(group_by, str):
-        group_by = [group_by]
-    return self._register(
-        self._math_cumsum,
-        {
-            "column": column,
-            "new_column": new_column or column,
-            "group_by": group_by,
-        },
-    )
-
-
-
- -
- -
- - -

- math_rank - - -

-
math_rank(
-    column: str,
-    new_column: str,
-    method: str = "ordinal",
-    descending: bool = False,
-    group_by: str | list[str] | None = None,
-) -> Self
-
- -
- -

Calculate rank of values.

- - -

Parameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- column - - str - -
-

Column to rank.

-
-
- required -
- new_column - - str - -
-

Name for result column.

-
-
- required -
- method - - str - -
-

Ranking method ('ordinal', 'dense', 'min', 'max', 'average').

-
-
- 'ordinal' -
- descending - - bool - -
-

Rank in descending order.

-
-
- False -
- group_by - - str | list[str] | None - -
-

Optional column(s) to group by.

-
-
- None -
- - -
- Source code in transformplan/ops/math.py -
def math_rank(
-    self,
-    column: str,
-    new_column: str,
-    method: str = "ordinal",
-    descending: bool = False,
-    group_by: str | list[str] | None = None,
-) -> Self:
-    """Calculate rank of values.
-
-    Args:
-        column: Column to rank.
-        new_column: Name for result column.
-        method: Ranking method ('ordinal', 'dense', 'min', 'max', 'average').
-        descending: Rank in descending order.
-        group_by: Optional column(s) to group by.
-    """
-    if isinstance(group_by, str):
-        group_by = [group_by]
-    return self._register(
-        self._math_rank,
-        {
-            "column": column,
-            "new_column": new_column,
-            "method": method,
-            "descending": descending,
-            "group_by": group_by,
-        },
-    )
-
-
-
- -
- - - -
- -
- -

Examples

-

Scalar Operations

-
# Add to every value
-plan = TransformPlan().math_add("price", 10)
-
-# Subtract from every value
-plan = TransformPlan().math_subtract("score", 5)
-
-# Multiply every value
-plan = TransformPlan().math_multiply("quantity", 1.5)
-
-# Divide every value
-plan = TransformPlan().math_divide("total", 100)
-
-

Column-wise Operations

-
# Add two columns into a new column
-plan = TransformPlan().math_add_columns("base", "bonus", "total")
-
-# Subtract columns
-plan = TransformPlan().math_subtract_columns("revenue", "cost", "profit")
-
-# Multiply columns
-plan = TransformPlan().math_multiply_columns("price", "quantity", "total")
-
-# Divide columns
-plan = TransformPlan().math_divide_columns("score", "max_score", "percentage")
-
-

Value Clamping

-
# Clamp to range
-plan = TransformPlan().math_clamp("score", lower=0, upper=100)
-
-# Set minimum value
-plan = TransformPlan().math_set_min("quantity", min_value=0)
-
-# Set maximum value
-plan = TransformPlan().math_set_max("discount", max_value=50)
-
-

Transformations

-
# Absolute value
-plan = TransformPlan().math_abs("difference")
-
-# Round to decimal places
-plan = TransformPlan().math_round("price", decimals=2)
-
-

Percentage Calculation

-
# Calculate percentage
-plan = TransformPlan().math_percent_of(
-    column="part",
-    total_column="whole",
-    new_column="percentage",
-    multiply_by=100  # default
-)
-
-

Cumulative and Ranking Operations

-
# Cumulative sum
-plan = TransformPlan().math_cumsum(
-    column="sales",
-    new_column="running_total",
-    group_by="region"
-)
-
-# Rank values
-plan = TransformPlan().math_rank(
-    column="score",
-    new_column="rank",
-    method="dense",
-    descending=True,
-    group_by="category"
-)
-
- - - - - - - - - - - - - -
-
- - - -
- - - -
- - - -
-
-
-
- - - - - - - - - - - - - \ No newline at end of file diff --git a/site/api/ops/rows/index.html b/site/api/ops/rows/index.html deleted file mode 100644 index cb8729d..0000000 --- a/site/api/ops/rows/index.html +++ /dev/null @@ -1,2849 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Row Operations - TransformPlan - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - Skip to content - - -
-
- -
- - - - - - -
- - -
- -
- - - - - - -
-
- - - -
-
-
- - - - - -
-
-
- - - - - - - -
- -
- - - - - - - - -

Row Operations

-

Operations for filtering, sorting, and transforming rows.

-

Overview

-

Row operations modify which rows are included in the DataFrame and how they are ordered. Use the Col class to build filter expressions.

-
from transformplan import TransformPlan, Col
-
-plan = (
-    TransformPlan()
-    .rows_filter(Col("status") == "active")
-    .rows_sort("created_at", descending=True)
-    .rows_unique(columns=["email"])
-)
-
-

Class Reference

- - -
- - - -

- RowOps - - -

- - -
- - - -

Mixin providing row-level operations.

- - - - - - - - - - - -
- - - - - - - - - - -
- - -

- rows_filter - - -

-
rows_filter(filter: Filter | dict) -> Self
-
- -
- -

Filter rows using a serializable Filter expression.

- - -
- Example -

from transformplan.filters import Col

-

.rows_filter(Col("age") > 18) -.rows_filter((Col("status") == "active") & (Col("score") >= 50))

-
- -
- Source code in transformplan/ops/rows.py -
def rows_filter(self, filter: Filter | dict) -> Self:
-    """Filter rows using a serializable Filter expression.
-
-    Example:
-        from transformplan.filters import Col
-
-        .rows_filter(Col("age") > 18)
-        .rows_filter((Col("status") == "active") & (Col("score") >= 50))
-    """
-    if isinstance(filter, dict):
-        filter_dict = filter
-    else:
-        filter_dict = filter.to_dict()
-    return self._register(self._rows_filter, {"filter": filter_dict})
-
-
-
- -
- -
- - -

- rows_drop - - -

-
rows_drop(filter: Filter | dict) -> Self
-
- -
- -

Drop rows matching a filter (inverse of rows_filter).

- - -
- Example -

.rows_drop(Col("status") == "deleted")

-
- -
- Source code in transformplan/ops/rows.py -
def rows_drop(self, filter: Filter | dict) -> Self:
-    """Drop rows matching a filter (inverse of rows_filter).
-
-    Example:
-        .rows_drop(Col("status") == "deleted")
-    """
-    if isinstance(filter, dict):
-        filter_dict = filter
-    else:
-        filter_dict = filter.to_dict()
-    return self._register(self._rows_drop, {"filter": filter_dict})
-
-
-
- -
- -
- - -

- rows_drop_nulls - - -

-
rows_drop_nulls(columns: str | Sequence[str] | None = None) -> Self
-
- -
- -

Drop rows with null values in specified columns (or any column if None).

- - -
- Source code in transformplan/ops/rows.py -
61
-62
-63
-64
-65
def rows_drop_nulls(self, columns: str | Sequence[str] | None = None) -> Self:
-    """Drop rows with null values in specified columns (or any column if None)."""
-    if isinstance(columns, str):
-        columns = [columns]
-    return self._register(self._rows_drop_nulls, {"columns": columns})
-
-
-
- -
- -
- - -

- rows_unique - - -

-
rows_unique(
-    columns: str | Sequence[str] | None = None,
-    keep: Literal["first", "last", "any", "none"] = "first",
-) -> Self
-
- -
- -

Keep unique rows based on specified columns.

- - -
- Source code in transformplan/ops/rows.py -
72
-73
-74
-75
-76
-77
-78
-79
-80
def rows_unique(
-    self,
-    columns: str | Sequence[str] | None = None,
-    keep: Literal["first", "last", "any", "none"] = "first",
-) -> Self:
-    """Keep unique rows based on specified columns."""
-    if isinstance(columns, str):
-        columns = [columns]
-    return self._register(self._rows_unique, {"columns": columns, "keep": keep})
-
-
-
- -
- -
- - -

- rows_deduplicate - - -

-
rows_deduplicate(
-    columns: str | Sequence[str],
-    sort_by: str,
-    keep: Literal["first", "last"] = "first",
-    descending: bool = False,
-) -> Self
-
- -
- -

Deduplicate rows by keeping first/last based on sort order.

- - -

Parameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- columns - - str | Sequence[str] - -
-

Columns that define duplicates.

-
-
- required -
- sort_by - - str - -
-

Column to sort by before deduplication.

-
-
- required -
- keep - - Literal['first', 'last'] - -
-

Keep 'first' or 'last' after sorting.

-
-
- 'first' -
- descending - - bool - -
-

Sort in descending order.

-
-
- False -
- - -
- Source code in transformplan/ops/rows.py -
def rows_deduplicate(
-    self,
-    columns: str | Sequence[str],
-    sort_by: str,
-    keep: Literal["first", "last"] = "first",
-    descending: bool = False,
-) -> Self:
-    """Deduplicate rows by keeping first/last based on sort order.
-
-    Args:
-        columns: Columns that define duplicates.
-        sort_by: Column to sort by before deduplication.
-        keep: Keep 'first' or 'last' after sorting.
-        descending: Sort in descending order.
-    """
-    if isinstance(columns, str):
-        columns = [columns]
-    return self._register(
-        self._rows_deduplicate,
-        {
-            "columns": list(columns),
-            "sort_by": sort_by,
-            "keep": keep,
-            "descending": descending,
-        },
-    )
-
-
-
- -
- -
- - -

- rows_sort - - -

-
rows_sort(
-    by: str | Sequence[str], descending: bool | Sequence[bool] = False
-) -> Self
-
- -
- -

Sort rows by one or more columns.

- - -

Parameters:

- - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- by - - str | Sequence[str] - -
-

Column(s) to sort by.

-
-
- required -
- descending - - bool | Sequence[bool] - -
-

Sort direction (single bool or list matching columns).

-
-
- False -
- - -
- Source code in transformplan/ops/rows.py -
def rows_sort(
-    self,
-    by: str | Sequence[str],
-    descending: bool | Sequence[bool] = False,
-) -> Self:
-    """Sort rows by one or more columns.
-
-    Args:
-        by: Column(s) to sort by.
-        descending: Sort direction (single bool or list matching columns).
-    """
-    if isinstance(by, str):
-        by = [by]
-    return self._register(
-        self._rows_sort, {"by": list(by), "descending": descending}
-    )
-
-
-
- -
- -
- - -

- rows_flag - - -

-
rows_flag(
-    filter: Filter | dict,
-    new_column: str,
-    true_value: Any = True,
-    false_value: Any = False,
-) -> Self
-
- -
- -

Add a flag column based on a filter condition (without dropping rows).

- - -

Parameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- filter - - Filter | dict - -
-

Filter condition.

-
-
- required -
- new_column - - str - -
-

Name for the flag column.

-
-
- required -
- true_value - - Any - -
-

Value when condition is True.

-
-
- True -
- false_value - - Any - -
-

Value when condition is False.

-
-
- False -
- - -
- Source code in transformplan/ops/rows.py -
def rows_flag(
-    self,
-    filter: Filter | dict,
-    new_column: str,
-    true_value: Any = True,
-    false_value: Any = False,
-) -> Self:
-    """Add a flag column based on a filter condition (without dropping rows).
-
-    Args:
-        filter: Filter condition.
-        new_column: Name for the flag column.
-        true_value: Value when condition is True.
-        false_value: Value when condition is False.
-    """
-    if isinstance(filter, dict):
-        filter_dict = filter
-    else:
-        filter_dict = filter.to_dict()
-    return self._register(
-        self._rows_flag,
-        {
-            "filter": filter_dict,
-            "new_column": new_column,
-            "true_value": true_value,
-            "false_value": false_value,
-        },
-    )
-
-
-
- -
- -
- - -

- rows_head - - -

-
rows_head(n: int = 5) -> Self
-
- -
- -

Keep only the first n rows.

- - -
- Source code in transformplan/ops/rows.py -
def rows_head(self, n: int = 5) -> Self:
-    """Keep only the first n rows."""
-    return self._register(self._rows_head, {"n": n})
-
-
-
- -
- -
- - -

- rows_tail - - -

-
rows_tail(n: int = 5) -> Self
-
- -
- -

Keep only the last n rows.

- - -
- Source code in transformplan/ops/rows.py -
def rows_tail(self, n: int = 5) -> Self:
-    """Keep only the last n rows."""
-    return self._register(self._rows_tail, {"n": n})
-
-
-
- -
- -
- - -

- rows_sample - - -

-
rows_sample(
-    n: int | None = None, fraction: float | None = None, seed: int | None = None
-) -> Self
-
- -
- -

Sample rows from the DataFrame.

- - -

Parameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- n - - int | None - -
-

Number of rows to sample.

-
-
- None -
- fraction - - float | None - -
-

Fraction of rows to sample (0.0 to 1.0).

-
-
- None -
- seed - - int | None - -
-

Random seed for reproducibility.

-
-
- None -
- - -
- Source code in transformplan/ops/rows.py -
def rows_sample(
-    self,
-    n: int | None = None,
-    fraction: float | None = None,
-    seed: int | None = None,
-) -> Self:
-    """Sample rows from the DataFrame.
-
-    Args:
-        n: Number of rows to sample.
-        fraction: Fraction of rows to sample (0.0 to 1.0).
-        seed: Random seed for reproducibility.
-    """
-    return self._register(
-        self._rows_sample, {"n": n, "fraction": fraction, "seed": seed}
-    )
-
-
-
- -
- -
- - -

- rows_explode - - -

-
rows_explode(column: str) -> Self
-
- -
- -

Explode a list column into multiple rows.

- - -
- Source code in transformplan/ops/rows.py -
def rows_explode(self, column: str) -> Self:
-    """Explode a list column into multiple rows."""
-    return self._register(self._rows_explode, {"column": column})
-
-
-
- -
- -
- - -

- rows_melt - - -

-
rows_melt(
-    id_columns: Sequence[str],
-    value_columns: Sequence[str],
-    variable_name: str = "variable",
-    value_name: str = "value",
-) -> Self
-
- -
- -

Unpivot a DataFrame from wide to long format.

- - -

Parameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- id_columns - - Sequence[str] - -
-

Columns to keep as identifiers.

-
-
- required -
- value_columns - - Sequence[str] - -
-

Columns to unpivot.

-
-
- required -
- variable_name - - str - -
-

Name for the variable column.

-
-
- 'variable' -
- value_name - - str - -
-

Name for the value column.

-
-
- 'value' -
- - -
- Source code in transformplan/ops/rows.py -
def rows_melt(
-    self,
-    id_columns: Sequence[str],
-    value_columns: Sequence[str],
-    variable_name: str = "variable",
-    value_name: str = "value",
-) -> Self:
-    """Unpivot a DataFrame from wide to long format.
-
-    Args:
-        id_columns: Columns to keep as identifiers.
-        value_columns: Columns to unpivot.
-        variable_name: Name for the variable column.
-        value_name: Name for the value column.
-    """
-    return self._register(
-        self._rows_melt,
-        {
-            "id_columns": list(id_columns),
-            "value_columns": list(value_columns),
-            "variable_name": variable_name,
-            "value_name": value_name,
-        },
-    )
-
-
-
- -
- -
- - -

- rows_pivot - - -

-
rows_pivot(
-    index: str | Sequence[str],
-    columns: str,
-    values: str,
-    aggregate_function: str = "first",
-) -> Self
-
- -
- -

Pivot from long to wide format.

- - -

Parameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- index - - str | Sequence[str] - -
-

Column(s) to use as row identifiers.

-
-
- required -
- columns - - str - -
-

Column whose unique values become new columns.

-
-
- required -
- values - - str - -
-

Column containing values to fill.

-
-
- required -
- aggregate_function - - str - -
-

How to aggregate ('first', 'sum', 'mean', 'count', etc.).

-
-
- 'first' -
- - -
- Source code in transformplan/ops/rows.py -
def rows_pivot(
-    self,
-    index: str | Sequence[str],
-    columns: str,
-    values: str,
-    aggregate_function: str = "first",
-) -> Self:
-    """Pivot from long to wide format.
-
-    Args:
-        index: Column(s) to use as row identifiers.
-        columns: Column whose unique values become new columns.
-        values: Column containing values to fill.
-        aggregate_function: How to aggregate ('first', 'sum', 'mean', 'count', etc.).
-    """
-    if isinstance(index, str):
-        index = [index]
-    return self._register(
-        self._rows_pivot,
-        {
-            "index": list(index),
-            "columns": columns,
-            "values": values,
-            "aggregate_function": aggregate_function,
-        },
-    )
-
-
-
- -
- - - -
- -
- -

Examples

-

Filtering Rows

-
from transformplan import Col
-
-# Keep rows matching condition
-plan = TransformPlan().rows_filter(Col("age") >= 18)
-
-# Drop rows matching condition
-plan = TransformPlan().rows_drop(Col("status") == "deleted")
-
-# Complex filters
-plan = TransformPlan().rows_filter(
-    (Col("score") >= 50) & (Col("active") == True)
-)
-
-

Flagging Rows

-

Add a boolean column based on a condition without removing rows:

-
plan = TransformPlan().rows_flag(
-    filter=Col("score") >= 90,
-    new_column="is_excellent",
-    true_value=True,
-    false_value=False
-)
-
-

Sorting

-
# Sort by single column
-plan = TransformPlan().rows_sort("name")
-
-# Sort descending
-plan = TransformPlan().rows_sort("score", descending=True)
-
-# Sort by multiple columns
-plan = TransformPlan().rows_sort(
-    by=["category", "price"],
-    descending=[False, True]
-)
-
-

Removing Duplicates

-
# Keep first occurrence of each unique value
-plan = TransformPlan().rows_unique(columns=["email"])
-
-# Keep last occurrence
-plan = TransformPlan().rows_unique(columns=["user_id"], keep="last")
-
-# Deduplicate with specific sort order
-plan = TransformPlan().rows_deduplicate(
-    columns=["user_id"],
-    sort_by="updated_at",
-    keep="last",
-    descending=True
-)
-
-

Handling Nulls

-
# Drop rows with nulls in any column
-plan = TransformPlan().rows_drop_nulls()
-
-# Drop rows with nulls in specific columns
-plan = TransformPlan().rows_drop_nulls(columns=["required_field"])
-
-

Limiting Rows

-
# Keep first n rows
-plan = TransformPlan().rows_head(10)
-
-# Keep last n rows
-plan = TransformPlan().rows_tail(10)
-
-# Random sample
-plan = TransformPlan().rows_sample(n=100, seed=42)
-plan = TransformPlan().rows_sample(fraction=0.1, seed=42)
-
-

Reshaping

-
# Explode list column into multiple rows
-plan = TransformPlan().rows_explode("tags")
-
-# Unpivot from wide to long format
-plan = TransformPlan().rows_melt(
-    id_columns=["id", "name"],
-    value_columns=["q1", "q2", "q3", "q4"],
-    variable_name="quarter",
-    value_name="sales"
-)
-
-# Pivot from long to wide format
-plan = TransformPlan().rows_pivot(
-    index=["id"],
-    columns="quarter",
-    values="sales",
-    aggregate_function="sum"
-)
-
- - - - - - - - - - - - - -
-
- - - -
- - - -
- - - -
-
-
-
- - - - - - - - - - - - - \ No newline at end of file diff --git a/site/api/ops/string/index.html b/site/api/ops/string/index.html deleted file mode 100644 index 67adff2..0000000 --- a/site/api/ops/string/index.html +++ /dev/null @@ -1,2723 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - String Operations - TransformPlan - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - Skip to content - - -
-
- -
- - - - - - -
- - -
- -
- - - - - - -
-
- - - -
-
-
- - - - - -
-
-
- - - - - - - -
- -
- - - - - - - - -

String Operations

-

Text manipulation operations on string columns.

-

Overview

-

String operations allow you to transform text data in DataFrame columns. Operations include case conversion, trimming, splitting, concatenation, and pattern matching.

-
from transformplan import TransformPlan
-
-plan = (
-    TransformPlan()
-    .str_lower("email")
-    .str_strip("name")
-    .str_replace("phone", "-", "")
-)
-
-

Class Reference

- - -
- - - -

- StrOps - - -

- - -
- - - -

Mixin providing string operations on columns.

- - - - - - - - - - - -
- - - - - - - - - - -
- - -

- str_replace - - -

-
str_replace(
-    column: str, pattern: str, replacement: str, literal: bool = True
-) -> Self
-
- -
- -

Replace occurrences of a pattern in a string column.

- - -

Parameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- column - - str - -
-

Column to modify.

-
-
- required -
- pattern - - str - -
-

Pattern to search for.

-
-
- required -
- replacement - - str - -
-

String to replace with.

-
-
- required -
- literal - - bool - -
-

If True, treat pattern as literal string. If False, treat as regex.

-
-
- True -
- - -
- Source code in transformplan/ops/string.py -
53
-54
-55
-56
-57
-58
-59
-60
-61
-62
-63
-64
-65
-66
-67
-68
-69
-70
-71
-72
-73
-74
-75
-76
def str_replace(
-    self,
-    column: str,
-    pattern: str,
-    replacement: str,
-    literal: bool = True,
-) -> Self:
-    """Replace occurrences of a pattern in a string column.
-
-    Args:
-        column: Column to modify.
-        pattern: Pattern to search for.
-        replacement: String to replace with.
-        literal: If True, treat pattern as literal string. If False, treat as regex.
-    """
-    return self._register(
-        self._str_replace,
-        {
-            "column": column,
-            "pattern": pattern,
-            "replacement": replacement,
-            "literal": literal,
-        },
-    )
-
-
-
- -
- -
- - -

- str_slice - - -

-
str_slice(column: str, offset: int, length: int | None = None) -> Self
-
- -
- -

Extract a substring from a string column.

- - -

Parameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- column - - str - -
-

Column to modify.

-
-
- required -
- offset - - int - -
-

Start position (0-indexed, negative counts from end).

-
-
- required -
- length - - int | None - -
-

Number of characters to extract (None = to end).

-
-
- None -
- - -
- Source code in transformplan/ops/string.py -
def str_slice(
-    self,
-    column: str,
-    offset: int,
-    length: int | None = None,
-) -> Self:
-    """Extract a substring from a string column.
-
-    Args:
-        column: Column to modify.
-        offset: Start position (0-indexed, negative counts from end).
-        length: Number of characters to extract (None = to end).
-    """
-    return self._register(
-        self._str_slice, {"column": column, "offset": offset, "length": length}
-    )
-
-
-
- -
- -
- - -

- str_truncate - - -

-
str_truncate(column: str, max_length: int, suffix: str = '...') -> Self
-
- -
- -

Truncate strings to a maximum length with optional suffix.

- - -

Parameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- column - - str - -
-

Column to modify.

-
-
- required -
- max_length - - int - -
-

Maximum length of the string (including suffix).

-
-
- required -
- suffix - - str - -
-

Suffix to append to truncated strings.

-
-
- '...' -
- - -
- Source code in transformplan/ops/string.py -
def str_truncate(self, column: str, max_length: int, suffix: str = "...") -> Self:
-    """Truncate strings to a maximum length with optional suffix.
-
-    Args:
-        column: Column to modify.
-        max_length: Maximum length of the string (including suffix).
-        suffix: Suffix to append to truncated strings.
-    """
-    return self._register(
-        self._str_truncate,
-        {"column": column, "max_length": max_length, "suffix": suffix},
-    )
-
-
-
- -
- -
- - -

- str_lower - - -

-
str_lower(column: str) -> Self
-
- -
- -

Convert string column to lowercase.

- - -
- Source code in transformplan/ops/string.py -
def str_lower(self, column: str) -> Self:
-    """Convert string column to lowercase."""
-    return self._register(self._str_lower, {"column": column})
-
-
-
- -
- -
- - -

- str_upper - - -

-
str_upper(column: str) -> Self
-
- -
- -

Convert string column to uppercase.

- - -
- Source code in transformplan/ops/string.py -
def str_upper(self, column: str) -> Self:
-    """Convert string column to uppercase."""
-    return self._register(self._str_upper, {"column": column})
-
-
-
- -
- -
- - -

- str_strip - - -

-
str_strip(column: str, chars: str | None = None) -> Self
-
- -
- -

Strip leading and trailing characters from a string column.

- - -

Parameters:

- - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- column - - str - -
-

Column to modify.

-
-
- required -
- chars - - str | None - -
-

Characters to strip (None = whitespace).

-
-
- None -
- - -
- Source code in transformplan/ops/string.py -
def str_strip(self, column: str, chars: str | None = None) -> Self:
-    """Strip leading and trailing characters from a string column.
-
-    Args:
-        column: Column to modify.
-        chars: Characters to strip (None = whitespace).
-    """
-    return self._register(self._str_strip, {"column": column, "chars": chars})
-
-
-
- -
- -
- - -

- str_pad - - -

-
str_pad(
-    column: str, length: int, fill_char: str = " ", side: str = "left"
-) -> Self
-
- -
- -

Pad a string column to a specified length.

- - -

Parameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- column - - str - -
-

Column to modify.

-
-
- required -
- length - - int - -
-

Target length.

-
-
- required -
- fill_char - - str - -
-

Character to pad with.

-
-
- ' ' -
- side - - str - -
-

'left' or 'right'.

-
-
- 'left' -
- - -
- Source code in transformplan/ops/string.py -
def str_pad(
-    self,
-    column: str,
-    length: int,
-    fill_char: str = " ",
-    side: str = "left",
-) -> Self:
-    """Pad a string column to a specified length.
-
-    Args:
-        column: Column to modify.
-        length: Target length.
-        fill_char: Character to pad with.
-        side: 'left' or 'right'.
-    """
-    return self._register(
-        self._str_pad,
-        {"column": column, "length": length, "fill_char": fill_char, "side": side},
-    )
-
-
-
- -
- -
- - -

- str_split - - -

-
str_split(
-    column: str,
-    separator: str,
-    new_columns: list[str] | None = None,
-    keep_original: bool = False,
-) -> Self
-
- -
- -

Split a string column by separator.

- - -

Parameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- column - - str - -
-

Column to split.

-
-
- required -
- separator - - str - -
-

String to split on.

-
-
- required -
- new_columns - - list[str] | None - -
-

Names for the resulting columns. If None, explodes into rows.

-
-
- None -
- keep_original - - bool - -
-

Whether to keep the original column.

-
-
- False -
- - -
- Source code in transformplan/ops/string.py -
def str_split(
-    self,
-    column: str,
-    separator: str,
-    new_columns: list[str] | None = None,
-    keep_original: bool = False,
-) -> Self:
-    """Split a string column by separator.
-
-    Args:
-        column: Column to split.
-        separator: String to split on.
-        new_columns: Names for the resulting columns. If None, explodes into rows.
-        keep_original: Whether to keep the original column.
-    """
-    return self._register(
-        self._str_split,
-        {
-            "column": column,
-            "separator": separator,
-            "new_columns": new_columns,
-            "keep_original": keep_original,
-        },
-    )
-
-
-
- -
- -
- - -

- str_concat - - -

-
str_concat(columns: list[str], new_column: str, separator: str = '') -> Self
-
- -
- -

Concatenate multiple string columns into one.

- - -

Parameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- columns - - list[str] - -
-

Columns to concatenate.

-
-
- required -
- new_column - - str - -
-

Name for the new column.

-
-
- required -
- separator - - str - -
-

Separator between values.

-
-
- '' -
- - -
- Source code in transformplan/ops/string.py -
def str_concat(
-    self,
-    columns: list[str],
-    new_column: str,
-    separator: str = "",
-) -> Self:
-    """Concatenate multiple string columns into one.
-
-    Args:
-        columns: Columns to concatenate.
-        new_column: Name for the new column.
-        separator: Separator between values.
-    """
-    return self._register(
-        self._str_concat,
-        {"columns": columns, "new_column": new_column, "separator": separator},
-    )
-
-
-
- -
- -
- - -

- str_extract - - -

-
str_extract(
-    column: str,
-    pattern: str,
-    group_index: int = 1,
-    new_column: str | None = None,
-) -> Self
-
- -
- -

Extract substring using regex capture group.

- - -

Parameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- column - - str - -
-

Column to extract from.

-
-
- required -
- pattern - - str - -
-

Regex pattern with capture group(s).

-
-
- required -
- group_index - - int - -
-

Which capture group to extract (1-indexed).

-
-
- 1 -
- new_column - - str | None - -
-

Name for result column (None = modify in place).

-
-
- None -
- - -
- Source code in transformplan/ops/string.py -
def str_extract(
-    self,
-    column: str,
-    pattern: str,
-    group_index: int = 1,
-    new_column: str | None = None,
-) -> Self:
-    """Extract substring using regex capture group.
-
-    Args:
-        column: Column to extract from.
-        pattern: Regex pattern with capture group(s).
-        group_index: Which capture group to extract (1-indexed).
-        new_column: Name for result column (None = modify in place).
-    """
-    return self._register(
-        self._str_extract,
-        {
-            "column": column,
-            "pattern": pattern,
-            "group_index": group_index,
-            "new_column": new_column or column,
-        },
-    )
-
-
-
- -
- - - -
- -
- -

Examples

-

Case Conversion

-
# Convert to lowercase
-plan = TransformPlan().str_lower("email")
-
-# Convert to uppercase
-plan = TransformPlan().str_upper("code")
-
-

Trimming and Padding

-
# Strip whitespace
-plan = TransformPlan().str_strip("name")
-
-# Strip specific characters
-plan = TransformPlan().str_strip("code", chars="-_")
-
-# Pad to fixed length
-plan = TransformPlan().str_pad("id", length=10, fill_char="0", side="left")
-
-

Replacement

-
# Replace literal string
-plan = TransformPlan().str_replace("phone", "-", "")
-
-# Replace with regex
-plan = TransformPlan().str_replace(
-    column="text",
-    pattern=r"\s+",
-    replacement=" ",
-    literal=False
-)
-
-

Substring Operations

-
# Extract substring by position
-plan = TransformPlan().str_slice("code", offset=0, length=3)
-
-# Truncate with suffix
-plan = TransformPlan().str_truncate("description", max_length=100, suffix="...")
-
-

Splitting

-
# Split into rows (explode)
-plan = TransformPlan().str_split("tags", separator=",")
-
-# Split into columns
-plan = TransformPlan().str_split(
-    column="full_name",
-    separator=" ",
-    new_columns=["first_name", "last_name"],
-    keep_original=False
-)
-
-

Concatenation

-
# Concatenate columns
-plan = TransformPlan().str_concat(
-    columns=["first_name", "last_name"],
-    new_column="full_name",
-    separator=" "
-)
-
-

Pattern Extraction

-
# Extract with regex capture group
-plan = TransformPlan().str_extract(
-    column="email",
-    pattern=r"@(.+)$",
-    group_index=1,
-    new_column="domain"
-)
-
- - - - - - - - - - - - - -
-
- - - -
- - - -
- - - -
-
-
-
- - - - - - - - - - - - - \ No newline at end of file diff --git a/site/api/plan/index.html b/site/api/plan/index.html deleted file mode 100644 index 0bc760c..0000000 --- a/site/api/plan/index.html +++ /dev/null @@ -1,1414 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - TransformPlan - TransformPlan - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - Skip to content - - -
-
- -
- - - - - - -
- - -
- -
- - - - - - -
-
- - - -
-
-
- - - - - -
-
-
- - - - - - - -
- -
- - - - - - - - -

TransformPlan

-

The main class for building and executing transformation pipelines.

-

Overview

-

TransformPlan uses a deferred execution model: operations are registered via method chaining, then executed together when you call process(), validate(), or dry_run().

-
from transformplan import TransformPlan, Col
-
-plan = (
-    TransformPlan()
-    .col_drop("temp_column")
-    .math_multiply("price", 1.1)
-    .rows_filter(Col("active") == True)
-)
-
-# Execute
-df_result, protocol = plan.process(df)
-
-

Class Reference

- - -
- - - -

- TransformPlan - - -

-
TransformPlan()
-
- -
-

- Bases: TransformPlanBase, ColumnOps, DatetimeOps, MapOps, MathOps, RowOps, StrOps

- - - -

Data processor with tracked transformations.

- - -
- Usage -

result, protocol = ( - TransformPlan() - .col_drop("temp") - .math_multiply("price", 1.1) - .rows_filter(Col("active") == True) - .process(df) -)

-
- - - - - - - -
- Source code in transformplan/core.py -
def __init__(self) -> None:
-    self._operations: list[tuple[Callable[..., pl.DataFrame], dict[str, Any]]] = []
-
-
- - - -
- - - - - - - - - - - - -
- -
- -

Execution Methods

-

process

-

Execute all registered operations and return transformed data with an audit protocol.

-
df_result, protocol = plan.process(df)
-
-

validate

-

Validate operations against the DataFrame schema without executing.

-
result = plan.validate(df)
-if not result.is_valid:
-    for error in result.errors:
-        print(error)
-
-

dry_run

-

Preview what the pipeline will do without executing it.

-
preview = plan.dry_run(df)
-preview.print()
-
-

Chunked Processing

-

For large Parquet files that exceed available RAM, use chunked processing methods.

-

process_chunked

-

Process a large Parquet file in chunks, optionally keeping related rows together.

-
result, protocol = plan.process_chunked(
-    source="large_file.parquet",
-    partition_key="patient_id",  # Keep patient rows together
-    chunk_size=100_000,
-)
-protocol.print()
-
-

See Chunked Processing for details on operation compatibility.

-

validate_chunked

-

Validate that a pipeline is compatible with chunked processing before executing.

-
validation = plan.validate_chunked(
-    schema={"id": pl.Int64, "name": pl.Utf8},
-    partition_key="id"
-)
-if not validation.is_valid:
-    print(validation.errors)
-
-

Serialization

-

Pipelines can be saved and loaded as JSON:

-
# Save
-plan.to_json("pipeline.json")
-
-# Load
-loaded = TransformPlan.from_json("pipeline.json")
-
-

Or generate executable Python code:

-
print(plan.to_python())
-
- - - - - - - - - - - - - -
-
- - - -
- - - -
- - - -
-
-
-
- - - - - - - - - - - - - \ No newline at end of file diff --git a/site/api/protocol/index.html b/site/api/protocol/index.html deleted file mode 100644 index fbd1a97..0000000 --- a/site/api/protocol/index.html +++ /dev/null @@ -1,2928 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Protocol - TransformPlan - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - Skip to content - - -
-
- -
- - - - - - -
- - -
- -
- - - - - - -
-
- - - -
-
-
- - - - - -
-
-
- - - - - - - -
- -
- - - - - - - - -

Protocol

-

The Protocol class captures transformation history for auditability and reproducibility.

-

Overview

-

When you process data with a TransformPlan, you receive both the transformed DataFrame and a Protocol object. The protocol contains:

-
    -
  • Input/output DataFrame hashes for verification
  • -
  • Step-by-step operation details
  • -
  • Shape changes and timing information
  • -
  • Optional metadata
  • -
-
from transformplan import TransformPlan
-
-plan = TransformPlan().col_drop("temp").math_multiply("price", 1.1)
-df_result, protocol = plan.process(df)
-
-# View the protocol
-protocol.print()
-
-# Save for audit
-protocol.to_json("audit_trail.json")
-
-

Protocol Class

- - -
- - - -

- Protocol - - -

-
Protocol()
-
- -
- - - -

Captures the transformation history for auditability.

- - - - - - - - -
- Source code in transformplan/protocol.py -
68
-69
-70
-71
-72
-73
def __init__(self) -> None:
-    self._steps: list[dict[str, Any]] = []
-    self._input_hash: str | None = None
-    self._input_shape: tuple[int, int] | None = None
-    self._created_at: str = datetime.utcnow().isoformat() + "Z"
-    self._metadata: dict[str, Any] = {}
-
-
- - - -
- - - - - - - -
- - - -

- input_hash - - - - property - - -

-
input_hash: str | None
-
- -
- -

Hash of the input DataFrame.

- -
- -
- -
- - - -

- output_hash - - - - property - - -

-
output_hash: str | None
-
- -
- -

Hash of the final output DataFrame.

- -
- -
- -
- - - -

- metadata - - - - property - - -

-
metadata: dict[str, Any]
-
- -
- -

Protocol metadata.

- -
- -
- - - - -
- - -

- set_input - - -

-
set_input(hash_value: str, shape: tuple[int, int]) -> None
-
- -
- -

Set the hash and shape of the input DataFrame.

- - -
- Source code in transformplan/protocol.py -
75
-76
-77
-78
def set_input(self, hash_value: str, shape: tuple[int, int]) -> None:
-    """Set the hash and shape of the input DataFrame."""
-    self._input_hash = hash_value
-    self._input_shape = shape
-
-
-
- -
- -
- - -

- set_metadata - - -

-
set_metadata(**kwargs: Any) -> None
-
- -
- -

Set arbitrary metadata on the protocol.

- - -
- Example -

protocol.set_metadata(author="alice", project="analysis-v2")

-
- -
- Source code in transformplan/protocol.py -
80
-81
-82
-83
-84
-85
-86
def set_metadata(self, **kwargs: Any) -> None:
-    """Set arbitrary metadata on the protocol.
-
-    Example:
-        protocol.set_metadata(author="alice", project="analysis-v2")
-    """
-    self._metadata.update(kwargs)
-
-
-
- -
- -
- - -

- add_step - - -

-
add_step(
-    operation: str,
-    params: dict[str, Any],
-    old_shape: tuple[int, int],
-    new_shape: tuple[int, int],
-    elapsed: float,
-    output_hash: str,
-) -> None
-
- -
- - -
- Source code in transformplan/protocol.py -
def add_step(
-    self,
-    operation: str,
-    params: dict[str, Any],
-    old_shape: tuple[int, int],
-    new_shape: tuple[int, int],
-    elapsed: float,
-    output_hash: str,
-) -> None:
-    self._steps.append(
-        {
-            "step": len(self._steps) + 1,
-            "operation": operation,
-            "params": params,
-            "old_shape": old_shape,
-            "new_shape": new_shape,
-            "rows_changed": old_shape[0] - new_shape[0],
-            "cols_changed": old_shape[1] - new_shape[1],
-            "elapsed_seconds": round(elapsed, 4),
-            "output_hash": output_hash,
-        }
-    )
-
-
-
- -
- -
- - -

- to_dataframe - - -

-
to_dataframe() -> DataFrame
-
- -
- - -
- Source code in transformplan/protocol.py -
def to_dataframe(self) -> pl.DataFrame:
-    rows = []
-
-    # Step 0: input state
-    if self._input_hash is not None:
-        rows.append(
-            {
-                "step": 0,
-                "operation": "input",
-                "params": None,
-                "old_shape": None,
-                "new_shape": self._input_shape,
-                "rows_changed": 0,
-                "cols_changed": 0,
-                "elapsed_seconds": 0.0,
-                "output_hash": self._input_hash,
-            }
-        )
-
-    rows.extend(self._steps)
-    return pl.DataFrame(rows)
-
-
-
- -
- -
- - -

- to_csv - - -

-
to_csv(path: str | Path) -> None
-
- -
- -

Write protocol to CSV file.

-

Params are serialized as JSON strings to avoid nested data issues.

- - -

Parameters:

- - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- path - - str | Path - -
-

File path to write to.

-
-
- required -
- - -
- Source code in transformplan/protocol.py -
def to_csv(self, path: str | Path) -> None:
-    """Write protocol to CSV file.
-
-    Params are serialized as JSON strings to avoid nested data issues.
-
-    Args:
-        path: File path to write to.
-    """
-    rows = []
-
-    # Step 0: input state
-    if self._input_hash is not None:
-        rows.append(
-            {
-                "step": 0,
-                "operation": "input",
-                "params": None,
-                "old_shape": None,
-                "new_shape": str(list(self._input_shape))
-                if self._input_shape
-                else None,
-                "rows_changed": 0,
-                "cols_changed": 0,
-                "elapsed_seconds": 0.0,
-                "output_hash": self._input_hash,
-            }
-        )
-
-    for step in self._steps:
-        rows.append(
-            {
-                "step": step["step"],
-                "operation": step["operation"],
-                "params": json.dumps(step["params"]) if step["params"] else None,
-                "old_shape": str(list(step["old_shape"])),
-                "new_shape": str(list(step["new_shape"])),
-                "rows_changed": step["rows_changed"],
-                "cols_changed": step["cols_changed"],
-                "elapsed_seconds": step["elapsed_seconds"],
-                "output_hash": step["output_hash"],
-            }
-        )
-
-    pl.DataFrame(rows).write_csv(path)
-
-
-
- -
- -
- - -

- to_dict - - -

-
to_dict() -> dict[str, Any]
-
- -
- -

Serialize protocol to a dictionary.

- - -
- Source code in transformplan/protocol.py -
def to_dict(self) -> dict[str, Any]:
-    """Serialize protocol to a dictionary."""
-    return {
-        "version": self.VERSION,
-        "created_at": self._created_at,
-        "metadata": self._metadata,
-        "input": {
-            "hash": self._input_hash,
-            "shape": list(self._input_shape) if self._input_shape else None,
-        },
-        "steps": [
-            {
-                "step": s["step"],
-                "operation": s["operation"],
-                "params": s["params"],
-                "old_shape": list(s["old_shape"]),
-                "new_shape": list(s["new_shape"]),
-                "rows_changed": s["rows_changed"],
-                "cols_changed": s["cols_changed"],
-                "elapsed_seconds": s["elapsed_seconds"],
-                "output_hash": s["output_hash"],
-            }
-            for s in self._steps
-        ],
-    }
-
-
-
- -
- -
- - -

- from_dict - - - - classmethod - - -

-
from_dict(data: dict[str, Any]) -> Protocol
-
- -
- -

Deserialize protocol from a dictionary.

- - -
- Source code in transformplan/protocol.py -
@classmethod
-def from_dict(cls, data: dict[str, Any]) -> Protocol:
-    """Deserialize protocol from a dictionary."""
-    protocol = cls()
-    protocol._created_at = data.get("created_at", protocol._created_at)
-    protocol._metadata = data.get("metadata", {})
-
-    input_data = data.get("input", {})
-    protocol._input_hash = input_data.get("hash")
-    shape = input_data.get("shape")
-    protocol._input_shape = tuple(shape) if shape else None
-
-    for step in data.get("steps", []):
-        protocol._steps.append(
-            {
-                "step": step["step"],
-                "operation": step["operation"],
-                "params": step["params"],
-                "old_shape": tuple(step["old_shape"]),
-                "new_shape": tuple(step["new_shape"]),
-                "rows_changed": step["rows_changed"],
-                "cols_changed": step["cols_changed"],
-                "elapsed_seconds": step["elapsed_seconds"],
-                "output_hash": step["output_hash"],
-            }
-        )
-
-    return protocol
-
-
-
- -
- -
- - -

- to_json - - -

-
to_json(path: str | Path | None = None, indent: int = 2) -> str
-
- -
- -

Serialize protocol to JSON.

- - -

Parameters:

- - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- path - - str | Path | None - -
-

Optional file path to write to.

-
-
- None -
- indent - - int - -
-

JSON indentation level.

-
-
- 2 -
- - -

Returns:

- - - - - - - - - - - - - -
TypeDescription
- str - -
-

JSON string.

-
-
- - -
- Source code in transformplan/protocol.py -
def to_json(self, path: str | Path | None = None, indent: int = 2) -> str:
-    """Serialize protocol to JSON.
-
-    Args:
-        path: Optional file path to write to.
-        indent: JSON indentation level.
-
-    Returns:
-        JSON string.
-    """
-    json_str = json.dumps(self.to_dict(), indent=indent)
-
-    if path is not None:
-        Path(path).write_text(json_str)
-
-    return json_str
-
-
-
- -
- -
- - -

- from_json - - - - classmethod - - -

-
from_json(source: str | Path) -> Protocol
-
- -
- -

Deserialize protocol from JSON.

- - -

Parameters:

- - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- source - - str | Path - -
-

Either a JSON string or a path to a JSON file.

-
-
- required -
- - -

Returns:

- - - - - - - - - - - - - -
TypeDescription
- Protocol - -
-

Protocol instance.

-
-
- - -
- Source code in transformplan/protocol.py -
@classmethod
-def from_json(cls, source: str | Path) -> Protocol:
-    """Deserialize protocol from JSON.
-
-    Args:
-        source: Either a JSON string or a path to a JSON file.
-
-    Returns:
-        Protocol instance.
-    """
-    if isinstance(source, Path) or (
-        isinstance(source, str) and not source.strip().startswith("{")
-    ):
-        # Treat as file path
-        content = Path(source).read_text()
-    else:
-        # Treat as JSON string
-        content = source
-
-    return cls.from_dict(json.loads(content))
-
-
-
- -
- -
- - -

- summary - - -

-
summary(show_params: bool = True) -> str
-
- -
- -

Generate a clean, human-readable summary of the protocol.

- - -

Parameters:

- - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- show_params - - bool - -
-

Whether to include operation parameters.

-
-
- True -
- - -

Returns:

- - - - - - - - - - - - - -
TypeDescription
- str - -
-

Formatted string summary.

-
-
- - -
- Source code in transformplan/protocol.py -
def summary(self, show_params: bool = True) -> str:
-    """Generate a clean, human-readable summary of the protocol.
-
-    Args:
-        show_params: Whether to include operation parameters.
-
-    Returns:
-        Formatted string summary.
-    """
-    lines = []
-
-    # Header
-    lines.append("=" * 70)
-    lines.append("TRANSFORM PROTOCOL")
-    lines.append("=" * 70)
-
-    # Metadata
-    if self._metadata:
-        for key, value in self._metadata.items():
-            lines.append(f"{key}: {value}")
-        lines.append("-" * 70)
-
-    # Input info
-    if self._input_hash:
-        shape_str = (
-            f"{self._input_shape[0]} rows × {self._input_shape[1]} cols"
-            if self._input_shape
-            else "unknown"
-        )
-        lines.append(f"Input:  {shape_str}  [{self._input_hash}]")
-
-    # Output info
-    if self._steps:
-        final = self._steps[-1]
-        shape_str = f"{final['new_shape'][0]} rows × {final['new_shape'][1]} cols"
-        lines.append(f"Output: {shape_str}  [{final['output_hash']}]")
-
-    # Total time
-    total_time = sum(s["elapsed_seconds"] for s in self._steps)
-    lines.append(f"Total time: {total_time:.4f}s")
-    lines.append("-" * 70)
-
-    # Steps
-    lines.append("")
-    lines.append(
-        f"{'#':<4} {'Operation':<20} {'Rows':<12} {'Cols':<12} {'Time':<10} {'Hash':<16}"
-    )
-    lines.append("-" * 70)
-
-    # Input row
-    if self._input_hash:
-        shape = self._input_shape or (0, 0)
-        lines.append(
-            f"{'0':<4} {'input':<20} {shape[0]:<12} {shape[1]:<12} {'-':<10} {self._input_hash:<16}"
-        )
-
-    # Operation rows
-    no_effect_steps = []
-    for step in self._steps:
-        step_num = str(step["step"])
-        op = step["operation"]
-        rows = step["new_shape"][0]
-        cols = step["new_shape"][1]
-
-        # Row/col change indicators (negative means removed)
-        row_change = -step[
-            "rows_changed"
-        ]  # flip: positive = added, negative = removed
-        col_change = -step["cols_changed"]
-        row_str = str(rows)
-        col_str = str(cols)
-        if row_change != 0:
-            row_str += f" ({row_change:+d})"
-        if col_change != 0:
-            col_str += f" ({col_change:+d})"
-
-        time_str = f"{step['elapsed_seconds']:.4f}s"
-        hash_str = step["output_hash"]
-
-        # Check if step had no effect (same hash as previous)
-        prev_hash = (
-            self._input_hash
-            if step["step"] == 1
-            else self._steps[step["step"] - 2]["output_hash"]
-        )
-        no_effect = hash_str == prev_hash
-        if no_effect:
-            no_effect_steps.append(step["step"])
-
-        # Add marker for no-effect steps
-        marker = " ○" if no_effect else ""
-
-        lines.append(
-            f"{step_num:<4} {op:<20} {row_str:<12} {col_str:<12} {time_str:<10} {hash_str:<16}{marker}"
-        )
-
-        # Params
-        if show_params and step["params"]:
-            params_str = self._format_params(step["params"])
-            lines.append(f"     └─ {params_str}")
-
-    lines.append("=" * 70)
-
-    # Add note about no-effect steps
-    if no_effect_steps:
-        lines.append(
-            f"○ = no effect (steps {', '.join(map(str, no_effect_steps))} did not change data)"
-        )
-
-    return "\n".join(lines)
-
-
-
- -
- -
- - -

- print - - -

-
print(show_params: bool = True) -> None
-
- -
- -

Print the protocol summary to stdout.

- - -

Parameters:

- - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- show_params - - bool - -
-

Whether to include operation parameters.

-
-
- True -
- - -
- Source code in transformplan/protocol.py -
def print(self, show_params: bool = True) -> None:
-    """Print the protocol summary to stdout.
-
-    Args:
-        show_params: Whether to include operation parameters.
-    """
-    print(self.summary(show_params))
-
-
-
- -
- - - -
- -
- -

frame_hash Function

- - -
- - -

- frame_hash - - -

-
frame_hash(df: DataFrame) -> str
-
- -
- -

Compute a deterministic hash of a DataFrame.

-

The hash is: -- Row-order invariant (sorted row hashes) -- Column-order invariant (columns sorted before hashing) -- Content-sensitive (any value change = different hash)

- - -

Parameters:

- - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- df - - DataFrame - -
-

The DataFrame to hash.

-
-
- required -
- - -

Returns:

- - - - - - - - - - - - - -
TypeDescription
- str - -
-

A 16-character hex string.

-
-
- - -
- Source code in transformplan/protocol.py -
34
-35
-36
-37
-38
-39
-40
-41
-42
-43
-44
-45
-46
-47
-48
-49
-50
-51
-52
-53
-54
-55
-56
-57
-58
-59
-60
def frame_hash(df: pl.DataFrame) -> str:
-    """Compute a deterministic hash of a DataFrame.
-
-    The hash is:
-    - Row-order invariant (sorted row hashes)
-    - Column-order invariant (columns sorted before hashing)
-    - Content-sensitive (any value change = different hash)
-
-    Args:
-        df: The DataFrame to hash.
-
-    Returns:
-        A 16-character hex string.
-    """
-    # Sort columns for column-order invariance
-    sorted_cols = sorted(df.columns)
-    df_sorted = df.select(sorted_cols)
-
-    # Schema hash (sorted columns + dtypes)
-    schema_str = str([(col, str(df_sorted.schema[col])) for col in sorted_cols])
-
-    # Row hashes (sorted for row-order invariance)
-    row_hashes = df_sorted.hash_rows().sort().to_list()
-
-    # Combine
-    content = f"{schema_str}|{row_hashes}"
-    return hashlib.sha256(content.encode()).hexdigest()[:16]
-
-
-
- -

Example Output

-

The print() method generates a formatted summary:

-
======================================================================
-TRANSFORM PROTOCOL
-======================================================================
-Input:  1000 rows x 5 cols  [a1b2c3d4e5f6g7h8]
-Output: 850 rows x 4 cols   [h8g7f6e5d4c3b2a1]
-Total time: 0.0234s
-----------------------------------------------------------------------
-
-#    Operation            Rows         Cols         Time       Hash
-----------------------------------------------------------------------
-0    input                1000         5            -          a1b2c3d4e5f6g7h8
-1    col_drop             1000         4 (-1)       0.0012s    b2c3d4e5f6g7h8a1
-     -> column='temp'
-2    math_multiply        1000         4            0.0008s    c3d4e5f6g7h8a1b2
-     -> column='price', value=1.1
-3    rows_filter          850 (-150)   4            0.0214s    h8g7f6e5d4c3b2a1
-     -> filter=(age >= 18)
-======================================================================
-
-

Reproducibility

-

The frame_hash function computes a deterministic hash that is:

-
    -
  • Row-order invariant: Same rows in different order produce the same hash
  • -
  • Column-order invariant: Same columns in different order produce the same hash
  • -
  • Content-sensitive: Any value change produces a different hash
  • -
-

This enables verification that the same pipeline on the same input produces identical results.

- - - - - - - - - - - - - -
-
- - - -
- - - -
- - - -
-
-
-
- - - - - - - - - - - - - \ No newline at end of file diff --git a/site/api/validation/index.html b/site/api/validation/index.html deleted file mode 100644 index e705d6a..0000000 --- a/site/api/validation/index.html +++ /dev/null @@ -1,2588 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Validation - TransformPlan - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - Skip to content - - -
-
- -
- - - - - - -
- - -
- -
- - - - - - -
-
- - - -
-
-
- - - - - -
-
-
- - - - - - - -
- -
- - - - - - - - -

Validation

-

Schema validation and dry-run preview for TransformPlan pipelines.

-

Overview

-

TransformPlan validates operations against DataFrame schemas before execution. This catches errors like:

-
    -
  • Referencing non-existent columns
  • -
  • Applying string operations to numeric columns
  • -
  • Creating columns that already exist
  • -
-
from transformplan import TransformPlan
-
-plan = TransformPlan().col_drop("nonexistent")
-result = plan.validate(df)
-
-if not result.is_valid:
-    for error in result.errors:
-        print(error)
-    # Step 1 (col_drop): Column 'nonexistent' does not exist
-
-

ValidationResult

- - -
- - - -

- ValidationResult - - -

-
ValidationResult()
-
- -
- - - -

Result of schema validation.

- - - - - - - - -
- Source code in transformplan/validation.py -
def __init__(self) -> None:
-    self._errors: list[ValidationError] = []
-
-
- - - -
- - - - - - - -
- - - -

- is_valid - - - - property - - -

-
is_valid: bool
-
- -
- -
- -
- -
- - - -

- errors - - - - property - - -

-
errors: list[ValidationError]
-
- -
- -
- -
- - - - -
- - -

- add_error - - -

-
add_error(step: int, operation: str, message: str) -> None
-
- -
- - -
- Source code in transformplan/validation.py -
def add_error(self, step: int, operation: str, message: str) -> None:
-    self._errors.append(ValidationError(step, operation, message))
-
-
-
- -
- -
- - -

- raise_if_invalid - - -

-
raise_if_invalid() -> None
-
- -
- -

Raise ValidationError if validation failed.

- - -
- Source code in transformplan/validation.py -
def raise_if_invalid(self) -> None:
-    """Raise ValidationError if validation failed."""
-    if not self.is_valid:
-        error_messages = "\n".join(f"  - {e}" for e in self._errors)
-        raise SchemaValidationError(
-            f"Schema validation failed with {len(self._errors)} error(s):\n{error_messages}"
-        )
-
-
-
- -
- - - -
- -
- -

ValidationError

- - -
- - - -

- ValidationError - - - - dataclass - - -

-
ValidationError(step: int, operation: str, message: str)
-
- -
- - - -

A single validation error.

- - - - - - - - - - - -
- - - - - - - - - - - - -
- -
- -

SchemaValidationError

- - -
- - - -

- SchemaValidationError - - -

- - -
-

- Bases: Exception

- - - -

Raised when schema validation fails.

- - - - - - - - - -
- -

DryRunResult

- - -
- - - -

- DryRunResult - - -

-
DryRunResult(
-    input_schema: dict[str, DataType],
-    steps: list[DryRunStep],
-    validation: ValidationResult,
-)
-
- -
- - - -

Result of a dry run showing what a pipeline will do.

- - - - - - - - -
- Source code in transformplan/validation.py -
def __init__(
-    self,
-    input_schema: dict[str, pl.DataType],
-    steps: list[DryRunStep],
-    validation: ValidationResult,
-) -> None:
-    self._input_schema = input_schema
-    self._steps = steps
-    self._validation = validation
-
-
- - - -
- - - - - - - -
- - - -

- is_valid - - - - property - - -

-
is_valid: bool
-
- -
- -

Whether the pipeline passed validation.

- -
- -
- -
- - - -

- errors - - - - property - - -

-
errors: list[ValidationError]
-
- -
- -

Validation errors.

- -
- -
- -
- - - -

- steps - - - - property - - -

-
steps: list[DryRunStep]
-
- -
- -

List of dry run steps.

- -
- -
- -
- - - -

- input_schema - - - - property - - -

-
input_schema: dict[str, DataType]
-
- -
- -

Input schema.

- -
- -
- -
- - - -

- output_schema - - - - property - - -

-
output_schema: dict[str, str]
-
- -
- -

Predicted output schema after all operations.

- -
- -
- -
- - - -

- input_columns - - - - property - - -

-
input_columns: list[str]
-
- -
- -

Input column names.

- -
- -
- -
- - - -

- output_columns - - - - property - - -

-
output_columns: list[str]
-
- -
- -

Predicted output column names.

- -
- -
- - - - -
- - -

- summary - - -

-
summary(show_params: bool = True, show_schema: bool = False) -> str
-
- -
- -

Generate a human-readable summary.

- - -

Parameters:

- - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionDefault
- show_params - - bool - -
-

Whether to show operation parameters.

-
-
- True -
- show_schema - - bool - -
-

Whether to show full schema at each step.

-
-
- False -
- - -

Returns:

- - - - - - - - - - - - - -
TypeDescription
- str - -
-

Formatted string.

-
-
- - -
- Source code in transformplan/validation.py -
def summary(self, show_params: bool = True, show_schema: bool = False) -> str:
-    """Generate a human-readable summary.
-
-    Args:
-        show_params: Whether to show operation parameters.
-        show_schema: Whether to show full schema at each step.
-
-    Returns:
-        Formatted string.
-    """
-    lines = []
-
-    # Header
-    lines.append("=" * 70)
-    lines.append("DRY RUN PREVIEW")
-    lines.append("=" * 70)
-
-    # Validation status
-    if self.is_valid:
-        lines.append("✓ Validation: PASSED")
-    else:
-        lines.append(f"✗ Validation: FAILED ({len(self.errors)} errors)")
-        for err in self.errors:
-            lines.append(f"  - {err}")
-
-    lines.append("-" * 70)
-
-    # Input schema summary
-    lines.append(f"Input: {len(self._input_schema)} columns")
-    if show_schema:
-        for col, dtype in self._input_schema.items():
-            lines.append(f"  {col}: {dtype_name(dtype)}")
-
-    lines.append("-" * 70)
-
-    # Steps
-    lines.append("")
-    lines.append(f"{'#':<4} {'Operation':<20} {'Columns':<15} {'Changes':<30}")
-    lines.append("-" * 70)
-
-    for step in self._steps:
-        step_num = str(step.step)
-        op = step.operation
-        col_count = len(step.schema_after)
-
-        # Build changes string
-        changes = []
-        if step.columns_added:
-            changes.append(f"+{step.columns_added}")
-        if step.columns_removed:
-            changes.append(f"-{step.columns_removed}")
-        if step.columns_modified:
-            changes.append(f"~{step.columns_modified}")
-        changes_str = " ".join(changes) if changes else "-"
-
-        # Error marker
-        err_marker = " ✗" if step.error else ""
-
-        lines.append(
-            f"{step_num:<4} {op:<20} {col_count:<15} {changes_str:<30}{err_marker}"
-        )
-
-        # Params
-        if show_params and step.params:
-            params_str = _format_params_short(step.params)
-            lines.append(f"     └─ {params_str}")
-
-        # Error detail
-        if step.error:
-            lines.append(f"     └─ ERROR: {step.error}")
-
-        # Full schema
-        if show_schema:
-            lines.append(f"     Schema: {step.schema_after}")
-
-    lines.append("=" * 70)
-
-    # Output schema summary
-    lines.append(f"Output: {len(self.output_schema)} columns")
-    if show_schema:
-        for col, dtype in self.output_schema.items():
-            lines.append(f"  {col}: {dtype}")
-
-    return "\n".join(lines)
-
-
-
- -
- -
- - -

- print - - -

-
print(show_params: bool = True, show_schema: bool = False) -> None
-
- -
- -

Print the dry run summary.

- - -
- Source code in transformplan/validation.py -
def print(self, show_params: bool = True, show_schema: bool = False) -> None:
-    """Print the dry run summary."""
-    print(self.summary(show_params, show_schema))
-
-
-
- -
- - - -
- -
- -

DryRunStep

- - -
- - - -

- DryRunStep - - - - dataclass - - -

-
DryRunStep(
-    step: int,
-    operation: str,
-    params: dict[str, Any],
-    schema_before: dict[str, str],
-    schema_after: dict[str, str],
-    columns_added: list[str],
-    columns_removed: list[str],
-    columns_modified: list[str],
-    error: str | None = None,
-)
-
- -
- - - -

A single step in a dry run.

- - - - - - - - - - - -
- - - - - - - - - - - - -
- -
- -

Example: Validation

-
from transformplan import TransformPlan, Col
-
-df = pl.DataFrame({
-    "name": ["Alice", "Bob"],
-    "age": [25, 30],
-    "salary": [50000, 60000]
-})
-
-plan = (
-    TransformPlan()
-    .col_drop("age")
-    .rows_filter(Col("age") > 18)  # Error: age was dropped!
-)
-
-result = plan.validate(df)
-print(result)
-# ValidationResult(valid=False, errors=1)
-
-for error in result.errors:
-    print(error)
-# Step 2 (rows_filter): Column 'age' does not exist
-
-

Example: Dry Run

-
plan = (
-    TransformPlan()
-    .col_drop("temp")
-    .col_add("bonus", value=1000)
-    .math_multiply("salary", 1.1)
-)
-
-preview = plan.dry_run(df)
-preview.print()
-
-

Output:

-
======================================================================
-DRY RUN PREVIEW
-======================================================================
-Validation: PASSED
-----------------------------------------------------------------------
-Input: 3 columns
-----------------------------------------------------------------------
-
-#    Operation            Columns        Changes
-----------------------------------------------------------------------
-1    col_drop             2              -['temp']
-     -> column='temp'
-2    col_add              3              +['bonus']
-     -> new_column='bonus', value=1000
-3    math_multiply        3              ~['salary']
-     -> column='salary', value=1.1
-======================================================================
-Output: 3 columns
-
-

Type Checking

-

Validation includes type checking for operations that require specific types:

- - - - - - - - - - - - - - - - - - - - - -
Operation TypeRequired Column Type
math_*Numeric (Int, Float)
str_*String (Utf8)
dt_*Datetime (Date, Datetime, Time)
- - - - - - - - - - - - - -
-
- - - -
- - - -
- - - -
-
-
-
- - - - - - - - - - - - - \ No newline at end of file diff --git a/site/assets/_mkdocstrings.css b/site/assets/_mkdocstrings.css deleted file mode 100644 index 854048c..0000000 --- a/site/assets/_mkdocstrings.css +++ /dev/null @@ -1,237 +0,0 @@ - -/* Avoid breaking parameter names, etc. in table cells. */ -.doc-contents td code { - word-break: normal !important; -} - -/* No line break before first paragraph of descriptions. */ -.doc-md-description, -.doc-md-description>p:first-child { - display: inline; -} - -/* No text transformation from Material for MkDocs for H5 headings. */ -.md-typeset h5 .doc-object-name { - text-transform: none; -} - -/* Max width for docstring sections tables. */ -.doc .md-typeset__table, -.doc .md-typeset__table table { - display: table !important; - width: 100%; -} - -.doc .md-typeset__table tr { - display: table-row; -} - -/* Defaults in Spacy table style. */ -.doc-param-default, -.doc-type_param-default { - float: right; -} - -/* Parameter headings must be inline, not blocks. */ -.doc-heading-parameter, -.doc-heading-type_parameter { - display: inline; -} - -/* Default font size for parameter headings. */ -.md-typeset .doc-heading-parameter { - font-size: inherit; -} - -/* Prefer space on the right, not the left of parameter permalinks. */ -.doc-heading-parameter .headerlink, -.doc-heading-type_parameter .headerlink { - margin-left: 0 !important; - margin-right: 0.2rem; -} - -/* Backward-compatibility: docstring section titles in bold. */ -.doc-section-title { - font-weight: bold; -} - -/* Backlinks crumb separator. */ -.doc-backlink-crumb { - display: inline-flex; - gap: .2rem; - white-space: nowrap; - align-items: center; - vertical-align: middle; -} -.doc-backlink-crumb:not(:first-child)::before { - background-color: var(--md-default-fg-color--lighter); - content: ""; - display: inline; - height: 1rem; - --md-path-icon: url('data:image/svg+xml;charset=utf-8,'); - -webkit-mask-image: var(--md-path-icon); - mask-image: var(--md-path-icon); - width: 1rem; -} -.doc-backlink-crumb.last { - font-weight: bold; -} - -/* Symbols in Navigation and ToC. */ -:root, :host, -[data-md-color-scheme="default"] { - --doc-symbol-parameter-fg-color: #df50af; - --doc-symbol-type_parameter-fg-color: #df50af; - --doc-symbol-attribute-fg-color: #953800; - --doc-symbol-function-fg-color: #8250df; - --doc-symbol-method-fg-color: #8250df; - --doc-symbol-class-fg-color: #0550ae; - --doc-symbol-type_alias-fg-color: #0550ae; - --doc-symbol-module-fg-color: #5cad0f; - - --doc-symbol-parameter-bg-color: #df50af1a; - --doc-symbol-type_parameter-bg-color: #df50af1a; - --doc-symbol-attribute-bg-color: #9538001a; - --doc-symbol-function-bg-color: #8250df1a; - --doc-symbol-method-bg-color: #8250df1a; - --doc-symbol-class-bg-color: #0550ae1a; - --doc-symbol-type_alias-bg-color: #0550ae1a; - --doc-symbol-module-bg-color: #5cad0f1a; -} - -[data-md-color-scheme="slate"] { - --doc-symbol-parameter-fg-color: #ffa8cc; - --doc-symbol-type_parameter-fg-color: #ffa8cc; - --doc-symbol-attribute-fg-color: #ffa657; - --doc-symbol-function-fg-color: #d2a8ff; - --doc-symbol-method-fg-color: #d2a8ff; - --doc-symbol-class-fg-color: #79c0ff; - --doc-symbol-type_alias-fg-color: #79c0ff; - --doc-symbol-module-fg-color: #baff79; - - --doc-symbol-parameter-bg-color: #ffa8cc1a; - --doc-symbol-type_parameter-bg-color: #ffa8cc1a; - --doc-symbol-attribute-bg-color: #ffa6571a; - --doc-symbol-function-bg-color: #d2a8ff1a; - --doc-symbol-method-bg-color: #d2a8ff1a; - --doc-symbol-class-bg-color: #79c0ff1a; - --doc-symbol-type_alias-bg-color: #79c0ff1a; - --doc-symbol-module-bg-color: #baff791a; -} - -code.doc-symbol { - border-radius: .1rem; - font-size: .85em; - padding: 0 .3em; - font-weight: bold; -} - -code.doc-symbol-parameter, -a code.doc-symbol-parameter { - color: var(--doc-symbol-parameter-fg-color); - background-color: var(--doc-symbol-parameter-bg-color); -} - -code.doc-symbol-parameter::after { - content: "param"; -} - -code.doc-symbol-type_parameter, -a code.doc-symbol-type_parameter { - color: var(--doc-symbol-type_parameter-fg-color); - background-color: var(--doc-symbol-type_parameter-bg-color); -} - -code.doc-symbol-type_parameter::after { - content: "type-param"; -} - -code.doc-symbol-attribute, -a code.doc-symbol-attribute { - color: var(--doc-symbol-attribute-fg-color); - background-color: var(--doc-symbol-attribute-bg-color); -} - -code.doc-symbol-attribute::after { - content: "attr"; -} - -code.doc-symbol-function, -a code.doc-symbol-function { - color: var(--doc-symbol-function-fg-color); - background-color: var(--doc-symbol-function-bg-color); -} - -code.doc-symbol-function::after { - content: "func"; -} - -code.doc-symbol-method, -a code.doc-symbol-method { - color: var(--doc-symbol-method-fg-color); - background-color: var(--doc-symbol-method-bg-color); -} - -code.doc-symbol-method::after { - content: "meth"; -} - -code.doc-symbol-class, -a code.doc-symbol-class { - color: var(--doc-symbol-class-fg-color); - background-color: var(--doc-symbol-class-bg-color); -} - -code.doc-symbol-class::after { - content: "class"; -} - - -code.doc-symbol-type_alias, -a code.doc-symbol-type_alias { - color: var(--doc-symbol-type_alias-fg-color); - background-color: var(--doc-symbol-type_alias-bg-color); -} - -code.doc-symbol-type_alias::after { - content: "type"; -} - -code.doc-symbol-module, -a code.doc-symbol-module { - color: var(--doc-symbol-module-fg-color); - background-color: var(--doc-symbol-module-bg-color); -} - -code.doc-symbol-module::after { - content: "mod"; -} - -.doc-signature .autorefs { - color: inherit; - border-bottom: 1px dotted currentcolor; -} - -/* Source code blocks (admonitions). */ -:root { - --md-admonition-icon--mkdocstrings-source: url('data:image/svg+xml;charset=utf-8,') -} -.md-typeset .admonition.mkdocstrings-source, -.md-typeset details.mkdocstrings-source { - border: none; - padding: 0; -} -.md-typeset .admonition.mkdocstrings-source:focus-within, -.md-typeset details.mkdocstrings-source:focus-within { - box-shadow: none; -} -.md-typeset .mkdocstrings-source > .admonition-title, -.md-typeset .mkdocstrings-source > summary { - background-color: inherit; -} -.md-typeset .mkdocstrings-source > .admonition-title::before, -.md-typeset .mkdocstrings-source > summary::before { - background-color: var(--md-default-fg-color); - -webkit-mask-image: var(--md-admonition-icon--mkdocstrings-source); - mask-image: var(--md-admonition-icon--mkdocstrings-source); -} diff --git a/site/assets/images/favicon.png b/site/assets/images/favicon.png deleted file mode 100644 index 1cf13b9f9d978896599290a74f77d5dbe7d1655c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1870 zcmV-U2eJ5xP)Gc)JR9QMau)O=X#!i9;T z37kk-upj^(fsR36MHs_+1RCI)NNu9}lD0S{B^g8PN?Ww(5|~L#Ng*g{WsqleV}|#l zz8@ri&cTzw_h33bHI+12+kK6WN$h#n5cD8OQt`5kw6p~9H3()bUQ8OS4Q4HTQ=1Ol z_JAocz`fLbT2^{`8n~UAo=#AUOf=SOq4pYkt;XbC&f#7lb$*7=$na!mWCQ`dBQsO0 zLFBSPj*N?#u5&pf2t4XjEGH|=pPQ8xh7tpx;US5Cx_Ju;!O`ya-yF`)b%TEt5>eP1ZX~}sjjA%FJF?h7cX8=b!DZl<6%Cv z*G0uvvU+vmnpLZ2paivG-(cd*y3$hCIcsZcYOGh{$&)A6*XX&kXZd3G8m)G$Zz-LV z^GF3VAW^Mdv!)4OM8EgqRiz~*Cji;uzl2uC9^=8I84vNp;ltJ|q-*uQwGp2ma6cY7 z;`%`!9UXO@fr&Ebapfs34OmS9^u6$)bJxrucutf>`dKPKT%%*d3XlFVKunp9 zasduxjrjs>f8V=D|J=XNZp;_Zy^WgQ$9WDjgY=z@stwiEBm9u5*|34&1Na8BMjjgf3+SHcr`5~>oz1Y?SW^=K z^bTyO6>Gar#P_W2gEMwq)ot3; zREHn~U&Dp0l6YT0&k-wLwYjb?5zGK`W6S2v+K>AM(95m2C20L|3m~rN8dprPr@t)5lsk9Hu*W z?pS990s;Ez=+Rj{x7p``4>+c0G5^pYnB1^!TL=(?HLHZ+HicG{~4F1d^5Awl_2!1jICM-!9eoLhbbT^;yHcefyTAaqRcY zmuctDopPT!%k+}x%lZRKnzykr2}}XfG_ne?nRQO~?%hkzo;@RN{P6o`&mMUWBYMTe z6i8ChtjX&gXl`nvrU>jah)2iNM%JdjqoaeaU%yVn!^70x-flljp6Q5tK}5}&X8&&G zX3fpb3E(!rH=zVI_9Gjl45w@{(ITqngWFe7@9{mX;tO25Z_8 zQHEpI+FkTU#4xu>RkN>b3Tnc3UpWzPXWm#o55GKF09j^Mh~)K7{QqbO_~(@CVq! zS<8954|P8mXN2MRs86xZ&Q4EfM@JB94b=(YGuk)s&^jiSF=t3*oNK3`rD{H`yQ?d; ztE=laAUoZx5?RC8*WKOj`%LXEkgDd>&^Q4M^z`%u0rg-It=hLCVsq!Z%^6eB-OvOT zFZ28TN&cRmgU}Elrnk43)!>Z1FCPL2K$7}gwzIc48NX}#!A1BpJP?#v5wkNprhV** z?Cpalt1oH&{r!o3eSKc&ap)iz2BTn_VV`4>9M^b3;(YY}4>#ML6{~(4mH+?%07*qo IM6N<$f(jP3KmY&$ diff --git a/site/assets/images/logo.png b/site/assets/images/logo.png deleted file mode 100644 index e056830128ef69432e6bac89847be8935a8edff9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33938 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%0DQJxGSF4Z#wevw6XA;mO_Bqcoo#;Ti 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 ax?pCo+o;m{n5`RPt6i4(9e2%L&iz0Cz~FlT diff --git a/site/assets/images/logo_white.png b/site/assets/images/logo_white.png deleted file mode 100644 index e056830128ef69432e6bac89847be8935a8edff9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33938 zcmeFZ2~-rnTUMr3MiIXOg$5dozq=0XG_vtolbHnf_=Bt%SN z6pVrc%23r45Ua&bjN}|K9ihf4#TfT31$9 zVAtN?{^n`#s*i&L{1(hzHWxvV1zX>Fdnba-x&;57nLP_e?AO}PhyTqzy+v)fQnWp2rEluK*p*&RE{-v5_WG3`UQW?2F}q#X z$8B(NSh=3PeuJ}%i!P@lsQhW&|BHL+{G< z?oJz4{sjc<#&&UWb4qqv@3p~oWt`XUSg-Z&Z?0VVm+jFpdr~Ooyh)+LK#P4ldVef; z6)baiWV<-7_u#p>a5lJd+}5vSyKvZSVxk>A++)4m+&sM2yT-WBtbaQ- zMoj4pwccn!yHjHT#-F=-?s8?XcZv3LjE(W~a&(K?u-Ex6xDB3OZZ5mM zW}pS`+XF@%o%GL8DXew_#NBZ&yWDrJcXM>z;0{>1d+c)bbn$X=+~DHA+bwQaOsuP? zD`h9N>T%xLlMZq{$XH%*>|W-#q&+KXZR148P*%iU6+>x2?Cw>}@SgvGAOEeLnf00P z#RAa(h478Lr>Dm4&y3y|`&K;g?tf47oc}Y*(xWr}*L1~tdB#S&c)B~fM|*lYZitC? zcl3<*h;{Ue@p5;M^N3@6t=~1nmw!jsjHYgQp{f7J>0%ff^ImjHd@T5L=T-mDDfMuR z_VidE8x0!09yBJ-)6H?$hV`C~aUO1NuIo3%?%o|6^IuTJr>^|dIsB&_-5tAc&p&C; zKkxDag_ZvY_xmq(^}pwnjH3BZboKvr2|80D+URl;gA1sc}`~k)c;)%OsN!2_&?<;GeeL7QNO-OzTl4+>04|H*!Vt3&UV=y z@J0|x(AKx#;vHZOK0J86Z#??%AqIX(gh*x5fAi`|v{JFaD^R@s~xfvgluQ zX8$t<81X-Rp|s(Dl!3O1|51kjQHKA2c})MuXR-KM>Jppw}z zK@yFDLzm*EkOb?$N^q4e_AsGfp&o@1TBT%b)ok>G0Y=Q}Hxu;gXNu#@%h zClb|@)S?Z>MXKuOxufu9-Q1;gXi~Q|V@hTA<&OF2=1*zB z1xHF{Q|%k+k$M1qvNCQTm&y7OnbFsHu^EMQ{6oLC~-niYNmdD zi4ni~mOPs|vpO-ImmTy5^ zU5pG|d@jmH9vONZiDN{C9oQmP8*t)H)?meEnt}2!MsXb5p)HlLW!&|~WyCo_M5Oi- z$MMC;;=;%XKG1Hbvo$6OYAgU(%0Uu#IwyvRADYX$McV5i32TN3u?2P;7xHqEjPH$H z7N#w0?gW^x_ie#T*8ThqXY+S7Gj^1^>oC`J;*aid6A| zIZO^SK>k5)Cvbd{HK;c7cwq1k5(6GjAsK&XR%J@WvHBkXu#J&S8AqX!ODK_B8j;a0 zqL7IUaWwJVYLwm6h<+jLEq6EdoWqPvHkT{0NCD`iyAi+MM4K{Yd1Mgi+j67XJo&j< zX(^yry=7c=AOUFL{NNTxBJmx;s5*0}H z31+Cri?~4^!#5)hjjL!)@brr3wT-`j7K>p$&ofRsciof_(;EK7+en+*73eR^{+hD} z9Wv67_Ma1IDl^Bj$3vy+r*hatyuU+Pdv?U$$MG_;Jf)lye zQP9gfvMUTW2fehKs}gb5LiN{1m!W%pjvsMsGf9$|`zL4j$GkKOnp^<-0 zZTE@C;ID&CPBZBA4Gn?^iv_B#Fq-kqTP5)Y{a&bkg%QIFCk(ubx?^^M)(FM{&{>Ds zVM|z*3_6eZZ51bj56RIbG17b%9v%@4G}y7X8EFs&P*&W=%az?XI$_=K9N?DWZh~18 zgUsp9`;>3OH|vrZNj)p%0~9%E$r1*cWzBM>+)3eXI`+WGK0)I%Q1^V{a#mJ?QQ-LF zOXLC~7e6lJ8Uuig82uD<+kzTls3iXQl+=)Gh*fACgRnRv9NaR54OGaCR22%reSx!5 zCo-EGnlUF;WJL0;!B32wkRLN8P7x=7C?-ar(sLn`R>U?C)m8?KHk=m`qP-Ss=5GuD z$Uy%Q@)KIyUT5GI743kXsL0YY;x4l9gUKos9t)?QOpwb*<3EH-_GQ!3d=&6jB_`N`&p zI^1?(PQUQ%jS&rQs32Pt)`|A!7JL(@A0)q|-EMDY1kYx0PK_Y!#@yvDA2ebXMrWcD zZ`JpKh0hkWuPV9fOwT>A&zj%HpTL&men*(J2^sswi#Us0Ll&~`J%(GSDzO@Vz91L9 z$?#aaKSO*_-;uCd^qNt^s45}|lpE`qFEFNc2qaP%=qVsNml0L^-Yw!-NQss@FnJ5x zz8@m7k7#J#>2{+H7n}v<28S|czKyZrrn@&mFLH6|4JMC5()CT`5EYwmd|~t-NP0$w zSgQYt{Bb6clGS=^C5aI97dm`wl#F_QbD2^Vxg&YjHl{n_0BId0ar|6#M&j$eRGLeg zuS3mT8Hu011E~X*!!VyCl zWD22sj#@Et;fvpbDELJ}l=?Q~3-G}sB$dE`L7(fx^_P%ahgJidvgHntG+^vjmJ=8^9aFlg$Em8MQ#o zwZQCDkwM9`a&$(fqZ7@l?r;b+^JkJO-S@Q@AiQs`TKYVU+5IHsd6&R#FPzD#bj_YM z^j;NX49ed9=OC7w6I;|pv045Bv(d0;17s{%$ET+l-`mdZ zrx;r99&?2eUPjwt$hNN#s|2kK+2e&_+H%Q`s)g(JFxYt%?a)>LzTf>2!i;1Vg+&Ap z;hT1Hm#vFo#%hX!wO6Qk^^VUPXI6*)z`?PWz&6 zA=h>r!wa@E7%0zK;l67R6O0PZn-XD|l-N$(6>%88AA}?YgHZU<6{BlM3RLqTkFgf6 zo=Kt5?xK(gfi%Z$*B+xeB7Gwv;2p+c3X@-=UK?v9+w?yrtd2`%h}c-fj}Su0|0skp z5r=#zpzwUb#I9v&h%ScOP(;~MN^hCi36N5#L`Mgv`B=2}Tz&HGLnExcQ>cdZ;I&lc)DH1mv?A!Pwp)8AF#MEEysHncc*O8~U?52UkhH0I z#Q0EosW*hO6N2%^yf6lCwk?)@Ra4Gca+2}s>d0X{rEC+=Cii;>V@4yg%jC83?G(eg zTN$+yS)Jhf?9euJeDeLwh;*E1SH3Ac`KEFPPDeH_3DZ}Q!|iUlG@^*0?Q_x|55rbh z;4=snDM%0Ogut#m?>prTN|NkiO^G3lxY)jEX5sC-MNPLD8+t7E1A$kN^#!MlML%-; zSCRfA`7QCVpm1iSznLr{kHW~6nUTjQ!K%(HY(KgmI-~rySV~zo##vHX&1~j%sNC=? zS&GNFWHD6qjo?rW+#yz1Fm(trnjzOzXcUsyPtPFq%y38?rXMCBiq_7o`qdQ@@)qIv z&3`a1gzaM`^x)g6EE{(++q0cB1vPv-%(C$xGb46UW!*v2%baD~m`ssvbqu%S+u?b) z{xQR!nrbXiP{XU?`|Vo8$hti&Q_v=WSvP-Rw0Tg`iJgbP-<@$_9`(Ggom!mp^1ApL zUs+cNQKO2p;)UC*v&B=Cn|9jdovLEkF@ET(MysjPcnCTe-go`>0jVI4;m2YgGY+*R z^P1+0;S3Nzk0HK9?58gxZ;^*Z%V$PLZ$jKXO@1y~$JoxV4+PT7lGj_C8MuYxXGl}< zctR{||@Z4(RIN^sESG3N|`81GkCr?ofe^OOrM7j58X@w+R zoTU$%De)2e{=)#eg=b|x-OB7XR97qs6sL<*pJdIju*jFt6In;?jfENFx%U_->WNu_ z(G0sjezseGm@Fo=H-Z^kNqcU|_X&}fN&fPb!v`ez27= zZ3Tn$A*q$nCsJD{`HN=&!=IuD0&yT9eUZG_YsO&oG&GFx!MlZ%e-me>9hq6Huz+mY ziXV2vopH3;4KRY8S5+TLX0+%@-KHq5LZdY#q`k~6+xRd|-waGRdSffY%O5MqKC+s; zfm$%zwRZJG?t6kaZtlI?NV2*)Pv9Z&5L%ggzsHOyhm|QDe`3~)#O&vKa0FxIUG#58dS&9hevmSMPS&HlW=5VI5O0H@*W1mR z5q@TrI7=J@BWq?xGW?)~SPdd@Vn~%^#e@QW9-{e-LlNBNLJ5K-;pzn={p<^6xIOVen;0Z&at4bMcBr(!!6CX zF=<`X{d}PH1pnBPH4HulxHEqQYy{@S8O6x<%M=R1g;<$yV@j;1J9o)=>;#B<8xxb* z_L%}ZdAG33#)0Xq>cSrgBluD^XUU5K$^y8^uMspJSVai)4k&+CS6LyA2_+(kDtiF()m%LZ?v zN0B#cT@s^>VfEqKVoinNUG+~47waBggyPH_i@C9kk-q;B6*~@-BJ~^0oM6L#DDzJP z?~gMwEcyL0nM_#VF*m+qBo^$+lU_I6hgBaktGtJzgd_4zky0;FFeAjm{g*WtHRTev zC}w74U#d7AV)qU74x>XE-}K`iLObQKC8KF+S8efbsy*${JIw?g&!tv8pNOuV+$B#& zm}S)ixda4Ctj;w1JY#4X8+U@sNYw}F+8EN#ebk7x2pWRA!nv!&Q&FFR1!@MZ!AUvU3^qjV&yX8Qb*YmqAZ|1pxhk~hM!4d51a;2KU0XveF9Sn zeE2p{`w>q>`U84vtiSQ9Awet#8}jUZ-*MO6OgJD}MhKAGiFhc_TG%qDJ$YI#*)EQ% zS|~quJR!;ra_6!wRj^51RY+lIvY?b7&x;qt3oWp8tP+aCd(3~8@OL_%Zh|@uV^i-2 zwp+_TR1`gtlg}zX&4aiC>6B&r>COJX{)U|HDJF_pSr6}$X;pmgGWjt-?}Nn@8L$mS zge#to&%@&d`GAiFRtY}<7aPTcO;C@GHHI7H7&J;8PuyG&U6XQ>OI(yCgHZ>ju11}c zcoVpvNx*lHh)2X~sT82PV>^aa0jg%aXooaLZ$qTxmGEOqcoDV0q4qc91?0zfPVI_V5dX3nL5pg~CE> zq*MQ#^ifQ`g=F;?L1aiKjhU{9Dlzkyt1e>Jy2c;Y^#d`U=A^H8)M1Al?G4 zvz16*V~e^ zf#WKBT&*(PBP_5wllG;J5C}T)3Db<5l+~XG^@_)rL#~i2uU8pd2|H!I7Z__JK2koF z0ZGzteF~t>mZj>Usic<_}WgXVQi+EYL(Gi=*psALIG>#NO zE_sW*VQj0TT^mJGIkX}t@HXM}2H8PLw*I_%+O8Bu3T8XMJ($WjTklUtMubfU=}FSs zv><;TW@p*^^iZ{?M3N=`3)(^}M^`exVaH^gz6SFVPCg@BcyQQUGsf=`aElH5kiel~ zNPD*v4`p=qs#{czIYWE|N-(k?^JNVxa&f!)z2)5Su29I|SFo2;`#udF7uGsZ_9LpyJ9 z1FH$@IJuBDczr}UH6RnGXX+n;kXy_h=)TpUyoCn`(q&WmB&Fb=l3Av$-`|ClGzL^{ z8+~rzJyJoAK!ZT5)U1{jjw*;s)7BdikQapEDl_9*l7Ig&c)(oje7WsBNc}VQrKW-| zQ#eFJ^&iNtP<=MpuahdmN~pp&S?j*{sx&AQ%;cB_2ZDG+^tnlU);NCzt8XymvsRqn3=U?(^fYl7GWkC8 zu{k3C_98Yy6DvHlU$Qz=?!E(LS7=nOvgGdl9N!~)S0w) zP_@MAwqn8?{LVNSk`Hh4rcAp(Reut)t#4V0sT8vvY80fF`_Mo($;9GNy_S{rI2XDQ zVZer`Hk6AHWrqkMa!)vN*XEatOZ zeYQSSsy0w2+&~|h3mPdL5;1@P4f*MHXw4xEPT4;s^N=YSg@gRGA3lJM^TJpIzjzU| z3pxb)yU;5UnjUzW#()dfp-NF!o3!4k;q}%EhvpGc<(n3&1B#*| zR;W+bhfnT?vgaM7k#-aK$VoWS3bc^tf>L{forxaW`8@1V3+A-gAX{KPLETznLPOllwIPGVb9OHmZntllBj3$oa)Y6vS2?HW7%8%x}*-hMlE_(q2M8KA(k;sOn!*|FDD@H3|4p z3k=B=j?YS?`?~6)O>or2d0G8KeJ#he_dr*KwqA3|5RTZ>{o{dwRAC!GjD?edf}WSs zHs(Q}4C=c1s0CHAqgrjPL8QYBW1vn&&4pmxsY*+8wJ(*w>m=Tx(3Q+1J52?5NAVq8 z+b6s0!z1`m8@#Hsq$~IHSY%Y!a`h+m8i;y>rdtX?NPdlQg z4ke2Bs=J5bHcdAVjNf=MCg<-LNhns#<)8TMoV<%lx#7~w8ZF=*LEl{|YMSP2SEKxM z5zEP_5KDf%G`n}&xQRUOvy{MFE9<6t89wqG84RoSgp@V-IVI*N`@18gUg-UZ zaA~UAuqh0-RT`3zEV?M(SlA-WaYOGX3tAQkIt6+wynzo=u%+ANjgLlzInJ+&AgcyBCuY{Vk-?V@}3rxE{6{7Ekhu6u#S@`33 z^f#b25`?(Ye8_`VYae-%b(?%Z2AbiIa)H!SM80Xk84BIBRoPl70Mm3=H7i96tc0nG z?wwsrb@M50mo2K`Ll$&O7AuB`tbPDWr%1u`%-MAQ)BCoxR+1_X)U7x85_VRn+fv~g zP(D9u0eXuTY{v5;YS&+uV?K)=N++=M^6n6+yX*{2YvUk*5Mq{Yp_gzBkZK(`@n^=e#k8ID$V~EA@=dk&=kD%u@f2CNaS981lT^ zKRt&t(ab#FNO^ad^iAeMV8%aGLc7_bB`wNk%duONL<`lyHB%okE|ioRCMYd>a)-Ei zW4NwVa}}-uvnG^-ao5ufC?E|km-w}jREa zLHk7XK?^!JjF=9UzJ?AL0ih)o{A5VrQ*cWQnhn8mf3OxIw#s_V#uhMuH-15US1)1hdb0ydiPgp}0-j@(1ev5rP&|GcQ$}b^W z9MZq1J6CAuAA@(yHC}v>y_w9@fuD0_DsIrf`VdKEZ!U$ETGI?eHK*P5mh=_BJD1;Tx+By zhC8y_`@+ISFE*UmuOEfnN5ui}h^SW^5%Uh64ArfO5W!I@GZgtWT%4(Uu(6o)(w{>@ z!e@TL*?569u;(LsOKWiI_Y&5j0YxDZFTa>rTg+L;wlC$kEI`_gDzAcuokHfdl-SHd zyy?#4;3=r!z0iO$e*CN%Xu>?+^mU0R>a+z~2B;s+4()zFF^r7y`vs-u$hoiSZS%A1 zG_Ghsv!dA&@!Uhx>O3jX2jU7@t!IN~CDMDDc5>Q3g7i4#R_S}+qU5)gw%n{CSK=p;$FJ82HDI8?52MxC0$auMT=Ce(5m@*K8xzo6d&Irk++7Tdx~(6BSvyy?ba zO77)~azCKXo>znhRJ@B?ndz1UK6S@IWD_2&-I4qc&c15eDL zQ28&@y}XyG+uX5*NZL2FK+f6BgWceAuWk-)yy;^iKnE_;A_4Rrtb$5EVAz`k>}Q;S z1jesJdfzvyYu4*fFwv*)Xmhm*B3nay>%OFQz=IbItZW1b^)nnmqFSi7vTPBplSqBh zak$xaPEFY-@_2h4uI@U!>doV_;T=HXWte-^sJ9A6StzAHKQLGCFq*fn~=~;B;RuV09)dLako>J+xfirBq}$b4S~oOEQM|bQj#!D_5wr*Hm}hL z@N9x?UIqvzi2H8ZhA>mi*QB~L^aTVlD$f1a6}+zXXdR%G^lvmx{9}R|%k~tUg)CoB zqd+JyZ?mKtz=_$ER44TO3y2|Ww)w!H^Xpl`YF7mH{SYvem3R06bfIw83-}i1Nj2;6 zb*3{YUb@I}K|V zB;5(Jv4s(!p`GTDn+4C$%>PC+uw*$chrZ89Tc8@!mT!$z*YfoQ41lMMrUmokgG(A; z*ach%GqB=S_Tv0MA=b<-ID+Gl(YPnkg8SkkXFWRCN#O)6P}3}U*RpIFu*etYgSRl^ zvBp9ewU8Q$0u^LtrRypOm>D>7(eQi1>;kw%IckE8{XnzR+=AZ|Gz5dUsg!PJutr($j;?jWsSIu%&BRw8 zRA^##yztP7C5T!`Gx|->l4YjU4yR^zXixFRCIl1a{x_+iWD1M+lp1+`Ej6zLk!fke zN~$Twvu63ayOL%)zRV9Rxb|BG4o4il4@DNuEZ zeDDHVX`L`i2o!vhi_ZfJIw-Q#g~Op;o6vWiluq-I_C%V33{DB>b#!ej9I3B+F)zYW zSziri0>jR{#1}|Ajh7GGvDXX`k6FmsT{Im|nV{bVUl8OMh-|wHWt~aKYzP|dt2?Q* z_Dz~8LutpzDI!nyX04iIkD$S{@92CONz|D6->D_cU%<2U zuucv0xa-kRlz1~N+hByn!8BpIR|CWjA&RX<`O~Db1Veu^r@Nz3f0A{IbJ?iWxIDw7LE4bQ1yB*?H zc)Hw00H-14oa+M$vnaBF41=wIaFVxDu&L)+Cqk#ViXn)V5vpqh068 z9)6%AZ%#@_G`xUw%*_9ODV7d=rXSZOP6IT1UZa`ENFtWS_ckdRJW5MBWj=V|uDwC} zMMMl)pWL)8 zw+*%tc6qg@k1Ojv(8zPapk(Ytl=?hBX+u;HIf~&GgSDpkG2w)HQ|>%mkI2rvY6{`o zZUs*orvI6Yym9M6v1FHSW9=#gjiW^4sWt=>Cvk6~ueyJ%yuP8s9*qnSmpU+y54O6czXNM1#%AYJ)t;q=eGhffHm4Tf?1g(O;oa>+t&?xvnIH1-(mqzK}B>wd6!9o^p}D3zGru-rN8`;z-9Ja(wPk}x17L-gNGI+X|pJ)s6yhekA=1AM5iedM`;i0ViPXS6#oslQ(Ipy*$oP^ z(QrVYKeP0}Sh;POO;ndldw5Nmt6fap$BI#7^mAp|RgNZEAjEux%wjWs&_ zQ^FbsaFa@k`SDuh!>=?o!^z^S>=6N$4&_f1#o*_L)1(Vgc;vp5vHA`(pFFyE)8L41k11`HDluooM35uMHIme!5ln`O<4t(BDZZ;5}DfPdKHq zYPaDCZmA|lcj;h`{vn`D=?U9$Lf8{wBb558-7Mt+%Hieek2Z-4mm})OG}4bPuz241 z?CRzmh3!&x*`}QlP-+?9RViST^b)n}K5Ey_kw9z3fu+psRT_RtA_so=z1(HkR5 zZ~|%t#m2aSzmtA=qaf3&x;2PfA=w2cxBl7NaPe7Jz04H!tx$^Dl@$fsEJ;$>!1+fD zW;tKvejawtpn>oGtG<`qN<`mi&3aq^1(XM|qo0$>z)aelM{PoZnft(;_28iniqoK} z{hnS(J%%WYrVo|bh8Ko|FVI>DVsnn+i-3L=Z$trk3f5ovk?u6 z5KTrSy4Pub=1t;$!a#Zbs&+}EAf8_{PZW1vIkf>nU#94CZ#)V&eY`EgMmpgJQL*Xi zuFZtC3GzlXweQHM5K~KenU>YvzVKR4rj;l|D98b~zm%eBc_ykaBfleyZnRDe!%I** zSU>g%Qx_lwpHe>h_)_ecprtI&vbr@7t})~bYb<*u*1YrJgy`qo%rt!}eiAP}ssL|Z z3@6pTCfB-~NtG$`Ml59$E#Qm}h{MJE)m4^QKzRVT$KLZ&xGsU{E>naJp2-R0R%y1Y zt5U+WWrjf3q4Gff2yFi)wS8B5kX}cA4B<>6J^?SGnVLS`IRIy+rx4jz%0FB?=&S#Q z+(UeRqt);gDJR#NUW@MMO|L`HeIEjCDcul9vAp@!t-f%M8MRb*eBwPl)ZgFJYxl;0 z-INpg7Gd%7?CX*(;foID>!p)4xyJ-e_$vH8hjfptb6W z?l4HP*;^^*PY+&z3+yMc&RJoP+=)f_d1cB%-Ef^!l34{bd_jqJ>V77#kzcc*`j!-n zDzkk>S2S^wbOzZb0(&o5%E8YC*(`19ehbc=A&ap@W2WE`20^dFVp%M-nacw3ltoS5 zQfv`sHxGT}q?)=4nP!0;_>10$sFwtpM0E!51eT|&cb92L;|Zwd-be=MBEf{v3M&#x z#q6+gY$_f6;ss=^`xrhCwx=g-nGpE!3U_uXIu3QvHDp(W|$&>Wi_=% z@pB-dq91S041!R%7i4{Qz=`n1;)ONy(CSvW3fwjeY5IU-#pgq1+1Df);t6#0LA{}y zw8tjQ)oyU|=7pdsl&0nmvf#|I#>%~s!p2WvH3vTUsf77xVnoCw_?B==-zs|Up}pDqPY_w{`dJW{zqly* ztN4&OAAK~2+rqJ*{t21EN>T;#`T@mL6pA}mgmUE^rGZ3aEOA>NS1x%+T$kX7bPoJP zHUSt)YaNbA@o5nk=d5Z^_k&wu(j2{(m}2=i9wbzd?G#dQx{pJm7bx&E%j(-ic<_6o zCOeUm_$oss`4t1Dsq!d5nan5aZwz}~(B$adz!{FuM>U(mpmER6r&xY)>?3#=-tp+! z%yGD(pi^LHb&Hc+=42NTFixXrDJ;D#%b&ISZB90DpDJ5z83uFo*1oz6G-V`<&TJ|i!-TYli9%Uivluj3Dq>%hNm*ssFTUp%Ez`+b8iQ^M|t1`>^ zsLK$RU88}_R!N~2>@KiU=BZ$2MX_ePK9&&j#+AcS(qvH1r`;ix^L{|-ZwYyUY~cAs zN;f-bOmSzt{wk*gpmt)NX6i%TJD|2?k5d?y8;am1SXRYHme_D)R}Du4)8OKgi74ef zT&WA4hZJN{^!_-XFvS<)rn`#d!htTeFRm)9uhqnecZ*eIJ?qKMDY+pRk$pf#760!K z6IOhCLDZ7bI6rZ0Rk0>f+yH`5V4eKT^5G?jN8dllaV&y`%!{UI|6-)PXot<>(O(9z z9=WAFCBlYpV~f-cm=Y=r#DVho=-dn{z(&C&Qb1&y>$bo>XQ%|CDHG`&x&T4Kod8lhy{Yj*aFuOt$~4IQdqQd=dn^ut10!T! z{Cr@=GF%$7!zO6*Ju1;nO`vidr>zGTVL?duHDu1C<H$hEJqG68sINKJK?g+^^x)&$kiXf@eobNH@1p!fKyCAv~dio6>I}y&s;7 zf}8A+nY}q5$U6IQib_sRkd!94xoD0!9H#`AxoYdVZ+FPF@sZ12x3L9Fdlb$fgq^wR&pb zDDa7(N3Dg^TCf8c2RNP=AlUy5#)vG}4-kOo)0|si-+ufSY#915JInBD?dXjTnIW3w>e!3fmy=6%BN7<3y?=D(&vw0>-V|<*k++0HM1<*XXeo9N!_m zZ_wkA3SS3HM9#)u;k=2+&Qg^_?f?tABHqv`E{Q6eT1$-r7jyfUK(P$<6SNr5YzmnS zN02&7m6zNWO-{gd;fDZZPrUv|X#OD?--2iz@}v+%ibUXq)c3mu;8BsfJ=B7455q0# zQXV*(_N0EHPZ+efbSYq@vYStA2vd zNq;6kLS!M&4ngU3aOT0|Uh)_gjCn)b;D=`NQxd#t5)J2s6gvjBP#!csu8a$OtfAcr z8H4PZItL&Vn@k{j@&`~MrZ-sp>e!t9^4NH;pzV1mVpxQM|ARt zAnt3>W1{f6=O!4(i-K&R!N0gD1PVso8x-(89i?!ame#?rhk>}ODboHrkigzTVBSVEHg~42ex4P*VVHODTjDl~Lj({Zld#z+e0ZaO2tRx1I1C?LTJy= zglf~WjUTAo1hQO0sonkiD#>p6(1IF>flE9?uz)(0DkuaWjavfqoZ$K!*0C6-Qz+f3 zNrjgu<`7ai+P&R~m-3*es$M`LmA-(mqUvT4XM|Ew6eeXuElyeY*@5LC2TDExrNV}n z5uH1=?;WT}FM#{N0noi^4DLqQFi*4|0-qr0ooqvN83vL55Aq+tq5IH_87zeO#<@s8 z0JVb;iy+7wD9GA<_%pcDz6hIuhHuS1yavR!kYcwBGdom_{kWQN$X6If-(@#wLKF&E4(PI z3o4Acn|2h2N}XWm<={$}*95}de(1(pApSRE;nq?r?EDi2>_c#;nfd|}W|g3DKZ!U) zNDyR^$1DViN+`44!BuG@04=#N9`1d&g8orQm`|=i?@rVOJ&m)0>_>+dR01Qr1&^*e|_+-7K|Aur2jQ2*1-_>Wbkft-B2G_bLF{t4n>;2+< z`IILj^Mb5Ich#E;6Qx)xH6B$K36eVpGDXiUATs$aDo9q?YvTE(ygHye7|?ORFG41M z84kmc;NQ;@!5AoXK@3R9q4+3tEo>q2gj3S%T6jzN2sH8@fD60*%SE{A-U;mZ9@{|fLl|)Isozlxc1@FaK{3Hmmi&AP{D%z-arg|Fe){IO z5OOnTBL!c9`a#G!0bA(}+>_Xy!E zfRxI&=UdN0)V&~2(FQAJ5g!6C+bw4&G|k-Mz#W{S?oxXx=yp8^@{WY=9T3P5Ajmcl znQXH$+j8(h(9WTro!|$SO|1ltY@nVcC^RJ<4F+d8=*XfFKZe=!pwEL$ ze*;npuBwE0FfNj{1iVvH(l-f{GR+WO{;MEHOrYuZ#ly^C%>>pc7?};Gv;iLSjz^3H z;vkdr?p{Mk z2YW?}2C;5v)^U6gw6zs$HWHqp9rXSOs`6AGrX?x=oSg z--<;k#}wJ+98W~#KZ4svO5cFML!DXPjuVPAz?%C_WH=-fK zw?Q{|0NyfHn93B;^D-wA0Usw|rS5oGd1SDH=M3jrNYe2rWl`|tc37sMmK}^2;_!VV z@a&bN-^0fjbFl$nCDoPC4IlKaN`)wt!P50j948z2IUrQJywB*lPvn6ESO`y^PLPA} zsTnv^RI@=7&~XJ6c;3*U@-s)4*F;W6>(2q2??P~)8nNxJIJ}Kg1@Ac2nIT!jUjR#; zshmakH?V3Mw??3??Wlp!-UxuSxFkUDO;PAT^%&a=^q?-;&>8;LM0Pe~uYNfgRV-}# zgxdCz0Nzg};45Xw5r?BaWsxD1JH!zn{2Yp}ePLjeLuEw~ zHj9y5crDdgs44?Tgw+2Vc-N*|p@3{WDBDMf==FYnPAIp|kX8jZo1U)&^q)e{;eq*DZLD(IefJgl9o6sr5IEQ<9Xij5^0U4 z?BMUu$TmExY!fUUETITpHUO_C=Rw!#A&V{ch9-9^mhA)MzOboU+={tk1Q`OEHJjfM zsjZT1gKij-qyvPL6a=UZ9~5r~i@hdiM_~x~L@2M+)|6NV6nyZWSQOPq=^RSMUEw9z zO-o_b5o%N0L_#Gv2E}aNeA&>GanKA8_*M%Xwi|1S&Uh_+bZ;k|o^g=sAL79~1gN?r z6c*qT!NVfeZK3)P6BLqYkX*r)dGNNDsw~?c*l>}8oxP4&uIX-pWJz`>gRi$H67e=z z8Ac_jCDD|bbK|{!OXz}>fEVh_16$pb6Ey;Z21!65r2hKXSf3mF&v&GyMvnD`#Mys zf$vO3Le~1=vu(Hszae5WL<}#2(z!7^g0hQKa17qPmC7i|$$Kh?WPAvqAQ3^h0tSuEj$hk1T1Tv|Uw2eySWl0=?RjkyLo2e4IC;`oG#c`{$^tGmc;S zf>s1;r=kS|RmV~#5|kNIRzsw|mDga{y}Mbm>&jbXAxOcEO^~plErS_lswBjLO;l!^ z`EtMv#zwYBqzB;WSDKeEP5C@Zs}0lLa+b8HR!NjcO9LHh72257CdK-%Ig*>Gqwsev&Xv6~EQkD`X#47*+NO`Y#kCRP zTtzMn+MKdZl-Tq?MEWcVF`x=bL;pt0t{bXHm-z}9XOB>}Y)!DlLJg)jfLr?C$_|!B z2OdxM3UJ}=k%LoK=r`%zmgs)yx99RZ)Ic4i^u<|$)r4Q9e<(t<6b`k0^!jMT9UNFp zO8ap=M?GuSN5S62c~VN*hBV2`gkX#O2A!0ds-bYRw3)yES}UI}%AFjqEr?sir2uG#wtK zoDppCVvb62i$U{2bJ~RPTkV=JA!+J`B%2(piwCDsSXZU%cu|Zm9#Y|7%ahx%+a^+K z&_M=tb6*D8K$AnW9lmFq7?yj`!^))A1UK@x=)@ zZo*6vnIFtr1Has?-EP@Fx@@_>#ER&X4*tZ|#93`Vp|5DU!bv;R^n~8pi??>$RSrjR z;2fy@S*?uv)bq6rGeEBr-XTlwiNOE5YfS!@CE;khmUP2Ni^v)rYQeMp4MiXPh}qka zBOVd>$_!aj|BeJ8qeV2ZZCy6RNWvYl>+PfyAJk{tNd+@giQp88^4)#i?gEiW29}{* zK4wo2z&BQVY~evwD|h?5EkU!Mm>0=)ET-nk3*>W`dY<*H_7qcbV<9&}N26C<@5)49 zvjuze&s(FlEgqD-dwIwnG)wMj8HF@1X=C*Htn6>pwIn*0JSbfxfO4dn8|2!j>Wx|& zxYO}N4TCRW=jX{8@L2eNC9f$IDIWU~Oi{Ufmo7`na2D8ZY5K0$s(ms{GB& zU$IS>brrQVm8=9S*h`CR>&*;EFtLiCi_oibg_x(v)CK@fPBG7!72qpRm&C8yPm|9Kmkx9xXjg=)l5kYq^aeDwIl<*;= zrAdm67a5Zd|Msx-dEXvwI_Fb+?xs`ccZ7fsb>{gjAT_trvUclTjGvdk)aUe>9da&? zNZfOH0X7{_Cg2!WsDR0~ndc~znnb$|)8ra0Z(E5%c8rkQZ$HCu-ZlA+Q_;D?c-7J% z=RY8FTbcQ(4BRigJ%_3Zw5)xDmwWAEF4wgDoQBTtL1(1{S+jI3D)nPR)#!9+c(JXB zO$Uf!yOqmgnZ*w7Z!|>>pn_57@G`(a;XfPj`Oz{;M#@|pH0@*j+#b24o!^ySnd_n^ zA~!M5zilZLb- z#2}Hi^>$f&PiHk7bG)*}7^v4x`RDs2F;0mMgX1XSSy%`%BW_RZGr9AfkLZb?I4*oC zV7wCEFOSXan8zM{ZQMeeT}ZO$dC_fZOu-flpVECY?=G9}T!OZgPD@2xCeR;{DmV*_ zm844yySEE6s`Qa#R3R2T#*H~cud)+93>PwjmDcnJoE#$}m^tIUi04>)UR}Ii)rVD_ zKp3*(cXO@5KGCY2zRBOVoC}(3s8S;YNcnujSt`L!-~d=vJn@c5<+au2Xr_efq@lGa zV~j1>#1_<7IYrO;cgvtLDW`F^4ZGPjf!8~OxS0&Dw^8EWT_zP6t45VO8p{W!QYtGP zpaM%sC8yr|da$=G`H|}5Md^tEr_vdok?8ecS@id0-Y=^0e{ZJ^oFX$NI!Q4Kp@%9Bmj`ZCT)N=k{2{Uo6GJ3St zgKQktbZPmS(QJa$pwK8FOAJ(l=I*2>O_0 zZzgh|40Gj%4oj#WM*$)`l_U|%&Z~3g`SOWp6>XnkycsZF&EzLqo0HlJ>6PK`5+rZ{ zXRoLOo5acGooI-_eT0$28jSps%vILpUpZzPEuA9XP%0fT+8S}#3x)~>PM+xxl-6}W zjbnGSbO^B5IOFHx2|>=thI}HVb-gIJVUZ+Nh@B%#slou&xT3PvnX+7ORwOstz#)EC z26|ftnz<|j_nSvPC2-dcbSh=0+iz+_n9ub*%BO2vmh*4_YXUh$wN}Bq2`GO}$n!po zE3i)7Rw;c6ooIcjr_^{zmcq}!)5%7D8KH(DeMgRjaS;CJ?#0C|-7mj9@s+L0XxHi| zz1It}p8R6_jrVs=dM-6x*W288e!N>pry532Nd14uNC$%uBXsz`IS)f!BGA3t-O9<{ zsj-X$Z|2M0_)YD(y?DLk=StJaB@sG|FpCkUDd%8BcnB3n=UHBo7xq;`Z*nv-S<(asP`(^h7R3S~@&OxX@e z<{~qdDH%fusrOntoP58(-~au8KJVM7b58B&dG6=F*S&^oU2EN))t(;CQvH*;#u$@t?jiE>q)0L`($q4`W2ovMdxJSWLULpUx6KhwvC8fwB@66V-w zjG-5*`MP_mIYh?=t63Qs>jxScnW&AjG6*mZ3^FzeF*DXvGchtTGc-0fG_uq;w%{09 zaZHTVetl`+g>gY4952Ulzg~ymY&60W5@I-phKY%Z28pHy(Q%=M##UBVhDIiaCMNp0 zLqC2&R6;;{)TuV-munqts}`fZ5UW5^OXu*`8I*jrlWdRQxYVL1uKKtKbQY{ z0i^5h{^uS4<6P#>{qu_W1gH5}#xH^Vk9Ws&7sLb`dIiTv&x;ETcAAfAYSL-N?|F1U(1#&{7Yf)5FHsEhZkdZrovgc zyK`Kl;u8X*0)t&0Z8RVdgYfVmj^%7KEAs%0K>d(_pxOFn!LtMP11y6r^(~DoO-uvK zEG&#e%zi)b7#%o|ww<2uF@m7zK#cM4TdgdN%>vDX0`;wetSt4-&CM+J15B-g^v%qI z&CE=VO^incoBSSaVq7@vFd(w`s5Gk}j2JX3Bq%7z$U@)T!q`IJEMT^oen5bg1!fgA z%4Af~DAUoa!Lh%7iVRm1DhwwekPN{_Bak#8I7s8y$Kn5j zJpQlV{OkF|uwV@Of7tw{yz)XVibm$PsBF|Wy! z$IYK_!DqXEKdCZ$J{D`6_i+B(`_=CrowRya`7{R?_R5c(>^W&tY65$}YSEMP-|jeg zFlWma<*cLs{4@6G+y`k{RbghAF6EE>_V{bnN_K-81EZOHb8av?MjDB^sJX7xQO!18 z*dA72U;pxMx#q@=8)^0P$FI}d9^F`#+;&!C#PO>eO;?V7e`>9|L)wi~3*xa%U46gG zoCi@SGwbWUiZv(AoO$h5sdw#_E6*SXwb7$DKh3t=RJ6J-#@(A9KE9``u*e6 z4-;;>PuOU(BL13{glm~A69Yx;RJ_qANpyrrm-up*`T(qZ?t`PT?@xankWu~e<}Mk` z_Q<{ZlJTt%kDJD~Tyv^Dfc34Pxp4K49U2!hhdNh;T!j>lJa|%_5q|HG*3eN=jvTK5 z#Y2hjXR4-j#y(#Bb;gp;r^7y7)XbJ$?YP_R+qZ9$%9c6hfi(j%$JNzM!OS5XdUUO} z`;G;lUbuL<%c;5Yxuz=w28G5(s+9^!L;K_wpF4~Z(`1HuXyi6s8~xr9+hsBBNQC^{ z`-cNs>q|z=JO4CxX3Ecb(~d^g9uB`5cK4teZ6q%j+WV9%Pj2Iu%Wtyw~^a z{UdWoQdK|RY1-IqE%WsBv_G@tXHw^fn$_cqb}!VQd1}L)1E<&Z9jG1i?s$uatN9on z+tur8BC|~|#->f1pCJ12)2AU8k%|F|^#^9zkr3Br4jmQuxnatut{?A|7xkF7Ow`_Mc#nubc#RDXW|?BYD$OzC-fjcU=39ZTgj$K`C@dNp^%h!KU(*{ar` zu9)WK=Wh=TtlQyqIPxBUctJA1zd@yltJ^$MoFnH}3oHwzj99RW>gsCAAtuWmHO}%F zEbKvav%d@BH>D7f;q;P2&#CJnegCiA{Z-Lgu(3 zqoR~y6er(TZ624Y6l?kA-J|iizQHn9$;9_N=iJ#(dYYcz zFHad`*dL2}q-YYx8l@?3qub_G^DQ_wq(P=iPmzYUhZ2t`D7etC5EZ_9P`z?(DC6Z z_Fq!TY}HQp&2dKa-#>QW<*~Kn@~p10)~e36nvxE2@$s$qDu#c3yvVTo^Q`U_?2;WK zuKeQrEqYDih5Bt4Ire<%n_WMD48c?(+38WoSG;)>Nz++sBsukXoYst!^MjlJS+VXuj80}@PVvhFE;e?38MW}sn_A2vCo3PW9ro5 z)rM==uXq0X?$Jh*;C=y$JDv?pQzuUWd96{k*#t2wy1JPQHRWcjAgEBQjU${C{M8Wa zBqK;0E~&n73=5O;nyQhjHfq#*OxQI3#RP7=50`7S;A0Tn*v}tdTeies8^dRpR2fMw zfH!z~t90OIi@9Stw{dx*&F4X_t*w$OoIGv!O>Iun%9a8_Dr+M&$8E{TDf^2ZR!Uoc zd$u&ZqZzLzVR&)WvYj{Y%XZQ{cDY_nOUi1|Rz5?s2rYrW9cDk`@@q(7E ziJn&nXV+}BUSLvEQX)CbcFT*an{kWR6vebF1$G4%bMGGvzo}gu^zr<_i8*W6I&^pa z42Q)Re6Af{V1!_xx}+l@kF(Bj*0s6E9$l!6eOlSHw0}-9SLj(zHi*p>St4;obS%5o z=y^+vslPx3tFl?xu4ZaF5F5&^Ncu3FGw;=TIJHS{<D<>JMQg^RwvGYhX@?lwB{UC^_Oxoecn`;}~a_|Pxv&T13!UI*bD6=66FIrLsSsG1@5%Z4qaGy;BttoJ zb7=Ibv!CzGx_*B3Kr4tA7G3&$YvSYP+tZ_iVS6sMZyqmtua)a^10Lzg2TRgX8Jl)l zw;7*WuJ3~0S$DRbOYZ*B=*;KOz<%&`w~AQzKg7cx{QSyERy5(&t<~-A3Ao6| zh_5?-zE8Q1t(o-p%a;ey@o$gZF^PTR=wbDI5N7wW|G6B5-;4**XSPZWwNNV|PZsk} z-{Cpy*Vj16*ev&I+~JfFc4xnej9sUZ-S@ieoZ=t_`1Rt6Tbb6*?N25 zP>MBfS~iQmuq7S1@p>~(&-5~>nW%*SH%y2H_p+pxl@ zQy<11I&=u$$j##Q?R`bR0z_-YYdaOV+=H)syzSnZxEb zg;uA_(?}~-7p^Mk?rd@KGUg`6elEf?Jk|YqWG;b6+%joLrR>Jd?PC1|o-(5Q! z&j0Y-2kW%PuTxu`d#3Zbq39UFjqMIrpKp0%KWm>qUq5T-^kY$x_YR4&lsc(Sl#z*q*m;UH$1ri9N#OOjajhsIBAmj&ac?v!^g{p-UvK*%LBiC zzjG!FZ?PblasOEK^-bn;VC4tH>NM^j4nGGFbgkzt%Y<*q*A{pS<_RW169x?$6hj~< zO`Rn!N!X9G&$oOj^rsxAUwrPv_tyvF&q`S$$O$4ev4!fxPcuFd1*Pwpq?zrED6j=asA$FG!DsqF6h@$Ib4 z5*}M+Jh^s`?=F#vj8TmDYOc;|N|`1xP4++;i@&$SU$=%4D7rH%#RfXySk~|TsZY}D zXDzYv-sMq(Xy?K&E_w}!FcNnMT-{`jg-k!_&vFQp;;{!@{uWm42HhJGP2Dyb;kvREaFx)= zl`9kA5Hi@UDOcg^fdQ6$Tw9>l+6HHkmZdV9zj<>;g4qOwZ|Id#!b?vGD~;VSj+XlQ zRm0OA83tn;M&`P-VGk*?E(6+Kx#?y8l$qN@uW#Es>x?R(FNN~Qr$-l!%X}hSG~Z## z65IDxKMW9mkC9q=xC_12xmZq{a*T~p|KAXltPTDZ$$|FBkr`aTU!`?=Q^(4RI%>#~ z2~bEsD>j&5dc@`@PiBW)+v19$5iYsM!pz(<6!b4%x#C!6fQTLZ_|yWR_LT<)tacos zuOAFU=e`2u*3!~S&gb(7?5}*%Pst*3Rm3*rGGs>|KYnzXIB^$eMSu$&^tZRsDdQI| z!hw0UYph`Nfq_s$9G16CU$rd_lCG(ox=8!+`~AoD>zV6A zLbs%HAN^zW3^64X>RG&NSG*CtNa&VW@xil_jmvD|h=R{96=M(RTQyeQ*7O%irZdEOply63<%>P$f{dP@06y2w6iys~4HE_e7iO6@zD9tozcu za+paOASA){j!4-DC7uQtm5_(SQJ34|{yg9sMJW+na2HUQVKyfK0M>C}ogvX%Z;M_G>|NArD zwg8~oeVDrDVK;b_ipG6-wTCvy(iezsR@cY%gtHDTemm!u8RE~gqHJ3YNeA1JrlxBf z_Ds2XGBFy;fjeI*q^UbOIVHcI-bmM9FCXa9bn4SJ2aX>w&)%7*H=SPemA`G92YY}z z)CdsRWZo+-BxZ@E!i&pwEP*)zc+5gXKe*^?)RK;K!{DoMtrdc9ep7LMTxr0MFK=li zn2W8?ftfevg%d@r=%>Zqy@Qv7NJ=gLpl zMw936b56E3tZesg|4Vr~=Vu#>%cxHH^y2C_x<6fhR*c3iq_5<~#ijz06#B0w?D2swIX z=f}&(o70BEP_z^>V!yZUivMyi)O$LzrE&xnKv4UT5be6KJc(h+Iacv6a@MW8aMp!A zV353gd3=k<>*cTf^`I3lbSM-lP{0GViJ|GgF zgkrVpTJ2c8C!URMC7@kc$UA)4%yv(}xwJJyM`hKn9Ui|I&YQQZV96#Ri+_xYy@0Br z4Hefo8362kTVt@{96k{5C*4|AJsW}5B;X&>Z7E#~ zFwH%u2FKP6AjrRge~|E^eO$oDL({<)L!Tszo{B%|7Tq0)%b}#ENxq zFb>?nnWu|&O?1+bgT$c^fhE{8<7xJAg^b+%d?&<+i?y|5Bo4s+eQiuBfS&L%elv2HXT@tho`kKcdHUvV z`7$6`Sy6_Rl37!#gsOjM#|JoUTc1gj#^7BmBj8#STI*LJQKbOt*?@HYGNK_=9Jleu zD5e#cl+^8=YN|9UYJ!h0!pVc9a~+E&_3PiC0v+kVfYoiKhYp$0eh-3?A*e`Klc$Zp zZsV+&hTQY|k}sz3PJf*V!DT9$Ke6k2YDZbkPT1V@pI;lv4LG}6E zp@i7i=}#xUzh?8*PkLNHfco^Kk?N}1-a6!R!15{RPxG8M?UG%FqXrC}Xs&WEPfr2) z^ePuXcQ8Nwe|&wv3mZj-jOeuefI>#~-J^4diiwFqBDs2)B4F{BdAFx3OTlA4Jtt$w z?!%0Wi`#(g5{c^b3fzHUVI*nAMScwVo;~rZY!5UxSr>LpJR`*HGQzu6n| z_thKcv%2Q#gaX^Gdk$1>B2JAoV+Od41q&8%FI~G90w+XC8Mus3Q=!U6lCG13gM*WE zkwL^fUeE%6ntzS+T5nvIa&2POCl%Vap`)}%j~@DS50vRf$@sy5VdG4`zn;~7X=2KE zZ)}WKYlBxH z6+l!1YbKCc(#C=_DCl=C=i|pXm}c$ny?fWBv>=ap(J*C}78GvkfvWGi$mdldTDO{E zDc18|-D(aaE`-=H#O&n(tGHV+$bmCm97ObI6&MEh2FYdD=;-P`shE6V=GBt*Gu_w& zwtK-HR{nT*GQ6Qo9ulHV8;R@o6hldenG2rFM^!e9^WEWH2=4~gWNCp0Ez5CX-#s#C zFg)*@hsW06KzStgwzJ|`WcfY)~pID>br&KOKIm+6wuq-%EF$z4-t&Qt|42$x`DYPtZCYlvBx zEa{A{q{EK75w&}F?_Op25q4=5_0STEUoSt!Zw#VQ*sa}i5E980O4xpYqM`e!N9RGV zyIPPUfVNUjd>G4J0pJ4WRtGMj&Uu)S#`MD8zyRTgv+{v>yY9WLD_Q_Hl_Kn!ACKNE z8qXF5M^FsG!6IYzC@(M7HDhiQ-!Jhyy^xoeSKbS=UU>=|+}XC$4s3c%C1#B1xklNF z59S*rJG>m+R9&76n}EU;zgV%g!zU6B5AvQl(*uSBlzR#}+u^83o32A;Zm|)lk|Um9 z%J)MW&n=sN%m9kL6~M^2Qn6d^FG>RZUpi3J{qzCSlish!pReaez=muCT= zU27gOawPGzi1)GGKfeyl9EVlG4nq)ykb~~Bh`cMP9z1ITe0-`Y2Q zy;>j7hV<&$Fe%`r=&EnpQ21uBWL%mT`Cs5GyC2ntOU{O70#cHv)4@u>hLl}`S0uc1 zuQFD%zMSvE#w0Z5!Cu??pdf)r+zK`>pMV;&LpU2)CNlCH-5439LP|3{jJl))zpmoC z?Raa|7I0#0jI0Szbsj|E@s~R?EMuBP@?mMt2oev%Ztj#qiG`3e?GGD@ObjMbNCArc zH3X8OdDwi3F|skcVXH_`$ZKJiUOJKvz~)1=2K2pj>C*1QG0kJUUZ3g8a06sH)%E=~ zF@ThJQiz%X`+?M{j^R9PF`QX%NscRV%20^CFeKEob#KMBClo*>>^d0m7V3a5W$Rqn zNl4d;P3Z|951E{#0cGm;Wu|;!AQJn%4OA%EtkdhGm@^9 z->{nPLm((R<;V1$9$PO>T=H=sLH8BBZu zTm@p3?s)cq$OlI=p`bt~K;sbI4Wxjt?i`p1#LY>DiRIgBo+ZB1Hhx5Szl08b>X{rdGI z6kzw`-gQU}(bxOYm$wgUXa_-|qCm040r+^OCis)cyGG*D$}T3LG@+`w$jVS|K$iZ@ zAI6)WkpWlG^$%e_U`C8pyvb2xObJwN$&Uw(UVeU8P*}Nu?RNpp86%q$3B*;pdv_Cd zBH+c9je`^v24vT``aleB@YyYQ&UBd%-d#zUH2zDtVg{MvkWDM&t+o9mgFCjLnzQBN=p(-4Pll_CJ2`7ig~YZ$KDHG|A81Sz(i>$uo7^te#sxI-`qcv0gMZOP)foK zydY_C;wAlxsRTbczk17p7aPr@9&UIwNT|(xaWe^3)_s;~PH_Yu|#}Y}&nwP;cyg_L6o@)dkPwbb~`e z>UU4ll)eq$cAkJVks8HCM0rq-m5HII%vzieAOX7Sg6VZ@2T(ZS^~HUUxWg}!JDPDK zL0ukIOv(Y5suAr=sOta)TRpv?uFh|uy;q((WlifY=K@54OG*Mh`1ZcLD;hCoTNz5g?<;hi1FZ!^hkR6kqS%yCRNX+E$e&} zYF8Bd^a7e)Vk(Jpc+l8&K_&4a6J;eU4P{YIsKl6Tm!| zodw!yy?RuJt5j+sXQv0RX^+tQ?&SLy?KeTm!QoCq z5fl380Dgfuo@qzY0gBt-zim8IfO?vDI8r_wd*IRFFoNDaJWgpjY$oD_a7?8AQ2#V4S3}?G!Bn?D zC(j4Mg@6vc9sZ-DY#GCY``wXZsFX>7SJxfU`|9RVNJ#MST`22Q!vx&Q(+iod6DPK0 z&`U-hIK`&|LFqzp6L3Zfx^wXsN=_vN8UfY8U`P+1Jt#5DNwpKKrIo2%|{h{+*ZNg(mk0jn4k8X7+W5&h;)_ory0@W;|z z_+17H8Rc{C7_?VoW6tg(a$sZVwpxndQ72lp4^>o9amV}~h_Vh+Kky7LTJbNgG^1h` zdiP*}UqYP|pn&Zj;A4;}>kMXGx|pj8-`*emZ;1sFG5P3NAU5K6eH$3dOjx-Jvt%U= z2ot-)7|SkI0&TsmYVi*=WmF=8B=7E}m`26QMt(d_>D44{}_&3wbZptUU6b3AkmD6wSc&B{1Pzj_}0oFYA^O z{lPsv@AYJgkN{GMB4F!!UX9ZFh3sM1@!F|X$yNwKw=4>hKlY`6s2GnAD0D-ZS*RxN zxPJNi^%l^jPw=m`A8i+b16y?!zn5gLGbEUsRIP%>_C;qtaaY0tI=XXRk^aW*qT-pC!n6_ zbMe+e;{e(D^ULc^!F2Fvp!kP6BRn2@bgmJ{Ops={YW63%wYK|n4v=?*IbvhP%Q%!G z=PQ|oQS~$YMj-=^|JI;kDV@fNkLC>qo*$n77JL|ivE1CVnWG+G1aY*%D1f;2t5nz{9%_r*tnNl8M@b2&>78 zqH@KDMVVmsY^{|P&ciRS)AvhiUO~BHXJ?BY@m)xUBhr(suR(DyHJx2Xq*rMfY>P4& zI811aRcrBDWbecV!#IqCFAaisb3XK=?VJq75@MJ}o%tp;Mz+m~04U6V0TM+hfwm8* zn7GEpU**tg1s;o{-|IWs1TOBE0p=l#nA8duk^D1k4rtl=GLolr;|)(!V*+Wwkw=JF z6oml=b^xxUd%zQwfT~MZHkwj}9BkScezE2eES?e$>LCD@yfW|33}@`}7+KGOFpf9( zDq0^LGlXSn9xMwax0VwJqsM--xBeZ5^32g&0uO z1e3sj)U`mC9|3JWOCdQ>2-yT4PNJ|Ai)`;BmQhp`4Pxuc=26)`dnSK)I;?vG?x3>q zi)&jmil3RHRSUkd2IC3)9zawJh#GPK@Ukeoe%Hidpdv~U42;pKw^Xe#@%>}!DWVX? zl90}EyS;zLc<2H$AHUdYd1TQaro!V&4z*ZAtqmmPp zpa%7JWYGHX;pz>iKi{5CsYxa8V`t4pWFd;A`P@qH>H84bP-Q6(I44fX zmwOte+1jgDoxCD^QOQPb@q`%lXKN?EpeD4^ccr_w?Et8`#Rivvy4goK;kBDLUk31V zaiXA#tpO(7U-do0Rvy)T32ExYxy1$~qw?U(PY8y9QlOV|3JNBI$EM~rp8!-kX^BHj zLvm1FuJT1y5W9o?4_?Y1`9nu2L|+UWLB&l8t}zYpD;|W9_pzuMWbbhKrQW-wOCzA- zl$z&{oZ_`s&-WlttiM8`;jz%`s&LH{VRTbw%#-)gA@;aIU82J@Myri#-wpmCvm=oQ zb4MYSCF-uDc_qOf&rXT~ST7*i5S7t}r`nk4pzLgxNfGwsQMKAt3++b52qNgd5Nn7+ z!l(wy1RWL|helPVLQ}k^pF#&g`lPA>hV&~?l z9oo7GP{&)gX*Lkxi+XedBpFd{`A8|w!F^473swbgtpu2cNKm{XjtwgsPgyfIPJ&tq zwtFF)qqYL?=u;oWQ7Q7Lwh4a1VdPLoK^J9@MmJt_8a(4X*%&izVlMT7D;VcNcE9e2U1S)YAqZ9_hvIe{Al26qRU3AW5A`%dKjyQx8(B%DowFrt7 z&U4r*C#XJ!3P9Ye(p@GnAZqN)b<>`>Z31-D^W%f#@xwvcjs{*Lm<^tA`~6Y`2(&{H zVF{-Y{;<+`WdxiXW$1|6)DV{Na$)<+F;E9;@cHuQ9-$pX4|P|sc{=DCLGDMe9!g|_ z{ss!lx*Kd3S)l141n|@a$ch?vh`&T?hGK=K7xjgdd4!6r<-Jb*$|LD)M!ZAR{*y|J2 zsfx9yA>81G5b(e*{H%v?Ijz9Xb8hc#p5FLffpUM>@#8f&3b7}Y19@zo617l*V9bTd z5vm7I4l9jA0EswKjGbHD;( zOoc^CWvv6p<6EfpfEooijf(a#fq4!?xOk3Q!`SZ{Am`!tiQxs@aD~iEsbeG@?QFmZ z4t(xBlqKA=Rc*rVAJ#!vR}oZ*T?98zX(4(M2`2Epyit42w)vt6M|F0Y#7(dRRhQ8G z`VyVg6qud&BVq$K4$Q8}Ww-h+B8$ItX?9P?kjXUk7r{#h(tghhB4moViyBy6na@a+ zEG?CVoj?j6V%x-~G2V;Nk}Xh3BNhzJ?kGod@*!KQNI7B2Zc=21u*Xnjo>WU_uf;F% zh>a!yEX9Uk{fIon4uNQ=Ha^dR)LH zhkZ_o30J76@Dxrf=-bEVgA^2~mp*SH+5*tRqz)nsM0gykf3bQ5l25)nkwK6VZ!BIW zpHbZbvV;mn2pL)k16-6sIGp)!E&>tevVF3#>o-B7!KIu7!&>>={N_vkLNXjx1B8Y{WNKNsZqfV@bu+jb+QH9J6RYzi3U^FF2s+7kg?cI(RF9bc2 zR!?%>Fm^sF=G0WNy$_*_^7;LYwu zFpmH6_Sh#dzxi+x#ph7cKT8>DiG*qrPpyxO7^O8J3|bx~qFp-S4dE`b2;5Aas%1Ap zSd+7rkXE+$x;hzE9O_TKf2ZmKRS>AR7~Uuo{(mu8%B1!>XDZI2g0lUDJ^;w(=&Fs> zp{ueQ6dE+a1hb zVa9GU?QobJ;x6^R)10+dBA*~k0*DU|wmA%Vd-cHKRG9^ppUXW7jGnuG{YvzJqQW?E zxCa{?IjW~wN0HpO5r+?Ek5#Ixg78l#a!}W!1Pl15+k?_*{?$>bD^vFiprzdY)UeX{ z^Iam<&!7X_ipZd=<)^ek4*{#Ze13URYQ(~Pk*!TInTq++K@7-ROVR;*77R{rjiNCQ z6;Ouhqpy_t@rvnn>inh&6HYowFbm-TMIEZrhD`;OCgeGK{u_Ia67mZBo@|8(I8+a- zT!a&?JC<6Gd|w6me(|&rFwTC9-X0+~#PpiwvNA5HZ!|$C30V%bjDe&ff*i|*-nxTu za1keMKvoB8hlv*P??_&W6hvJ);mzG#Y(d#rND!VW`1~q4(sG!Ot5*rA7(g~Bz(Asn z;J&$_o@vpDs=*ZhoNp5A0mT6}+KvxF)qT27crb78HIZ%1C{b!AcP$J4-M$U3w&V7( zDkt>VxGctNC>KJCq3*L1CPi%7KiKXZb+ftm^8z7JO5R$oZJmk|Aiwz8F(k`WkwBf{ zA|*!HN9P^j$&3RpP_rqL&J4Vny?8RnNQxpvm}fz_3KC zyRiR(7aWU7Q6LOwLGyLntYhTcq;VJmk_NB_NeG7RV)r+@?vs_p9`2qwW2R`A=qV{S zlFoIgl}tfFIuJ<0FXpV6au#5z3HYsT$sb*d1A_qph`a|Oe-n|f?B=gd!;K(*_{Fy> zaDnu}og6U|8xgU|)X3Ms#McMMZ1d!dE)fAR|pZXdog+Q_oL*xdpHw3a>9w z=R{R%JLlPp7xfjP6GN_V%Y-!FK03J`{b;Cf97nGb)sg&vQdJ!mI*tS>O*>D;almjQ z000A|s}b)A9a2{Y=QbS780>Wlnl^U}OJ>|j!#0TciCw`V1gI(T4Db*&04X)-!##WU zE^-I}pJxd9j&2IX5}+NT{q2MGl>C_KqoJ07m=K|q8X6i>Ixm}DLucjyj+65T&rC}F zbvDwK!^NYN5rMOU!Gxo&Ln5%2uSI9cvTeWj(V5!$Av@(6Q0|j6-9OIsMOZG|sA0c+ zQ%g{jPSZkvTO^}i`W0xP-+$_(+s+9a$5Jn-2_;Q%^FZiQI`^#G1T56h;6beDz?Kr7 z0)S)i1qsYA-lT~xFeoF$7mkw~s4FGsVlFtDbkS$Z3>*nSkNj#PMerdVNx%eWg@6#J z?t?dvPY>0(=Z7vvI_`w>Oz|ci>GP!A)$ zIs68I0vEt;%(VpM2DgOzV{W>Sq83q{J-ZG_@)BGZWb|<1>wQ#Sp}s0g${=9?8z9|g zL{92C0Nl%6yY}2*atqYOLNygH6J4}}z}l%pzF~s8Y|#r&|D=AEL+Bh2pL?0kCIKVR zVMk(p+olp1PRTD$Be>MQ168pLpcNz69S-*{Bf_3qT74@*q_EVH5FD%qNf+glNZq*9 z#Rt>)#WErGVS3NOM(+SKN)6v&dOuC2vmSJSrLjsyXCtr_v&peg-4hV2V0{|5LMw-R zbGg0M4do66^9Mr&HwBr3(KtBbdgRLM17|$e8_b|qCn^FVTA!q2dGIh}-NcA3!BIp+ zAsh{?zEkzZJnKBHu@ZD2y3A>|2hdfC*`iNCg--7PA|uKo`=x3LZE|T@CL$CHzBo@a zmg=>K!P(Q%4-ok|USmYsC6?`8&*?SyhG^1|O6G>Xuzt4AbC}Dofn1&GK)LG?Cry8o z81tU@_B;tuuj_fXUNmVyrQ8PLF8&(!$OOV+5u3eJSMnb4{>U1iaWlx}lv;3Pk`yad zHptKme?0_LDZF;{yL8(i;9?wA`E!X~nnt{?*WCFE!o$lJ(62uadPxb7xZFoFdfmRi zKj~htYXnr-XZs4+O%Xyw_-D*wr4h8Y?KYm(`- zIDXgZ7dR0&lqYl?f1cFQ6V5Vj7ZaoqGybnxdU5IH?F}A`E=fO`D@=stKhL?1Av{rk z)=F%HsGr0sa_oPu=FKK0EMAtV(I$zb;0pgMjie6iwvqHFnmG2~* z)SE2)=_ugu?~+wQX|*K}Rg_8)6QbDPmt5WAg!5?rA#J1(_Mh`OFPw+wP30u! zH;A+F8cUlwiWLt^_s?_utnEUTnRij-mQ^g56 z);?b1U%Os5m3E!GcoSnFZ>GSkP_)+z%O(l0o!-P)#>*Ed{aGEx8b|x!^?6#~hoXCb z`!QTEjv(t{`xJ;FrcUei07qOX1 zrS7KvelbRL75b?%P6luD-w*uK6k~}nG1L(MJWGe~YKZ+%|Ixi3DB%eoXuHCQVjUF*$)1Z|=|Y{6e)Pb8y8po559A0RxN$`+ zO8h8|`Rm&&U8oPzk2MO-4~qT!Ip52Zq@NI@z_`PD-P<6YgkqgFS1yKiP@(q&LZ8(B zJcMyfWN7aPs)Y}9Ki(|3Dsb*4RxYc@IYo-c^ly{7 z>+`Z|;XpU_nfoOydP_r}lnJM^7%?^naSrv*BaXDw5oK2Yp_HxN!cgmeSR|}(Z#!ZC z6gD_SY!ZVvv-mR#+-nx zHKX=u(ixowebzLQ*S)sEb(Su9sM0s{AXL@f59HID^U7w5a~KbLKOmtcd?1W-W5{2o z%LyZuaWg-_+REG4drA&Mnv82A5hjsLFzNRcLqQLH&YB%1l$en^h}_mh^g5}R0!VkULvT=BgOTaiPa zJ}wgVsb*^ZFN!OPeCFw?4Rm3d@FLZQx;T{CZswKP%jHNFZ+DPmZDKaElydJYF!%i} zSxp6zK;8%YKFeKF1~b)TY}+JnD65R0!;2EkR#Wj=(MOh{VXu@U9mM?16Sr5$VVH?{ zhof+gfq8p^n#ba(0Rt;hbA0Z$Z{DKICg^-~tH zU{>&kr>^C_XLk4P9AwFAZ9FO3A>L+NrazgvFZC}4FM<=`s_62p)nt78Phfd44MYvZ zmoUtnzn?PYUFIFJXEr3n^X3bF^j5(lgca_i0ru*ieIi9?GCWvI7=FyoQL<`39|a4N z)MOfh`U4O2Hu?HV2rQg+V+$31S;_d*6v=}-q}oO|ak&o`59{b=+~mnNIFA3XXW$Op z6#Dub8nKcjk_L3xv<+?2;7)H4>k{phTD5&sh-%EhzpRq04O%EO}m!BAY_~-r4(b5nis$yOIfFsuJSN zZdUR3P4ASO{QPa*g8So7JpAREi~+OUoa6<%n6in#Weg`LEu!p#;5u)GJ+ov-Fycw? zVA;9>p0DJPNmH=D@eP$QsasCwlawG&xkA~JO~HZ!US{uk2f`=iy&52}Xp;7qHj=bd zad9xenwa`R;Fah00@1T~9C5Ajz~5@5~EXf7ziiSr-Y#y*mGaRP;K%e=YHIZ>|M1Jx>OUB*Ivdbm_TfM_X z9-qWAqtB^6$pc@m^m3JsF*qpRr>^d(LZ!mr3NaO7Ym8NlU5rAEyqi3uO{Ga*Ej??e z6Es+*w_r*jnD)dUsa=9mybMoW7gm+nGm%9%>P%z+NqZ$A?2=P}BVBjIn+*I1w+@xQh%VhskgHSA~2 z>{PDoqjgfGLpz6&S5%fG@>rDf7cz7;ccU?11>)T%p2M^%%$kx5f9{dpz&O>ucyC^8lwm<)g3DSB~S?aQPlNq=dM;wRqb zzY?9|X}~ze%S=@&866Yz)|jM5i-g-V7jHanKj$xl@@Pcki(^W;@s8~}p5w+WQq1}<%FTFivf%4iiG zVAQZ4c$VaJA5pGU{VQ`TYk+cdlKT29-B7Y*X>A_J3lW%bbwsX7SO9;&|LfJK>D5=q zHHj4tcC*OjdEeN@Y_e(6^4IF6+pgUObeFqilGIC)TTZoXk!wtoR7G9UcZ`pJrL4u> z6R{yHqomp_+NS44R`tEbnmwYQW3SxA^*br6^dpA9Hr?@xb+4B%V7K;%5hc9T(beGkEWdg5tzwMgFJDKhQCX%k{Ft#P7Eu)Stnx zt!>pjs}c8}0YGZ}zCcrGw}M8iZ2T;k+{De(^AM>LPhQ&lx-Fbmv1Wx#$HfI zJxI+IOdT;KhGH1jyq%FFmIr?L&j7_K5K3vp8_{&0a-L+Cgc9@HU-G7@l_?d7 zY)*Y&800ClyLSZP3x!fvKFn7tSwd@>$lubY#D{CH%R4<{Y7k@NUxU&nmMIzgtH`nd z?v>nc2$mpf z58r}Ge&>o_6gkAxD^&Fye35x5mD4#x9Py4Bt0sM(=T#`WiWzJ_L{{vVqIz8ts-Tvq z$!ek9c)iVIn2Evwr7Rhia$)_pNSZM=0mMl0l+`j*4lpf6-iY_(*$52$M;{z`SLzLG zKH~@0soWzTt*s>UnM~nt#joNIsXg(3DFq4*p8?%5A)N@yC;)FPN_ zzgTTxpWQR>2^I(z*o(=s`laRz8hK)ZmG-KhWTeGG7P1{SIcmp?8S;X!6rrTYLquh@ z^}?+#4RK{DiG(uOv(~dRQ^(oce?)e;yMHIMqpzn(q}WW+$D$T2F(GeS9JH2Urfrpv zltu&k_G_(Y5(?$o`WFt4F^RF3%~3j(8rdWYyZ|1ca@dBxgBa>`C*Mh@c26JtVOUNi zY3e>%{79d3%{A-?7V5{?#F%&%+*&8KMG}G<=0Ehtu#Ta!zbGbWA57P0B=eW@B|Lk@ zP~m+MthTqmx|X#?!n(hnc$D}r>amz|G9T%|+Gh-sP%x=4; zDFW?Zm-t!fv`dpkwK8N^34@(iM0h_*R+q@FUGsHVkAL&et-FPmT(Uh#$TOg{mn+>l zKv8;&^V`FNdbo?9-S?KsSdQ+`jIL~X{BVM{sl!6`zH&@9Geg$#l5T$upYlVxBmEuZ z2I-{x-cIvBS2cgoB*w{+UkuXgMe}r3fZ&S12AOO;{&mr}L!I`;S{pxFCw8?sIyx4V ztzNb26$4_*p8Dp?(Bev9S79~ zB`Z5CJ$zSvVA5cWFiBOuTt`VmLu0mm11`7kSKpnk;hU{0SM77wwA|)|Q~Ork+sy;6 z2nTjLq*9gk^ZcxXzBJTvgyXTJocWQwzry-uzjeM8E!cr zQYxIR{_fQpyNjG1V;kCRmGyR{D6f9MLme;GNcR^v5U9?Us>+^6zg~LY|E@>-QI9L< ztRH5JZCLiBTx5JXbNs;pbLv*4ty;Az$twbPExZ#WDKGco{3~Cz9VvPOYkHHTqg>>& zVg7?Z@aDvd(Rws41=}7SczU1wO{1$%Pm0}X7Js;AQMuyfgZ*aU;*&|j)o$x|p=ib+ zrx}ip*1L7-p>%)cYc&Q>#-vo62*o=zRN@U!v$LYHS))_FqFiy$4NctDm12&or+206 z*Quu$8qk9fwr|?gjJwX(VSSgRMF}51{Zvi)nR?23nHa5i=S=nvl#?@F_3O21MY`!d z62OCsWl5uFYOB7B@LNWUT(zpI-0sV1zQ}W+)4>6+e?2F8sv(KIcy?VE7hQ{S)704Su4VtaN9`bbK^Ws3tZJM%+Qw5yFw>UU@fw^JiMl(X~c z)Jo>Cv!BB-ZWyy?4_KOxllD3YbsPF7*!|?&-Ko}_F2|k}zI*lqrRjkaJ0%OwUmj^YRhLT07YD zt80wLV+hw<_*Z+@_6@q}vsG^YnOsrZ?1WX$pPc<@ZW=qR4mV3BX+K^*l2ms`lTagS z`b-$?RF~PKrgCyWqem%gs9vu2?RjDWx4i$7ti&ekg!EObMjRCGsPT}|*wKt5!aFoH zzBg7FxhbAd%((VAu`$kIB12oFEZLzAHKnrw?S6Wf=Az@2eKA!2<B3<5>Y!Y8PQr@l!)y9Qp`H!hnuAVde ziSVmC{M@xm&C`^Z&8fTb<6TWnth9%~xI-#R=uC|(-(-J^Wh-DMBUX`&uO8Ftav$WR zlK97?bX$v!9$9s!xukM>MjgfmrJlSY z+p~bWl==U7q`&o0PCfa#K($j!_)fWHcba{(1|FOYQxVxiGPK^;kvNGxr+ko9&tq~w zLkAxou|MjRbfS`C&u7aX%rPE1Y%JLagDZU5g{I>>TyFc9{8G6tQ_%+_j$UsXARNLt z$D_u*L!FFP+y!3i{EK_4Hyv~J&T9O5gQ&Ldey0xW9`AGB{)a}CK>yF}JBOx+rtg@V zE-&0BsM__g30yzB^VY)0Cy737b2_Cs67rSgk<{Er4Y)qosY3kGpW7WLOr9`4M)jAM zob)F9Xo;>zFr>`QY-$oPMF`u2FH+du9~$YHcFW5ew%$IWpe z9h`EL!>zG$nysXqwj5d|g$y(NQb`gucaif_tdi{fOH_V}l|wt+Nu}EE4pvFerRRA) z{m~!FzSs4+KF9a_eSLQg(jxP~fhxc-TS*t`^`|jA^mp3MHJgLF3%6mMxOv6I;Qt16 z7NWE64tdiASPl4NanwAqOl#%9#v)e2(sMKq6QDnOXv8o|2Qpo_8`t&CJ(3+j58g@6 zl{k0sP)|M1!yLI5|L?Qf@hn1p^~K2F8!3)5(L%pe%T;Mt^^fw`dVwAm$M7Gpv=4(2 zp0@=XA43E&s?MpE8ZWDKGE(P=U1f*n+4~hvIca_~t&+$AEj@$D1d3i}u)1-5r~zd9 zcT9cXuF5qSCyWIC&ac`*2};euYUf?r4QhK(`W`n4RyKstFad?mfz#OmHxCrtmDn|u zuhg)D6|cOQru~Sg%9<9V)P7W0?4drh7?u@Gci)Ea6?Ltw5B!%s+gHMbCunD2(VZO4 zmE%C;a;1bN6$n3uxlJpGsQGnho5-{o4a=yHFz6n7Osf3@2??_ud2w%$gX>+xa)aD+87f%>R zMuwoQAn<>-v;qg%`K+p1ykoj37ucz>(lg3^hNXJx_bIRV;d*UsSVHUXvtxHKmL@<; zak842Hn6&zyY8mlef!4B7HJIF!|@J~3_SVk^4E0V+BPF*IxR=*N7>H(|9byx7b*aq z+x**?7)L&fod>kc1P>K?VS(+3rcAmycR_*p%mmIY!dWWF$TazNDPOc(f6RB<8aP>< zy=j8KZzFBzNXfBBDT4g+xsS9lIreicW>;wo>oPF*%<{KBTA z)^eXvolt6)V_^BhNcvWjE&MG3C)z}Kc;C>D6>sM6NH5IO-u6r_*ZoNe7FYEOM-9Xk zox@d{BHVdgY-+N_>{%PJ1<;P2@WXm;@I zJ9vL~1R5H9rk2f{86dQ``2^wh1$f6GhKkAj))_S{;we+dfeCxagoiTMK4fanq+Y%f z0P=|RfpNK;!lpP{H5h+5YS$tGIQD!f$Uv(8DC09d1_I&+8{!%pyoDfHf6r3;6nSGz zTB3n=pn4F|*9Pw^z#|38z5;@N-%x6f$ae-;v=2v9#g$p(PJ+L0<4#_k5AaX#(ll1< z*Q630ZHQ@MoZ7mg5YdJ+C{rw=8=-|Z%SIc1LK$YGwX&Jox$XhGccIprz$?q^PHMob zHQ*IYc*SXW<>}O0Z({J9V8o5JgiSF7Gz^cPBbwI|JSG_G5U7G;-^wr{qE*kJVP}}u z0-}Y0=o=);Nkf@c^uV>yx}Q*H*(mjj9^=iaWwVjn{CzLeV(7wMVeGYG>{@s!S+s#T zCK>DG?M$7IGjP`)&@}!YYrsXXj$nJ%(ObkD!Aj~*!4O%hAB4UmeB_AWcwNbl9^~d! zc)WcoKOl^+pI)-DuIxb_y&RrA?#Ny!s3Y~99=h@v9`Ch`&U*S%v;!XwPPL{CDJ~7un9IK>? z;=$4~4Q>*A8;d%b)_pwvFhM|id6egJSR@`~am8Lb?*e8_ z?4c7}W^u9P9LNXVBSSj0t5xsF)q|5oXpve3yQ;2yGm3xhw%$9BYRx_94SJ~6(LI~5 z5!D)Lop(GVZP;f0LxoaEDk9>zZ|GjJ)gpwIINZjV79`)ghKOwajQuQiq^ z?*01GQ2F6$+Fg=Y%;hWJc0Hjvp&?Uvo3FgY?*dSibe6HU&irSr)rSeNTKv7TLvKMr z(rRLV7~Zdz$E&nx6s^BWbnoM>^;Mk$U(JQTvpi1QSSMA?P!S5z57ApbVyxZSsU}Un)J*D?2yeNh4WzvESE~9GTyRS~;hHAZasZ zR*Yqz7Gd?VhME4=AP607C2RSg^77X}?sY@XFipO4vU{2+k*e?E)ek6|dHivOSt?dX z4-HkP>@alZShI{52|6*C%}pOem37i@>t-uV@3pI*VLE*sG8ZMG!}z-fM${h4^_HMs zG9AZ2f9+GAVM8%>Rrg4L^hN88qi627$y`{PlgXZQL}(1*-rUoz{^&24&A*4LHlvKD z2)YZE#ror6BcC64ak5ArA2>)y{{&wnOZk1;>%1lPxp&90bbg*lLtToZN1EQ1wa{Mo zW037%8k0quQ^X`>;d||l5Q13H>PM*Bq!bJr_%-i?qm_KJaF6&8t>&ob!{ooefl-!sIq7q9j)K%u{Lfi0i>pe;1gPCe&Xjdo2zW?a97Q z*s}22JWU=KPrFa_gy9X0zbC13qqfb=wG%_>QDM#K)!C75B>Pmn$=mu+RZF@42v1A# z*Q;CVU*-R4k{GhodVAJxmNIi38oj%=o}YbIm?3vWmLESiX_0d!khuie?3tu0>rv6+ zTu+UT#|{{G_pxs){knwtPtOGd@6k#;HR(|5Pi}=|a$@ECr>n34J_%|l`qXpx-&8gm zqxX)FkKN`YNJjWV9%HQ6ZAalGr>NpPOeAhg71(|+G zwEE+}|9LFk&w^@2dTm`XEc2%15~Z2lS!77OW_QB}9SeGst8LQjjJCtt{AhXlfQcKg zm`YreH#A7um;%pFEQ}S24KZf1$Fb6#P$uvp(sRq;BM`0{JD0#Zyq;_a&;~e8|I(hXy3=_ zmBSXUG*q$CEQpyfkflCDOT+XDu22kZ?7A<>jS%rCU1ULyhEb+olD$jDCc{MSK7k1V z)rQv!6NDj8HIETUM#a%2O>f1%C4H}C=uuv=J*|aC8}&j?iH}fh1snU%kTztpgB7;Z zrKUeunm^N>KHu!mQe$TbHZh;OKW?P2$moilmcp9Z=Cn>1D)CK^WV#fAw! zkzqlpI&N}o2m6HV1lll{&s5D5yd%@+vfZl$W{Pd`#aAtcC5IYlz2|_pm->vYFW#nV ziGm)JdSJHtwi>jE^{1~?*iQ34rvdp79Q~P5hHNLi{kCmrI|lhW?|FoCi^9o zt;W;#6w5rlz==v9CTR9~XT0>MBmv0cm?#j4!iREuI<&fl2FzDb*-LV&El)wMjV8N? z^X*1I!jZ*U(lad8clPb-<$1a~n$iNvuPuTyv$`(EztJ@Tpak;@hp|LN}8pp=Q47YrLm- zpj#?GoPwy@ip@^rw#Z*9*AG@7Nu4LIqC=H?P zOP$pVl#vT3s*V~(AezvQlQ#L$9GHKeeMUhRM)zD|zkY_%{#D#9DRAczujAcyh7O1z z9!zFiUHM~z`EpS<*`t;b_c8Udv)Xo(&BIrzA(V|(!mq8g(u;*I$vVzMUL==^iW`Wk z6T)NikEsTMp2)`>b*9h3`j`43LKM#N#m+z5RefadF>9N(oqqr@ZIu~6VICS4e+grx zPB?X_uE?`PfAks+Dq1ZRD`9`8Cv8T>{X$fV^@=0gKj`tLuS?cC7d(T({J;h(hvN*E&VCn7o4P%Q` z{Dx(%ZXWnbiko=j*$PYf=6u!cD8qfx>^=%YN4OUFc6y1MJU$G?x`{urw8~H{!?k_H zqEv0A26YGJ2ia()ZvMJ0L&i|Phx*5%^Jlr3F#b-)Zf$ldvS>2dHcbCw+J-EzbEq4WU|dQ+N|el7tSN4s$W?NW4@1bSMe8JPxpn&Ye{}X;XdhVj|G~p_$4~% zb4ip)>zHK6l1>sY{d3v;aRWLh3Zr(HVecu>9x!~&T_ow$(r%=(qcEy>%NPjSdGUME zjM&rQBPWxxr7I}mWh%ddWdC)&Zhh5b*{T7(>_Gi!mDe(Vc(c-m=rv8J+V$ECB+oEN zpX5{X8E*1KB+W%OHLo8S!*^38BFow+Lw3mWOA6@H`kA9Eh{sJC7Zc)}YCP*Od>P@D zd;yHx!Lj^h<+`sZ)-8~&EBE8+^J!BOs|A2ZEzUy&qq*CZ>+|%gQ;@SoEwX^=GAcXG zcedCNUqA117jK9xdMO^StDu(6qV-F~?hBPL=B7O12aI+Sr(}LZpr`3awjvc8=(WL2 za2jrHnq${UtCk>h-X%=QWSI3P($n7ZKNn7p%lEqn=sz}YD6ek5Ci3nv94yU@3Ni>2 zXo$>?RV??YH&TC+Z8G%&pBCzoTy4sOd$k@35-}zZzc7G%vO7U8TC2G1*+6~6(7r>~ zTY-j_+LJ-^j*w|5a^kN8HojWmMRfLXTyqX_uQEuk1L8|D^=8vKyz zi4wHWUShg677{^!G3o^|{+&ZzD{X*up8u8@q2k< z&Wn71`I?3jBaplbKT1?Eh+HV>wZCCh;~WMvqWt^qE;1<&jinbD_iss47l03&d@f_u z6-LO8NXzL%8|w=D#h&*-P9?~Vs}?1d(rs8IHMZ;qjvfbaf~EG;O82Vm73}Lp#}%s= z3O&*P5r{KH5TPU-{p6bT9cF7f{XI#ik8eAcL4sGEk;jCs4cYoWWGjRg{ugW#0gVS_Q*Zy{1Du}@msR{3LE-{h>3uFwJGt;JS@ub zA=#)eCf{<579IuT$`1}e-S}HCR`oJ%XUjbZmrYK&B>OPYjinh1wWUB8fUUqAUdRg? zirOeo9#M3I%5Jb!7r|Vr+)tEmLLQLU7e$k;Y-kpnR&C)kSz*A;3a5n?45gt;qD0+s z?{^~GR0UkLEAmjnCMrZZQ{sD+P2K?~M8;-3H@Wk)joM$TF6`InkhAP;`Mxm2h>5f) zZU~qfcCv-?q*hwxv!Z^Y-E6rtls{JdQFSR@62J&G6U+LO z&Bk7~fzXuQQ<}eJ+{cow{0hq%tw#ZL@LKY)&fasv0lij8ylR`ZKVMV5d^FcJMi!P+ zZbwyf<~lB16TLv2_6D4;%xgJxXH>Hv^WXkZ`rSR-v+m2Z?;I%|pUUYgKK6KZU^?}h~?ul9` zt+%^#PjP$zHc1=2Y5FdTA16<~SvHGWoy1O4*t(||tpqYr->=MzVQ=cbwp&o%N^{*^ zf`|g}XYpi-h9V#&8sfKebBZ=3&RLRG}$oKiV6H*Dc9rTvhg1=ca}7 zkA!-=dNaviU>g>*g7x^yZc<0HKN>LR-eak=exIw}OI{ioV$5*SW_MxM znfYTsU+Yu^4rsNbqGn6JT`mniSdi{MYWUf~+x{@7vY==(m-dj4jk+Oil5=iBbu@F3$H#~GbtMNFVR`IHlKZmxch24c)nny@H&g~sS&~{RK@f~B1<6z}QNHo2 zkJfXXyKtZer~@^8VyU zf=^VydC5U&M55~BA+Me@b~o-R;s*2$o1Kq_ns7<$6Hk&E+6Upb-#J1OqJVm*bb-3% z^3Km!(Mqci>|C&3m0q%0-e2i)413z!#3ipm%i$E8L3*q-b8JeG+%TVZS9XR1seQf< zSa~!q|hT@k7DD{Ukl#Fl#Fon7N_YpVV1)sGjzVUD31S zTY9+`%3uFkFcUPY{acm|n*)sK>_7^%pwM&-(IzrEM!Zu-84{9xGH5 zkmNp5Zr2hk@lp;RYL!}3F4)~ToP4^>Hilh6x?4yUtU7v~@ zKQ>MPmZB_pyx2wjtq%MG30a;6klOr%6fI}&aaxwQUztwSE-iWR(?H)rO`RdHz~@+n z2eR@dfYcd_CJt(UA!f_pt-#u;_)Ahm5zX!nUkQH{yz}f%t#H9UR$#!UjLLp-!u;nI zLcIRrj)p=n@c3Q)Mx}AyhEZ_@&SfQ;KfI*$n>8P91uarFD=uKu`${j#@%fne_ZYpn z3X30qd@xk-zVdFv&Fk5id|0;tFjNd96Z(1C!_+c3JWW3df{n__jX&kkQ*GCrAdec* zeiwLle4|oh`5%pyZC5*yU{!j$v0N?rrZDKJIB$A|0x*s68l)K}t49SnVGu4nZ|JMf z#!LFKcGom@1Se<~aYb5Dh6-gKFBXM$KVziakXX3Fn7bWyE2y3d{Cr>SPQd5J5~X`% zVN6tz%g{T8HT2aNzv;3-(_qlfWv2?&$qf`%GR5N&$8OB=5qE`cRs?(y98vbzwHK4u z6;J_8ec_ZI$z;O5k*q|uG5+gUc*Mf=B_&_&Zdr=EUXu`ta|Bi!4aaDj0VTjPc3TBt z_=n8M8$r6;#XqLd8!?Cq{~<#)lv0I&_lYWauUr+rLD$Q)Me4pQ(CwS*3em^Yd&6EN zyXS4#_r)ll#O3w7X(1r7_jB zU+la>lkv`=wUUIwz}-8!74n^*%j}w<7^Cy}N-4a0vBIKV7FuU1Wn$$iNK3WiNf|6( z6HuOGVSR$r6tklN;LAby{id54jv%JHxCM-ks-gX)Gi8Tfth5?qay#Hl!|f(!yb$R& zu6%n#)ujM8Z{7CaPsIwW6$cj_hu`Kb83W%RAEbCY2Xco%J_^$d>|6VC=qR*0V@rdz z+8JG`@rsI|e)N+9JMF~y^|Yr%`Ys6U)jl6~mjL#vGoUBD47~9k=p5EmGr(ROs1lQv zCf#C|-p|fZO4sRQ0r-v)KGP7e>QZehlm3(3Iw5Y+7X{tcoNOr6(Z%uld6m-DROu3@^q{+7gu?8`FZ5cWPm^iEL8b3OJ@&jWI`39#$uwjb34lLAye%gYOQ6D zDkyO5E7UV&je=5O(^S~8WS1pV2=sV+QAk~f5_To{>{tZZIM1smMYac^;b`?xCais| z{0mTWxu-{NTUlU6YOq64GGb&XQxx&(0Q-v43RY&<@{{Sc{Lk;56W2cCU5s>NRSL7o zj$Z>RQu9LPalchEJI96&lh&rZ-ExATL{>?W6*vksa{xzkvAZaFekdTl_&s@5;^j@d z$QWm!-gy7^Ga=xfh?wdATD z6JExx6@~xn`6iGeN877HNd}qh?T*3?9h{?|FRL49^CNE(w1bkh^M*c%3_I%|uNyF& zBowaLAcJDyyuG??5Xd>e*A@Q3oeF(4&gJXm& zWYGc7*?r3P!SN~$!XNY@+{G4ZKOt>&AGDz4A)q5lO6Iq71?`uqOu+K+O+{ut2TULH z+7&?EkTS9U_;o}tT6d9XJ#l4RtuvJkt1ELtgEq{0y6_vz3<&6)pN=4Aqmp6$KBE26{ zJNjRV85EC#!hMYSJ0!!oWatFm$vY}gwG&=7R!4tO*LwSXArtpcGKeHvx5`K1#V9w- zE$IOaL_PT|JiVY~MblCLFksi0?j*71Lg(UiRXfRgnLfodhN!exl_*V?Pkt1q4yihV@)Mak_9;p`4zJzEzMmZDYv49^?KZObKH**=+Q5mH z<~a*AfS3LSkb9=UN$H%&D^wm{DAs5cY3A#-f4OuUV`4JT{Pwo4>@}f=u!pKe*4+bX zaE>Q_S}5y3sFT22b7IJh1estYfAZ)tU6d6p8NB3w#lY32TW{L++s{@diEYL@_B?Su z8g1JpxWrPKlqIgj!WH#gchp+!O}y3$U83sNW0|)N>$_WvWb#4qo&{aRZ;VOa{I<2Z z&>S$^^r5v2Cr@y{iXh|e)gdCYEdl-kFO90+(6T({E;|n&-0WA`D$jlXMH=YZL3aRz0h z&BpUUE1}i1L;L18md2#?HA^RSY%BLnuTDF2Z7&&8K-aV(oY~IE@o;C z=yeWfK)N`K^38zjdU+6UvRrsV9KU2+CFp`T4+tceFs5k%r#nFUU~6{p|8(SPcCd{* zf_(OjRh?eR&SOKH3E8<4SbTMoOc!@j&Db=lA2LDM^nshpTJPWE)L5#b&<@kDPEqX= zdMJ&SsryndOBA_Ncu1`{GrnAtDmg&W(BW0hM`m_XtertGG#JIs6vZeydHGR!_XQv2 zniuzbW^yAG#F1_OXmCy1P(oNJUBv6=*qv8k)_W!2gYU?lF26|9C@A(}!XOckj^+@o zVbZ*KDxlKUbhz0)4mS4p(Mp$mUC6H%Q0iIUB-rf9dNy&LK)i*7{JPDdjnH!kxuV5_ z!xM1SSGY0<^2W@e{0{a}ci$rhPa^}*FCRub^9ww~y=uj$mtwRUIrVVs6*-sI| zX_&ZWg!d7E97OXjp`ilN@$9Z~s@SigS_`FOE4~V^fW3NFbb@Q0I-m4jv>WZj_E8*j zcdu5P5L?8bI)G7q%-?nM=ngMu-ofMDR~8{r@!TiH)I-PAbYc^8rwboT<2yV^wX*np zM6mQeTD5cdja@!8wyf}`CXhw+OE<(9@5WW#YCZO^eJjE4&e-~k1jq-$WBKk8e}4dM zBl;zt`Ky0O^nJCOc`CKyynKU~!awhUOdDU)E{{T1co8)BoSQ62E>)H}iX zLH2n2Jy@k*YL&0uG*t1VZ9r$p0M~sY7f&U8_b4c*&UtFt%8DuKRsIeKS=xs7Et>YU z_9EILM5XLWTf-f>|A4BNez5H$ezhDrpqVk;LK~MGcj*7Tj%Y_|y_W4~A!a~7v3VYP z@gUT%IwJY%V87{op_!sd3Gefy${q!DU^R9$W1Wrkk0rye0qO@^Y2uHpwJQR`pG1vJZm?XvW1Ih{ zZ(3b~LDQG6OR6mrPj~n8ffSVw?5QUVWt#}fWyX9i%^cw%NE&w_4L?X$%PL9Syx}*g z`I_&(bVt=)q0~zJ=iMoeyaP)3Y`LW()KO0{|5cEEZrrhf*2D-#J8?AtdSBRi?y*Ga z?hO5z44ISNRobUCJJT3?hLV8ktG~X8e<&>ni(WUUkaib#OSw44FDKyvrRQLY$=L{m z4)0uR1(4PctdBm&^#xV6$MhvX8OSA^hBhg!YE%7U#ar{Wet=eCfrc1c|1;U3pt2vY zW2_crVkp~9aholL4)1qwtgcvU0%#TC&Cmsk<-y^r62Sie3e>By`X0)tIbf;RyP%Q} zD&hB8izOX1PR4wY|ED`kR`NddDKubC)z8 zbDmS2+lF6NvS952NxO@G$eovT+M^5uZ>;(#^m=jt@5GCu=*||!6myZtlF+dXvAg^* z@?^IR|LQM(l+y04#nD(TzmqZi$r2zUuID$A%&R`l!z6|juQ|q*BQwRZV&8>st_J<3%mt?w9cz4J9=CCIQ?=T3^>EcMQBPr(F9Wtf<6ImAmRu-gnWM4ESrW+S{D zI_P;*WUp7Ww_}H6B!@tccV%3xFpMw$xzy`JRx3D#RVxk8S35&)kWN7#4~;ThW-5pZ zb-r(}ulpPTXkh5xu`R{RWD*mP=^vbt@!xhhK*;27!)ft#@9!S&5iHAU|*~Dx5}k<^LceW6B5r5@sn< zrc0=V1LDV&O^<|+hW9xN@TY&<03HN#Onbw@RDV@23MT7u%40vOE4&n`r}$?{vwuae z>60zbT!KG@_*E#-0MhRmD#7UWMtZS2fJ4FZV$wTByFXt|lY7TEi(s+eU~A-S7Vhfl1sSehFYn6j?`+`IiUkd#XB(T4QG*L^I?0n9pf9t zsB5b~Ge@ciFO7EZekr|G*G`#}VQxx+L<2HeKEIi$P<=(-l2V?Mz3CBuqi0=7ge>3D zO*u{fB{+=U&djKEBKOZbBysODBbf;m&5GkAk)|F?2i!cewr$plh7nMCr)8``Mc6k|LvC#J+Rl-I5pTw?V9gmiOu z4ksIhbF3EHeRbITnc=^#Z@dODILBjBeye?Tufn+_DzjZ~dQ-e}SmuOL_bQ+5(fkNB zBk}srTnF^eJ9Onzbo2Pj^^eWu-XqxfPb7;1n$a-v@N}6e^kSBs-${*O;_8^PlwMOq zF_r}p0+kWG!B}bHS2Au8XE3ga1u9A(!z`ieE0ReS{}-X*P%7I^vELmuxPU>PEj@E~ z3f^GHSX_d}UIAbZWAQlRBWE+gAe={vFS(<1c*IB3GB)(_W*A;G<&#pIS2tE=39?Wj zn|$>$aMynA(vN_QM?#_1b6dl(Un$OBL|Cqb9u0`fmB4{r zVq}yhW6(At$i5$<^ZTV4N>A^z+*#JD%@&>!1}>b7XM+u$gQ`=5ehr0^Z9hO~FzUg{ zcL-CEEn8dWcW&Wa$6bqGE?Sgowb~EHAd7mRfVthZmBILSQFeZyz+V0g3&SqVpVv_ZMA-TouS|{OAFIXi^i+I z&VngseD>}VFQR?pb!1{i5`Iv$Yf<`4*=%ZClgY~Q4m9!7<(*YGRHRzJEnZmkF(H(3N{1D|#)m4X?g>AnC6+ zmWHs5AU~V0)HPOh0veCP+D~+O)w8xGxpD61I$No*u1)!s9bV_+$ww|3I_m`<-so3I zJ@iejvAXzNoR?pb!?{phKbk)}M3GT?1?NB*-zj{J{`0mY#}Qf_ta3t7DcVhbPd$xu zJjz85m};Gy^lqRzj4_nP@WMaS2l5@W-S1W-h@M1O!b37Y{(<`+F3o1K0rH`aq?|ifVm+b1xZL zTSoc!1OJOCkX-4n3Sn+ak>%tZ)Va?wBb|~?!TiUXPvqENGoC&5P@&=;z@sd5Ae{x@ z=EtVrDq%Y83jLR#H^3O?1kzE@AiKd{awq#$x6(M=A4|T4w^=?#rC3Z#i|6(C zkyd~BD-#lb>KGb(yMz4=*D`IaMC)+Z@;z0tztMCX>8=6O=CjeZ1%=Mg;_*M1*0@B+ z$0gg{;a&TV?3#=>R%$nvYtY)MZ{XmL(%9k)vRe2CX!Yy-c-xgsu>@-h^ygi>P#Oqt zuJcB1(Bfa42P|3Sr`;zjNT`FfG&SnGEf2|BHg;W5jETDRlhUcS*_j|u73vI2nsHgA z-PKwot84asGbm&lPqw?z4HbNroxZec^YBOKC4F1F^JcQ@dTtYFNZLf#F53gN_DLa^ zr1|x)%zjClg<7{-W>mT!N`m{oHhm6>=m8 zZ?*uMu*{{^_jw68fDq308eX&O3t!SbsvVxdS_PyG{97g|(jWycMUsuP9G1FvOd^e0 z25C>OA)VQ;>K|fpB~V(LJ+%ygnQ@XGU|L6Q&t|D68A2Z_Vh3>OjPe-f#t;0(p7890 z;#i8c&BiXtZ-4N5LG}*c4!pS0nzerAs>D&D{nEO$fb>T14+7(x8yj`#P%z{<2m628 zQ{+Gb_cPkDzupMm4<7&*D~lC2BV6Q?X{;<02u}lUV|y&yXzwjXJXkY@7kj#;ZG`=R zAg-P}w6yaq-6QRQ7wc)w!a<|%A;T=j{|2oJtT*suEL>9#uR0Z)v!7ISR{$+n9#%2E#n7pRVC3-$SZny>Mf z*cp&PdP}oEd9$kjn6>Ev=pfKaITloYtAF@cd{wbQlF4NAmD?kHh$xO+xgDDh#?!yJ zJHj|&zUt#>X!KayrcrhYXxhHi6=y!xf1IkVhe$IRtaw&GWij0>YD)#+M|7Ooj?bZ-XtA-yJKV6-IL-pGI;~0L%*G=+n6{AnF%wkZJ2| z8EOjTsT}QDm&*xVwC+PwC);;~`xAHSYF)){7JxZ7-U-mA zx8NQ4c?K~wbw^GoxKd=3Z2!TqItLw7aRW!4a&J=m5SyR7WR&gE@&j~p2F&Yw$jHX( zFKQE!Pf=_A$BHEI3eTt7iHD`W0OwZEWpuHq6_vSWAI6uAS^i8c8b~=Ahv_^cASnR1 zD(XslnR`}p$h@GW&VjM$$TsGC#HV*$L<_|r z&sm5?f@uoT6h^cLRNW=bWyl%*SVQN4^OUWIgT?p87QLEDfysNhOSrgXdy2UUb;TPs!7>S-Dmg}& zr~u#Hx%nzyzwLTSxyd_lY*(Z|AbP-c7%u82xMOmKW=~}t6uUnea<1!O*%OFQ6>%LX zOow0#2?L%2w@;nla#)-NG-ffkwgDtj)91(j8J32ZEN_~645h2ZT(jBYXt3(I!PE>3 znX5SI?HpvgM?V^JR~#pmWO8;g!n)XhjvfHQ>^xe#*5ZN0Y?4b~bi4s{^c^8z9sF*R zfroaW7WE1keYBn6G#I!U;va=3y=|3PgC>(Ef>)5xZ`DeVuvDV;qYb#A&-Q!_19YN3 z6d|)|sQ9vyVICV#JFEQ(f{o_6!U-9lK|$jPj@Q7|m}K9evYl81L~~X9*gn#{;5e;Q z)c{O|)=K!GZQ~9I5TyM|H!3K8NVc3Kplh#$@~#;bfeu{e z5NPwd{yYJ$s6?$g7k5o$3|i8QDO&k!ZOh6kb6@?MG;JJ8Bdk+<`8D^^^o1~+Gygr9 z@ptXtr@u0Hg_Hy-{1BRFl*NsP3%Aez%9UtqTZi1prz-#G`|nrR>I9~mmoQnG>$}5s z6O6=sj^vQ-l590r6i=?6ql)fmZsd73g|I>hz%Bf*mexKxvv z`A10C#Tn9?Mym&36jAx>!7C^nyUhoSPdk<-kSv$%m)DnuT;rbad6vHWKTZE^@YVWw z`u+O?mKhbJ(Jg$A`%nCrZkhau=vEX%3Dta_-i=FPuOju)K#zlBhht7a=ohpSN4TX@m(f&q9EJLN)d zwn(MDvOAQ_Txx|n4>1(C9KN3Yh>-fY9(crsjb}Vi?%`d9U&sutS+Hx7bi)7a+g-Ss zq`PEZp!%ui4O#UO`dBUfcA?enygXsH!aby`J6diOQj>1BpV%I6mMwym$_^;4r*W>A z21^2a_qE5LaZsdBo2oyoy0Jl*-z`m2xaPy+_SVjg&Xvs8X+N4bP_8H7gD|0GDSOO^DO;~Yg!BepP#|r(e5e&F+4!0?7U9A0a8+4Y0in+Ex!D`ivh7$E|LgFRwY_5>oU2 z-4uNI7Sp!CFoJWX=J^c`%LmnQoH$anZ$w^gv0M{bbzbZSf6!6qHd~9TX?Js7p8MyA zGjE^k0T{aG(t(hg_!GZqF9n-i-|2AB6zz!}DZ=$yu6e+`Z{Jg8a_x}Jvd7YMxE1r> z(((UxK!>lO)a~`mIJdM{i?T6BTD@=o(RCk+UQ0}HXAgLldI+Xc=>NQROx~Lh=?NOl z+Uz;}_jCH`1xHKAkeYX|!Y~jOVtu=T2&~sXt44bUro`C7cd59uOt=47{Tly3(P^}P0Qr(KpbLUH{lvvr zw$BZMWotWXM*0FDMD(AWl%`S4)NZ($lwOo>6j_ZQJoS*?f3EI)yIbidSsL{GgY{bQ z;lD4OvAW>?kw3-?NO`_fu^nlBA;VI8-^#&YV`Q5s*Q~srPLee}Lu&R4MMbBVT0NBa zm6ju+uC}-HV2W+irYC5}9z4p?&*P7Y?8XnWDN_d7hjQmC`_23QyXb_s11pOVO7_NY z>|L0^Xz12j5fA=)xkLTjei8%lDOy1uaB2ao;@$Vb>Fw})NaDWYQqAY7^4U;PA2QGf~UqKh@^|8 z>q|pO@Zz`!hj7*BB@ouGl$lDot8ZjY*vUex-t@!1x)Fk{HHd?5jb**oAvKrw=`0G$ zaiDfgcMUa3ij_N;ygyVO-y6KU#8C23Sszvbb>^NE2`MiOe~1a+qJ9uTAaeGB*AUfC zz7*#w;Z)amdF)>Kju2OKwVIK(XB6`)_STwL@?H6V0ux=(8M@2dQ?vJ&kz~Wb>W@Rk zavzMoI^mD0DHri`jIoxhle6%ZU~*7omL%L!fY<2=W+3&_>}xk zj*Hj@eb%gwJJa(&<()OL;2mGL3p~ZW6qsM->3{6o6?+#fLu^LCG2bw@m6q2yo)1;% zH-N}m2=n4hS%(PNLHXKdT>B+t6|b0S_I1cu`yc8ZuL7;C+5Rs7YOi;}$SDu6X{~jFBlwEj`gB~osrSxhj z&nBUrg==b2H%Hf5`b=cV&trChTbIJ@IC9BVNBBVmxtr_}^U&kAO4U&C z-v2?!g^I6k%H8o6FTG0rd;UlHvGNM8v^>8FsrC}6e%t;%Gb+iXv_9x93ngg`Qcv(- zll`3e=Wx9B{!?_+4cUmqOAhO(-Fp|l!}a}FUN(tt_3%vs#o812pwGDPhtg*@!j$-v z)soFh*9oFqA7Sr62Nwpqz#tsY!c$_Auk(>_-@%^6tnX*Pm)|Gpb+Ef|<=DFOKS|nt z7H2X~{SvH+F^=%!{v^X-`&6rV2W4q0gCNKJBkU9gD%Z^xb`v+(T5Xvz_MP7Q9LPtq zF~7Sn|EF8_+>&HVGkn zw+&-daYIv=Gobilk{=D;Q}3V7`=(swNAH&Ar`N=L-7gES)4r&P0=MZ%&_4KyG^^)b zYBgc)GYZ?4RkWPl^9n^35zfGM)h%kHv?7$5q^7RVs4VXHv8N+esqI-v6KWzQn7kYkjnWfHH$J zCIl@3s?1@E5}++V2NdPDwhS^4N+|&ntPli2WRy{ZiWG#~>#ep|nH7RWrjVp50<~NP zBtQm)YY8Ng3b90Z-{C$hbiIGzt@UFa*2+5P?7hGJP5YbnK7?vZZ|dXb5}Bpk%%kW5 zUJGSDGNj7gp(qqvQ9M?4463Znyj$;PVTZqRm50f{18S+FOMF2fgy9DAe@U0k2^woT z@O6O~jXB3gbHseKe_&QfliO0S$EaFccRV>w{$axDbE4dQ-do(u&ipeaZSGS0>H_nY zxC?99uppldh64c~Q;Q(~?BtQ(Le{ipQm6CARSp?u#_%ZV$Ed+2F!*WbRl_xZ>WljY zQPK=Vr;{7)FcI{dmn?a3aH-qRx-OAswn^9a-Rr}QYI$al^JYmg=!hf@l9qboN99DT z&M3Nu#E`C8Q5BLVj4(7zoazR~#g*jAKJ3hD5C+LK2bsA6Wp&gS&iP?|5v=i8il*%~ ztS9K!*k)8sehet=Qua$jP_jb74DFHhtZmK3p~KYTY@OC{HCvXyq%0u!JXt#-KaF?A zVhBUivAyD*qVE!Q4UnCovqp3o;8&1$2; zEjc=^pqp40!KGG>+p8LliVTk=*KElcl$|V#5$JlqIss}#`L2{rF`h9GJf7Gq9R;)( zmtVv0Tx1P<3hkNzf3dcVF5Z+RRK6cmG>%HNBHIu49<~@uksbp8OiIIgyDHtga?*@5 z*Dft0*HZ$Nid4|bahaWGM?=+ZbUEc2oU z6M*a_u=&sWDN27vQ{ACLb7Y;AmgXsZ&YC4M9lKtX?LWKW&0}i*9xW>@x@(JeV)Ad z0NnW*$u@Z@hosb5j2z4wojRXDunEt8iL>J*J71304Zft4%$8DAm7(XgZU!0YdAlDf zZ0=auJ~<)dAazp_#5c`jm3A_ov@!`!ci^sYF*r zZ;KvGKzF8qLF>ZyM-+5*HwA!J^eNnT̩r!1Rn>V2A%K(TcZVuN@2$3U>4wX2Lj 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 VN5WUZAHbjYf#d&E|7rNe{{!m%(}Vy3 diff --git a/site/assets/javascripts/bundle.79ae519e.min.js b/site/assets/javascripts/bundle.79ae519e.min.js deleted file mode 100644 index 3df3e5e..0000000 --- a/site/assets/javascripts/bundle.79ae519e.min.js +++ /dev/null @@ -1,16 +0,0 @@ -"use strict";(()=>{var Zi=Object.create;var _r=Object.defineProperty;var ea=Object.getOwnPropertyDescriptor;var ta=Object.getOwnPropertyNames,Bt=Object.getOwnPropertySymbols,ra=Object.getPrototypeOf,Ar=Object.prototype.hasOwnProperty,bo=Object.prototype.propertyIsEnumerable;var ho=(e,t,r)=>t in e?_r(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,P=(e,t)=>{for(var r in t||(t={}))Ar.call(t,r)&&ho(e,r,t[r]);if(Bt)for(var r of Bt(t))bo.call(t,r)&&ho(e,r,t[r]);return e};var vo=(e,t)=>{var r={};for(var o in e)Ar.call(e,o)&&t.indexOf(o)<0&&(r[o]=e[o]);if(e!=null&&Bt)for(var o of Bt(e))t.indexOf(o)<0&&bo.call(e,o)&&(r[o]=e[o]);return r};var Cr=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var oa=(e,t,r,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of ta(t))!Ar.call(e,n)&&n!==r&&_r(e,n,{get:()=>t[n],enumerable:!(o=ea(t,n))||o.enumerable});return e};var $t=(e,t,r)=>(r=e!=null?Zi(ra(e)):{},oa(t||!e||!e.__esModule?_r(r,"default",{value:e,enumerable:!0}):r,e));var go=(e,t,r)=>new Promise((o,n)=>{var i=c=>{try{a(r.next(c))}catch(p){n(p)}},s=c=>{try{a(r.throw(c))}catch(p){n(p)}},a=c=>c.done?o(c.value):Promise.resolve(c.value).then(i,s);a((r=r.apply(e,t)).next())});var xo=Cr((kr,yo)=>{(function(e,t){typeof kr=="object"&&typeof yo!="undefined"?t():typeof define=="function"&&define.amd?define(t):t()})(kr,(function(){"use strict";function e(r){var o=!0,n=!1,i=null,s={text:!0,search:!0,url:!0,tel:!0,email:!0,password:!0,number:!0,date:!0,month:!0,week:!0,time:!0,datetime:!0,"datetime-local":!0};function a(k){return!!(k&&k!==document&&k.nodeName!=="HTML"&&k.nodeName!=="BODY"&&"classList"in k&&"contains"in k.classList)}function c(k){var ut=k.type,je=k.tagName;return!!(je==="INPUT"&&s[ut]&&!k.readOnly||je==="TEXTAREA"&&!k.readOnly||k.isContentEditable)}function p(k){k.classList.contains("focus-visible")||(k.classList.add("focus-visible"),k.setAttribute("data-focus-visible-added",""))}function l(k){k.hasAttribute("data-focus-visible-added")&&(k.classList.remove("focus-visible"),k.removeAttribute("data-focus-visible-added"))}function f(k){k.metaKey||k.altKey||k.ctrlKey||(a(r.activeElement)&&p(r.activeElement),o=!0)}function u(k){o=!1}function d(k){a(k.target)&&(o||c(k.target))&&p(k.target)}function v(k){a(k.target)&&(k.target.classList.contains("focus-visible")||k.target.hasAttribute("data-focus-visible-added"))&&(n=!0,window.clearTimeout(i),i=window.setTimeout(function(){n=!1},100),l(k.target))}function S(k){document.visibilityState==="hidden"&&(n&&(o=!0),X())}function X(){document.addEventListener("mousemove",ee),document.addEventListener("mousedown",ee),document.addEventListener("mouseup",ee),document.addEventListener("pointermove",ee),document.addEventListener("pointerdown",ee),document.addEventListener("pointerup",ee),document.addEventListener("touchmove",ee),document.addEventListener("touchstart",ee),document.addEventListener("touchend",ee)}function re(){document.removeEventListener("mousemove",ee),document.removeEventListener("mousedown",ee),document.removeEventListener("mouseup",ee),document.removeEventListener("pointermove",ee),document.removeEventListener("pointerdown",ee),document.removeEventListener("pointerup",ee),document.removeEventListener("touchmove",ee),document.removeEventListener("touchstart",ee),document.removeEventListener("touchend",ee)}function ee(k){k.target.nodeName&&k.target.nodeName.toLowerCase()==="html"||(o=!1,re())}document.addEventListener("keydown",f,!0),document.addEventListener("mousedown",u,!0),document.addEventListener("pointerdown",u,!0),document.addEventListener("touchstart",u,!0),document.addEventListener("visibilitychange",S,!0),X(),r.addEventListener("focus",d,!0),r.addEventListener("blur",v,!0),r.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&r.host?r.host.setAttribute("data-js-focus-visible",""):r.nodeType===Node.DOCUMENT_NODE&&(document.documentElement.classList.add("js-focus-visible"),document.documentElement.setAttribute("data-js-focus-visible",""))}if(typeof window!="undefined"&&typeof document!="undefined"){window.applyFocusVisiblePolyfill=e;var t;try{t=new CustomEvent("focus-visible-polyfill-ready")}catch(r){t=document.createEvent("CustomEvent"),t.initCustomEvent("focus-visible-polyfill-ready",!1,!1,{})}window.dispatchEvent(t)}typeof document!="undefined"&&e(document)}))});var ro=Cr((jy,Rn)=>{"use strict";/*! - * escape-html - * Copyright(c) 2012-2013 TJ Holowaychuk - * Copyright(c) 2015 Andreas Lubbe - * Copyright(c) 2015 Tiancheng "Timothy" Gu - * MIT Licensed - */var qa=/["'&<>]/;Rn.exports=Ka;function Ka(e){var t=""+e,r=qa.exec(t);if(!r)return t;var o,n="",i=0,s=0;for(i=r.index;i{/*! - * clipboard.js v2.0.11 - * https://clipboardjs.com/ - * - * Licensed MIT © Zeno Rocha - */(function(t,r){typeof Nt=="object"&&typeof io=="object"?io.exports=r():typeof define=="function"&&define.amd?define([],r):typeof Nt=="object"?Nt.ClipboardJS=r():t.ClipboardJS=r()})(Nt,function(){return(function(){var e={686:(function(o,n,i){"use strict";i.d(n,{default:function(){return Xi}});var s=i(279),a=i.n(s),c=i(370),p=i.n(c),l=i(817),f=i.n(l);function u(q){try{return document.execCommand(q)}catch(C){return!1}}var d=function(C){var _=f()(C);return u("cut"),_},v=d;function S(q){var C=document.documentElement.getAttribute("dir")==="rtl",_=document.createElement("textarea");_.style.fontSize="12pt",_.style.border="0",_.style.padding="0",_.style.margin="0",_.style.position="absolute",_.style[C?"right":"left"]="-9999px";var D=window.pageYOffset||document.documentElement.scrollTop;return _.style.top="".concat(D,"px"),_.setAttribute("readonly",""),_.value=q,_}var X=function(C,_){var D=S(C);_.container.appendChild(D);var N=f()(D);return u("copy"),D.remove(),N},re=function(C){var _=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body},D="";return typeof C=="string"?D=X(C,_):C instanceof HTMLInputElement&&!["text","search","url","tel","password"].includes(C==null?void 0:C.type)?D=X(C.value,_):(D=f()(C),u("copy")),D},ee=re;function k(q){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?k=function(_){return typeof _}:k=function(_){return _&&typeof Symbol=="function"&&_.constructor===Symbol&&_!==Symbol.prototype?"symbol":typeof _},k(q)}var ut=function(){var C=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},_=C.action,D=_===void 0?"copy":_,N=C.container,G=C.target,We=C.text;if(D!=="copy"&&D!=="cut")throw new Error('Invalid "action" value, use either "copy" or "cut"');if(G!==void 0)if(G&&k(G)==="object"&&G.nodeType===1){if(D==="copy"&&G.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if(D==="cut"&&(G.hasAttribute("readonly")||G.hasAttribute("disabled")))throw new Error(`Invalid "target" attribute. You can't cut text from elements with "readonly" or "disabled" attributes`)}else throw new Error('Invalid "target" value, use a valid Element');if(We)return ee(We,{container:N});if(G)return D==="cut"?v(G):ee(G,{container:N})},je=ut;function R(q){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?R=function(_){return typeof _}:R=function(_){return _&&typeof Symbol=="function"&&_.constructor===Symbol&&_!==Symbol.prototype?"symbol":typeof _},R(q)}function se(q,C){if(!(q instanceof C))throw new TypeError("Cannot call a class as a function")}function ce(q,C){for(var _=0;_0&&arguments[0]!==void 0?arguments[0]:{};this.action=typeof N.action=="function"?N.action:this.defaultAction,this.target=typeof N.target=="function"?N.target:this.defaultTarget,this.text=typeof N.text=="function"?N.text:this.defaultText,this.container=R(N.container)==="object"?N.container:document.body}},{key:"listenClick",value:function(N){var G=this;this.listener=p()(N,"click",function(We){return G.onClick(We)})}},{key:"onClick",value:function(N){var G=N.delegateTarget||N.currentTarget,We=this.action(G)||"copy",Yt=je({action:We,container:this.container,target:this.target(G),text:this.text(G)});this.emit(Yt?"success":"error",{action:We,text:Yt,trigger:G,clearSelection:function(){G&&G.focus(),window.getSelection().removeAllRanges()}})}},{key:"defaultAction",value:function(N){return Mr("action",N)}},{key:"defaultTarget",value:function(N){var G=Mr("target",N);if(G)return document.querySelector(G)}},{key:"defaultText",value:function(N){return Mr("text",N)}},{key:"destroy",value:function(){this.listener.destroy()}}],[{key:"copy",value:function(N){var G=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body};return ee(N,G)}},{key:"cut",value:function(N){return v(N)}},{key:"isSupported",value:function(){var N=arguments.length>0&&arguments[0]!==void 0?arguments[0]:["copy","cut"],G=typeof N=="string"?[N]:N,We=!!document.queryCommandSupported;return G.forEach(function(Yt){We=We&&!!document.queryCommandSupported(Yt)}),We}}]),_})(a()),Xi=Ji}),828:(function(o){var n=9;if(typeof Element!="undefined"&&!Element.prototype.matches){var i=Element.prototype;i.matches=i.matchesSelector||i.mozMatchesSelector||i.msMatchesSelector||i.oMatchesSelector||i.webkitMatchesSelector}function s(a,c){for(;a&&a.nodeType!==n;){if(typeof a.matches=="function"&&a.matches(c))return a;a=a.parentNode}}o.exports=s}),438:(function(o,n,i){var s=i(828);function a(l,f,u,d,v){var S=p.apply(this,arguments);return l.addEventListener(u,S,v),{destroy:function(){l.removeEventListener(u,S,v)}}}function c(l,f,u,d,v){return typeof l.addEventListener=="function"?a.apply(null,arguments):typeof u=="function"?a.bind(null,document).apply(null,arguments):(typeof l=="string"&&(l=document.querySelectorAll(l)),Array.prototype.map.call(l,function(S){return a(S,f,u,d,v)}))}function p(l,f,u,d){return function(v){v.delegateTarget=s(v.target,f),v.delegateTarget&&d.call(l,v)}}o.exports=c}),879:(function(o,n){n.node=function(i){return i!==void 0&&i instanceof HTMLElement&&i.nodeType===1},n.nodeList=function(i){var s=Object.prototype.toString.call(i);return i!==void 0&&(s==="[object NodeList]"||s==="[object HTMLCollection]")&&"length"in i&&(i.length===0||n.node(i[0]))},n.string=function(i){return typeof i=="string"||i instanceof String},n.fn=function(i){var s=Object.prototype.toString.call(i);return s==="[object Function]"}}),370:(function(o,n,i){var s=i(879),a=i(438);function c(u,d,v){if(!u&&!d&&!v)throw new Error("Missing required arguments");if(!s.string(d))throw new TypeError("Second argument must be a String");if(!s.fn(v))throw new TypeError("Third argument must be a Function");if(s.node(u))return p(u,d,v);if(s.nodeList(u))return l(u,d,v);if(s.string(u))return f(u,d,v);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function p(u,d,v){return u.addEventListener(d,v),{destroy:function(){u.removeEventListener(d,v)}}}function l(u,d,v){return Array.prototype.forEach.call(u,function(S){S.addEventListener(d,v)}),{destroy:function(){Array.prototype.forEach.call(u,function(S){S.removeEventListener(d,v)})}}}function f(u,d,v){return a(document.body,u,d,v)}o.exports=c}),817:(function(o){function n(i){var s;if(i.nodeName==="SELECT")i.focus(),s=i.value;else if(i.nodeName==="INPUT"||i.nodeName==="TEXTAREA"){var a=i.hasAttribute("readonly");a||i.setAttribute("readonly",""),i.select(),i.setSelectionRange(0,i.value.length),a||i.removeAttribute("readonly"),s=i.value}else{i.hasAttribute("contenteditable")&&i.focus();var c=window.getSelection(),p=document.createRange();p.selectNodeContents(i),c.removeAllRanges(),c.addRange(p),s=c.toString()}return s}o.exports=n}),279:(function(o){function n(){}n.prototype={on:function(i,s,a){var c=this.e||(this.e={});return(c[i]||(c[i]=[])).push({fn:s,ctx:a}),this},once:function(i,s,a){var c=this;function p(){c.off(i,p),s.apply(a,arguments)}return p._=s,this.on(i,p,a)},emit:function(i){var s=[].slice.call(arguments,1),a=((this.e||(this.e={}))[i]||[]).slice(),c=0,p=a.length;for(c;c0&&i[i.length-1])&&(p[0]===6||p[0]===2)){r=0;continue}if(p[0]===3&&(!i||p[1]>i[0]&&p[1]=e.length&&(e=void 0),{value:e&&e[o++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function K(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var o=r.call(e),n,i=[],s;try{for(;(t===void 0||t-- >0)&&!(n=o.next()).done;)i.push(n.value)}catch(a){s={error:a}}finally{try{n&&!n.done&&(r=o.return)&&r.call(o)}finally{if(s)throw s.error}}return i}function B(e,t,r){if(r||arguments.length===2)for(var o=0,n=t.length,i;o1||c(d,S)})},v&&(n[d]=v(n[d])))}function c(d,v){try{p(o[d](v))}catch(S){u(i[0][3],S)}}function p(d){d.value instanceof dt?Promise.resolve(d.value.v).then(l,f):u(i[0][2],d)}function l(d){c("next",d)}function f(d){c("throw",d)}function u(d,v){d(v),i.shift(),i.length&&c(i[0][0],i[0][1])}}function To(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t=e[Symbol.asyncIterator],r;return t?t.call(e):(e=typeof Oe=="function"?Oe(e):e[Symbol.iterator](),r={},o("next"),o("throw"),o("return"),r[Symbol.asyncIterator]=function(){return this},r);function o(i){r[i]=e[i]&&function(s){return new Promise(function(a,c){s=e[i](s),n(a,c,s.done,s.value)})}}function n(i,s,a,c){Promise.resolve(c).then(function(p){i({value:p,done:a})},s)}}function I(e){return typeof e=="function"}function yt(e){var t=function(o){Error.call(o),o.stack=new Error().stack},r=e(t);return r.prototype=Object.create(Error.prototype),r.prototype.constructor=r,r}var Jt=yt(function(e){return function(r){e(this),this.message=r?r.length+` errors occurred during unsubscription: -`+r.map(function(o,n){return n+1+") "+o.toString()}).join(` - `):"",this.name="UnsubscriptionError",this.errors=r}});function Ze(e,t){if(e){var r=e.indexOf(t);0<=r&&e.splice(r,1)}}var qe=(function(){function e(t){this.initialTeardown=t,this.closed=!1,this._parentage=null,this._finalizers=null}return e.prototype.unsubscribe=function(){var t,r,o,n,i;if(!this.closed){this.closed=!0;var s=this._parentage;if(s)if(this._parentage=null,Array.isArray(s))try{for(var a=Oe(s),c=a.next();!c.done;c=a.next()){var p=c.value;p.remove(this)}}catch(S){t={error:S}}finally{try{c&&!c.done&&(r=a.return)&&r.call(a)}finally{if(t)throw t.error}}else s.remove(this);var l=this.initialTeardown;if(I(l))try{l()}catch(S){i=S instanceof Jt?S.errors:[S]}var f=this._finalizers;if(f){this._finalizers=null;try{for(var u=Oe(f),d=u.next();!d.done;d=u.next()){var v=d.value;try{So(v)}catch(S){i=i!=null?i:[],S instanceof Jt?i=B(B([],K(i)),K(S.errors)):i.push(S)}}}catch(S){o={error:S}}finally{try{d&&!d.done&&(n=u.return)&&n.call(u)}finally{if(o)throw o.error}}}if(i)throw new Jt(i)}},e.prototype.add=function(t){var r;if(t&&t!==this)if(this.closed)So(t);else{if(t instanceof e){if(t.closed||t._hasParent(this))return;t._addParent(this)}(this._finalizers=(r=this._finalizers)!==null&&r!==void 0?r:[]).push(t)}},e.prototype._hasParent=function(t){var r=this._parentage;return r===t||Array.isArray(r)&&r.includes(t)},e.prototype._addParent=function(t){var r=this._parentage;this._parentage=Array.isArray(r)?(r.push(t),r):r?[r,t]:t},e.prototype._removeParent=function(t){var r=this._parentage;r===t?this._parentage=null:Array.isArray(r)&&Ze(r,t)},e.prototype.remove=function(t){var r=this._finalizers;r&&Ze(r,t),t instanceof e&&t._removeParent(this)},e.EMPTY=(function(){var t=new e;return t.closed=!0,t})(),e})();var $r=qe.EMPTY;function Xt(e){return e instanceof qe||e&&"closed"in e&&I(e.remove)&&I(e.add)&&I(e.unsubscribe)}function So(e){I(e)?e():e.unsubscribe()}var De={onUnhandledError:null,onStoppedNotification:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1};var xt={setTimeout:function(e,t){for(var r=[],o=2;o0},enumerable:!1,configurable:!0}),t.prototype._trySubscribe=function(r){return this._throwIfClosed(),e.prototype._trySubscribe.call(this,r)},t.prototype._subscribe=function(r){return this._throwIfClosed(),this._checkFinalizedStatuses(r),this._innerSubscribe(r)},t.prototype._innerSubscribe=function(r){var o=this,n=this,i=n.hasError,s=n.isStopped,a=n.observers;return i||s?$r:(this.currentObservers=null,a.push(r),new qe(function(){o.currentObservers=null,Ze(a,r)}))},t.prototype._checkFinalizedStatuses=function(r){var o=this,n=o.hasError,i=o.thrownError,s=o.isStopped;n?r.error(i):s&&r.complete()},t.prototype.asObservable=function(){var r=new F;return r.source=this,r},t.create=function(r,o){return new Ho(r,o)},t})(F);var Ho=(function(e){ie(t,e);function t(r,o){var n=e.call(this)||this;return n.destination=r,n.source=o,n}return t.prototype.next=function(r){var o,n;(n=(o=this.destination)===null||o===void 0?void 0:o.next)===null||n===void 0||n.call(o,r)},t.prototype.error=function(r){var o,n;(n=(o=this.destination)===null||o===void 0?void 0:o.error)===null||n===void 0||n.call(o,r)},t.prototype.complete=function(){var r,o;(o=(r=this.destination)===null||r===void 0?void 0:r.complete)===null||o===void 0||o.call(r)},t.prototype._subscribe=function(r){var o,n;return(n=(o=this.source)===null||o===void 0?void 0:o.subscribe(r))!==null&&n!==void 0?n:$r},t})(T);var jr=(function(e){ie(t,e);function t(r){var o=e.call(this)||this;return o._value=r,o}return Object.defineProperty(t.prototype,"value",{get:function(){return this.getValue()},enumerable:!1,configurable:!0}),t.prototype._subscribe=function(r){var o=e.prototype._subscribe.call(this,r);return!o.closed&&r.next(this._value),o},t.prototype.getValue=function(){var r=this,o=r.hasError,n=r.thrownError,i=r._value;if(o)throw n;return this._throwIfClosed(),i},t.prototype.next=function(r){e.prototype.next.call(this,this._value=r)},t})(T);var Rt={now:function(){return(Rt.delegate||Date).now()},delegate:void 0};var It=(function(e){ie(t,e);function t(r,o,n){r===void 0&&(r=1/0),o===void 0&&(o=1/0),n===void 0&&(n=Rt);var i=e.call(this)||this;return i._bufferSize=r,i._windowTime=o,i._timestampProvider=n,i._buffer=[],i._infiniteTimeWindow=!0,i._infiniteTimeWindow=o===1/0,i._bufferSize=Math.max(1,r),i._windowTime=Math.max(1,o),i}return t.prototype.next=function(r){var o=this,n=o.isStopped,i=o._buffer,s=o._infiniteTimeWindow,a=o._timestampProvider,c=o._windowTime;n||(i.push(r),!s&&i.push(a.now()+c)),this._trimBuffer(),e.prototype.next.call(this,r)},t.prototype._subscribe=function(r){this._throwIfClosed(),this._trimBuffer();for(var o=this._innerSubscribe(r),n=this,i=n._infiniteTimeWindow,s=n._buffer,a=s.slice(),c=0;c0?e.prototype.schedule.call(this,r,o):(this.delay=o,this.state=r,this.scheduler.flush(this),this)},t.prototype.execute=function(r,o){return o>0||this.closed?e.prototype.execute.call(this,r,o):this._execute(r,o)},t.prototype.requestAsyncId=function(r,o,n){return n===void 0&&(n=0),n!=null&&n>0||n==null&&this.delay>0?e.prototype.requestAsyncId.call(this,r,o,n):(r.flush(this),0)},t})(St);var Ro=(function(e){ie(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t})(Ot);var Dr=new Ro(Po);var Io=(function(e){ie(t,e);function t(r,o){var n=e.call(this,r,o)||this;return n.scheduler=r,n.work=o,n}return t.prototype.requestAsyncId=function(r,o,n){return n===void 0&&(n=0),n!==null&&n>0?e.prototype.requestAsyncId.call(this,r,o,n):(r.actions.push(this),r._scheduled||(r._scheduled=Tt.requestAnimationFrame(function(){return r.flush(void 0)})))},t.prototype.recycleAsyncId=function(r,o,n){var i;if(n===void 0&&(n=0),n!=null?n>0:this.delay>0)return e.prototype.recycleAsyncId.call(this,r,o,n);var s=r.actions;o!=null&&o===r._scheduled&&((i=s[s.length-1])===null||i===void 0?void 0:i.id)!==o&&(Tt.cancelAnimationFrame(o),r._scheduled=void 0)},t})(St);var Fo=(function(e){ie(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t.prototype.flush=function(r){this._active=!0;var o;r?o=r.id:(o=this._scheduled,this._scheduled=void 0);var n=this.actions,i;r=r||n.shift();do if(i=r.execute(r.state,r.delay))break;while((r=n[0])&&r.id===o&&n.shift());if(this._active=!1,i){for(;(r=n[0])&&r.id===o&&n.shift();)r.unsubscribe();throw i}},t})(Ot);var ye=new Fo(Io);var y=new F(function(e){return e.complete()});function tr(e){return e&&I(e.schedule)}function Vr(e){return e[e.length-1]}function pt(e){return I(Vr(e))?e.pop():void 0}function Fe(e){return tr(Vr(e))?e.pop():void 0}function rr(e,t){return typeof Vr(e)=="number"?e.pop():t}var Lt=(function(e){return e&&typeof e.length=="number"&&typeof e!="function"});function or(e){return I(e==null?void 0:e.then)}function nr(e){return I(e[wt])}function ir(e){return Symbol.asyncIterator&&I(e==null?void 0:e[Symbol.asyncIterator])}function ar(e){return new TypeError("You provided "+(e!==null&&typeof e=="object"?"an invalid object":"'"+e+"'")+" where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.")}function fa(){return typeof Symbol!="function"||!Symbol.iterator?"@@iterator":Symbol.iterator}var sr=fa();function cr(e){return I(e==null?void 0:e[sr])}function pr(e){return wo(this,arguments,function(){var r,o,n,i;return Gt(this,function(s){switch(s.label){case 0:r=e.getReader(),s.label=1;case 1:s.trys.push([1,,9,10]),s.label=2;case 2:return[4,dt(r.read())];case 3:return o=s.sent(),n=o.value,i=o.done,i?[4,dt(void 0)]:[3,5];case 4:return[2,s.sent()];case 5:return[4,dt(n)];case 6:return[4,s.sent()];case 7:return s.sent(),[3,2];case 8:return[3,10];case 9:return r.releaseLock(),[7];case 10:return[2]}})})}function lr(e){return I(e==null?void 0:e.getReader)}function U(e){if(e instanceof F)return e;if(e!=null){if(nr(e))return ua(e);if(Lt(e))return da(e);if(or(e))return ha(e);if(ir(e))return jo(e);if(cr(e))return ba(e);if(lr(e))return va(e)}throw ar(e)}function ua(e){return new F(function(t){var r=e[wt]();if(I(r.subscribe))return r.subscribe(t);throw new TypeError("Provided object does not correctly implement Symbol.observable")})}function da(e){return new F(function(t){for(var r=0;r=2;return function(o){return o.pipe(e?g(function(n,i){return e(n,i,o)}):be,Ee(1),r?Qe(t):tn(function(){return new fr}))}}function Yr(e){return e<=0?function(){return y}:E(function(t,r){var o=[];t.subscribe(w(r,function(n){o.push(n),e=2,!0))}function le(e){e===void 0&&(e={});var t=e.connector,r=t===void 0?function(){return new T}:t,o=e.resetOnError,n=o===void 0?!0:o,i=e.resetOnComplete,s=i===void 0?!0:i,a=e.resetOnRefCountZero,c=a===void 0?!0:a;return function(p){var l,f,u,d=0,v=!1,S=!1,X=function(){f==null||f.unsubscribe(),f=void 0},re=function(){X(),l=u=void 0,v=S=!1},ee=function(){var k=l;re(),k==null||k.unsubscribe()};return E(function(k,ut){d++,!S&&!v&&X();var je=u=u!=null?u:r();ut.add(function(){d--,d===0&&!S&&!v&&(f=Br(ee,c))}),je.subscribe(ut),!l&&d>0&&(l=new bt({next:function(R){return je.next(R)},error:function(R){S=!0,X(),f=Br(re,n,R),je.error(R)},complete:function(){v=!0,X(),f=Br(re,s),je.complete()}}),U(k).subscribe(l))})(p)}}function Br(e,t){for(var r=[],o=2;oe.next(document)),e}function M(e,t=document){return Array.from(t.querySelectorAll(e))}function j(e,t=document){let r=ue(e,t);if(typeof r=="undefined")throw new ReferenceError(`Missing element: expected "${e}" to be present`);return r}function ue(e,t=document){return t.querySelector(e)||void 0}function Ne(){var e,t,r,o;return(o=(r=(t=(e=document.activeElement)==null?void 0:e.shadowRoot)==null?void 0:t.activeElement)!=null?r:document.activeElement)!=null?o:void 0}var Ra=L(h(document.body,"focusin"),h(document.body,"focusout")).pipe(Ae(1),Q(void 0),m(()=>Ne()||document.body),Z(1));function Ye(e){return Ra.pipe(m(t=>e.contains(t)),Y())}function it(e,t){return H(()=>L(h(e,"mouseenter").pipe(m(()=>!0)),h(e,"mouseleave").pipe(m(()=>!1))).pipe(t?jt(r=>He(+!r*t)):be,Q(e.matches(":hover"))))}function sn(e,t){if(typeof t=="string"||typeof t=="number")e.innerHTML+=t.toString();else if(t instanceof Node)e.appendChild(t);else if(Array.isArray(t))for(let r of t)sn(e,r)}function x(e,t,...r){let o=document.createElement(e);if(t)for(let n of Object.keys(t))typeof t[n]!="undefined"&&(typeof t[n]!="boolean"?o.setAttribute(n,t[n]):o.setAttribute(n,""));for(let n of r)sn(o,n);return o}function br(e){if(e>999){let t=+((e-950)%1e3>99);return`${((e+1e-6)/1e3).toFixed(t)}k`}else return e.toString()}function _t(e){let t=x("script",{src:e});return H(()=>(document.head.appendChild(t),L(h(t,"load"),h(t,"error").pipe(b(()=>Nr(()=>new ReferenceError(`Invalid script: ${e}`))))).pipe(m(()=>{}),A(()=>document.head.removeChild(t)),Ee(1))))}var cn=new T,Ia=H(()=>typeof ResizeObserver=="undefined"?_t("https://unpkg.com/resize-observer-polyfill"):$(void 0)).pipe(m(()=>new ResizeObserver(e=>e.forEach(t=>cn.next(t)))),b(e=>L(tt,$(e)).pipe(A(()=>e.disconnect()))),Z(1));function de(e){return{width:e.offsetWidth,height:e.offsetHeight}}function Le(e){let t=e;for(;t.clientWidth===0&&t.parentElement;)t=t.parentElement;return Ia.pipe(O(r=>r.observe(t)),b(r=>cn.pipe(g(o=>o.target===t),A(()=>r.unobserve(t)))),m(()=>de(e)),Q(de(e)))}function At(e){return{width:e.scrollWidth,height:e.scrollHeight}}function vr(e){let t=e.parentElement;for(;t&&(e.scrollWidth<=t.scrollWidth&&e.scrollHeight<=t.scrollHeight);)t=(e=t).parentElement;return t?e:void 0}function pn(e){let t=[],r=e.parentElement;for(;r;)(e.clientWidth>r.clientWidth||e.clientHeight>r.clientHeight)&&t.push(r),r=(e=r).parentElement;return t.length===0&&t.push(document.documentElement),t}function Be(e){return{x:e.offsetLeft,y:e.offsetTop}}function ln(e){let t=e.getBoundingClientRect();return{x:t.x+window.scrollX,y:t.y+window.scrollY}}function mn(e){return L(h(window,"load"),h(window,"resize")).pipe($e(0,ye),m(()=>Be(e)),Q(Be(e)))}function gr(e){return{x:e.scrollLeft,y:e.scrollTop}}function Ge(e){return L(h(e,"scroll"),h(window,"scroll"),h(window,"resize")).pipe($e(0,ye),m(()=>gr(e)),Q(gr(e)))}var fn=new T,Fa=H(()=>$(new IntersectionObserver(e=>{for(let t of e)fn.next(t)},{threshold:0}))).pipe(b(e=>L(tt,$(e)).pipe(A(()=>e.disconnect()))),Z(1));function mt(e){return Fa.pipe(O(t=>t.observe(e)),b(t=>fn.pipe(g(({target:r})=>r===e),A(()=>t.unobserve(e)),m(({isIntersecting:r})=>r))))}function un(e,t=16){return Ge(e).pipe(m(({y:r})=>{let o=de(e),n=At(e);return r>=n.height-o.height-t}),Y())}var yr={drawer:j("[data-md-toggle=drawer]"),search:j("[data-md-toggle=search]")};function dn(e){return yr[e].checked}function at(e,t){yr[e].checked!==t&&yr[e].click()}function Je(e){let t=yr[e];return h(t,"change").pipe(m(()=>t.checked),Q(t.checked))}function ja(e,t){switch(e.constructor){case HTMLInputElement:return e.type==="radio"?/^Arrow/.test(t):!0;case HTMLSelectElement:case HTMLTextAreaElement:return!0;default:return e.isContentEditable}}function Ua(){return L(h(window,"compositionstart").pipe(m(()=>!0)),h(window,"compositionend").pipe(m(()=>!1))).pipe(Q(!1))}function hn(){let e=h(window,"keydown").pipe(g(t=>!(t.metaKey||t.ctrlKey)),m(t=>({mode:dn("search")?"search":"global",type:t.key,claim(){t.preventDefault(),t.stopPropagation()}})),g(({mode:t,type:r})=>{if(t==="global"){let o=Ne();if(typeof o!="undefined")return!ja(o,r)}return!0}),le());return Ua().pipe(b(t=>t?y:e))}function we(){return new URL(location.href)}function st(e,t=!1){if(V("navigation.instant")&&!t){let r=x("a",{href:e.href});document.body.appendChild(r),r.click(),r.remove()}else location.href=e.href}function bn(){return new T}function vn(){return location.hash.slice(1)}function gn(e){let t=x("a",{href:e});t.addEventListener("click",r=>r.stopPropagation()),t.click()}function Zr(e){return L(h(window,"hashchange"),e).pipe(m(vn),Q(vn()),g(t=>t.length>0),Z(1))}function yn(e){return Zr(e).pipe(m(t=>ue(`[id="${t}"]`)),g(t=>typeof t!="undefined"))}function Wt(e){let t=matchMedia(e);return ur(r=>t.addListener(()=>r(t.matches))).pipe(Q(t.matches))}function xn(){let e=matchMedia("print");return L(h(window,"beforeprint").pipe(m(()=>!0)),h(window,"afterprint").pipe(m(()=>!1))).pipe(Q(e.matches))}function eo(e,t){return e.pipe(b(r=>r?t():y))}function to(e,t){return new F(r=>{let o=new XMLHttpRequest;return o.open("GET",`${e}`),o.responseType="blob",o.addEventListener("load",()=>{o.status>=200&&o.status<300?(r.next(o.response),r.complete()):r.error(new Error(o.statusText))}),o.addEventListener("error",()=>{r.error(new Error("Network error"))}),o.addEventListener("abort",()=>{r.complete()}),typeof(t==null?void 0:t.progress$)!="undefined"&&(o.addEventListener("progress",n=>{var i;if(n.lengthComputable)t.progress$.next(n.loaded/n.total*100);else{let s=(i=o.getResponseHeader("Content-Length"))!=null?i:0;t.progress$.next(n.loaded/+s*100)}}),t.progress$.next(5)),o.send(),()=>o.abort()})}function ze(e,t){return to(e,t).pipe(b(r=>r.text()),m(r=>JSON.parse(r)),Z(1))}function xr(e,t){let r=new DOMParser;return to(e,t).pipe(b(o=>o.text()),m(o=>r.parseFromString(o,"text/html")),Z(1))}function En(e,t){let r=new DOMParser;return to(e,t).pipe(b(o=>o.text()),m(o=>r.parseFromString(o,"text/xml")),Z(1))}function wn(){return{x:Math.max(0,scrollX),y:Math.max(0,scrollY)}}function Tn(){return L(h(window,"scroll",{passive:!0}),h(window,"resize",{passive:!0})).pipe(m(wn),Q(wn()))}function Sn(){return{width:innerWidth,height:innerHeight}}function On(){return h(window,"resize",{passive:!0}).pipe(m(Sn),Q(Sn()))}function Ln(){return z([Tn(),On()]).pipe(m(([e,t])=>({offset:e,size:t})),Z(1))}function Er(e,{viewport$:t,header$:r}){let o=t.pipe(ne("size")),n=z([o,r]).pipe(m(()=>Be(e)));return z([r,t,n]).pipe(m(([{height:i},{offset:s,size:a},{x:c,y:p}])=>({offset:{x:s.x-c,y:s.y-p+i},size:a})))}function Wa(e){return h(e,"message",t=>t.data)}function Da(e){let t=new T;return t.subscribe(r=>e.postMessage(r)),t}function Mn(e,t=new Worker(e)){let r=Wa(t),o=Da(t),n=new T;n.subscribe(o);let i=o.pipe(oe(),ae(!0));return n.pipe(oe(),Ve(r.pipe(W(i))),le())}var Va=j("#__config"),Ct=JSON.parse(Va.textContent);Ct.base=`${new URL(Ct.base,we())}`;function Te(){return Ct}function V(e){return Ct.features.includes(e)}function Me(e,t){return typeof t!="undefined"?Ct.translations[e].replace("#",t.toString()):Ct.translations[e]}function Ce(e,t=document){return j(`[data-md-component=${e}]`,t)}function me(e,t=document){return M(`[data-md-component=${e}]`,t)}function Na(e){let t=j(".md-typeset > :first-child",e);return h(t,"click",{once:!0}).pipe(m(()=>j(".md-typeset",e)),m(r=>({hash:__md_hash(r.innerHTML)})))}function _n(e){if(!V("announce.dismiss")||!e.childElementCount)return y;if(!e.hidden){let t=j(".md-typeset",e);__md_hash(t.innerHTML)===__md_get("__announce")&&(e.hidden=!0)}return H(()=>{let t=new T;return t.subscribe(({hash:r})=>{e.hidden=!0,__md_set("__announce",r)}),Na(e).pipe(O(r=>t.next(r)),A(()=>t.complete()),m(r=>P({ref:e},r)))})}function za(e,{target$:t}){return t.pipe(m(r=>({hidden:r!==e})))}function An(e,t){let r=new T;return r.subscribe(({hidden:o})=>{e.hidden=o}),za(e,t).pipe(O(o=>r.next(o)),A(()=>r.complete()),m(o=>P({ref:e},o)))}function Dt(e,t){return t==="inline"?x("div",{class:"md-tooltip md-tooltip--inline",id:e,role:"tooltip"},x("div",{class:"md-tooltip__inner md-typeset"})):x("div",{class:"md-tooltip",id:e,role:"tooltip"},x("div",{class:"md-tooltip__inner md-typeset"}))}function wr(...e){return x("div",{class:"md-tooltip2",role:"dialog"},x("div",{class:"md-tooltip2__inner md-typeset"},e))}function Cn(...e){return x("div",{class:"md-tooltip2",role:"tooltip"},x("div",{class:"md-tooltip2__inner md-typeset"},e))}function kn(e,t){if(t=t?`${t}_annotation_${e}`:void 0,t){let r=t?`#${t}`:void 0;return x("aside",{class:"md-annotation",tabIndex:0},Dt(t),x("a",{href:r,class:"md-annotation__index",tabIndex:-1},x("span",{"data-md-annotation-id":e})))}else return x("aside",{class:"md-annotation",tabIndex:0},Dt(t),x("span",{class:"md-annotation__index",tabIndex:-1},x("span",{"data-md-annotation-id":e})))}function Hn(e){return x("button",{class:"md-code__button",title:Me("clipboard.copy"),"data-clipboard-target":`#${e} > code`,"data-md-type":"copy"})}function $n(){return x("button",{class:"md-code__button",title:"Toggle line selection","data-md-type":"select"})}function Pn(){return x("nav",{class:"md-code__nav"})}var In=$t(ro());function oo(e,t){let r=t&2,o=t&1,n=Object.keys(e.terms).filter(c=>!e.terms[c]).reduce((c,p)=>[...c,x("del",null,(0,In.default)(p))," "],[]).slice(0,-1),i=Te(),s=new URL(e.location,i.base);V("search.highlight")&&s.searchParams.set("h",Object.entries(e.terms).filter(([,c])=>c).reduce((c,[p])=>`${c} ${p}`.trim(),""));let{tags:a}=Te();return x("a",{href:`${s}`,class:"md-search-result__link",tabIndex:-1},x("article",{class:"md-search-result__article md-typeset","data-md-score":e.score.toFixed(2)},r>0&&x("div",{class:"md-search-result__icon md-icon"}),r>0&&x("h1",null,e.title),r<=0&&x("h2",null,e.title),o>0&&e.text.length>0&&e.text,e.tags&&x("nav",{class:"md-tags"},e.tags.map(c=>{let p=a?c in a?`md-tag-icon md-tag--${a[c]}`:"md-tag-icon":"";return x("span",{class:`md-tag ${p}`},c)})),o>0&&n.length>0&&x("p",{class:"md-search-result__terms"},Me("search.result.term.missing"),": ",...n)))}function Fn(e){let t=e[0].score,r=[...e],o=Te(),n=r.findIndex(l=>!`${new URL(l.location,o.base)}`.includes("#")),[i]=r.splice(n,1),s=r.findIndex(l=>l.scoreoo(l,1)),...c.length?[x("details",{class:"md-search-result__more"},x("summary",{tabIndex:-1},x("div",null,c.length>0&&c.length===1?Me("search.result.more.one"):Me("search.result.more.other",c.length))),...c.map(l=>oo(l,1)))]:[]];return x("li",{class:"md-search-result__item"},p)}function jn(e){return x("ul",{class:"md-source__facts"},Object.entries(e).map(([t,r])=>x("li",{class:`md-source__fact md-source__fact--${t}`},typeof r=="number"?br(r):r)))}function no(e){let t=`tabbed-control tabbed-control--${e}`;return x("div",{class:t,hidden:!0},x("button",{class:"tabbed-button",tabIndex:-1,"aria-hidden":"true"}))}function Un(e){return x("div",{class:"md-typeset__scrollwrap"},x("div",{class:"md-typeset__table"},e))}function Qa(e){var o;let t=Te(),r=new URL(`../${e.version}/`,t.base);return x("li",{class:"md-version__item"},x("a",{href:`${r}`,class:"md-version__link"},e.title,((o=t.version)==null?void 0:o.alias)&&e.aliases.length>0&&x("span",{class:"md-version__alias"},e.aliases[0])))}function Wn(e,t){var o;let r=Te();return e=e.filter(n=>{var i;return!((i=n.properties)!=null&&i.hidden)}),x("div",{class:"md-version"},x("button",{class:"md-version__current","aria-label":Me("select.version")},t.title,((o=r.version)==null?void 0:o.alias)&&t.aliases.length>0&&x("span",{class:"md-version__alias"},t.aliases[0])),x("ul",{class:"md-version__list"},e.map(Qa)))}var Ya=0;function Ba(e,t=250){let r=z([Ye(e),it(e,t)]).pipe(m(([n,i])=>n||i),Y()),o=H(()=>pn(e)).pipe(J(Ge),gt(1),Pe(r),m(()=>ln(e)));return r.pipe(Re(n=>n),b(()=>z([r,o])),m(([n,i])=>({active:n,offset:i})),le())}function Vt(e,t,r=250){let{content$:o,viewport$:n}=t,i=`__tooltip2_${Ya++}`;return H(()=>{let s=new T,a=new jr(!1);s.pipe(oe(),ae(!1)).subscribe(a);let c=a.pipe(jt(l=>He(+!l*250,Dr)),Y(),b(l=>l?o:y),O(l=>l.id=i),le());z([s.pipe(m(({active:l})=>l)),c.pipe(b(l=>it(l,250)),Q(!1))]).pipe(m(l=>l.some(f=>f))).subscribe(a);let p=a.pipe(g(l=>l),te(c,n),m(([l,f,{size:u}])=>{let d=e.getBoundingClientRect(),v=d.width/2;if(f.role==="tooltip")return{x:v,y:8+d.height};if(d.y>=u.height/2){let{height:S}=de(f);return{x:v,y:-16-S}}else return{x:v,y:16+d.height}}));return z([c,s,p]).subscribe(([l,{offset:f},u])=>{l.style.setProperty("--md-tooltip-host-x",`${f.x}px`),l.style.setProperty("--md-tooltip-host-y",`${f.y}px`),l.style.setProperty("--md-tooltip-x",`${u.x}px`),l.style.setProperty("--md-tooltip-y",`${u.y}px`),l.classList.toggle("md-tooltip2--top",u.y<0),l.classList.toggle("md-tooltip2--bottom",u.y>=0)}),a.pipe(g(l=>l),te(c,(l,f)=>f),g(l=>l.role==="tooltip")).subscribe(l=>{let f=de(j(":scope > *",l));l.style.setProperty("--md-tooltip-width",`${f.width}px`),l.style.setProperty("--md-tooltip-tail","0px")}),a.pipe(Y(),xe(ye),te(c)).subscribe(([l,f])=>{f.classList.toggle("md-tooltip2--active",l)}),z([a.pipe(g(l=>l)),c]).subscribe(([l,f])=>{f.role==="dialog"?(e.setAttribute("aria-controls",i),e.setAttribute("aria-haspopup","dialog")):e.setAttribute("aria-describedby",i)}),a.pipe(g(l=>!l)).subscribe(()=>{e.removeAttribute("aria-controls"),e.removeAttribute("aria-describedby"),e.removeAttribute("aria-haspopup")}),Ba(e,r).pipe(O(l=>s.next(l)),A(()=>s.complete()),m(l=>P({ref:e},l)))})}function Xe(e,{viewport$:t},r=document.body){return Vt(e,{content$:new F(o=>{let n=e.title,i=Cn(n);return o.next(i),e.removeAttribute("title"),r.append(i),()=>{i.remove(),e.setAttribute("title",n)}}),viewport$:t},0)}function Ga(e,t){let r=H(()=>z([mn(e),Ge(t)])).pipe(m(([{x:o,y:n},i])=>{let{width:s,height:a}=de(e);return{x:o-i.x+s/2,y:n-i.y+a/2}}));return Ye(e).pipe(b(o=>r.pipe(m(n=>({active:o,offset:n})),Ee(+!o||1/0))))}function Dn(e,t,{target$:r}){let[o,n]=Array.from(e.children);return H(()=>{let i=new T,s=i.pipe(oe(),ae(!0));return i.subscribe({next({offset:a}){e.style.setProperty("--md-tooltip-x",`${a.x}px`),e.style.setProperty("--md-tooltip-y",`${a.y}px`)},complete(){e.style.removeProperty("--md-tooltip-x"),e.style.removeProperty("--md-tooltip-y")}}),mt(e).pipe(W(s)).subscribe(a=>{e.toggleAttribute("data-md-visible",a)}),L(i.pipe(g(({active:a})=>a)),i.pipe(Ae(250),g(({active:a})=>!a))).subscribe({next({active:a}){a?e.prepend(o):o.remove()},complete(){e.prepend(o)}}),i.pipe($e(16,ye)).subscribe(({active:a})=>{o.classList.toggle("md-tooltip--active",a)}),i.pipe(gt(125,ye),g(()=>!!e.offsetParent),m(()=>e.offsetParent.getBoundingClientRect()),m(({x:a})=>a)).subscribe({next(a){a?e.style.setProperty("--md-tooltip-0",`${-a}px`):e.style.removeProperty("--md-tooltip-0")},complete(){e.style.removeProperty("--md-tooltip-0")}}),h(n,"click").pipe(W(s),g(a=>!(a.metaKey||a.ctrlKey))).subscribe(a=>{a.stopPropagation(),a.preventDefault()}),h(n,"mousedown").pipe(W(s),te(i)).subscribe(([a,{active:c}])=>{var p;if(a.button!==0||a.metaKey||a.ctrlKey)a.preventDefault();else if(c){a.preventDefault();let l=e.parentElement.closest(".md-annotation");l instanceof HTMLElement?l.focus():(p=Ne())==null||p.blur()}}),r.pipe(W(s),g(a=>a===o),nt(125)).subscribe(()=>e.focus()),Ga(e,t).pipe(O(a=>i.next(a)),A(()=>i.complete()),m(a=>P({ref:e},a)))})}function Ja(e){let t=Te();if(e.tagName!=="CODE")return[e];let r=[".c",".c1",".cm"];if(t.annotate&&typeof t.annotate=="object"){let o=e.closest("[class|=language]");if(o)for(let n of Array.from(o.classList)){if(!n.startsWith("language-"))continue;let[,i]=n.split("-");i in t.annotate&&r.push(...t.annotate[i])}}return M(r.join(", "),e)}function Xa(e){let t=[];for(let r of Ja(e)){let o=[],n=document.createNodeIterator(r,NodeFilter.SHOW_TEXT);for(let i=n.nextNode();i;i=n.nextNode())o.push(i);for(let i of o){let s;for(;s=/(\(\d+\))(!)?/.exec(i.textContent);){let[,a,c]=s;if(typeof c=="undefined"){let p=i.splitText(s.index);i=p.splitText(a.length),t.push(p)}else{i.textContent=a,t.push(i);break}}}}return t}function Vn(e,t){t.append(...Array.from(e.childNodes))}function Tr(e,t,{target$:r,print$:o}){let n=t.closest("[id]"),i=n==null?void 0:n.id,s=new Map;for(let a of Xa(t)){let[,c]=a.textContent.match(/\((\d+)\)/);ue(`:scope > li:nth-child(${c})`,e)&&(s.set(c,kn(c,i)),a.replaceWith(s.get(c)))}return s.size===0?y:H(()=>{let a=new T,c=a.pipe(oe(),ae(!0)),p=[];for(let[l,f]of s)p.push([j(".md-typeset",f),j(`:scope > li:nth-child(${l})`,e)]);return o.pipe(W(c)).subscribe(l=>{e.hidden=!l,e.classList.toggle("md-annotation-list",l);for(let[f,u]of p)l?Vn(f,u):Vn(u,f)}),L(...[...s].map(([,l])=>Dn(l,t,{target$:r}))).pipe(A(()=>a.complete()),le())})}function Nn(e){if(e.nextElementSibling){let t=e.nextElementSibling;if(t.tagName==="OL")return t;if(t.tagName==="P"&&!t.children.length)return Nn(t)}}function zn(e,t){return H(()=>{let r=Nn(e);return typeof r!="undefined"?Tr(r,e,t):y})}var Kn=$t(ao());var Za=0,qn=L(h(window,"keydown").pipe(m(()=>!0)),L(h(window,"keyup"),h(window,"contextmenu")).pipe(m(()=>!1))).pipe(Q(!1),Z(1));function Qn(e){if(e.nextElementSibling){let t=e.nextElementSibling;if(t.tagName==="OL")return t;if(t.tagName==="P"&&!t.children.length)return Qn(t)}}function es(e){return Le(e).pipe(m(({width:t})=>({scrollable:At(e).width>t})),ne("scrollable"))}function Yn(e,t){let{matches:r}=matchMedia("(hover)"),o=H(()=>{let n=new T,i=n.pipe(Yr(1));n.subscribe(({scrollable:d})=>{d&&r?e.setAttribute("tabindex","0"):e.removeAttribute("tabindex")});let s=[],a=e.closest("pre"),c=a.closest("[id]"),p=c?c.id:Za++;a.id=`__code_${p}`;let l=[],f=e.closest(".highlight");if(f instanceof HTMLElement){let d=Qn(f);if(typeof d!="undefined"&&(f.classList.contains("annotate")||V("content.code.annotate"))){let v=Tr(d,e,t);l.push(Le(f).pipe(W(i),m(({width:S,height:X})=>S&&X),Y(),b(S=>S?v:y)))}}let u=M(":scope > span[id]",e);if(u.length&&(e.classList.add("md-code__content"),e.closest(".select")||V("content.code.select")&&!e.closest(".no-select"))){let d=+u[0].id.split("-").pop(),v=$n();s.push(v),V("content.tooltips")&&l.push(Xe(v,{viewport$}));let S=h(v,"click").pipe(Ut(R=>!R,!1),O(()=>v.blur()),le());S.subscribe(R=>{v.classList.toggle("md-code__button--active",R)});let X=fe(u).pipe(J(R=>it(R).pipe(m(se=>[R,se]))));S.pipe(b(R=>R?X:y)).subscribe(([R,se])=>{let ce=ue(".hll.select",R);if(ce&&!se)ce.replaceWith(...Array.from(ce.childNodes));else if(!ce&&se){let he=document.createElement("span");he.className="hll select",he.append(...Array.from(R.childNodes).slice(1)),R.append(he)}});let re=fe(u).pipe(J(R=>h(R,"mousedown").pipe(O(se=>se.preventDefault()),m(()=>R)))),ee=S.pipe(b(R=>R?re:y),te(qn),m(([R,se])=>{var he;let ce=u.indexOf(R)+d;if(se===!1)return[ce,ce];{let Se=M(".hll",e).map(Ue=>u.indexOf(Ue.parentElement)+d);return(he=window.getSelection())==null||he.removeAllRanges(),[Math.min(ce,...Se),Math.max(ce,...Se)]}})),k=Zr(y).pipe(g(R=>R.startsWith(`__codelineno-${p}-`)));k.subscribe(R=>{let[,,se]=R.split("-"),ce=se.split(":").map(Se=>+Se-d+1);ce.length===1&&ce.push(ce[0]);for(let Se of M(".hll:not(.select)",e))Se.replaceWith(...Array.from(Se.childNodes));let he=u.slice(ce[0]-1,ce[1]);for(let Se of he){let Ue=document.createElement("span");Ue.className="hll",Ue.append(...Array.from(Se.childNodes).slice(1)),Se.append(Ue)}}),k.pipe(Ee(1),xe(pe)).subscribe(R=>{if(R.includes(":")){let se=document.getElementById(R.split(":")[0]);se&&setTimeout(()=>{let ce=se,he=-64;for(;ce!==document.body;)he+=ce.offsetTop,ce=ce.offsetParent;window.scrollTo({top:he})},1)}});let je=fe(M('a[href^="#__codelineno"]',f)).pipe(J(R=>h(R,"click").pipe(O(se=>se.preventDefault()),m(()=>R)))).pipe(W(i),te(qn),m(([R,se])=>{let he=+j(`[id="${R.hash.slice(1)}"]`).parentElement.id.split("-").pop();if(se===!1)return[he,he];{let Se=M(".hll",e).map(Ue=>+Ue.parentElement.id.split("-").pop());return[Math.min(he,...Se),Math.max(he,...Se)]}}));L(ee,je).subscribe(R=>{let se=`#__codelineno-${p}-`;R[0]===R[1]?se+=R[0]:se+=`${R[0]}:${R[1]}`,history.replaceState({},"",se),window.dispatchEvent(new HashChangeEvent("hashchange",{newURL:window.location.origin+window.location.pathname+se,oldURL:window.location.href}))})}if(Kn.default.isSupported()&&(e.closest(".copy")||V("content.code.copy")&&!e.closest(".no-copy"))){let d=Hn(a.id);s.push(d),V("content.tooltips")&&l.push(Xe(d,{viewport$}))}if(s.length){let d=Pn();d.append(...s),a.insertBefore(d,e)}return es(e).pipe(O(d=>n.next(d)),A(()=>n.complete()),m(d=>P({ref:e},d)),Ve(L(...l).pipe(W(i))))});return V("content.lazy")?mt(e).pipe(g(n=>n),Ee(1),b(()=>o)):o}function ts(e,{target$:t,print$:r}){let o=!0;return L(t.pipe(m(n=>n.closest("details:not([open])")),g(n=>e===n),m(()=>({action:"open",reveal:!0}))),r.pipe(g(n=>n||!o),O(()=>o=e.open),m(n=>({action:n?"open":"close"}))))}function Bn(e,t){return H(()=>{let r=new T;return r.subscribe(({action:o,reveal:n})=>{e.toggleAttribute("open",o==="open"),n&&e.scrollIntoView()}),ts(e,t).pipe(O(o=>r.next(o)),A(()=>r.complete()),m(o=>P({ref:e},o)))})}var Gn=0;function rs(e){let t=document.createElement("h3");t.innerHTML=e.innerHTML;let r=[t],o=e.nextElementSibling;for(;o&&!(o instanceof HTMLHeadingElement);)r.push(o),o=o.nextElementSibling;return r}function os(e,t){for(let r of M("[href], [src]",e))for(let o of["href","src"]){let n=r.getAttribute(o);if(n&&!/^(?:[a-z]+:)?\/\//i.test(n)){r[o]=new URL(r.getAttribute(o),t).toString();break}}for(let r of M("[name^=__], [for]",e))for(let o of["id","for","name"]){let n=r.getAttribute(o);n&&r.setAttribute(o,`${n}$preview_${Gn}`)}return Gn++,$(e)}function Jn(e,t){let{sitemap$:r}=t;if(!(e instanceof HTMLAnchorElement))return y;if(!(V("navigation.instant.preview")||e.hasAttribute("data-preview")))return y;e.removeAttribute("title");let o=z([Ye(e),it(e)]).pipe(m(([i,s])=>i||s),Y(),g(i=>i));return rt([r,o]).pipe(b(([i])=>{let s=new URL(e.href);return s.search=s.hash="",i.has(`${s}`)?$(s):y}),b(i=>xr(i).pipe(b(s=>os(s,i)))),b(i=>{let s=e.hash?`article [id="${e.hash.slice(1)}"]`:"article h1",a=ue(s,i);return typeof a=="undefined"?y:$(rs(a))})).pipe(b(i=>{let s=new F(a=>{let c=wr(...i);return a.next(c),document.body.append(c),()=>c.remove()});return Vt(e,P({content$:s},t))}))}var Xn=".node circle,.node ellipse,.node path,.node polygon,.node rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}marker{fill:var(--md-mermaid-edge-color)!important}.edgeLabel .label rect{fill:#0000}.flowchartTitleText{fill:var(--md-mermaid-label-fg-color)}.label{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.label foreignObject{line-height:normal;overflow:visible}.label div .edgeLabel{color:var(--md-mermaid-label-fg-color)}.edgeLabel,.edgeLabel p,.label div .edgeLabel{background-color:var(--md-mermaid-label-bg-color)}.edgeLabel,.edgeLabel p{fill:var(--md-mermaid-label-bg-color);color:var(--md-mermaid-edge-color)}.edgePath .path,.flowchart-link{stroke:var(--md-mermaid-edge-color)}.edgePath .arrowheadPath{fill:var(--md-mermaid-edge-color);stroke:none}.cluster rect{fill:var(--md-default-fg-color--lightest);stroke:var(--md-default-fg-color--lighter)}.cluster span{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}g #flowchart-circleEnd,g #flowchart-circleStart,g #flowchart-crossEnd,g #flowchart-crossStart,g #flowchart-pointEnd,g #flowchart-pointStart{stroke:none}.classDiagramTitleText{fill:var(--md-mermaid-label-fg-color)}g.classGroup line,g.classGroup rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}g.classGroup text{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.classLabel .box{fill:var(--md-mermaid-label-bg-color);background-color:var(--md-mermaid-label-bg-color);opacity:1}.classLabel .label{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.node .divider{stroke:var(--md-mermaid-node-fg-color)}.relation{stroke:var(--md-mermaid-edge-color)}.cardinality{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.cardinality text{fill:inherit!important}defs marker.marker.composition.class path,defs marker.marker.dependency.class path,defs marker.marker.extension.class path{fill:var(--md-mermaid-edge-color)!important;stroke:var(--md-mermaid-edge-color)!important}defs marker.marker.aggregation.class path{fill:var(--md-mermaid-label-bg-color)!important;stroke:var(--md-mermaid-edge-color)!important}.statediagramTitleText{fill:var(--md-mermaid-label-fg-color)}g.stateGroup rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}g.stateGroup .state-title{fill:var(--md-mermaid-label-fg-color)!important;font-family:var(--md-mermaid-font-family)}g.stateGroup .composit{fill:var(--md-mermaid-label-bg-color)}.nodeLabel,.nodeLabel p{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}a .nodeLabel{text-decoration:underline}.node circle.state-end,.node circle.state-start,.start-state{fill:var(--md-mermaid-edge-color);stroke:none}.end-state-inner,.end-state-outer{fill:var(--md-mermaid-edge-color)}.end-state-inner,.node circle.state-end{stroke:var(--md-mermaid-label-bg-color)}.transition{stroke:var(--md-mermaid-edge-color)}[id^=state-fork] rect,[id^=state-join] rect{fill:var(--md-mermaid-edge-color)!important;stroke:none!important}.statediagram-cluster.statediagram-cluster .inner{fill:var(--md-default-bg-color)}.statediagram-cluster rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}.statediagram-state rect.divider{fill:var(--md-default-fg-color--lightest);stroke:var(--md-default-fg-color--lighter)}defs #statediagram-barbEnd{stroke:var(--md-mermaid-edge-color)}[id^=entity] path,[id^=entity] rect{fill:var(--md-default-bg-color)}.relationshipLine{stroke:var(--md-mermaid-edge-color)}defs .marker.oneOrMore.er *,defs .marker.onlyOne.er *,defs .marker.zeroOrMore.er *,defs .marker.zeroOrOne.er *{stroke:var(--md-mermaid-edge-color)!important}text:not([class]):last-child{fill:var(--md-mermaid-label-fg-color)}.actor{fill:var(--md-mermaid-sequence-actor-bg-color);stroke:var(--md-mermaid-sequence-actor-border-color)}text.actor>tspan{fill:var(--md-mermaid-sequence-actor-fg-color);font-family:var(--md-mermaid-font-family)}line{stroke:var(--md-mermaid-sequence-actor-line-color)}.actor-man circle,.actor-man line{fill:var(--md-mermaid-sequence-actorman-bg-color);stroke:var(--md-mermaid-sequence-actorman-line-color)}.messageLine0,.messageLine1{stroke:var(--md-mermaid-sequence-message-line-color)}.note{fill:var(--md-mermaid-sequence-note-bg-color);stroke:var(--md-mermaid-sequence-note-border-color)}.loopText,.loopText>tspan,.messageText,.noteText>tspan{stroke:none;font-family:var(--md-mermaid-font-family)!important}.messageText{fill:var(--md-mermaid-sequence-message-fg-color)}.loopText,.loopText>tspan{fill:var(--md-mermaid-sequence-loop-fg-color)}.noteText>tspan{fill:var(--md-mermaid-sequence-note-fg-color)}#arrowhead path{fill:var(--md-mermaid-sequence-message-line-color);stroke:none}.loopLine{fill:var(--md-mermaid-sequence-loop-bg-color);stroke:var(--md-mermaid-sequence-loop-border-color)}.labelBox{fill:var(--md-mermaid-sequence-label-bg-color);stroke:none}.labelText,.labelText>span{fill:var(--md-mermaid-sequence-label-fg-color);font-family:var(--md-mermaid-font-family)}.sequenceNumber{fill:var(--md-mermaid-sequence-number-fg-color)}rect.rect{fill:var(--md-mermaid-sequence-box-bg-color);stroke:none}rect.rect+text.text{fill:var(--md-mermaid-sequence-box-fg-color)}defs #sequencenumber{fill:var(--md-mermaid-sequence-number-bg-color)!important}";var so,is=0;function as(){return typeof mermaid=="undefined"||mermaid instanceof Element?_t("https://unpkg.com/mermaid@11/dist/mermaid.min.js"):$(void 0)}function Zn(e){return e.classList.remove("mermaid"),so||(so=as().pipe(O(()=>mermaid.initialize({startOnLoad:!1,themeCSS:Xn,sequence:{actorFontSize:"16px",messageFontSize:"16px",noteFontSize:"16px"}})),m(()=>{}),Z(1))),so.subscribe(()=>go(null,null,function*(){e.classList.add("mermaid");let t=`__mermaid_${is++}`,r=x("div",{class:"mermaid"}),o=e.textContent,{svg:n,fn:i}=yield mermaid.render(t,o),s=r.attachShadow({mode:"closed"});s.innerHTML=n,e.replaceWith(r),i==null||i(s)})),so.pipe(m(()=>({ref:e})))}var ei=x("table");function ti(e){return e.replaceWith(ei),ei.replaceWith(Un(e)),$({ref:e})}function ss(e){let t=e.find(r=>r.checked)||e[0];return L(...e.map(r=>h(r,"change").pipe(m(()=>j(`label[for="${r.id}"]`))))).pipe(Q(j(`label[for="${t.id}"]`)),m(r=>({active:r})))}function ri(e,{viewport$:t,target$:r}){let o=j(".tabbed-labels",e),n=M(":scope > input",e),i=no("prev");e.append(i);let s=no("next");return e.append(s),H(()=>{let a=new T,c=a.pipe(oe(),ae(!0));z([a,Le(e),mt(e)]).pipe(W(c),$e(1,ye)).subscribe({next([{active:p},l]){let f=Be(p),{width:u}=de(p);e.style.setProperty("--md-indicator-x",`${f.x}px`),e.style.setProperty("--md-indicator-width",`${u}px`);let d=gr(o);(f.xd.x+l.width)&&o.scrollTo({left:Math.max(0,f.x-16),behavior:"smooth"})},complete(){e.style.removeProperty("--md-indicator-x"),e.style.removeProperty("--md-indicator-width")}}),z([Ge(o),Le(o)]).pipe(W(c)).subscribe(([p,l])=>{let f=At(o);i.hidden=p.x<16,s.hidden=p.x>f.width-l.width-16}),L(h(i,"click").pipe(m(()=>-1)),h(s,"click").pipe(m(()=>1))).pipe(W(c)).subscribe(p=>{let{width:l}=de(o);o.scrollBy({left:l*p,behavior:"smooth"})}),r.pipe(W(c),g(p=>n.includes(p))).subscribe(p=>p.click()),o.classList.add("tabbed-labels--linked");for(let p of n){let l=j(`label[for="${p.id}"]`);l.replaceChildren(x("a",{href:`#${l.htmlFor}`,tabIndex:-1},...Array.from(l.childNodes))),h(l.firstElementChild,"click").pipe(W(c),g(f=>!(f.metaKey||f.ctrlKey)),O(f=>{f.preventDefault(),f.stopPropagation()})).subscribe(()=>{history.replaceState({},"",`#${l.htmlFor}`),l.click()})}return V("content.tabs.link")&&a.pipe(Ie(1),te(t)).subscribe(([{active:p},{offset:l}])=>{let f=p.innerText.trim();if(p.hasAttribute("data-md-switching"))p.removeAttribute("data-md-switching");else{let u=e.offsetTop-l.y;for(let v of M("[data-tabs]"))for(let S of M(":scope > input",v)){let X=j(`label[for="${S.id}"]`);if(X!==p&&X.innerText.trim()===f){X.setAttribute("data-md-switching",""),S.click();break}}window.scrollTo({top:e.offsetTop-u});let d=__md_get("__tabs")||[];__md_set("__tabs",[...new Set([f,...d])])}}),a.pipe(W(c)).subscribe(()=>{for(let p of M("audio, video",e))p.offsetWidth&&p.autoplay?p.play().catch(()=>{}):p.pause()}),ss(n).pipe(O(p=>a.next(p)),A(()=>a.complete()),m(p=>P({ref:e},p)))}).pipe(et(pe))}function oi(e,t){let{viewport$:r,target$:o,print$:n}=t;return L(...M(".annotate:not(.highlight)",e).map(i=>zn(i,{target$:o,print$:n})),...M("pre:not(.mermaid) > code",e).map(i=>Yn(i,{target$:o,print$:n})),...M("a",e).map(i=>Jn(i,t)),...M("pre.mermaid",e).map(i=>Zn(i)),...M("table:not([class])",e).map(i=>ti(i)),...M("details",e).map(i=>Bn(i,{target$:o,print$:n})),...M("[data-tabs]",e).map(i=>ri(i,{viewport$:r,target$:o})),...M("[title]:not([data-preview])",e).filter(()=>V("content.tooltips")).map(i=>Xe(i,{viewport$:r})),...M(".footnote-ref",e).filter(()=>V("content.footnote.tooltips")).map(i=>Vt(i,{content$:new F(s=>{let a=new URL(i.href).hash.slice(1),c=Array.from(document.getElementById(a).cloneNode(!0).children),p=wr(...c);return s.next(p),document.body.append(p),()=>p.remove()}),viewport$:r})))}function cs(e,{alert$:t}){return t.pipe(b(r=>L($(!0),$(!1).pipe(nt(2e3))).pipe(m(o=>({message:r,active:o})))))}function ni(e,t){let r=j(".md-typeset",e);return H(()=>{let o=new T;return o.subscribe(({message:n,active:i})=>{e.classList.toggle("md-dialog--active",i),r.textContent=n}),cs(e,t).pipe(O(n=>o.next(n)),A(()=>o.complete()),m(n=>P({ref:e},n)))})}var ps=0;function ls(e,t){document.body.append(e);let{width:r}=de(e);e.style.setProperty("--md-tooltip-width",`${r}px`),e.remove();let o=vr(t),n=typeof o!="undefined"?Ge(o):$({x:0,y:0}),i=L(Ye(t),it(t)).pipe(Y());return z([i,n]).pipe(m(([s,a])=>{let{x:c,y:p}=Be(t),l=de(t),f=t.closest("table");return f&&t.parentElement&&(c+=f.offsetLeft+t.parentElement.offsetLeft,p+=f.offsetTop+t.parentElement.offsetTop),{active:s,offset:{x:c-a.x+l.width/2-r/2,y:p-a.y+l.height+8}}}))}function ii(e){let t=e.title;if(!t.length)return y;let r=`__tooltip_${ps++}`,o=Dt(r,"inline"),n=j(".md-typeset",o);return n.innerHTML=t,H(()=>{let i=new T;return i.subscribe({next({offset:s}){o.style.setProperty("--md-tooltip-x",`${s.x}px`),o.style.setProperty("--md-tooltip-y",`${s.y}px`)},complete(){o.style.removeProperty("--md-tooltip-x"),o.style.removeProperty("--md-tooltip-y")}}),L(i.pipe(g(({active:s})=>s)),i.pipe(Ae(250),g(({active:s})=>!s))).subscribe({next({active:s}){s?(e.insertAdjacentElement("afterend",o),e.setAttribute("aria-describedby",r),e.removeAttribute("title")):(o.remove(),e.removeAttribute("aria-describedby"),e.setAttribute("title",t))},complete(){o.remove(),e.removeAttribute("aria-describedby"),e.setAttribute("title",t)}}),i.pipe($e(16,ye)).subscribe(({active:s})=>{o.classList.toggle("md-tooltip--active",s)}),i.pipe(gt(125,ye),g(()=>!!e.offsetParent),m(()=>e.offsetParent.getBoundingClientRect()),m(({x:s})=>s)).subscribe({next(s){s?o.style.setProperty("--md-tooltip-0",`${-s}px`):o.style.removeProperty("--md-tooltip-0")},complete(){o.style.removeProperty("--md-tooltip-0")}}),ls(o,e).pipe(O(s=>i.next(s)),A(()=>i.complete()),m(s=>P({ref:e},s)))}).pipe(et(pe))}function ms({viewport$:e}){if(!V("header.autohide"))return $(!1);let t=e.pipe(m(({offset:{y:n}})=>n),ot(2,1),m(([n,i])=>[nMath.abs(i-n.y)>100),m(([,[n]])=>n),Y()),o=Je("search");return z([e,o]).pipe(m(([{offset:n},i])=>n.y>400&&!i),Y(),b(n=>n?r:$(!1)),Q(!1))}function ai(e,t){return H(()=>z([Le(e),ms(t)])).pipe(m(([{height:r},o])=>({height:r,hidden:o})),Y((r,o)=>r.height===o.height&&r.hidden===o.hidden),Z(1))}function si(e,{header$:t,main$:r}){return H(()=>{let o=new T,n=o.pipe(oe(),ae(!0));o.pipe(ne("active"),Pe(t)).subscribe(([{active:s},{hidden:a}])=>{e.classList.toggle("md-header--shadow",s&&!a),e.hidden=a});let i=fe(M("[title]",e)).pipe(g(()=>V("content.tooltips")),J(s=>ii(s)));return r.subscribe(o),t.pipe(W(n),m(s=>P({ref:e},s)),Ve(i.pipe(W(n))))})}function fs(e,{viewport$:t,header$:r}){return Er(e,{viewport$:t,header$:r}).pipe(m(({offset:{y:o}})=>{let{height:n}=de(e);return{active:n>0&&o>=n}}),ne("active"))}function ci(e,t){return H(()=>{let r=new T;r.subscribe({next({active:n}){e.classList.toggle("md-header__title--active",n)},complete(){e.classList.remove("md-header__title--active")}});let o=ue(".md-content h1");return typeof o=="undefined"?y:fs(o,t).pipe(O(n=>r.next(n)),A(()=>r.complete()),m(n=>P({ref:e},n)))})}function pi(e,{viewport$:t,header$:r}){let o=r.pipe(m(({height:i})=>i),Y()),n=o.pipe(b(()=>Le(e).pipe(m(({height:i})=>({top:e.offsetTop,bottom:e.offsetTop+i})),ne("bottom"))));return z([o,n,t]).pipe(m(([i,{top:s,bottom:a},{offset:{y:c},size:{height:p}}])=>(p=Math.max(0,p-Math.max(0,s-c,i)-Math.max(0,p+c-a)),{offset:s-i,height:p,active:s-i<=c})),Y((i,s)=>i.offset===s.offset&&i.height===s.height&&i.active===s.active))}function us(e){let t=__md_get("__palette")||{index:e.findIndex(o=>matchMedia(o.getAttribute("data-md-color-media")).matches)},r=Math.max(0,Math.min(t.index,e.length-1));return $(...e).pipe(J(o=>h(o,"change").pipe(m(()=>o))),Q(e[r]),m(o=>({index:e.indexOf(o),color:{media:o.getAttribute("data-md-color-media"),scheme:o.getAttribute("data-md-color-scheme"),primary:o.getAttribute("data-md-color-primary"),accent:o.getAttribute("data-md-color-accent")}})),Z(1))}function li(e){let t=M("input",e),r=x("meta",{name:"theme-color"});document.head.appendChild(r);let o=x("meta",{name:"color-scheme"});document.head.appendChild(o);let n=Wt("(prefers-color-scheme: light)");return H(()=>{let i=new T;return i.subscribe(s=>{if(document.body.setAttribute("data-md-color-switching",""),s.color.media==="(prefers-color-scheme)"){let a=matchMedia("(prefers-color-scheme: light)"),c=document.querySelector(a.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");s.color.scheme=c.getAttribute("data-md-color-scheme"),s.color.primary=c.getAttribute("data-md-color-primary"),s.color.accent=c.getAttribute("data-md-color-accent")}for(let[a,c]of Object.entries(s.color))document.body.setAttribute(`data-md-color-${a}`,c);for(let a=0;as.key==="Enter"),te(i,(s,a)=>a)).subscribe(({index:s})=>{s=(s+1)%t.length,t[s].click(),t[s].focus()}),i.pipe(m(()=>{let s=Ce("header"),a=window.getComputedStyle(s);return o.content=a.colorScheme,a.backgroundColor.match(/\d+/g).map(c=>(+c).toString(16).padStart(2,"0")).join("")})).subscribe(s=>r.content=`#${s}`),i.pipe(xe(pe)).subscribe(()=>{document.body.removeAttribute("data-md-color-switching")}),us(t).pipe(W(n.pipe(Ie(1))),vt(),O(s=>i.next(s)),A(()=>i.complete()),m(s=>P({ref:e},s)))})}function mi(e,{progress$:t}){return H(()=>{let r=new T;return r.subscribe(({value:o})=>{e.style.setProperty("--md-progress-value",`${o}`)}),t.pipe(O(o=>r.next({value:o})),A(()=>r.complete()),m(o=>({ref:e,value:o})))})}function fi(e,t){return e.protocol=t.protocol,e.hostname=t.hostname,e}function ds(e,t){let r=new Map;for(let o of M("url",e)){let n=j("loc",o),i=[fi(new URL(n.textContent),t)];r.set(`${i[0]}`,i);for(let s of M("[rel=alternate]",o)){let a=s.getAttribute("href");a!=null&&i.push(fi(new URL(a),t))}}return r}function kt(e){return En(new URL("sitemap.xml",e)).pipe(m(t=>ds(t,new URL(e))),ve(()=>$(new Map)),le())}function ui({document$:e}){let t=new Map;e.pipe(b(()=>M("link[rel=alternate]")),m(r=>new URL(r.href)),g(r=>!t.has(r.toString())),J(r=>kt(r).pipe(m(o=>[r,o]),ve(()=>y)))).subscribe(([r,o])=>{t.set(r.toString().replace(/\/$/,""),o)}),h(document.body,"click").pipe(g(r=>!r.metaKey&&!r.ctrlKey),b(r=>{if(r.target instanceof Element){let o=r.target.closest("a");if(o&&!o.target){let n=[...t].find(([f])=>o.href.startsWith(`${f}/`));if(typeof n=="undefined")return y;let[i,s]=n,a=we();if(a.href.startsWith(i))return y;let c=Te(),p=a.href.replace(c.base,"");p=`${i}/${p}`;let l=s.has(p.split("#")[0])?new URL(p,c.base):new URL(i);return r.preventDefault(),$(l)}}return y})).subscribe(r=>st(r,!0))}var co=$t(ao());function hs(e){e.setAttribute("data-md-copying","");let t=e.closest("[data-copy]"),r=t?t.getAttribute("data-copy"):e.innerText;return e.removeAttribute("data-md-copying"),r.trimEnd()}function di({alert$:e}){co.default.isSupported()&&new F(t=>{new co.default("[data-clipboard-target], [data-clipboard-text]",{text:r=>r.getAttribute("data-clipboard-text")||hs(j(r.getAttribute("data-clipboard-target")))}).on("success",r=>t.next(r))}).pipe(O(t=>{t.trigger.focus()}),m(()=>Me("clipboard.copied"))).subscribe(e)}function hi(e,t){if(!(e.target instanceof Element))return y;let r=e.target.closest("a");if(r===null)return y;if(r.target||e.metaKey||e.ctrlKey)return y;let o=new URL(r.href);return o.search=o.hash="",t.has(`${o}`)?(e.preventDefault(),$(r)):y}function bi(e){let t=new Map;for(let r of M(":scope > *",e.head))t.set(r.outerHTML,r);return t}function vi(e){for(let t of M("[href], [src]",e))for(let r of["href","src"]){let o=t.getAttribute(r);if(o&&!/^(?:[a-z]+:)?\/\//i.test(o)){t[r]=t[r];break}}return $(e)}function bs(e){for(let o of["[data-md-component=announce]","[data-md-component=container]","[data-md-component=header-topic]","[data-md-component=outdated]","[data-md-component=logo]","[data-md-component=skip]",...V("navigation.tabs.sticky")?["[data-md-component=tabs]"]:[]]){let n=ue(o),i=ue(o,e);typeof n!="undefined"&&typeof i!="undefined"&&n.replaceWith(i)}let t=bi(document);for(let[o,n]of bi(e))t.has(o)?t.delete(o):document.head.appendChild(n);for(let o of t.values()){let n=o.getAttribute("name");n!=="theme-color"&&n!=="color-scheme"&&o.remove()}let r=Ce("container");return Ke(M("script",r)).pipe(b(o=>{let n=e.createElement("script");if(o.src){for(let i of o.getAttributeNames())n.setAttribute(i,o.getAttribute(i));return o.replaceWith(n),new F(i=>{n.onload=()=>i.complete()})}else return n.textContent=o.textContent,o.replaceWith(n),y}),oe(),ae(document))}function gi({sitemap$:e,location$:t,viewport$:r,progress$:o}){if(location.protocol==="file:")return y;$(document).subscribe(vi);let n=h(document.body,"click").pipe(Pe(e),b(([a,c])=>hi(a,c)),m(({href:a})=>new URL(a)),le()),i=h(window,"popstate").pipe(m(we),le());n.pipe(te(r)).subscribe(([a,{offset:c}])=>{history.replaceState(c,""),history.pushState(null,"",a)}),L(n,i).subscribe(t);let s=t.pipe(ne("pathname"),b(a=>xr(a,{progress$:o}).pipe(ve(()=>(st(a,!0),y)))),b(vi),b(bs),le());return L(s.pipe(te(t,(a,c)=>c)),s.pipe(b(()=>t),ne("hash")),t.pipe(Y((a,c)=>a.pathname===c.pathname&&a.hash===c.hash),b(()=>n),O(()=>history.back()))).subscribe(a=>{var c,p;history.state!==null||!a.hash?window.scrollTo(0,(p=(c=history.state)==null?void 0:c.y)!=null?p:0):(history.scrollRestoration="auto",gn(a.hash),history.scrollRestoration="manual")}),t.subscribe(()=>{history.scrollRestoration="manual"}),h(window,"beforeunload").subscribe(()=>{history.scrollRestoration="auto"}),r.pipe(ne("offset"),Ae(100)).subscribe(({offset:a})=>{history.replaceState(a,"")}),V("navigation.instant.prefetch")&&L(h(document.body,"mousemove"),h(document.body,"focusin")).pipe(Pe(e),b(([a,c])=>hi(a,c)),Ae(25),Qr(({href:a})=>a),hr(a=>{let c=document.createElement("link");return c.rel="prefetch",c.href=a.toString(),document.head.appendChild(c),h(c,"load").pipe(m(()=>c),Ee(1))})).subscribe(a=>a.remove()),s}var yi=$t(ro());function xi(e){let t=e.separator.split("|").map(n=>n.replace(/(\(\?[!=<][^)]+\))/g,"").length===0?"\uFFFD":n).join("|"),r=new RegExp(t,"img"),o=(n,i,s)=>`${i}${s}`;return n=>{n=n.replace(/[\s*+\-:~^]+/g," ").replace(/&/g,"&").trim();let i=new RegExp(`(^|${e.separator}|)(${n.replace(/[|\\{}()[\]^$+*?.-]/g,"\\$&").replace(r,"|")})`,"img");return s=>(0,yi.default)(s).replace(i,o).replace(/<\/mark>(\s+)]*>/img,"$1")}}function zt(e){return e.type===1}function Sr(e){return e.type===3}function Ei(e,t){let r=Mn(e);return L($(location.protocol!=="file:"),Je("search")).pipe(Re(o=>o),b(()=>t)).subscribe(({config:o,docs:n})=>r.next({type:0,data:{config:o,docs:n,options:{suggest:V("search.suggest")}}})),r}function wi(e){var l;let{selectedVersionSitemap:t,selectedVersionBaseURL:r,currentLocation:o,currentBaseURL:n}=e,i=(l=po(n))==null?void 0:l.pathname;if(i===void 0)return;let s=ys(o.pathname,i);if(s===void 0)return;let a=Es(t.keys());if(!t.has(a))return;let c=po(s,a);if(!c||!t.has(c.href))return;let p=po(s,r);if(p)return p.hash=o.hash,p.search=o.search,p}function po(e,t){try{return new URL(e,t)}catch(r){return}}function ys(e,t){if(e.startsWith(t))return e.slice(t.length)}function xs(e,t){let r=Math.min(e.length,t.length),o;for(o=0;oy)),o=r.pipe(m(n=>{let[,i]=t.base.match(/([^/]+)\/?$/);return n.find(({version:s,aliases:a})=>s===i||a.includes(i))||n[0]}));r.pipe(m(n=>new Map(n.map(i=>[`${new URL(`../${i.version}/`,t.base)}`,i]))),b(n=>h(document.body,"click").pipe(g(i=>!i.metaKey&&!i.ctrlKey),te(o),b(([i,s])=>{if(i.target instanceof Element){let a=i.target.closest("a");if(a&&!a.target&&n.has(a.href)){let c=a.href;return!i.target.closest(".md-version")&&n.get(c)===s?y:(i.preventDefault(),$(new URL(c)))}}return y}),b(i=>kt(i).pipe(m(s=>{var a;return(a=wi({selectedVersionSitemap:s,selectedVersionBaseURL:i,currentLocation:we(),currentBaseURL:t.base}))!=null?a:i})))))).subscribe(n=>st(n,!0)),z([r,o]).subscribe(([n,i])=>{j(".md-header__topic").appendChild(Wn(n,i))}),e.pipe(b(()=>o)).subscribe(n=>{var a;let i=new URL(t.base),s=__md_get("__outdated",sessionStorage,i);if(s===null){s=!0;let c=((a=t.version)==null?void 0:a.default)||"latest";Array.isArray(c)||(c=[c]);e:for(let p of c)for(let l of n.aliases.concat(n.version))if(new RegExp(p,"i").test(l)){s=!1;break e}__md_set("__outdated",s,sessionStorage,i)}if(s)for(let c of me("outdated"))c.hidden=!1})}function ws(e,{worker$:t}){let{searchParams:r}=we();r.has("q")&&(at("search",!0),e.value=r.get("q"),e.focus(),Je("search").pipe(Re(i=>!i)).subscribe(()=>{let i=we();i.searchParams.delete("q"),history.replaceState({},"",`${i}`)}));let o=Ye(e),n=L(t.pipe(Re(zt)),h(e,"keyup"),o).pipe(m(()=>e.value),Y());return z([n,o]).pipe(m(([i,s])=>({value:i,focus:s})),Z(1))}function Si(e,{worker$:t}){let r=new T,o=r.pipe(oe(),ae(!0));z([t.pipe(Re(zt)),r],(i,s)=>s).pipe(ne("value")).subscribe(({value:i})=>t.next({type:2,data:i})),r.pipe(ne("focus")).subscribe(({focus:i})=>{i&&at("search",i)}),h(e.form,"reset").pipe(W(o)).subscribe(()=>e.focus());let n=j("header [for=__search]");return h(n,"click").subscribe(()=>e.focus()),ws(e,{worker$:t}).pipe(O(i=>r.next(i)),A(()=>r.complete()),m(i=>P({ref:e},i)),Z(1))}function Oi(e,{worker$:t,query$:r}){let o=new T,n=un(e.parentElement).pipe(g(Boolean)),i=e.parentElement,s=j(":scope > :first-child",e),a=j(":scope > :last-child",e);Je("search").subscribe(l=>{a.setAttribute("role",l?"list":"presentation"),a.hidden=!l}),o.pipe(te(r),Gr(t.pipe(Re(zt)))).subscribe(([{items:l},{value:f}])=>{switch(l.length){case 0:s.textContent=f.length?Me("search.result.none"):Me("search.result.placeholder");break;case 1:s.textContent=Me("search.result.one");break;default:let u=br(l.length);s.textContent=Me("search.result.other",u)}});let c=o.pipe(O(()=>a.innerHTML=""),b(({items:l})=>L($(...l.slice(0,10)),$(...l.slice(10)).pipe(ot(4),Xr(n),b(([f])=>f)))),m(Fn),le());return c.subscribe(l=>a.appendChild(l)),c.pipe(J(l=>{let f=ue("details",l);return typeof f=="undefined"?y:h(f,"toggle").pipe(W(o),m(()=>f))})).subscribe(l=>{l.open===!1&&l.offsetTop<=i.scrollTop&&i.scrollTo({top:l.offsetTop})}),t.pipe(g(Sr),m(({data:l})=>l)).pipe(O(l=>o.next(l)),A(()=>o.complete()),m(l=>P({ref:e},l)))}function Ts(e,{query$:t}){return t.pipe(m(({value:r})=>{let o=we();return o.hash="",r=r.replace(/\s+/g,"+").replace(/&/g,"%26").replace(/=/g,"%3D"),o.search=`q=${r}`,{url:o}}))}function Li(e,t){let r=new T,o=r.pipe(oe(),ae(!0));return r.subscribe(({url:n})=>{e.setAttribute("data-clipboard-text",e.href),e.href=`${n}`}),h(e,"click").pipe(W(o)).subscribe(n=>n.preventDefault()),Ts(e,t).pipe(O(n=>r.next(n)),A(()=>r.complete()),m(n=>P({ref:e},n)))}function Mi(e,{worker$:t,keyboard$:r}){let o=new T,n=Ce("search-query"),i=L(h(n,"keydown"),h(n,"focus")).pipe(xe(pe),m(()=>n.value),Y());return o.pipe(Pe(i),m(([{suggest:a},c])=>{let p=c.split(/([\s-]+)/);if(a!=null&&a.length&&p[p.length-1]){let l=a[a.length-1];l.startsWith(p[p.length-1])&&(p[p.length-1]=l)}else p.length=0;return p})).subscribe(a=>e.innerHTML=a.join("").replace(/\s/g," ")),r.pipe(g(({mode:a})=>a==="search")).subscribe(a=>{a.type==="ArrowRight"&&e.innerText.length&&n.selectionStart===n.value.length&&(n.value=e.innerText)}),t.pipe(g(Sr),m(({data:a})=>a)).pipe(O(a=>o.next(a)),A(()=>o.complete()),m(()=>({ref:e})))}function _i(e,{index$:t,keyboard$:r}){let o=Te();try{let n=Ei(o.search,t),i=Ce("search-query",e),s=Ce("search-result",e);h(e,"click").pipe(g(({target:c})=>c instanceof Element&&!!c.closest("a"))).subscribe(()=>at("search",!1)),r.pipe(g(({mode:c})=>c==="search")).subscribe(c=>{let p=Ne();switch(c.type){case"Enter":if(p===i){let l=new Map;for(let f of M(":first-child [href]",s)){let u=f.firstElementChild;l.set(f,parseFloat(u.getAttribute("data-md-score")))}if(l.size){let[[f]]=[...l].sort(([,u],[,d])=>d-u);f.click()}c.claim()}break;case"Escape":case"Tab":at("search",!1),i.blur();break;case"ArrowUp":case"ArrowDown":if(typeof p=="undefined")i.focus();else{let l=[i,...M(":not(details) > [href], summary, details[open] [href]",s)],f=Math.max(0,(Math.max(0,l.indexOf(p))+l.length+(c.type==="ArrowUp"?-1:1))%l.length);l[f].focus()}c.claim();break;default:i!==Ne()&&i.focus()}}),r.pipe(g(({mode:c})=>c==="global")).subscribe(c=>{switch(c.type){case"f":case"s":case"/":i.focus(),i.select(),c.claim();break}});let a=Si(i,{worker$:n});return L(a,Oi(s,{worker$:n,query$:a})).pipe(Ve(...me("search-share",e).map(c=>Li(c,{query$:a})),...me("search-suggest",e).map(c=>Mi(c,{worker$:n,keyboard$:r}))))}catch(n){return e.hidden=!0,tt}}function Ai(e,{index$:t,location$:r}){return z([t,r.pipe(Q(we()),g(o=>!!o.searchParams.get("h")))]).pipe(m(([o,n])=>xi(o.config)(n.searchParams.get("h"))),m(o=>{var s;let n=new Map,i=document.createNodeIterator(e,NodeFilter.SHOW_TEXT);for(let a=i.nextNode();a;a=i.nextNode())if((s=a.parentElement)!=null&&s.offsetHeight){let c=a.textContent,p=o(c);p.length>c.length&&n.set(a,p)}for(let[a,c]of n){let{childNodes:p}=x("span",null,c);a.replaceWith(...Array.from(p))}return{ref:e,nodes:n}}))}function Ss(e,{viewport$:t,main$:r}){let o=e.closest(".md-grid"),n=o.offsetTop-o.parentElement.offsetTop;return z([r,t]).pipe(m(([{offset:i,height:s},{offset:{y:a}}])=>(s=s+Math.min(n,Math.max(0,a-i))-n,{height:s,locked:a>=i+n})),Y((i,s)=>i.height===s.height&&i.locked===s.locked))}function lo(e,o){var n=o,{header$:t}=n,r=vo(n,["header$"]);let i=j(".md-sidebar__scrollwrap",e),{y:s}=Be(i);return H(()=>{let a=new T,c=a.pipe(oe(),ae(!0)),p=a.pipe($e(0,ye));return p.pipe(te(t)).subscribe({next([{height:l},{height:f}]){i.style.height=`${l-2*s}px`,e.style.top=`${f}px`},complete(){i.style.height="",e.style.top=""}}),p.pipe(Re()).subscribe(()=>{for(let l of M(".md-nav__link--active[href]",e)){if(!l.clientHeight)continue;let f=l.closest(".md-sidebar__scrollwrap");if(typeof f!="undefined"){let u=l.offsetTop-f.offsetTop,{height:d}=de(f);f.scrollTo({top:u-d/2})}}}),fe(M("label[tabindex]",e)).pipe(J(l=>h(l,"click").pipe(xe(pe),m(()=>l),W(c)))).subscribe(l=>{let f=j(`[id="${l.htmlFor}"]`);j(`[aria-labelledby="${l.id}"]`).setAttribute("aria-expanded",`${f.checked}`)}),V("content.tooltips")&&fe(M("abbr[title]",e)).pipe(J(l=>Xe(l,{viewport$})),W(c)).subscribe(),Ss(e,r).pipe(O(l=>a.next(l)),A(()=>a.complete()),m(l=>P({ref:e},l)))})}function Ci(e,t){if(typeof t!="undefined"){let r=`https://api.github.com/repos/${e}/${t}`;return rt(ze(`${r}/releases/latest`).pipe(ve(()=>y),m(o=>({version:o.tag_name})),Qe({})),ze(r).pipe(ve(()=>y),m(o=>({stars:o.stargazers_count,forks:o.forks_count})),Qe({}))).pipe(m(([o,n])=>P(P({},o),n)))}else{let r=`https://api.github.com/users/${e}`;return ze(r).pipe(m(o=>({repositories:o.public_repos})),Qe({}))}}function ki(e,t){let r=`https://${e}/api/v4/projects/${encodeURIComponent(t)}`;return rt(ze(`${r}/releases/permalink/latest`).pipe(ve(()=>y),m(({tag_name:o})=>({version:o})),Qe({})),ze(r).pipe(ve(()=>y),m(({star_count:o,forks_count:n})=>({stars:o,forks:n})),Qe({}))).pipe(m(([o,n])=>P(P({},o),n)))}function Hi(e){let t=e.match(/^.+github\.com\/([^/]+)\/?([^/]+)?/i);if(t){let[,r,o]=t;return Ci(r,o)}if(t=e.match(/^.+?([^/]*gitlab[^/]+)\/(.+?)\/?$/i),t){let[,r,o]=t;return ki(r,o)}return y}var Os;function Ls(e){return Os||(Os=H(()=>{let t=__md_get("__source",sessionStorage);if(t)return $(t);if(me("consent").length){let o=__md_get("__consent");if(!(o&&o.github))return y}return Hi(e.href).pipe(O(o=>__md_set("__source",o,sessionStorage)))}).pipe(ve(()=>y),g(t=>Object.keys(t).length>0),m(t=>({facts:t})),Z(1)))}function $i(e){let t=j(":scope > :last-child",e);return H(()=>{let r=new T;return r.subscribe(({facts:o})=>{t.appendChild(jn(o)),t.classList.add("md-source__repository--active")}),Ls(e).pipe(O(o=>r.next(o)),A(()=>r.complete()),m(o=>P({ref:e},o)))})}function Ms(e,{viewport$:t,header$:r}){return Le(document.body).pipe(b(()=>Er(e,{header$:r,viewport$:t})),m(({offset:{y:o}})=>({hidden:o>=10})),ne("hidden"))}function Pi(e,t){return H(()=>{let r=new T;return r.subscribe({next({hidden:o}){e.hidden=o},complete(){e.hidden=!1}}),(V("navigation.tabs.sticky")?$({hidden:!1}):Ms(e,t)).pipe(O(o=>r.next(o)),A(()=>r.complete()),m(o=>P({ref:e},o)))})}function _s(e,{viewport$:t,header$:r}){let o=new Map,n=M(".md-nav__link",e);for(let a of n){let c=decodeURIComponent(a.hash.substring(1)),p=ue(`[id="${c}"]`);typeof p!="undefined"&&o.set(a,p)}let i=r.pipe(ne("height"),m(({height:a})=>{let c=Ce("main"),p=j(":scope > :first-child",c);return a+.8*(p.offsetTop-c.offsetTop)}),le());return Le(document.body).pipe(ne("height"),b(a=>H(()=>{let c=[];return $([...o].reduce((p,[l,f])=>{for(;c.length&&o.get(c[c.length-1]).tagName>=f.tagName;)c.pop();let u=f.offsetTop;for(;!u&&f.parentElement;)f=f.parentElement,u=f.offsetTop;let d=f.offsetParent;for(;d;d=d.offsetParent)u+=d.offsetTop;return p.set([...c=[...c,l]].reverse(),u)},new Map))}).pipe(m(c=>new Map([...c].sort(([,p],[,l])=>p-l))),Pe(i),b(([c,p])=>t.pipe(Ut(([l,f],{offset:{y:u},size:d})=>{let v=u+d.height>=Math.floor(a.height);for(;f.length;){let[,S]=f[0];if(S-p=u&&!v)f=[l.pop(),...f];else break}return[l,f]},[[],[...c]]),Y((l,f)=>l[0]===f[0]&&l[1]===f[1])))))).pipe(m(([a,c])=>({prev:a.map(([p])=>p),next:c.map(([p])=>p)})),Q({prev:[],next:[]}),ot(2,1),m(([a,c])=>a.prev.length{let i=new T,s=i.pipe(oe(),ae(!0));if(i.subscribe(({prev:a,next:c})=>{for(let[p]of c)p.classList.remove("md-nav__link--passed"),p.classList.remove("md-nav__link--active");for(let[p,[l]]of a.entries())l.classList.add("md-nav__link--passed"),l.classList.toggle("md-nav__link--active",p===a.length-1)}),V("toc.follow")){let a=L(t.pipe(Ae(1),m(()=>{})),t.pipe(Ae(250),m(()=>"smooth")));i.pipe(g(({prev:c})=>c.length>0),Pe(o.pipe(xe(pe))),te(a)).subscribe(([[{prev:c}],p])=>{let[l]=c[c.length-1];if(l.offsetHeight){let f=vr(l);if(typeof f!="undefined"){let u=l.offsetTop-f.offsetTop,{height:d}=de(f);f.scrollTo({top:u-d/2,behavior:p})}}})}return V("navigation.tracking")&&t.pipe(W(s),ne("offset"),Ae(250),Ie(1),W(n.pipe(Ie(1))),vt({delay:250}),te(i)).subscribe(([,{prev:a}])=>{let c=we(),p=a[a.length-1];if(p&&p.length){let[l]=p,{hash:f}=new URL(l.href);c.hash!==f&&(c.hash=f,history.replaceState({},"",`${c}`))}else c.hash="",history.replaceState({},"",`${c}`)}),_s(e,{viewport$:t,header$:r}).pipe(O(a=>i.next(a)),A(()=>i.complete()),m(a=>P({ref:e},a)))})}function As(e,{viewport$:t,main$:r,target$:o}){let n=t.pipe(m(({offset:{y:s}})=>s),ot(2,1),m(([s,a])=>s>a&&a>0),Y()),i=r.pipe(m(({active:s})=>s));return z([i,n]).pipe(m(([s,a])=>!(s&&a)),Y(),W(o.pipe(Ie(1))),ae(!0),vt({delay:250}),m(s=>({hidden:s})))}function Ii(e,{viewport$:t,header$:r,main$:o,target$:n}){let i=new T,s=i.pipe(oe(),ae(!0));return i.subscribe({next({hidden:a}){e.hidden=a,a?(e.setAttribute("tabindex","-1"),e.blur()):e.removeAttribute("tabindex")},complete(){e.style.top="",e.hidden=!0,e.removeAttribute("tabindex")}}),r.pipe(W(s),ne("height")).subscribe(({height:a})=>{e.style.top=`${a+16}px`}),h(e,"click").subscribe(a=>{a.preventDefault(),window.scrollTo({top:0})}),As(e,{viewport$:t,main$:o,target$:n}).pipe(O(a=>i.next(a)),A(()=>i.complete()),m(a=>P({ref:e},a)))}function Fi({document$:e,viewport$:t}){e.pipe(b(()=>M(".md-ellipsis")),J(r=>mt(r).pipe(W(e.pipe(Ie(1))),g(o=>o),m(()=>r),Ee(1))),g(r=>r.offsetWidth{let o=r.innerText,n=r.closest("a")||r;return n.title=o,V("content.tooltips")?Xe(n,{viewport$:t}).pipe(W(e.pipe(Ie(1))),A(()=>n.removeAttribute("title"))):y})).subscribe(),V("content.tooltips")&&e.pipe(b(()=>M(".md-status")),J(r=>Xe(r,{viewport$:t}))).subscribe()}function ji({document$:e,tablet$:t}){e.pipe(b(()=>M(".md-toggle--indeterminate")),O(r=>{r.indeterminate=!0,r.checked=!1}),J(r=>h(r,"change").pipe(Jr(()=>r.classList.contains("md-toggle--indeterminate")),m(()=>r))),te(t)).subscribe(([r,o])=>{r.classList.remove("md-toggle--indeterminate"),o&&(r.checked=!1)})}function Cs(){return/(iPad|iPhone|iPod)/.test(navigator.userAgent)}function Ui({document$:e}){e.pipe(b(()=>M("[data-md-scrollfix]")),O(t=>t.removeAttribute("data-md-scrollfix")),g(Cs),J(t=>h(t,"touchstart").pipe(m(()=>t)))).subscribe(t=>{let r=t.scrollTop;r===0?t.scrollTop=1:r+t.offsetHeight===t.scrollHeight&&(t.scrollTop=r-1)})}function Wi({viewport$:e,tablet$:t}){z([Je("search"),t]).pipe(m(([r,o])=>r&&!o),b(r=>$(r).pipe(nt(r?400:100))),te(e)).subscribe(([r,{offset:{y:o}}])=>{if(r)document.body.setAttribute("data-md-scrolllock",""),document.body.style.top=`-${o}px`;else{let n=-1*parseInt(document.body.style.top,10);document.body.removeAttribute("data-md-scrolllock"),document.body.style.top="",n&&window.scrollTo(0,n)}})}Object.entries||(Object.entries=function(e){let t=[];for(let r of Object.keys(e))t.push([r,e[r]]);return t});Object.values||(Object.values=function(e){let t=[];for(let r of Object.keys(e))t.push(e[r]);return t});typeof Element!="undefined"&&(Element.prototype.scrollTo||(Element.prototype.scrollTo=function(e,t){typeof e=="object"?(this.scrollLeft=e.left,this.scrollTop=e.top):(this.scrollLeft=e,this.scrollTop=t)}),Element.prototype.replaceWith||(Element.prototype.replaceWith=function(...e){let t=this.parentNode;if(t){e.length===0&&t.removeChild(this);for(let r=e.length-1;r>=0;r--){let o=e[r];typeof o=="string"?o=document.createTextNode(o):o.parentNode&&o.parentNode.removeChild(o),r?t.insertBefore(this.previousSibling,o):t.replaceChild(o,this)}}}));function ks(){return location.protocol==="file:"?_t(`${new URL("search/search_index.js",Or.base)}`).pipe(m(()=>__index),Z(1)):ze(new URL("search/search_index.json",Or.base))}document.documentElement.classList.remove("no-js");document.documentElement.classList.add("js");var ct=an(),Kt=bn(),Ht=yn(Kt),mo=hn(),ke=Ln(),Lr=Wt("(min-width: 60em)"),Vi=Wt("(min-width: 76.25em)"),Ni=xn(),Or=Te(),zi=document.forms.namedItem("search")?ks():tt,fo=new T;di({alert$:fo});ui({document$:ct});var uo=new T,qi=kt(Or.base);V("navigation.instant")&&gi({sitemap$:qi,location$:Kt,viewport$:ke,progress$:uo}).subscribe(ct);var Di;((Di=Or.version)==null?void 0:Di.provider)==="mike"&&Ti({document$:ct});L(Kt,Ht).pipe(nt(125)).subscribe(()=>{at("drawer",!1),at("search",!1)});mo.pipe(g(({mode:e})=>e==="global")).subscribe(e=>{switch(e.type){case"p":case",":let t=ue("link[rel=prev]");typeof t!="undefined"&&st(t);break;case"n":case".":let r=ue("link[rel=next]");typeof r!="undefined"&&st(r);break;case"Enter":let o=Ne();o instanceof HTMLLabelElement&&o.click()}});Fi({viewport$:ke,document$:ct});ji({document$:ct,tablet$:Lr});Ui({document$:ct});Wi({viewport$:ke,tablet$:Lr});var ft=ai(Ce("header"),{viewport$:ke}),qt=ct.pipe(m(()=>Ce("main")),b(e=>pi(e,{viewport$:ke,header$:ft})),Z(1)),Hs=L(...me("consent").map(e=>An(e,{target$:Ht})),...me("dialog").map(e=>ni(e,{alert$:fo})),...me("palette").map(e=>li(e)),...me("progress").map(e=>mi(e,{progress$:uo})),...me("search").map(e=>_i(e,{index$:zi,keyboard$:mo})),...me("source").map(e=>$i(e))),$s=H(()=>L(...me("announce").map(e=>_n(e)),...me("content").map(e=>oi(e,{sitemap$:qi,viewport$:ke,target$:Ht,print$:Ni})),...me("content").map(e=>V("search.highlight")?Ai(e,{index$:zi,location$:Kt}):y),...me("header").map(e=>si(e,{viewport$:ke,header$:ft,main$:qt})),...me("header-title").map(e=>ci(e,{viewport$:ke,header$:ft})),...me("sidebar").map(e=>e.getAttribute("data-md-type")==="navigation"?eo(Vi,()=>lo(e,{viewport$:ke,header$:ft,main$:qt})):eo(Lr,()=>lo(e,{viewport$:ke,header$:ft,main$:qt}))),...me("tabs").map(e=>Pi(e,{viewport$:ke,header$:ft})),...me("toc").map(e=>Ri(e,{viewport$:ke,header$:ft,main$:qt,target$:Ht})),...me("top").map(e=>Ii(e,{viewport$:ke,header$:ft,main$:qt,target$:Ht})))),Ki=ct.pipe(b(()=>$s),Ve(Hs),Z(1));Ki.subscribe();window.document$=ct;window.location$=Kt;window.target$=Ht;window.keyboard$=mo;window.viewport$=ke;window.tablet$=Lr;window.screen$=Vi;window.print$=Ni;window.alert$=fo;window.progress$=uo;window.component$=Ki;})(); -//# sourceMappingURL=bundle.79ae519e.min.js.map - diff --git a/site/assets/javascripts/bundle.79ae519e.min.js.map b/site/assets/javascripts/bundle.79ae519e.min.js.map deleted file mode 100644 index 5cf0289..0000000 --- a/site/assets/javascripts/bundle.79ae519e.min.js.map +++ /dev/null @@ -1,7 +0,0 @@ -{ - "version": 3, - "sources": ["node_modules/focus-visible/dist/focus-visible.js", "node_modules/escape-html/index.js", "node_modules/clipboard/dist/clipboard.js", "src/templates/assets/javascripts/bundle.ts", "node_modules/tslib/tslib.es6.mjs", "node_modules/rxjs/src/internal/util/isFunction.ts", "node_modules/rxjs/src/internal/util/createErrorClass.ts", "node_modules/rxjs/src/internal/util/UnsubscriptionError.ts", "node_modules/rxjs/src/internal/util/arrRemove.ts", "node_modules/rxjs/src/internal/Subscription.ts", "node_modules/rxjs/src/internal/config.ts", "node_modules/rxjs/src/internal/scheduler/timeoutProvider.ts", "node_modules/rxjs/src/internal/util/reportUnhandledError.ts", "node_modules/rxjs/src/internal/util/noop.ts", "node_modules/rxjs/src/internal/NotificationFactories.ts", "node_modules/rxjs/src/internal/util/errorContext.ts", "node_modules/rxjs/src/internal/Subscriber.ts", "node_modules/rxjs/src/internal/symbol/observable.ts", "node_modules/rxjs/src/internal/util/identity.ts", "node_modules/rxjs/src/internal/util/pipe.ts", "node_modules/rxjs/src/internal/Observable.ts", "node_modules/rxjs/src/internal/util/lift.ts", "node_modules/rxjs/src/internal/operators/OperatorSubscriber.ts", "node_modules/rxjs/src/internal/scheduler/animationFrameProvider.ts", "node_modules/rxjs/src/internal/util/ObjectUnsubscribedError.ts", "node_modules/rxjs/src/internal/Subject.ts", "node_modules/rxjs/src/internal/BehaviorSubject.ts", "node_modules/rxjs/src/internal/scheduler/dateTimestampProvider.ts", "node_modules/rxjs/src/internal/ReplaySubject.ts", "node_modules/rxjs/src/internal/scheduler/Action.ts", "node_modules/rxjs/src/internal/scheduler/intervalProvider.ts", "node_modules/rxjs/src/internal/scheduler/AsyncAction.ts", "node_modules/rxjs/src/internal/Scheduler.ts", "node_modules/rxjs/src/internal/scheduler/AsyncScheduler.ts", "node_modules/rxjs/src/internal/scheduler/async.ts", "node_modules/rxjs/src/internal/scheduler/QueueAction.ts", "node_modules/rxjs/src/internal/scheduler/QueueScheduler.ts", "node_modules/rxjs/src/internal/scheduler/queue.ts", "node_modules/rxjs/src/internal/scheduler/AnimationFrameAction.ts", "node_modules/rxjs/src/internal/scheduler/AnimationFrameScheduler.ts", "node_modules/rxjs/src/internal/scheduler/animationFrame.ts", "node_modules/rxjs/src/internal/observable/empty.ts", "node_modules/rxjs/src/internal/util/isScheduler.ts", "node_modules/rxjs/src/internal/util/args.ts", "node_modules/rxjs/src/internal/util/isArrayLike.ts", "node_modules/rxjs/src/internal/util/isPromise.ts", "node_modules/rxjs/src/internal/util/isInteropObservable.ts", "node_modules/rxjs/src/internal/util/isAsyncIterable.ts", "node_modules/rxjs/src/internal/util/throwUnobservableError.ts", "node_modules/rxjs/src/internal/symbol/iterator.ts", "node_modules/rxjs/src/internal/util/isIterable.ts", "node_modules/rxjs/src/internal/util/isReadableStreamLike.ts", "node_modules/rxjs/src/internal/observable/innerFrom.ts", "node_modules/rxjs/src/internal/util/executeSchedule.ts", "node_modules/rxjs/src/internal/operators/observeOn.ts", "node_modules/rxjs/src/internal/operators/subscribeOn.ts", "node_modules/rxjs/src/internal/scheduled/scheduleObservable.ts", "node_modules/rxjs/src/internal/scheduled/schedulePromise.ts", "node_modules/rxjs/src/internal/scheduled/scheduleArray.ts", "node_modules/rxjs/src/internal/scheduled/scheduleIterable.ts", "node_modules/rxjs/src/internal/scheduled/scheduleAsyncIterable.ts", "node_modules/rxjs/src/internal/scheduled/scheduleReadableStreamLike.ts", "node_modules/rxjs/src/internal/scheduled/scheduled.ts", "node_modules/rxjs/src/internal/observable/from.ts", "node_modules/rxjs/src/internal/observable/of.ts", "node_modules/rxjs/src/internal/observable/throwError.ts", "node_modules/rxjs/src/internal/util/EmptyError.ts", "node_modules/rxjs/src/internal/util/isDate.ts", "node_modules/rxjs/src/internal/operators/map.ts", "node_modules/rxjs/src/internal/util/mapOneOrManyArgs.ts", "node_modules/rxjs/src/internal/util/argsArgArrayOrObject.ts", "node_modules/rxjs/src/internal/util/createObject.ts", "node_modules/rxjs/src/internal/observable/combineLatest.ts", "node_modules/rxjs/src/internal/operators/mergeInternals.ts", "node_modules/rxjs/src/internal/operators/mergeMap.ts", "node_modules/rxjs/src/internal/operators/mergeAll.ts", "node_modules/rxjs/src/internal/operators/concatAll.ts", "node_modules/rxjs/src/internal/observable/concat.ts", "node_modules/rxjs/src/internal/observable/defer.ts", "node_modules/rxjs/src/internal/observable/fromEvent.ts", "node_modules/rxjs/src/internal/observable/fromEventPattern.ts", "node_modules/rxjs/src/internal/observable/timer.ts", "node_modules/rxjs/src/internal/observable/merge.ts", "node_modules/rxjs/src/internal/observable/never.ts", "node_modules/rxjs/src/internal/util/argsOrArgArray.ts", "node_modules/rxjs/src/internal/operators/filter.ts", "node_modules/rxjs/src/internal/observable/zip.ts", "node_modules/rxjs/src/internal/operators/audit.ts", "node_modules/rxjs/src/internal/operators/auditTime.ts", "node_modules/rxjs/src/internal/operators/bufferCount.ts", "node_modules/rxjs/src/internal/operators/catchError.ts", "node_modules/rxjs/src/internal/operators/scanInternals.ts", "node_modules/rxjs/src/internal/operators/combineLatest.ts", "node_modules/rxjs/src/internal/operators/combineLatestWith.ts", "node_modules/rxjs/src/internal/operators/debounce.ts", "node_modules/rxjs/src/internal/operators/debounceTime.ts", "node_modules/rxjs/src/internal/operators/defaultIfEmpty.ts", "node_modules/rxjs/src/internal/operators/take.ts", "node_modules/rxjs/src/internal/operators/ignoreElements.ts", "node_modules/rxjs/src/internal/operators/mapTo.ts", "node_modules/rxjs/src/internal/operators/delayWhen.ts", "node_modules/rxjs/src/internal/operators/delay.ts", "node_modules/rxjs/src/internal/operators/distinct.ts", "node_modules/rxjs/src/internal/operators/distinctUntilChanged.ts", "node_modules/rxjs/src/internal/operators/distinctUntilKeyChanged.ts", "node_modules/rxjs/src/internal/operators/throwIfEmpty.ts", "node_modules/rxjs/src/internal/operators/endWith.ts", "node_modules/rxjs/src/internal/operators/exhaustMap.ts", "node_modules/rxjs/src/internal/operators/finalize.ts", "node_modules/rxjs/src/internal/operators/first.ts", "node_modules/rxjs/src/internal/operators/takeLast.ts", "node_modules/rxjs/src/internal/operators/merge.ts", "node_modules/rxjs/src/internal/operators/mergeWith.ts", "node_modules/rxjs/src/internal/operators/repeat.ts", "node_modules/rxjs/src/internal/operators/scan.ts", "node_modules/rxjs/src/internal/operators/share.ts", "node_modules/rxjs/src/internal/operators/shareReplay.ts", "node_modules/rxjs/src/internal/operators/skip.ts", "node_modules/rxjs/src/internal/operators/skipUntil.ts", "node_modules/rxjs/src/internal/operators/startWith.ts", "node_modules/rxjs/src/internal/operators/switchMap.ts", "node_modules/rxjs/src/internal/operators/takeUntil.ts", "node_modules/rxjs/src/internal/operators/takeWhile.ts", "node_modules/rxjs/src/internal/operators/tap.ts", "node_modules/rxjs/src/internal/operators/throttle.ts", "node_modules/rxjs/src/internal/operators/throttleTime.ts", "node_modules/rxjs/src/internal/operators/withLatestFrom.ts", "node_modules/rxjs/src/internal/operators/zip.ts", "node_modules/rxjs/src/internal/operators/zipWith.ts", "src/templates/assets/javascripts/browser/document/index.ts", "src/templates/assets/javascripts/browser/element/_/index.ts", "src/templates/assets/javascripts/browser/element/focus/index.ts", "src/templates/assets/javascripts/browser/element/hover/index.ts", "src/templates/assets/javascripts/utilities/h/index.ts", "src/templates/assets/javascripts/utilities/round/index.ts", "src/templates/assets/javascripts/browser/script/index.ts", "src/templates/assets/javascripts/browser/element/size/_/index.ts", "src/templates/assets/javascripts/browser/element/size/content/index.ts", "src/templates/assets/javascripts/browser/element/offset/_/index.ts", "src/templates/assets/javascripts/browser/element/offset/content/index.ts", "src/templates/assets/javascripts/browser/element/visibility/index.ts", "src/templates/assets/javascripts/browser/toggle/index.ts", "src/templates/assets/javascripts/browser/keyboard/index.ts", "src/templates/assets/javascripts/browser/location/_/index.ts", "src/templates/assets/javascripts/browser/location/hash/index.ts", "src/templates/assets/javascripts/browser/media/index.ts", "src/templates/assets/javascripts/browser/request/index.ts", "src/templates/assets/javascripts/browser/viewport/offset/index.ts", "src/templates/assets/javascripts/browser/viewport/size/index.ts", "src/templates/assets/javascripts/browser/viewport/_/index.ts", "src/templates/assets/javascripts/browser/viewport/at/index.ts", "src/templates/assets/javascripts/browser/worker/index.ts", "src/templates/assets/javascripts/_/index.ts", "src/templates/assets/javascripts/components/_/index.ts", "src/templates/assets/javascripts/components/announce/index.ts", "src/templates/assets/javascripts/components/consent/index.ts", "src/templates/assets/javascripts/templates/tooltip/index.tsx", "src/templates/assets/javascripts/templates/annotation/index.tsx", "src/templates/assets/javascripts/templates/clipboard/index.tsx", "src/templates/assets/javascripts/templates/search/index.tsx", "src/templates/assets/javascripts/templates/source/index.tsx", "src/templates/assets/javascripts/templates/tabbed/index.tsx", "src/templates/assets/javascripts/templates/table/index.tsx", "src/templates/assets/javascripts/templates/version/index.tsx", "src/templates/assets/javascripts/components/tooltip2/index.ts", "src/templates/assets/javascripts/components/content/annotation/_/index.ts", "src/templates/assets/javascripts/components/content/annotation/list/index.ts", "src/templates/assets/javascripts/components/content/annotation/block/index.ts", "src/templates/assets/javascripts/components/content/code/_/index.ts", "src/templates/assets/javascripts/components/content/details/index.ts", "src/templates/assets/javascripts/components/content/link/index.ts", "src/templates/assets/javascripts/components/content/mermaid/index.css", "src/templates/assets/javascripts/components/content/mermaid/index.ts", "src/templates/assets/javascripts/components/content/table/index.ts", "src/templates/assets/javascripts/components/content/tabs/index.ts", "src/templates/assets/javascripts/components/content/_/index.ts", "src/templates/assets/javascripts/components/dialog/index.ts", "src/templates/assets/javascripts/components/tooltip/index.ts", "src/templates/assets/javascripts/components/header/_/index.ts", "src/templates/assets/javascripts/components/header/title/index.ts", "src/templates/assets/javascripts/components/main/index.ts", "src/templates/assets/javascripts/components/palette/index.ts", "src/templates/assets/javascripts/components/progress/index.ts", "src/templates/assets/javascripts/integrations/sitemap/index.ts", "src/templates/assets/javascripts/integrations/alternate/index.ts", "src/templates/assets/javascripts/integrations/clipboard/index.ts", "src/templates/assets/javascripts/integrations/instant/index.ts", "src/templates/assets/javascripts/integrations/search/highlighter/index.ts", "src/templates/assets/javascripts/integrations/search/worker/message/index.ts", "src/templates/assets/javascripts/integrations/search/worker/_/index.ts", "src/templates/assets/javascripts/integrations/version/findurl/index.ts", "src/templates/assets/javascripts/integrations/version/index.ts", "src/templates/assets/javascripts/components/search/query/index.ts", "src/templates/assets/javascripts/components/search/result/index.ts", "src/templates/assets/javascripts/components/search/share/index.ts", "src/templates/assets/javascripts/components/search/suggest/index.ts", "src/templates/assets/javascripts/components/search/_/index.ts", "src/templates/assets/javascripts/components/search/highlight/index.ts", "src/templates/assets/javascripts/components/sidebar/index.ts", "src/templates/assets/javascripts/components/source/facts/github/index.ts", "src/templates/assets/javascripts/components/source/facts/gitlab/index.ts", "src/templates/assets/javascripts/components/source/facts/_/index.ts", "src/templates/assets/javascripts/components/source/_/index.ts", "src/templates/assets/javascripts/components/tabs/index.ts", "src/templates/assets/javascripts/components/toc/index.ts", "src/templates/assets/javascripts/components/top/index.ts", "src/templates/assets/javascripts/patches/ellipsis/index.ts", "src/templates/assets/javascripts/patches/indeterminate/index.ts", "src/templates/assets/javascripts/patches/scrollfix/index.ts", "src/templates/assets/javascripts/patches/scrolllock/index.ts", "src/templates/assets/javascripts/polyfills/index.ts"], - "sourcesContent": ["(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? factory() :\n typeof define === 'function' && define.amd ? define(factory) :\n (factory());\n}(this, (function () { 'use strict';\n\n /**\n * Applies the :focus-visible polyfill at the given scope.\n * A scope in this case is either the top-level Document or a Shadow Root.\n *\n * @param {(Document|ShadowRoot)} scope\n * @see https://github.com/WICG/focus-visible\n */\n function applyFocusVisiblePolyfill(scope) {\n var hadKeyboardEvent = true;\n var hadFocusVisibleRecently = false;\n var hadFocusVisibleRecentlyTimeout = null;\n\n var inputTypesAllowlist = {\n text: true,\n search: true,\n url: true,\n tel: true,\n email: true,\n password: true,\n number: true,\n date: true,\n month: true,\n week: true,\n time: true,\n datetime: true,\n 'datetime-local': true\n };\n\n /**\n * Helper function for legacy browsers and iframes which sometimes focus\n * elements like document, body, and non-interactive SVG.\n * @param {Element} el\n */\n function isValidFocusTarget(el) {\n if (\n el &&\n el !== document &&\n el.nodeName !== 'HTML' &&\n el.nodeName !== 'BODY' &&\n 'classList' in el &&\n 'contains' in el.classList\n ) {\n return true;\n }\n return false;\n }\n\n /**\n * Computes whether the given element should automatically trigger the\n * `focus-visible` class being added, i.e. whether it should always match\n * `:focus-visible` when focused.\n * @param {Element} el\n * @return {boolean}\n */\n function focusTriggersKeyboardModality(el) {\n var type = el.type;\n var tagName = el.tagName;\n\n if (tagName === 'INPUT' && inputTypesAllowlist[type] && !el.readOnly) {\n return true;\n }\n\n if (tagName === 'TEXTAREA' && !el.readOnly) {\n return true;\n }\n\n if (el.isContentEditable) {\n return true;\n }\n\n return false;\n }\n\n /**\n * Add the `focus-visible` class to the given element if it was not added by\n * the author.\n * @param {Element} el\n */\n function addFocusVisibleClass(el) {\n if (el.classList.contains('focus-visible')) {\n return;\n }\n el.classList.add('focus-visible');\n el.setAttribute('data-focus-visible-added', '');\n }\n\n /**\n * Remove the `focus-visible` class from the given element if it was not\n * originally added by the author.\n * @param {Element} el\n */\n function removeFocusVisibleClass(el) {\n if (!el.hasAttribute('data-focus-visible-added')) {\n return;\n }\n el.classList.remove('focus-visible');\n el.removeAttribute('data-focus-visible-added');\n }\n\n /**\n * If the most recent user interaction was via the keyboard;\n * and the key press did not include a meta, alt/option, or control key;\n * then the modality is keyboard. Otherwise, the modality is not keyboard.\n * Apply `focus-visible` to any current active element and keep track\n * of our keyboard modality state with `hadKeyboardEvent`.\n * @param {KeyboardEvent} e\n */\n function onKeyDown(e) {\n if (e.metaKey || e.altKey || e.ctrlKey) {\n return;\n }\n\n if (isValidFocusTarget(scope.activeElement)) {\n addFocusVisibleClass(scope.activeElement);\n }\n\n hadKeyboardEvent = true;\n }\n\n /**\n * If at any point a user clicks with a pointing device, ensure that we change\n * the modality away from keyboard.\n * This avoids the situation where a user presses a key on an already focused\n * element, and then clicks on a different element, focusing it with a\n * pointing device, while we still think we're in keyboard modality.\n * @param {Event} e\n */\n function onPointerDown(e) {\n hadKeyboardEvent = false;\n }\n\n /**\n * On `focus`, add the `focus-visible` class to the target if:\n * - the target received focus as a result of keyboard navigation, or\n * - the event target is an element that will likely require interaction\n * via the keyboard (e.g. a text box)\n * @param {Event} e\n */\n function onFocus(e) {\n // Prevent IE from focusing the document or HTML element.\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (hadKeyboardEvent || focusTriggersKeyboardModality(e.target)) {\n addFocusVisibleClass(e.target);\n }\n }\n\n /**\n * On `blur`, remove the `focus-visible` class from the target.\n * @param {Event} e\n */\n function onBlur(e) {\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (\n e.target.classList.contains('focus-visible') ||\n e.target.hasAttribute('data-focus-visible-added')\n ) {\n // To detect a tab/window switch, we look for a blur event followed\n // rapidly by a visibility change.\n // If we don't see a visibility change within 100ms, it's probably a\n // regular focus change.\n hadFocusVisibleRecently = true;\n window.clearTimeout(hadFocusVisibleRecentlyTimeout);\n hadFocusVisibleRecentlyTimeout = window.setTimeout(function() {\n hadFocusVisibleRecently = false;\n }, 100);\n removeFocusVisibleClass(e.target);\n }\n }\n\n /**\n * If the user changes tabs, keep track of whether or not the previously\n * focused element had .focus-visible.\n * @param {Event} e\n */\n function onVisibilityChange(e) {\n if (document.visibilityState === 'hidden') {\n // If the tab becomes active again, the browser will handle calling focus\n // on the element (Safari actually calls it twice).\n // If this tab change caused a blur on an element with focus-visible,\n // re-apply the class when the user switches back to the tab.\n if (hadFocusVisibleRecently) {\n hadKeyboardEvent = true;\n }\n addInitialPointerMoveListeners();\n }\n }\n\n /**\n * Add a group of listeners to detect usage of any pointing devices.\n * These listeners will be added when the polyfill first loads, and anytime\n * the window is blurred, so that they are active when the window regains\n * focus.\n */\n function addInitialPointerMoveListeners() {\n document.addEventListener('mousemove', onInitialPointerMove);\n document.addEventListener('mousedown', onInitialPointerMove);\n document.addEventListener('mouseup', onInitialPointerMove);\n document.addEventListener('pointermove', onInitialPointerMove);\n document.addEventListener('pointerdown', onInitialPointerMove);\n document.addEventListener('pointerup', onInitialPointerMove);\n document.addEventListener('touchmove', onInitialPointerMove);\n document.addEventListener('touchstart', onInitialPointerMove);\n document.addEventListener('touchend', onInitialPointerMove);\n }\n\n function removeInitialPointerMoveListeners() {\n document.removeEventListener('mousemove', onInitialPointerMove);\n document.removeEventListener('mousedown', onInitialPointerMove);\n document.removeEventListener('mouseup', onInitialPointerMove);\n document.removeEventListener('pointermove', onInitialPointerMove);\n document.removeEventListener('pointerdown', onInitialPointerMove);\n document.removeEventListener('pointerup', onInitialPointerMove);\n document.removeEventListener('touchmove', onInitialPointerMove);\n document.removeEventListener('touchstart', onInitialPointerMove);\n document.removeEventListener('touchend', onInitialPointerMove);\n }\n\n /**\n * When the polfyill first loads, assume the user is in keyboard modality.\n * If any event is received from a pointing device (e.g. mouse, pointer,\n * touch), turn off keyboard modality.\n * This accounts for situations where focus enters the page from the URL bar.\n * @param {Event} e\n */\n function onInitialPointerMove(e) {\n // Work around a Safari quirk that fires a mousemove on whenever the\n // window blurs, even if you're tabbing out of the page. \u00AF\\_(\u30C4)_/\u00AF\n if (e.target.nodeName && e.target.nodeName.toLowerCase() === 'html') {\n return;\n }\n\n hadKeyboardEvent = false;\n removeInitialPointerMoveListeners();\n }\n\n // For some kinds of state, we are interested in changes at the global scope\n // only. For example, global pointer input, global key presses and global\n // visibility change should affect the state at every scope:\n document.addEventListener('keydown', onKeyDown, true);\n document.addEventListener('mousedown', onPointerDown, true);\n document.addEventListener('pointerdown', onPointerDown, true);\n document.addEventListener('touchstart', onPointerDown, true);\n document.addEventListener('visibilitychange', onVisibilityChange, true);\n\n addInitialPointerMoveListeners();\n\n // For focus and blur, we specifically care about state changes in the local\n // scope. This is because focus / blur events that originate from within a\n // shadow root are not re-dispatched from the host element if it was already\n // the active element in its own scope:\n scope.addEventListener('focus', onFocus, true);\n scope.addEventListener('blur', onBlur, true);\n\n // We detect that a node is a ShadowRoot by ensuring that it is a\n // DocumentFragment and also has a host property. This check covers native\n // implementation and polyfill implementation transparently. If we only cared\n // about the native implementation, we could just check if the scope was\n // an instance of a ShadowRoot.\n if (scope.nodeType === Node.DOCUMENT_FRAGMENT_NODE && scope.host) {\n // Since a ShadowRoot is a special kind of DocumentFragment, it does not\n // have a root element to add a class to. So, we add this attribute to the\n // host element instead:\n scope.host.setAttribute('data-js-focus-visible', '');\n } else if (scope.nodeType === Node.DOCUMENT_NODE) {\n document.documentElement.classList.add('js-focus-visible');\n document.documentElement.setAttribute('data-js-focus-visible', '');\n }\n }\n\n // It is important to wrap all references to global window and document in\n // these checks to support server-side rendering use cases\n // @see https://github.com/WICG/focus-visible/issues/199\n if (typeof window !== 'undefined' && typeof document !== 'undefined') {\n // Make the polyfill helper globally available. This can be used as a signal\n // to interested libraries that wish to coordinate with the polyfill for e.g.,\n // applying the polyfill to a shadow root:\n window.applyFocusVisiblePolyfill = applyFocusVisiblePolyfill;\n\n // Notify interested libraries of the polyfill's presence, in case the\n // polyfill was loaded lazily:\n var event;\n\n try {\n event = new CustomEvent('focus-visible-polyfill-ready');\n } catch (error) {\n // IE11 does not support using CustomEvent as a constructor directly:\n event = document.createEvent('CustomEvent');\n event.initCustomEvent('focus-visible-polyfill-ready', false, false, {});\n }\n\n window.dispatchEvent(event);\n }\n\n if (typeof document !== 'undefined') {\n // Apply the polyfill to the global document, so that no JavaScript\n // coordination is required to use the polyfill in the top-level document:\n applyFocusVisiblePolyfill(document);\n }\n\n})));\n", "/*!\n * escape-html\n * Copyright(c) 2012-2013 TJ Holowaychuk\n * Copyright(c) 2015 Andreas Lubbe\n * Copyright(c) 2015 Tiancheng \"Timothy\" Gu\n * MIT Licensed\n */\n\n'use strict';\n\n/**\n * Module variables.\n * @private\n */\n\nvar matchHtmlRegExp = /[\"'&<>]/;\n\n/**\n * Module exports.\n * @public\n */\n\nmodule.exports = escapeHtml;\n\n/**\n * Escape special characters in the given string of html.\n *\n * @param {string} string The string to escape for inserting into HTML\n * @return {string}\n * @public\n */\n\nfunction escapeHtml(string) {\n var str = '' + string;\n var match = matchHtmlRegExp.exec(str);\n\n if (!match) {\n return str;\n }\n\n var escape;\n var html = '';\n var index = 0;\n var lastIndex = 0;\n\n for (index = match.index; index < str.length; index++) {\n switch (str.charCodeAt(index)) {\n case 34: // \"\n escape = '"';\n break;\n case 38: // &\n escape = '&';\n break;\n case 39: // '\n escape = ''';\n break;\n case 60: // <\n escape = '<';\n break;\n case 62: // >\n escape = '>';\n break;\n default:\n continue;\n }\n\n if (lastIndex !== index) {\n html += str.substring(lastIndex, index);\n }\n\n lastIndex = index + 1;\n html += escape;\n }\n\n return lastIndex !== index\n ? html + str.substring(lastIndex, index)\n : html;\n}\n", "/*!\n * clipboard.js v2.0.11\n * https://clipboardjs.com/\n *\n * Licensed MIT \u00A9 Zeno Rocha\n */\n(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"ClipboardJS\"] = factory();\n\telse\n\t\troot[\"ClipboardJS\"] = factory();\n})(this, function() {\nreturn /******/ (function() { // webpackBootstrap\n/******/ \tvar __webpack_modules__ = ({\n\n/***/ 686:\n/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n\n// EXPORTS\n__webpack_require__.d(__webpack_exports__, {\n \"default\": function() { return /* binding */ clipboard; }\n});\n\n// EXTERNAL MODULE: ./node_modules/tiny-emitter/index.js\nvar tiny_emitter = __webpack_require__(279);\nvar tiny_emitter_default = /*#__PURE__*/__webpack_require__.n(tiny_emitter);\n// EXTERNAL MODULE: ./node_modules/good-listener/src/listen.js\nvar listen = __webpack_require__(370);\nvar listen_default = /*#__PURE__*/__webpack_require__.n(listen);\n// EXTERNAL MODULE: ./node_modules/select/src/select.js\nvar src_select = __webpack_require__(817);\nvar select_default = /*#__PURE__*/__webpack_require__.n(src_select);\n;// CONCATENATED MODULE: ./src/common/command.js\n/**\n * Executes a given operation type.\n * @param {String} type\n * @return {Boolean}\n */\nfunction command(type) {\n try {\n return document.execCommand(type);\n } catch (err) {\n return false;\n }\n}\n;// CONCATENATED MODULE: ./src/actions/cut.js\n\n\n/**\n * Cut action wrapper.\n * @param {String|HTMLElement} target\n * @return {String}\n */\n\nvar ClipboardActionCut = function ClipboardActionCut(target) {\n var selectedText = select_default()(target);\n command('cut');\n return selectedText;\n};\n\n/* harmony default export */ var actions_cut = (ClipboardActionCut);\n;// CONCATENATED MODULE: ./src/common/create-fake-element.js\n/**\n * Creates a fake textarea element with a value.\n * @param {String} value\n * @return {HTMLElement}\n */\nfunction createFakeElement(value) {\n var isRTL = document.documentElement.getAttribute('dir') === 'rtl';\n var fakeElement = document.createElement('textarea'); // Prevent zooming on iOS\n\n fakeElement.style.fontSize = '12pt'; // Reset box model\n\n fakeElement.style.border = '0';\n fakeElement.style.padding = '0';\n fakeElement.style.margin = '0'; // Move element out of screen horizontally\n\n fakeElement.style.position = 'absolute';\n fakeElement.style[isRTL ? 'right' : 'left'] = '-9999px'; // Move element to the same position vertically\n\n var yPosition = window.pageYOffset || document.documentElement.scrollTop;\n fakeElement.style.top = \"\".concat(yPosition, \"px\");\n fakeElement.setAttribute('readonly', '');\n fakeElement.value = value;\n return fakeElement;\n}\n;// CONCATENATED MODULE: ./src/actions/copy.js\n\n\n\n/**\n * Create fake copy action wrapper using a fake element.\n * @param {String} target\n * @param {Object} options\n * @return {String}\n */\n\nvar fakeCopyAction = function fakeCopyAction(value, options) {\n var fakeElement = createFakeElement(value);\n options.container.appendChild(fakeElement);\n var selectedText = select_default()(fakeElement);\n command('copy');\n fakeElement.remove();\n return selectedText;\n};\n/**\n * Copy action wrapper.\n * @param {String|HTMLElement} target\n * @param {Object} options\n * @return {String}\n */\n\n\nvar ClipboardActionCopy = function ClipboardActionCopy(target) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {\n container: document.body\n };\n var selectedText = '';\n\n if (typeof target === 'string') {\n selectedText = fakeCopyAction(target, options);\n } else if (target instanceof HTMLInputElement && !['text', 'search', 'url', 'tel', 'password'].includes(target === null || target === void 0 ? void 0 : target.type)) {\n // If input type doesn't support `setSelectionRange`. Simulate it. https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/setSelectionRange\n selectedText = fakeCopyAction(target.value, options);\n } else {\n selectedText = select_default()(target);\n command('copy');\n }\n\n return selectedText;\n};\n\n/* harmony default export */ var actions_copy = (ClipboardActionCopy);\n;// CONCATENATED MODULE: ./src/actions/default.js\nfunction _typeof(obj) { \"@babel/helpers - typeof\"; if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return _typeof(obj); }\n\n\n\n/**\n * Inner function which performs selection from either `text` or `target`\n * properties and then executes copy or cut operations.\n * @param {Object} options\n */\n\nvar ClipboardActionDefault = function ClipboardActionDefault() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n // Defines base properties passed from constructor.\n var _options$action = options.action,\n action = _options$action === void 0 ? 'copy' : _options$action,\n container = options.container,\n target = options.target,\n text = options.text; // Sets the `action` to be performed which can be either 'copy' or 'cut'.\n\n if (action !== 'copy' && action !== 'cut') {\n throw new Error('Invalid \"action\" value, use either \"copy\" or \"cut\"');\n } // Sets the `target` property using an element that will be have its content copied.\n\n\n if (target !== undefined) {\n if (target && _typeof(target) === 'object' && target.nodeType === 1) {\n if (action === 'copy' && target.hasAttribute('disabled')) {\n throw new Error('Invalid \"target\" attribute. Please use \"readonly\" instead of \"disabled\" attribute');\n }\n\n if (action === 'cut' && (target.hasAttribute('readonly') || target.hasAttribute('disabled'))) {\n throw new Error('Invalid \"target\" attribute. You can\\'t cut text from elements with \"readonly\" or \"disabled\" attributes');\n }\n } else {\n throw new Error('Invalid \"target\" value, use a valid Element');\n }\n } // Define selection strategy based on `text` property.\n\n\n if (text) {\n return actions_copy(text, {\n container: container\n });\n } // Defines which selection strategy based on `target` property.\n\n\n if (target) {\n return action === 'cut' ? actions_cut(target) : actions_copy(target, {\n container: container\n });\n }\n};\n\n/* harmony default export */ var actions_default = (ClipboardActionDefault);\n;// CONCATENATED MODULE: ./src/clipboard.js\nfunction clipboard_typeof(obj) { \"@babel/helpers - typeof\"; if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { clipboard_typeof = function _typeof(obj) { return typeof obj; }; } else { clipboard_typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return clipboard_typeof(obj); }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function\"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }\n\nfunction _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }\n\nfunction _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }\n\nfunction _possibleConstructorReturn(self, call) { if (call && (clipboard_typeof(call) === \"object\" || typeof call === \"function\")) { return call; } return _assertThisInitialized(self); }\n\nfunction _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return self; }\n\nfunction _isNativeReflectConstruct() { if (typeof Reflect === \"undefined\" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === \"function\") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }\n\nfunction _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }\n\n\n\n\n\n\n/**\n * Helper function to retrieve attribute value.\n * @param {String} suffix\n * @param {Element} element\n */\n\nfunction getAttributeValue(suffix, element) {\n var attribute = \"data-clipboard-\".concat(suffix);\n\n if (!element.hasAttribute(attribute)) {\n return;\n }\n\n return element.getAttribute(attribute);\n}\n/**\n * Base class which takes one or more elements, adds event listeners to them,\n * and instantiates a new `ClipboardAction` on each click.\n */\n\n\nvar Clipboard = /*#__PURE__*/function (_Emitter) {\n _inherits(Clipboard, _Emitter);\n\n var _super = _createSuper(Clipboard);\n\n /**\n * @param {String|HTMLElement|HTMLCollection|NodeList} trigger\n * @param {Object} options\n */\n function Clipboard(trigger, options) {\n var _this;\n\n _classCallCheck(this, Clipboard);\n\n _this = _super.call(this);\n\n _this.resolveOptions(options);\n\n _this.listenClick(trigger);\n\n return _this;\n }\n /**\n * Defines if attributes would be resolved using internal setter functions\n * or custom functions that were passed in the constructor.\n * @param {Object} options\n */\n\n\n _createClass(Clipboard, [{\n key: \"resolveOptions\",\n value: function resolveOptions() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n this.action = typeof options.action === 'function' ? options.action : this.defaultAction;\n this.target = typeof options.target === 'function' ? options.target : this.defaultTarget;\n this.text = typeof options.text === 'function' ? options.text : this.defaultText;\n this.container = clipboard_typeof(options.container) === 'object' ? options.container : document.body;\n }\n /**\n * Adds a click event listener to the passed trigger.\n * @param {String|HTMLElement|HTMLCollection|NodeList} trigger\n */\n\n }, {\n key: \"listenClick\",\n value: function listenClick(trigger) {\n var _this2 = this;\n\n this.listener = listen_default()(trigger, 'click', function (e) {\n return _this2.onClick(e);\n });\n }\n /**\n * Defines a new `ClipboardAction` on each click event.\n * @param {Event} e\n */\n\n }, {\n key: \"onClick\",\n value: function onClick(e) {\n var trigger = e.delegateTarget || e.currentTarget;\n var action = this.action(trigger) || 'copy';\n var text = actions_default({\n action: action,\n container: this.container,\n target: this.target(trigger),\n text: this.text(trigger)\n }); // Fires an event based on the copy operation result.\n\n this.emit(text ? 'success' : 'error', {\n action: action,\n text: text,\n trigger: trigger,\n clearSelection: function clearSelection() {\n if (trigger) {\n trigger.focus();\n }\n\n window.getSelection().removeAllRanges();\n }\n });\n }\n /**\n * Default `action` lookup function.\n * @param {Element} trigger\n */\n\n }, {\n key: \"defaultAction\",\n value: function defaultAction(trigger) {\n return getAttributeValue('action', trigger);\n }\n /**\n * Default `target` lookup function.\n * @param {Element} trigger\n */\n\n }, {\n key: \"defaultTarget\",\n value: function defaultTarget(trigger) {\n var selector = getAttributeValue('target', trigger);\n\n if (selector) {\n return document.querySelector(selector);\n }\n }\n /**\n * Allow fire programmatically a copy action\n * @param {String|HTMLElement} target\n * @param {Object} options\n * @returns Text copied.\n */\n\n }, {\n key: \"defaultText\",\n\n /**\n * Default `text` lookup function.\n * @param {Element} trigger\n */\n value: function defaultText(trigger) {\n return getAttributeValue('text', trigger);\n }\n /**\n * Destroy lifecycle.\n */\n\n }, {\n key: \"destroy\",\n value: function destroy() {\n this.listener.destroy();\n }\n }], [{\n key: \"copy\",\n value: function copy(target) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {\n container: document.body\n };\n return actions_copy(target, options);\n }\n /**\n * Allow fire programmatically a cut action\n * @param {String|HTMLElement} target\n * @returns Text cutted.\n */\n\n }, {\n key: \"cut\",\n value: function cut(target) {\n return actions_cut(target);\n }\n /**\n * Returns the support of the given action, or all actions if no action is\n * given.\n * @param {String} [action]\n */\n\n }, {\n key: \"isSupported\",\n value: function isSupported() {\n var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ['copy', 'cut'];\n var actions = typeof action === 'string' ? [action] : action;\n var support = !!document.queryCommandSupported;\n actions.forEach(function (action) {\n support = support && !!document.queryCommandSupported(action);\n });\n return support;\n }\n }]);\n\n return Clipboard;\n}((tiny_emitter_default()));\n\n/* harmony default export */ var clipboard = (Clipboard);\n\n/***/ }),\n\n/***/ 828:\n/***/ (function(module) {\n\nvar DOCUMENT_NODE_TYPE = 9;\n\n/**\n * A polyfill for Element.matches()\n */\nif (typeof Element !== 'undefined' && !Element.prototype.matches) {\n var proto = Element.prototype;\n\n proto.matches = proto.matchesSelector ||\n proto.mozMatchesSelector ||\n proto.msMatchesSelector ||\n proto.oMatchesSelector ||\n proto.webkitMatchesSelector;\n}\n\n/**\n * Finds the closest parent that matches a selector.\n *\n * @param {Element} element\n * @param {String} selector\n * @return {Function}\n */\nfunction closest (element, selector) {\n while (element && element.nodeType !== DOCUMENT_NODE_TYPE) {\n if (typeof element.matches === 'function' &&\n element.matches(selector)) {\n return element;\n }\n element = element.parentNode;\n }\n}\n\nmodule.exports = closest;\n\n\n/***/ }),\n\n/***/ 438:\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\nvar closest = __webpack_require__(828);\n\n/**\n * Delegates event to a selector.\n *\n * @param {Element} element\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @param {Boolean} useCapture\n * @return {Object}\n */\nfunction _delegate(element, selector, type, callback, useCapture) {\n var listenerFn = listener.apply(this, arguments);\n\n element.addEventListener(type, listenerFn, useCapture);\n\n return {\n destroy: function() {\n element.removeEventListener(type, listenerFn, useCapture);\n }\n }\n}\n\n/**\n * Delegates event to a selector.\n *\n * @param {Element|String|Array} [elements]\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @param {Boolean} useCapture\n * @return {Object}\n */\nfunction delegate(elements, selector, type, callback, useCapture) {\n // Handle the regular Element usage\n if (typeof elements.addEventListener === 'function') {\n return _delegate.apply(null, arguments);\n }\n\n // Handle Element-less usage, it defaults to global delegation\n if (typeof type === 'function') {\n // Use `document` as the first parameter, then apply arguments\n // This is a short way to .unshift `arguments` without running into deoptimizations\n return _delegate.bind(null, document).apply(null, arguments);\n }\n\n // Handle Selector-based usage\n if (typeof elements === 'string') {\n elements = document.querySelectorAll(elements);\n }\n\n // Handle Array-like based usage\n return Array.prototype.map.call(elements, function (element) {\n return _delegate(element, selector, type, callback, useCapture);\n });\n}\n\n/**\n * Finds closest match and invokes callback.\n *\n * @param {Element} element\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @return {Function}\n */\nfunction listener(element, selector, type, callback) {\n return function(e) {\n e.delegateTarget = closest(e.target, selector);\n\n if (e.delegateTarget) {\n callback.call(element, e);\n }\n }\n}\n\nmodule.exports = delegate;\n\n\n/***/ }),\n\n/***/ 879:\n/***/ (function(__unused_webpack_module, exports) {\n\n/**\n * Check if argument is a HTML element.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.node = function(value) {\n return value !== undefined\n && value instanceof HTMLElement\n && value.nodeType === 1;\n};\n\n/**\n * Check if argument is a list of HTML elements.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.nodeList = function(value) {\n var type = Object.prototype.toString.call(value);\n\n return value !== undefined\n && (type === '[object NodeList]' || type === '[object HTMLCollection]')\n && ('length' in value)\n && (value.length === 0 || exports.node(value[0]));\n};\n\n/**\n * Check if argument is a string.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.string = function(value) {\n return typeof value === 'string'\n || value instanceof String;\n};\n\n/**\n * Check if argument is a function.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.fn = function(value) {\n var type = Object.prototype.toString.call(value);\n\n return type === '[object Function]';\n};\n\n\n/***/ }),\n\n/***/ 370:\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\nvar is = __webpack_require__(879);\nvar delegate = __webpack_require__(438);\n\n/**\n * Validates all params and calls the right\n * listener function based on its target type.\n *\n * @param {String|HTMLElement|HTMLCollection|NodeList} target\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listen(target, type, callback) {\n if (!target && !type && !callback) {\n throw new Error('Missing required arguments');\n }\n\n if (!is.string(type)) {\n throw new TypeError('Second argument must be a String');\n }\n\n if (!is.fn(callback)) {\n throw new TypeError('Third argument must be a Function');\n }\n\n if (is.node(target)) {\n return listenNode(target, type, callback);\n }\n else if (is.nodeList(target)) {\n return listenNodeList(target, type, callback);\n }\n else if (is.string(target)) {\n return listenSelector(target, type, callback);\n }\n else {\n throw new TypeError('First argument must be a String, HTMLElement, HTMLCollection, or NodeList');\n }\n}\n\n/**\n * Adds an event listener to a HTML element\n * and returns a remove listener function.\n *\n * @param {HTMLElement} node\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenNode(node, type, callback) {\n node.addEventListener(type, callback);\n\n return {\n destroy: function() {\n node.removeEventListener(type, callback);\n }\n }\n}\n\n/**\n * Add an event listener to a list of HTML elements\n * and returns a remove listener function.\n *\n * @param {NodeList|HTMLCollection} nodeList\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenNodeList(nodeList, type, callback) {\n Array.prototype.forEach.call(nodeList, function(node) {\n node.addEventListener(type, callback);\n });\n\n return {\n destroy: function() {\n Array.prototype.forEach.call(nodeList, function(node) {\n node.removeEventListener(type, callback);\n });\n }\n }\n}\n\n/**\n * Add an event listener to a selector\n * and returns a remove listener function.\n *\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenSelector(selector, type, callback) {\n return delegate(document.body, selector, type, callback);\n}\n\nmodule.exports = listen;\n\n\n/***/ }),\n\n/***/ 817:\n/***/ (function(module) {\n\nfunction select(element) {\n var selectedText;\n\n if (element.nodeName === 'SELECT') {\n element.focus();\n\n selectedText = element.value;\n }\n else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {\n var isReadOnly = element.hasAttribute('readonly');\n\n if (!isReadOnly) {\n element.setAttribute('readonly', '');\n }\n\n element.select();\n element.setSelectionRange(0, element.value.length);\n\n if (!isReadOnly) {\n element.removeAttribute('readonly');\n }\n\n selectedText = element.value;\n }\n else {\n if (element.hasAttribute('contenteditable')) {\n element.focus();\n }\n\n var selection = window.getSelection();\n var range = document.createRange();\n\n range.selectNodeContents(element);\n selection.removeAllRanges();\n selection.addRange(range);\n\n selectedText = selection.toString();\n }\n\n return selectedText;\n}\n\nmodule.exports = select;\n\n\n/***/ }),\n\n/***/ 279:\n/***/ (function(module) {\n\nfunction E () {\n // Keep this empty so it's easier to inherit from\n // (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3)\n}\n\nE.prototype = {\n on: function (name, callback, ctx) {\n var e = this.e || (this.e = {});\n\n (e[name] || (e[name] = [])).push({\n fn: callback,\n ctx: ctx\n });\n\n return this;\n },\n\n once: function (name, callback, ctx) {\n var self = this;\n function listener () {\n self.off(name, listener);\n callback.apply(ctx, arguments);\n };\n\n listener._ = callback\n return this.on(name, listener, ctx);\n },\n\n emit: function (name) {\n var data = [].slice.call(arguments, 1);\n var evtArr = ((this.e || (this.e = {}))[name] || []).slice();\n var i = 0;\n var len = evtArr.length;\n\n for (i; i < len; i++) {\n evtArr[i].fn.apply(evtArr[i].ctx, data);\n }\n\n return this;\n },\n\n off: function (name, callback) {\n var e = this.e || (this.e = {});\n var evts = e[name];\n var liveEvents = [];\n\n if (evts && callback) {\n for (var i = 0, len = evts.length; i < len; i++) {\n if (evts[i].fn !== callback && evts[i].fn._ !== callback)\n liveEvents.push(evts[i]);\n }\n }\n\n // Remove event from queue to prevent memory leak\n // Suggested by https://github.com/lazd\n // Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910\n\n (liveEvents.length)\n ? e[name] = liveEvents\n : delete e[name];\n\n return this;\n }\n};\n\nmodule.exports = E;\nmodule.exports.TinyEmitter = E;\n\n\n/***/ })\n\n/******/ \t});\n/************************************************************************/\n/******/ \t// The module cache\n/******/ \tvar __webpack_module_cache__ = {};\n/******/ \t\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(__webpack_module_cache__[moduleId]) {\n/******/ \t\t\treturn __webpack_module_cache__[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = __webpack_module_cache__[moduleId] = {\n/******/ \t\t\t// no module.id needed\n/******/ \t\t\t// no module.loaded needed\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/ \t\n/******/ \t\t// Execute the module function\n/******/ \t\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n/******/ \t\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/ \t\n/************************************************************************/\n/******/ \t/* webpack/runtime/compat get default export */\n/******/ \t!function() {\n/******/ \t\t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t\t__webpack_require__.n = function(module) {\n/******/ \t\t\tvar getter = module && module.__esModule ?\n/******/ \t\t\t\tfunction() { return module['default']; } :\n/******/ \t\t\t\tfunction() { return module; };\n/******/ \t\t\t__webpack_require__.d(getter, { a: getter });\n/******/ \t\t\treturn getter;\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/define property getters */\n/******/ \t!function() {\n/******/ \t\t// define getter functions for harmony exports\n/******/ \t\t__webpack_require__.d = function(exports, definition) {\n/******/ \t\t\tfor(var key in definition) {\n/******/ \t\t\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n/******/ \t\t\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n/******/ \t\t\t\t}\n/******/ \t\t\t}\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/hasOwnProperty shorthand */\n/******/ \t!function() {\n/******/ \t\t__webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }\n/******/ \t}();\n/******/ \t\n/************************************************************************/\n/******/ \t// module exports must be returned from runtime so entry inlining is disabled\n/******/ \t// startup\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(686);\n/******/ })()\n.default;\n});", "/*\n * Copyright (c) 2016-2025 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport \"focus-visible\"\n\nimport {\n EMPTY,\n NEVER,\n Observable,\n Subject,\n defer,\n delay,\n filter,\n map,\n merge,\n mergeWith,\n shareReplay,\n switchMap\n} from \"rxjs\"\n\nimport { configuration, feature } from \"./_\"\nimport {\n at,\n getActiveElement,\n getOptionalElement,\n requestJSON,\n setLocation,\n setToggle,\n watchDocument,\n watchKeyboard,\n watchLocation,\n watchLocationTarget,\n watchMedia,\n watchPrint,\n watchScript,\n watchViewport\n} from \"./browser\"\nimport {\n getComponentElement,\n getComponentElements,\n mountAnnounce,\n mountBackToTop,\n mountConsent,\n mountContent,\n mountDialog,\n mountHeader,\n mountHeaderTitle,\n mountPalette,\n mountProgress,\n mountSearch,\n mountSearchHiglight,\n mountSidebar,\n mountSource,\n mountTableOfContents,\n mountTabs,\n watchHeader,\n watchMain\n} from \"./components\"\nimport {\n SearchIndex,\n fetchSitemap,\n setupAlternate,\n setupClipboardJS,\n setupInstantNavigation,\n setupVersionSelector\n} from \"./integrations\"\nimport {\n patchEllipsis,\n patchIndeterminate,\n patchScrollfix,\n patchScrolllock\n} from \"./patches\"\nimport \"./polyfills\"\n\n/* ----------------------------------------------------------------------------\n * Functions - @todo refactor\n * ------------------------------------------------------------------------- */\n\n/**\n * Fetch search index\n *\n * @returns Search index observable\n */\nfunction fetchSearchIndex(): Observable {\n if (location.protocol === \"file:\") {\n return watchScript(\n `${new URL(\"search/search_index.js\", config.base)}`\n )\n .pipe(\n // @ts-ignore - @todo fix typings\n map(() => __index),\n shareReplay(1)\n )\n } else {\n return requestJSON(\n new URL(\"search/search_index.json\", config.base)\n )\n }\n}\n\n/* ----------------------------------------------------------------------------\n * Application\n * ------------------------------------------------------------------------- */\n\n/* Yay, JavaScript is available */\ndocument.documentElement.classList.remove(\"no-js\")\ndocument.documentElement.classList.add(\"js\")\n\n/* Set up navigation observables and subjects */\nconst document$ = watchDocument()\nconst location$ = watchLocation()\nconst target$ = watchLocationTarget(location$)\nconst keyboard$ = watchKeyboard()\n\n/* Set up media observables */\nconst viewport$ = watchViewport()\nconst tablet$ = watchMedia(\"(min-width: 60em)\")\nconst screen$ = watchMedia(\"(min-width: 76.25em)\")\nconst print$ = watchPrint()\n\n/* Retrieve search index, if search is enabled */\nconst config = configuration()\nconst index$ = document.forms.namedItem(\"search\")\n ? fetchSearchIndex()\n : NEVER\n\n/* Set up Clipboard.js integration */\nconst alert$ = new Subject()\nsetupClipboardJS({ alert$ })\n\n/* Set up language selector */\nsetupAlternate({ document$ })\n\n/* Set up progress indicator */\nconst progress$ = new Subject()\n\n/* Set up sitemap for instant navigation and previews */\nconst sitemap$ = fetchSitemap(config.base)\n\n/* Set up instant navigation, if enabled */\nif (feature(\"navigation.instant\"))\n setupInstantNavigation({ sitemap$, location$, viewport$, progress$ })\n .subscribe(document$)\n\n/* Set up version selector */\nif (config.version?.provider === \"mike\")\n setupVersionSelector({ document$ })\n\n/* Always close drawer and search on navigation */\nmerge(location$, target$)\n .pipe(\n delay(125)\n )\n .subscribe(() => {\n setToggle(\"drawer\", false)\n setToggle(\"search\", false)\n })\n\n/* Set up global keyboard handlers */\nkeyboard$\n .pipe(\n filter(({ mode }) => mode === \"global\")\n )\n .subscribe(key => {\n switch (key.type) {\n\n /* Go to previous page */\n case \"p\":\n case \",\":\n const prev = getOptionalElement(\"link[rel=prev]\")\n if (typeof prev !== \"undefined\")\n setLocation(prev)\n break\n\n /* Go to next page */\n case \"n\":\n case \".\":\n const next = getOptionalElement(\"link[rel=next]\")\n if (typeof next !== \"undefined\")\n setLocation(next)\n break\n\n /* Expand navigation, see https://bit.ly/3ZjG5io */\n case \"Enter\":\n const active = getActiveElement()\n if (active instanceof HTMLLabelElement)\n active.click()\n }\n })\n\n/* Set up patches */\npatchEllipsis({ viewport$, document$ })\npatchIndeterminate({ document$, tablet$ })\npatchScrollfix({ document$ })\npatchScrolllock({ viewport$, tablet$ })\n\n/* Set up header and main area observable */\nconst header$ = watchHeader(getComponentElement(\"header\"), { viewport$ })\nconst main$ = document$\n .pipe(\n map(() => getComponentElement(\"main\")),\n switchMap(el => watchMain(el, { viewport$, header$ })),\n shareReplay(1)\n )\n\n/* Set up control component observables */\nconst control$ = merge(\n\n /* Consent */\n ...getComponentElements(\"consent\")\n .map(el => mountConsent(el, { target$ })),\n\n /* Dialog */\n ...getComponentElements(\"dialog\")\n .map(el => mountDialog(el, { alert$ })),\n\n /* Color palette */\n ...getComponentElements(\"palette\")\n .map(el => mountPalette(el)),\n\n /* Progress bar */\n ...getComponentElements(\"progress\")\n .map(el => mountProgress(el, { progress$ })),\n\n /* Search */\n ...getComponentElements(\"search\")\n .map(el => mountSearch(el, { index$, keyboard$ })),\n\n /* Repository information */\n ...getComponentElements(\"source\")\n .map(el => mountSource(el))\n)\n\n/* Set up content component observables */\nconst content$ = defer(() => merge(\n\n /* Announcement bar */\n ...getComponentElements(\"announce\")\n .map(el => mountAnnounce(el)),\n\n /* Content */\n ...getComponentElements(\"content\")\n .map(el => mountContent(el, { sitemap$, viewport$, target$, print$ })),\n\n /* Search highlighting */\n ...getComponentElements(\"content\")\n .map(el => feature(\"search.highlight\")\n ? mountSearchHiglight(el, { index$, location$ })\n : EMPTY\n ),\n\n /* Header */\n ...getComponentElements(\"header\")\n .map(el => mountHeader(el, { viewport$, header$, main$ })),\n\n /* Header title */\n ...getComponentElements(\"header-title\")\n .map(el => mountHeaderTitle(el, { viewport$, header$ })),\n\n /* Sidebar */\n ...getComponentElements(\"sidebar\")\n .map(el => el.getAttribute(\"data-md-type\") === \"navigation\"\n ? at(screen$, () => mountSidebar(el, { viewport$, header$, main$ }))\n : at(tablet$, () => mountSidebar(el, { viewport$, header$, main$ }))\n ),\n\n /* Navigation tabs */\n ...getComponentElements(\"tabs\")\n .map(el => mountTabs(el, { viewport$, header$ })),\n\n /* Table of contents */\n ...getComponentElements(\"toc\")\n .map(el => mountTableOfContents(el, {\n viewport$, header$, main$, target$\n })),\n\n /* Back-to-top button */\n ...getComponentElements(\"top\")\n .map(el => mountBackToTop(el, { viewport$, header$, main$, target$ }))\n))\n\n/* Set up component observables */\nconst component$ = document$\n .pipe(\n switchMap(() => content$),\n mergeWith(control$),\n shareReplay(1)\n )\n\n/* Subscribe to all components */\ncomponent$.subscribe()\n\n/* ----------------------------------------------------------------------------\n * Exports\n * ------------------------------------------------------------------------- */\n\nwindow.document$ = document$ /* Document observable */\nwindow.location$ = location$ /* Location subject */\nwindow.target$ = target$ /* Location target observable */\nwindow.keyboard$ = keyboard$ /* Keyboard observable */\nwindow.viewport$ = viewport$ /* Viewport observable */\nwindow.tablet$ = tablet$ /* Media tablet observable */\nwindow.screen$ = screen$ /* Media screen observable */\nwindow.print$ = print$ /* Media print observable */\nwindow.alert$ = alert$ /* Alert subject */\nwindow.progress$ = progress$ /* Progress indicator subject */\nwindow.component$ = component$ /* Component observable */\n", "/******************************************************************************\nCopyright (c) Microsoft Corporation.\n\nPermission to use, copy, modify, and/or distribute this software for any\npurpose with or without fee is hereby granted.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\nAND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\nLOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\nOTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\nPERFORMANCE OF THIS SOFTWARE.\n***************************************************************************** */\n/* global Reflect, Promise, SuppressedError, Symbol, Iterator */\n\nvar extendStatics = function(d, b) {\n extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };\n return extendStatics(d, b);\n};\n\nexport function __extends(d, b) {\n if (typeof b !== \"function\" && b !== null)\n throw new TypeError(\"Class extends value \" + String(b) + \" is not a constructor or null\");\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n}\n\nexport var __assign = function() {\n __assign = Object.assign || function __assign(t) {\n for (var s, i = 1, n = arguments.length; i < n; i++) {\n s = arguments[i];\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];\n }\n return t;\n }\n return __assign.apply(this, arguments);\n}\n\nexport function __rest(s, e) {\n var t = {};\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)\n t[p] = s[p];\n if (s != null && typeof Object.getOwnPropertySymbols === \"function\")\n for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {\n if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))\n t[p[i]] = s[p[i]];\n }\n return t;\n}\n\nexport function __decorate(decorators, target, key, desc) {\n var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\n if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\") r = Reflect.decorate(decorators, target, key, desc);\n else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\n return c > 3 && r && Object.defineProperty(target, key, r), r;\n}\n\nexport function __param(paramIndex, decorator) {\n return function (target, key) { decorator(target, key, paramIndex); }\n}\n\nexport function __esDecorate(ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {\n function accept(f) { if (f !== void 0 && typeof f !== \"function\") throw new TypeError(\"Function expected\"); return f; }\n var kind = contextIn.kind, key = kind === \"getter\" ? \"get\" : kind === \"setter\" ? \"set\" : \"value\";\n var target = !descriptorIn && ctor ? contextIn[\"static\"] ? ctor : ctor.prototype : null;\n var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});\n var _, done = false;\n for (var i = decorators.length - 1; i >= 0; i--) {\n var context = {};\n for (var p in contextIn) context[p] = p === \"access\" ? {} : contextIn[p];\n for (var p in contextIn.access) context.access[p] = contextIn.access[p];\n context.addInitializer = function (f) { if (done) throw new TypeError(\"Cannot add initializers after decoration has completed\"); extraInitializers.push(accept(f || null)); };\n var result = (0, decorators[i])(kind === \"accessor\" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);\n if (kind === \"accessor\") {\n if (result === void 0) continue;\n if (result === null || typeof result !== \"object\") throw new TypeError(\"Object expected\");\n if (_ = accept(result.get)) descriptor.get = _;\n if (_ = accept(result.set)) descriptor.set = _;\n if (_ = accept(result.init)) initializers.unshift(_);\n }\n else if (_ = accept(result)) {\n if (kind === \"field\") initializers.unshift(_);\n else descriptor[key] = _;\n }\n }\n if (target) Object.defineProperty(target, contextIn.name, descriptor);\n done = true;\n};\n\nexport function __runInitializers(thisArg, initializers, value) {\n var useValue = arguments.length > 2;\n for (var i = 0; i < initializers.length; i++) {\n value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);\n }\n return useValue ? value : void 0;\n};\n\nexport function __propKey(x) {\n return typeof x === \"symbol\" ? x : \"\".concat(x);\n};\n\nexport function __setFunctionName(f, name, prefix) {\n if (typeof name === \"symbol\") name = name.description ? \"[\".concat(name.description, \"]\") : \"\";\n return Object.defineProperty(f, \"name\", { configurable: true, value: prefix ? \"\".concat(prefix, \" \", name) : name });\n};\n\nexport function __metadata(metadataKey, metadataValue) {\n if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\") return Reflect.metadata(metadataKey, metadataValue);\n}\n\nexport function __awaiter(thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n}\n\nexport function __generator(thisArg, body) {\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === \"function\" ? Iterator : Object).prototype);\n return g.next = verb(0), g[\"throw\"] = verb(1), g[\"return\"] = verb(2), typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\n function verb(n) { return function (v) { return step([n, v]); }; }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [op[0] & 2, t.value];\n switch (op[0]) {\n case 0: case 1: t = op; break;\n case 4: _.label++; return { value: op[1], done: false };\n case 5: _.label++; y = op[1]; op = [0]; continue;\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\n if (t[2]) _.ops.pop();\n _.trys.pop(); continue;\n }\n op = body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\n }\n}\n\nexport var __createBinding = Object.create ? (function(o, m, k, k2) {\n if (k2 === undefined) k2 = k;\n var desc = Object.getOwnPropertyDescriptor(m, k);\n if (!desc || (\"get\" in desc ? !m.__esModule : desc.writable || desc.configurable)) {\n desc = { enumerable: true, get: function() { return m[k]; } };\n }\n Object.defineProperty(o, k2, desc);\n}) : (function(o, m, k, k2) {\n if (k2 === undefined) k2 = k;\n o[k2] = m[k];\n});\n\nexport function __exportStar(m, o) {\n for (var p in m) if (p !== \"default\" && !Object.prototype.hasOwnProperty.call(o, p)) __createBinding(o, m, p);\n}\n\nexport function __values(o) {\n var s = typeof Symbol === \"function\" && Symbol.iterator, m = s && o[s], i = 0;\n if (m) return m.call(o);\n if (o && typeof o.length === \"number\") return {\n next: function () {\n if (o && i >= o.length) o = void 0;\n return { value: o && o[i++], done: !o };\n }\n };\n throw new TypeError(s ? \"Object is not iterable.\" : \"Symbol.iterator is not defined.\");\n}\n\nexport function __read(o, n) {\n var m = typeof Symbol === \"function\" && o[Symbol.iterator];\n if (!m) return o;\n var i = m.call(o), r, ar = [], e;\n try {\n while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);\n }\n catch (error) { e = { error: error }; }\n finally {\n try {\n if (r && !r.done && (m = i[\"return\"])) m.call(i);\n }\n finally { if (e) throw e.error; }\n }\n return ar;\n}\n\n/** @deprecated */\nexport function __spread() {\n for (var ar = [], i = 0; i < arguments.length; i++)\n ar = ar.concat(__read(arguments[i]));\n return ar;\n}\n\n/** @deprecated */\nexport function __spreadArrays() {\n for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;\n for (var r = Array(s), k = 0, i = 0; i < il; i++)\n for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)\n r[k] = a[j];\n return r;\n}\n\nexport function __spreadArray(to, from, pack) {\n if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {\n if (ar || !(i in from)) {\n if (!ar) ar = Array.prototype.slice.call(from, 0, i);\n ar[i] = from[i];\n }\n }\n return to.concat(ar || Array.prototype.slice.call(from));\n}\n\nexport function __await(v) {\n return this instanceof __await ? (this.v = v, this) : new __await(v);\n}\n\nexport function __asyncGenerator(thisArg, _arguments, generator) {\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\n var g = generator.apply(thisArg, _arguments || []), i, q = [];\n return i = Object.create((typeof AsyncIterator === \"function\" ? AsyncIterator : Object).prototype), verb(\"next\"), verb(\"throw\"), verb(\"return\", awaitReturn), i[Symbol.asyncIterator] = function () { return this; }, i;\n function awaitReturn(f) { return function (v) { return Promise.resolve(v).then(f, reject); }; }\n function verb(n, f) { if (g[n]) { i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; if (f) i[n] = f(i[n]); } }\n function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }\n function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }\n function fulfill(value) { resume(\"next\", value); }\n function reject(value) { resume(\"throw\", value); }\n function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }\n}\n\nexport function __asyncDelegator(o) {\n var i, p;\n return i = {}, verb(\"next\"), verb(\"throw\", function (e) { throw e; }), verb(\"return\"), i[Symbol.iterator] = function () { return this; }, i;\n function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: false } : f ? f(v) : v; } : f; }\n}\n\nexport function __asyncValues(o) {\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\n var m = o[Symbol.asyncIterator], i;\n return m ? m.call(o) : (o = typeof __values === \"function\" ? __values(o) : o[Symbol.iterator](), i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i);\n function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }\n function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }\n}\n\nexport function __makeTemplateObject(cooked, raw) {\n if (Object.defineProperty) { Object.defineProperty(cooked, \"raw\", { value: raw }); } else { cooked.raw = raw; }\n return cooked;\n};\n\nvar __setModuleDefault = Object.create ? (function(o, v) {\n Object.defineProperty(o, \"default\", { enumerable: true, value: v });\n}) : function(o, v) {\n o[\"default\"] = v;\n};\n\nexport function __importStar(mod) {\n if (mod && mod.__esModule) return mod;\n var result = {};\n if (mod != null) for (var k in mod) if (k !== \"default\" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);\n __setModuleDefault(result, mod);\n return result;\n}\n\nexport function __importDefault(mod) {\n return (mod && mod.__esModule) ? mod : { default: mod };\n}\n\nexport function __classPrivateFieldGet(receiver, state, kind, f) {\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a getter\");\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot read private member from an object whose class did not declare it\");\n return kind === \"m\" ? f : kind === \"a\" ? f.call(receiver) : f ? f.value : state.get(receiver);\n}\n\nexport function __classPrivateFieldSet(receiver, state, value, kind, f) {\n if (kind === \"m\") throw new TypeError(\"Private method is not writable\");\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a setter\");\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot write private member to an object whose class did not declare it\");\n return (kind === \"a\" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;\n}\n\nexport function __classPrivateFieldIn(state, receiver) {\n if (receiver === null || (typeof receiver !== \"object\" && typeof receiver !== \"function\")) throw new TypeError(\"Cannot use 'in' operator on non-object\");\n return typeof state === \"function\" ? receiver === state : state.has(receiver);\n}\n\nexport function __addDisposableResource(env, value, async) {\n if (value !== null && value !== void 0) {\n if (typeof value !== \"object\" && typeof value !== \"function\") throw new TypeError(\"Object expected.\");\n var dispose, inner;\n if (async) {\n if (!Symbol.asyncDispose) throw new TypeError(\"Symbol.asyncDispose is not defined.\");\n dispose = value[Symbol.asyncDispose];\n }\n if (dispose === void 0) {\n if (!Symbol.dispose) throw new TypeError(\"Symbol.dispose is not defined.\");\n dispose = value[Symbol.dispose];\n if (async) inner = dispose;\n }\n if (typeof dispose !== \"function\") throw new TypeError(\"Object not disposable.\");\n if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };\n env.stack.push({ value: value, dispose: dispose, async: async });\n }\n else if (async) {\n env.stack.push({ async: true });\n }\n return value;\n}\n\nvar _SuppressedError = typeof SuppressedError === \"function\" ? SuppressedError : function (error, suppressed, message) {\n var e = new Error(message);\n return e.name = \"SuppressedError\", e.error = error, e.suppressed = suppressed, e;\n};\n\nexport function __disposeResources(env) {\n function fail(e) {\n env.error = env.hasError ? new _SuppressedError(e, env.error, \"An error was suppressed during disposal.\") : e;\n env.hasError = true;\n }\n var r, s = 0;\n function next() {\n while (r = env.stack.pop()) {\n try {\n if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);\n if (r.dispose) {\n var result = r.dispose.call(r.value);\n if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });\n }\n else s |= 1;\n }\n catch (e) {\n fail(e);\n }\n }\n if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();\n if (env.hasError) throw env.error;\n }\n return next();\n}\n\nexport default {\n __extends,\n __assign,\n __rest,\n __decorate,\n __param,\n __metadata,\n __awaiter,\n __generator,\n __createBinding,\n __exportStar,\n __values,\n __read,\n __spread,\n __spreadArrays,\n __spreadArray,\n __await,\n __asyncGenerator,\n __asyncDelegator,\n __asyncValues,\n __makeTemplateObject,\n __importStar,\n __importDefault,\n __classPrivateFieldGet,\n __classPrivateFieldSet,\n __classPrivateFieldIn,\n __addDisposableResource,\n __disposeResources,\n};\n", "/**\n * Returns true if the object is a function.\n * @param value The value to check\n */\nexport function isFunction(value: any): value is (...args: any[]) => any {\n return typeof value === 'function';\n}\n", "/**\n * Used to create Error subclasses until the community moves away from ES5.\n *\n * This is because compiling from TypeScript down to ES5 has issues with subclassing Errors\n * as well as other built-in types: https://github.com/Microsoft/TypeScript/issues/12123\n *\n * @param createImpl A factory function to create the actual constructor implementation. The returned\n * function should be a named function that calls `_super` internally.\n */\nexport function createErrorClass(createImpl: (_super: any) => any): T {\n const _super = (instance: any) => {\n Error.call(instance);\n instance.stack = new Error().stack;\n };\n\n const ctorFunc = createImpl(_super);\n ctorFunc.prototype = Object.create(Error.prototype);\n ctorFunc.prototype.constructor = ctorFunc;\n return ctorFunc;\n}\n", "import { createErrorClass } from './createErrorClass';\n\nexport interface UnsubscriptionError extends Error {\n readonly errors: any[];\n}\n\nexport interface UnsubscriptionErrorCtor {\n /**\n * @deprecated Internal implementation detail. Do not construct error instances.\n * Cannot be tagged as internal: https://github.com/ReactiveX/rxjs/issues/6269\n */\n new (errors: any[]): UnsubscriptionError;\n}\n\n/**\n * An error thrown when one or more errors have occurred during the\n * `unsubscribe` of a {@link Subscription}.\n */\nexport const UnsubscriptionError: UnsubscriptionErrorCtor = createErrorClass(\n (_super) =>\n function UnsubscriptionErrorImpl(this: any, errors: (Error | string)[]) {\n _super(this);\n this.message = errors\n ? `${errors.length} errors occurred during unsubscription:\n${errors.map((err, i) => `${i + 1}) ${err.toString()}`).join('\\n ')}`\n : '';\n this.name = 'UnsubscriptionError';\n this.errors = errors;\n }\n);\n", "/**\n * Removes an item from an array, mutating it.\n * @param arr The array to remove the item from\n * @param item The item to remove\n */\nexport function arrRemove(arr: T[] | undefined | null, item: T) {\n if (arr) {\n const index = arr.indexOf(item);\n 0 <= index && arr.splice(index, 1);\n }\n}\n", "import { isFunction } from './util/isFunction';\nimport { UnsubscriptionError } from './util/UnsubscriptionError';\nimport { SubscriptionLike, TeardownLogic, Unsubscribable } from './types';\nimport { arrRemove } from './util/arrRemove';\n\n/**\n * Represents a disposable resource, such as the execution of an Observable. A\n * Subscription has one important method, `unsubscribe`, that takes no argument\n * and just disposes the resource held by the subscription.\n *\n * Additionally, subscriptions may be grouped together through the `add()`\n * method, which will attach a child Subscription to the current Subscription.\n * When a Subscription is unsubscribed, all its children (and its grandchildren)\n * will be unsubscribed as well.\n */\nexport class Subscription implements SubscriptionLike {\n public static EMPTY = (() => {\n const empty = new Subscription();\n empty.closed = true;\n return empty;\n })();\n\n /**\n * A flag to indicate whether this Subscription has already been unsubscribed.\n */\n public closed = false;\n\n private _parentage: Subscription[] | Subscription | null = null;\n\n /**\n * The list of registered finalizers to execute upon unsubscription. Adding and removing from this\n * list occurs in the {@link #add} and {@link #remove} methods.\n */\n private _finalizers: Exclude[] | null = null;\n\n /**\n * @param initialTeardown A function executed first as part of the finalization\n * process that is kicked off when {@link #unsubscribe} is called.\n */\n constructor(private initialTeardown?: () => void) {}\n\n /**\n * Disposes the resources held by the subscription. May, for instance, cancel\n * an ongoing Observable execution or cancel any other type of work that\n * started when the Subscription was created.\n */\n unsubscribe(): void {\n let errors: any[] | undefined;\n\n if (!this.closed) {\n this.closed = true;\n\n // Remove this from it's parents.\n const { _parentage } = this;\n if (_parentage) {\n this._parentage = null;\n if (Array.isArray(_parentage)) {\n for (const parent of _parentage) {\n parent.remove(this);\n }\n } else {\n _parentage.remove(this);\n }\n }\n\n const { initialTeardown: initialFinalizer } = this;\n if (isFunction(initialFinalizer)) {\n try {\n initialFinalizer();\n } catch (e) {\n errors = e instanceof UnsubscriptionError ? e.errors : [e];\n }\n }\n\n const { _finalizers } = this;\n if (_finalizers) {\n this._finalizers = null;\n for (const finalizer of _finalizers) {\n try {\n execFinalizer(finalizer);\n } catch (err) {\n errors = errors ?? [];\n if (err instanceof UnsubscriptionError) {\n errors = [...errors, ...err.errors];\n } else {\n errors.push(err);\n }\n }\n }\n }\n\n if (errors) {\n throw new UnsubscriptionError(errors);\n }\n }\n }\n\n /**\n * Adds a finalizer to this subscription, so that finalization will be unsubscribed/called\n * when this subscription is unsubscribed. If this subscription is already {@link #closed},\n * because it has already been unsubscribed, then whatever finalizer is passed to it\n * will automatically be executed (unless the finalizer itself is also a closed subscription).\n *\n * Closed Subscriptions cannot be added as finalizers to any subscription. Adding a closed\n * subscription to a any subscription will result in no operation. (A noop).\n *\n * Adding a subscription to itself, or adding `null` or `undefined` will not perform any\n * operation at all. (A noop).\n *\n * `Subscription` instances that are added to this instance will automatically remove themselves\n * if they are unsubscribed. Functions and {@link Unsubscribable} objects that you wish to remove\n * will need to be removed manually with {@link #remove}\n *\n * @param teardown The finalization logic to add to this subscription.\n */\n add(teardown: TeardownLogic): void {\n // Only add the finalizer if it's not undefined\n // and don't add a subscription to itself.\n if (teardown && teardown !== this) {\n if (this.closed) {\n // If this subscription is already closed,\n // execute whatever finalizer is handed to it automatically.\n execFinalizer(teardown);\n } else {\n if (teardown instanceof Subscription) {\n // We don't add closed subscriptions, and we don't add the same subscription\n // twice. Subscription unsubscribe is idempotent.\n if (teardown.closed || teardown._hasParent(this)) {\n return;\n }\n teardown._addParent(this);\n }\n (this._finalizers = this._finalizers ?? []).push(teardown);\n }\n }\n }\n\n /**\n * Checks to see if a this subscription already has a particular parent.\n * This will signal that this subscription has already been added to the parent in question.\n * @param parent the parent to check for\n */\n private _hasParent(parent: Subscription) {\n const { _parentage } = this;\n return _parentage === parent || (Array.isArray(_parentage) && _parentage.includes(parent));\n }\n\n /**\n * Adds a parent to this subscription so it can be removed from the parent if it\n * unsubscribes on it's own.\n *\n * NOTE: THIS ASSUMES THAT {@link _hasParent} HAS ALREADY BEEN CHECKED.\n * @param parent The parent subscription to add\n */\n private _addParent(parent: Subscription) {\n const { _parentage } = this;\n this._parentage = Array.isArray(_parentage) ? (_parentage.push(parent), _parentage) : _parentage ? [_parentage, parent] : parent;\n }\n\n /**\n * Called on a child when it is removed via {@link #remove}.\n * @param parent The parent to remove\n */\n private _removeParent(parent: Subscription) {\n const { _parentage } = this;\n if (_parentage === parent) {\n this._parentage = null;\n } else if (Array.isArray(_parentage)) {\n arrRemove(_parentage, parent);\n }\n }\n\n /**\n * Removes a finalizer from this subscription that was previously added with the {@link #add} method.\n *\n * Note that `Subscription` instances, when unsubscribed, will automatically remove themselves\n * from every other `Subscription` they have been added to. This means that using the `remove` method\n * is not a common thing and should be used thoughtfully.\n *\n * If you add the same finalizer instance of a function or an unsubscribable object to a `Subscription` instance\n * more than once, you will need to call `remove` the same number of times to remove all instances.\n *\n * All finalizer instances are removed to free up memory upon unsubscription.\n *\n * @param teardown The finalizer to remove from this subscription\n */\n remove(teardown: Exclude): void {\n const { _finalizers } = this;\n _finalizers && arrRemove(_finalizers, teardown);\n\n if (teardown instanceof Subscription) {\n teardown._removeParent(this);\n }\n }\n}\n\nexport const EMPTY_SUBSCRIPTION = Subscription.EMPTY;\n\nexport function isSubscription(value: any): value is Subscription {\n return (\n value instanceof Subscription ||\n (value && 'closed' in value && isFunction(value.remove) && isFunction(value.add) && isFunction(value.unsubscribe))\n );\n}\n\nfunction execFinalizer(finalizer: Unsubscribable | (() => void)) {\n if (isFunction(finalizer)) {\n finalizer();\n } else {\n finalizer.unsubscribe();\n }\n}\n", "import { Subscriber } from './Subscriber';\nimport { ObservableNotification } from './types';\n\n/**\n * The {@link GlobalConfig} object for RxJS. It is used to configure things\n * like how to react on unhandled errors.\n */\nexport const config: GlobalConfig = {\n onUnhandledError: null,\n onStoppedNotification: null,\n Promise: undefined,\n useDeprecatedSynchronousErrorHandling: false,\n useDeprecatedNextContext: false,\n};\n\n/**\n * The global configuration object for RxJS, used to configure things\n * like how to react on unhandled errors. Accessible via {@link config}\n * object.\n */\nexport interface GlobalConfig {\n /**\n * A registration point for unhandled errors from RxJS. These are errors that\n * cannot were not handled by consuming code in the usual subscription path. For\n * example, if you have this configured, and you subscribe to an observable without\n * providing an error handler, errors from that subscription will end up here. This\n * will _always_ be called asynchronously on another job in the runtime. This is because\n * we do not want errors thrown in this user-configured handler to interfere with the\n * behavior of the library.\n */\n onUnhandledError: ((err: any) => void) | null;\n\n /**\n * A registration point for notifications that cannot be sent to subscribers because they\n * have completed, errored or have been explicitly unsubscribed. By default, next, complete\n * and error notifications sent to stopped subscribers are noops. However, sometimes callers\n * might want a different behavior. For example, with sources that attempt to report errors\n * to stopped subscribers, a caller can configure RxJS to throw an unhandled error instead.\n * This will _always_ be called asynchronously on another job in the runtime. This is because\n * we do not want errors thrown in this user-configured handler to interfere with the\n * behavior of the library.\n */\n onStoppedNotification: ((notification: ObservableNotification, subscriber: Subscriber) => void) | null;\n\n /**\n * The promise constructor used by default for {@link Observable#toPromise toPromise} and {@link Observable#forEach forEach}\n * methods.\n *\n * @deprecated As of version 8, RxJS will no longer support this sort of injection of a\n * Promise constructor. If you need a Promise implementation other than native promises,\n * please polyfill/patch Promise as you see appropriate. Will be removed in v8.\n */\n Promise?: PromiseConstructorLike;\n\n /**\n * If true, turns on synchronous error rethrowing, which is a deprecated behavior\n * in v6 and higher. This behavior enables bad patterns like wrapping a subscribe\n * call in a try/catch block. It also enables producer interference, a nasty bug\n * where a multicast can be broken for all observers by a downstream consumer with\n * an unhandled error. DO NOT USE THIS FLAG UNLESS IT'S NEEDED TO BUY TIME\n * FOR MIGRATION REASONS.\n *\n * @deprecated As of version 8, RxJS will no longer support synchronous throwing\n * of unhandled errors. All errors will be thrown on a separate call stack to prevent bad\n * behaviors described above. Will be removed in v8.\n */\n useDeprecatedSynchronousErrorHandling: boolean;\n\n /**\n * If true, enables an as-of-yet undocumented feature from v5: The ability to access\n * `unsubscribe()` via `this` context in `next` functions created in observers passed\n * to `subscribe`.\n *\n * This is being removed because the performance was severely problematic, and it could also cause\n * issues when types other than POJOs are passed to subscribe as subscribers, as they will likely have\n * their `this` context overwritten.\n *\n * @deprecated As of version 8, RxJS will no longer support altering the\n * context of next functions provided as part of an observer to Subscribe. Instead,\n * you will have access to a subscription or a signal or token that will allow you to do things like\n * unsubscribe and test closed status. Will be removed in v8.\n */\n useDeprecatedNextContext: boolean;\n}\n", "import type { TimerHandle } from './timerHandle';\ntype SetTimeoutFunction = (handler: () => void, timeout?: number, ...args: any[]) => TimerHandle;\ntype ClearTimeoutFunction = (handle: TimerHandle) => void;\n\ninterface TimeoutProvider {\n setTimeout: SetTimeoutFunction;\n clearTimeout: ClearTimeoutFunction;\n delegate:\n | {\n setTimeout: SetTimeoutFunction;\n clearTimeout: ClearTimeoutFunction;\n }\n | undefined;\n}\n\nexport const timeoutProvider: TimeoutProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n setTimeout(handler: () => void, timeout?: number, ...args) {\n const { delegate } = timeoutProvider;\n if (delegate?.setTimeout) {\n return delegate.setTimeout(handler, timeout, ...args);\n }\n return setTimeout(handler, timeout, ...args);\n },\n clearTimeout(handle) {\n const { delegate } = timeoutProvider;\n return (delegate?.clearTimeout || clearTimeout)(handle as any);\n },\n delegate: undefined,\n};\n", "import { config } from '../config';\nimport { timeoutProvider } from '../scheduler/timeoutProvider';\n\n/**\n * Handles an error on another job either with the user-configured {@link onUnhandledError},\n * or by throwing it on that new job so it can be picked up by `window.onerror`, `process.on('error')`, etc.\n *\n * This should be called whenever there is an error that is out-of-band with the subscription\n * or when an error hits a terminal boundary of the subscription and no error handler was provided.\n *\n * @param err the error to report\n */\nexport function reportUnhandledError(err: any) {\n timeoutProvider.setTimeout(() => {\n const { onUnhandledError } = config;\n if (onUnhandledError) {\n // Execute the user-configured error handler.\n onUnhandledError(err);\n } else {\n // Throw so it is picked up by the runtime's uncaught error mechanism.\n throw err;\n }\n });\n}\n", "/* tslint:disable:no-empty */\nexport function noop() { }\n", "import { CompleteNotification, NextNotification, ErrorNotification } from './types';\n\n/**\n * A completion object optimized for memory use and created to be the\n * same \"shape\" as other notifications in v8.\n * @internal\n */\nexport const COMPLETE_NOTIFICATION = (() => createNotification('C', undefined, undefined) as CompleteNotification)();\n\n/**\n * Internal use only. Creates an optimized error notification that is the same \"shape\"\n * as other notifications.\n * @internal\n */\nexport function errorNotification(error: any): ErrorNotification {\n return createNotification('E', undefined, error) as any;\n}\n\n/**\n * Internal use only. Creates an optimized next notification that is the same \"shape\"\n * as other notifications.\n * @internal\n */\nexport function nextNotification(value: T) {\n return createNotification('N', value, undefined) as NextNotification;\n}\n\n/**\n * Ensures that all notifications created internally have the same \"shape\" in v8.\n *\n * TODO: This is only exported to support a crazy legacy test in `groupBy`.\n * @internal\n */\nexport function createNotification(kind: 'N' | 'E' | 'C', value: any, error: any) {\n return {\n kind,\n value,\n error,\n };\n}\n", "import { config } from '../config';\n\nlet context: { errorThrown: boolean; error: any } | null = null;\n\n/**\n * Handles dealing with errors for super-gross mode. Creates a context, in which\n * any synchronously thrown errors will be passed to {@link captureError}. Which\n * will record the error such that it will be rethrown after the call back is complete.\n * TODO: Remove in v8\n * @param cb An immediately executed function.\n */\nexport function errorContext(cb: () => void) {\n if (config.useDeprecatedSynchronousErrorHandling) {\n const isRoot = !context;\n if (isRoot) {\n context = { errorThrown: false, error: null };\n }\n cb();\n if (isRoot) {\n const { errorThrown, error } = context!;\n context = null;\n if (errorThrown) {\n throw error;\n }\n }\n } else {\n // This is the general non-deprecated path for everyone that\n // isn't crazy enough to use super-gross mode (useDeprecatedSynchronousErrorHandling)\n cb();\n }\n}\n\n/**\n * Captures errors only in super-gross mode.\n * @param err the error to capture\n */\nexport function captureError(err: any) {\n if (config.useDeprecatedSynchronousErrorHandling && context) {\n context.errorThrown = true;\n context.error = err;\n }\n}\n", "import { isFunction } from './util/isFunction';\nimport { Observer, ObservableNotification } from './types';\nimport { isSubscription, Subscription } from './Subscription';\nimport { config } from './config';\nimport { reportUnhandledError } from './util/reportUnhandledError';\nimport { noop } from './util/noop';\nimport { nextNotification, errorNotification, COMPLETE_NOTIFICATION } from './NotificationFactories';\nimport { timeoutProvider } from './scheduler/timeoutProvider';\nimport { captureError } from './util/errorContext';\n\n/**\n * Implements the {@link Observer} interface and extends the\n * {@link Subscription} class. While the {@link Observer} is the public API for\n * consuming the values of an {@link Observable}, all Observers get converted to\n * a Subscriber, in order to provide Subscription-like capabilities such as\n * `unsubscribe`. Subscriber is a common type in RxJS, and crucial for\n * implementing operators, but it is rarely used as a public API.\n */\nexport class Subscriber extends Subscription implements Observer {\n /**\n * A static factory for a Subscriber, given a (potentially partial) definition\n * of an Observer.\n * @param next The `next` callback of an Observer.\n * @param error The `error` callback of an\n * Observer.\n * @param complete The `complete` callback of an\n * Observer.\n * @return A Subscriber wrapping the (partially defined)\n * Observer represented by the given arguments.\n * @deprecated Do not use. Will be removed in v8. There is no replacement for this\n * method, and there is no reason to be creating instances of `Subscriber` directly.\n * If you have a specific use case, please file an issue.\n */\n static create(next?: (x?: T) => void, error?: (e?: any) => void, complete?: () => void): Subscriber {\n return new SafeSubscriber(next, error, complete);\n }\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n protected isStopped: boolean = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n protected destination: Subscriber | Observer; // this `any` is the escape hatch to erase extra type param (e.g. R)\n\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n * There is no reason to directly create an instance of Subscriber. This type is exported for typings reasons.\n */\n constructor(destination?: Subscriber | Observer) {\n super();\n if (destination) {\n this.destination = destination;\n // Automatically chain subscriptions together here.\n // if destination is a Subscription, then it is a Subscriber.\n if (isSubscription(destination)) {\n destination.add(this);\n }\n } else {\n this.destination = EMPTY_OBSERVER;\n }\n }\n\n /**\n * The {@link Observer} callback to receive notifications of type `next` from\n * the Observable, with a value. The Observable may call this method 0 or more\n * times.\n * @param value The `next` value.\n */\n next(value: T): void {\n if (this.isStopped) {\n handleStoppedNotification(nextNotification(value), this);\n } else {\n this._next(value!);\n }\n }\n\n /**\n * The {@link Observer} callback to receive notifications of type `error` from\n * the Observable, with an attached `Error`. Notifies the Observer that\n * the Observable has experienced an error condition.\n * @param err The `error` exception.\n */\n error(err?: any): void {\n if (this.isStopped) {\n handleStoppedNotification(errorNotification(err), this);\n } else {\n this.isStopped = true;\n this._error(err);\n }\n }\n\n /**\n * The {@link Observer} callback to receive a valueless notification of type\n * `complete` from the Observable. Notifies the Observer that the Observable\n * has finished sending push-based notifications.\n */\n complete(): void {\n if (this.isStopped) {\n handleStoppedNotification(COMPLETE_NOTIFICATION, this);\n } else {\n this.isStopped = true;\n this._complete();\n }\n }\n\n unsubscribe(): void {\n if (!this.closed) {\n this.isStopped = true;\n super.unsubscribe();\n this.destination = null!;\n }\n }\n\n protected _next(value: T): void {\n this.destination.next(value);\n }\n\n protected _error(err: any): void {\n try {\n this.destination.error(err);\n } finally {\n this.unsubscribe();\n }\n }\n\n protected _complete(): void {\n try {\n this.destination.complete();\n } finally {\n this.unsubscribe();\n }\n }\n}\n\n/**\n * This bind is captured here because we want to be able to have\n * compatibility with monoid libraries that tend to use a method named\n * `bind`. In particular, a library called Monio requires this.\n */\nconst _bind = Function.prototype.bind;\n\nfunction bind any>(fn: Fn, thisArg: any): Fn {\n return _bind.call(fn, thisArg);\n}\n\n/**\n * Internal optimization only, DO NOT EXPOSE.\n * @internal\n */\nclass ConsumerObserver implements Observer {\n constructor(private partialObserver: Partial>) {}\n\n next(value: T): void {\n const { partialObserver } = this;\n if (partialObserver.next) {\n try {\n partialObserver.next(value);\n } catch (error) {\n handleUnhandledError(error);\n }\n }\n }\n\n error(err: any): void {\n const { partialObserver } = this;\n if (partialObserver.error) {\n try {\n partialObserver.error(err);\n } catch (error) {\n handleUnhandledError(error);\n }\n } else {\n handleUnhandledError(err);\n }\n }\n\n complete(): void {\n const { partialObserver } = this;\n if (partialObserver.complete) {\n try {\n partialObserver.complete();\n } catch (error) {\n handleUnhandledError(error);\n }\n }\n }\n}\n\nexport class SafeSubscriber extends Subscriber {\n constructor(\n observerOrNext?: Partial> | ((value: T) => void) | null,\n error?: ((e?: any) => void) | null,\n complete?: (() => void) | null\n ) {\n super();\n\n let partialObserver: Partial>;\n if (isFunction(observerOrNext) || !observerOrNext) {\n // The first argument is a function, not an observer. The next\n // two arguments *could* be observers, or they could be empty.\n partialObserver = {\n next: (observerOrNext ?? undefined) as ((value: T) => void) | undefined,\n error: error ?? undefined,\n complete: complete ?? undefined,\n };\n } else {\n // The first argument is a partial observer.\n let context: any;\n if (this && config.useDeprecatedNextContext) {\n // This is a deprecated path that made `this.unsubscribe()` available in\n // next handler functions passed to subscribe. This only exists behind a flag\n // now, as it is *very* slow.\n context = Object.create(observerOrNext);\n context.unsubscribe = () => this.unsubscribe();\n partialObserver = {\n next: observerOrNext.next && bind(observerOrNext.next, context),\n error: observerOrNext.error && bind(observerOrNext.error, context),\n complete: observerOrNext.complete && bind(observerOrNext.complete, context),\n };\n } else {\n // The \"normal\" path. Just use the partial observer directly.\n partialObserver = observerOrNext;\n }\n }\n\n // Wrap the partial observer to ensure it's a full observer, and\n // make sure proper error handling is accounted for.\n this.destination = new ConsumerObserver(partialObserver);\n }\n}\n\nfunction handleUnhandledError(error: any) {\n if (config.useDeprecatedSynchronousErrorHandling) {\n captureError(error);\n } else {\n // Ideal path, we report this as an unhandled error,\n // which is thrown on a new call stack.\n reportUnhandledError(error);\n }\n}\n\n/**\n * An error handler used when no error handler was supplied\n * to the SafeSubscriber -- meaning no error handler was supplied\n * do the `subscribe` call on our observable.\n * @param err The error to handle\n */\nfunction defaultErrorHandler(err: any) {\n throw err;\n}\n\n/**\n * A handler for notifications that cannot be sent to a stopped subscriber.\n * @param notification The notification being sent.\n * @param subscriber The stopped subscriber.\n */\nfunction handleStoppedNotification(notification: ObservableNotification, subscriber: Subscriber) {\n const { onStoppedNotification } = config;\n onStoppedNotification && timeoutProvider.setTimeout(() => onStoppedNotification(notification, subscriber));\n}\n\n/**\n * The observer used as a stub for subscriptions where the user did not\n * pass any arguments to `subscribe`. Comes with the default error handling\n * behavior.\n */\nexport const EMPTY_OBSERVER: Readonly> & { closed: true } = {\n closed: true,\n next: noop,\n error: defaultErrorHandler,\n complete: noop,\n};\n", "/**\n * Symbol.observable or a string \"@@observable\". Used for interop\n *\n * @deprecated We will no longer be exporting this symbol in upcoming versions of RxJS.\n * Instead polyfill and use Symbol.observable directly *or* use https://www.npmjs.com/package/symbol-observable\n */\nexport const observable: string | symbol = (() => (typeof Symbol === 'function' && Symbol.observable) || '@@observable')();\n", "/**\n * This function takes one parameter and just returns it. Simply put,\n * this is like `(x: T): T => x`.\n *\n * ## Examples\n *\n * This is useful in some cases when using things like `mergeMap`\n *\n * ```ts\n * import { interval, take, map, range, mergeMap, identity } from 'rxjs';\n *\n * const source$ = interval(1000).pipe(take(5));\n *\n * const result$ = source$.pipe(\n * map(i => range(i)),\n * mergeMap(identity) // same as mergeMap(x => x)\n * );\n *\n * result$.subscribe({\n * next: console.log\n * });\n * ```\n *\n * Or when you want to selectively apply an operator\n *\n * ```ts\n * import { interval, take, identity } from 'rxjs';\n *\n * const shouldLimit = () => Math.random() < 0.5;\n *\n * const source$ = interval(1000);\n *\n * const result$ = source$.pipe(shouldLimit() ? take(5) : identity);\n *\n * result$.subscribe({\n * next: console.log\n * });\n * ```\n *\n * @param x Any value that is returned by this function\n * @returns The value passed as the first parameter to this function\n */\nexport function identity(x: T): T {\n return x;\n}\n", "import { identity } from './identity';\nimport { UnaryFunction } from '../types';\n\nexport function pipe(): typeof identity;\nexport function pipe(fn1: UnaryFunction): UnaryFunction;\nexport function pipe(fn1: UnaryFunction, fn2: UnaryFunction): UnaryFunction;\nexport function pipe(fn1: UnaryFunction, fn2: UnaryFunction, fn3: UnaryFunction): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction,\n fn9: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction,\n fn9: UnaryFunction,\n ...fns: UnaryFunction[]\n): UnaryFunction;\n\n/**\n * pipe() can be called on one or more functions, each of which can take one argument (\"UnaryFunction\")\n * and uses it to return a value.\n * It returns a function that takes one argument, passes it to the first UnaryFunction, and then\n * passes the result to the next one, passes that result to the next one, and so on. \n */\nexport function pipe(...fns: Array>): UnaryFunction {\n return pipeFromArray(fns);\n}\n\n/** @internal */\nexport function pipeFromArray(fns: Array>): UnaryFunction {\n if (fns.length === 0) {\n return identity as UnaryFunction;\n }\n\n if (fns.length === 1) {\n return fns[0];\n }\n\n return function piped(input: T): R {\n return fns.reduce((prev: any, fn: UnaryFunction) => fn(prev), input as any);\n };\n}\n", "import { Operator } from './Operator';\nimport { SafeSubscriber, Subscriber } from './Subscriber';\nimport { isSubscription, Subscription } from './Subscription';\nimport { TeardownLogic, OperatorFunction, Subscribable, Observer } from './types';\nimport { observable as Symbol_observable } from './symbol/observable';\nimport { pipeFromArray } from './util/pipe';\nimport { config } from './config';\nimport { isFunction } from './util/isFunction';\nimport { errorContext } from './util/errorContext';\n\n/**\n * A representation of any set of values over any amount of time. This is the most basic building block\n * of RxJS.\n */\nexport class Observable implements Subscribable {\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n */\n source: Observable | undefined;\n\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n */\n operator: Operator | undefined;\n\n /**\n * @param subscribe The function that is called when the Observable is\n * initially subscribed to. This function is given a Subscriber, to which new values\n * can be `next`ed, or an `error` method can be called to raise an error, or\n * `complete` can be called to notify of a successful completion.\n */\n constructor(subscribe?: (this: Observable, subscriber: Subscriber) => TeardownLogic) {\n if (subscribe) {\n this._subscribe = subscribe;\n }\n }\n\n // HACK: Since TypeScript inherits static properties too, we have to\n // fight against TypeScript here so Subject can have a different static create signature\n /**\n * Creates a new Observable by calling the Observable constructor\n * @param subscribe the subscriber function to be passed to the Observable constructor\n * @return A new observable.\n * @deprecated Use `new Observable()` instead. Will be removed in v8.\n */\n static create: (...args: any[]) => any = (subscribe?: (subscriber: Subscriber) => TeardownLogic) => {\n return new Observable(subscribe);\n };\n\n /**\n * Creates a new Observable, with this Observable instance as the source, and the passed\n * operator defined as the new observable's operator.\n * @param operator the operator defining the operation to take on the observable\n * @return A new observable with the Operator applied.\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n * If you have implemented an operator using `lift`, it is recommended that you create an\n * operator by simply returning `new Observable()` directly. See \"Creating new operators from\n * scratch\" section here: https://rxjs.dev/guide/operators\n */\n lift(operator?: Operator): Observable {\n const observable = new Observable();\n observable.source = this;\n observable.operator = operator;\n return observable;\n }\n\n subscribe(observerOrNext?: Partial> | ((value: T) => void)): Subscription;\n /** @deprecated Instead of passing separate callback arguments, use an observer argument. Signatures taking separate callback arguments will be removed in v8. Details: https://rxjs.dev/deprecations/subscribe-arguments */\n subscribe(next?: ((value: T) => void) | null, error?: ((error: any) => void) | null, complete?: (() => void) | null): Subscription;\n /**\n * Invokes an execution of an Observable and registers Observer handlers for notifications it will emit.\n *\n * Use it when you have all these Observables, but still nothing is happening.\n *\n * `subscribe` is not a regular operator, but a method that calls Observable's internal `subscribe` function. It\n * might be for example a function that you passed to Observable's constructor, but most of the time it is\n * a library implementation, which defines what will be emitted by an Observable, and when it be will emitted. This means\n * that calling `subscribe` is actually the moment when Observable starts its work, not when it is created, as it is often\n * the thought.\n *\n * Apart from starting the execution of an Observable, this method allows you to listen for values\n * that an Observable emits, as well as for when it completes or errors. You can achieve this in two\n * of the following ways.\n *\n * The first way is creating an object that implements {@link Observer} interface. It should have methods\n * defined by that interface, but note that it should be just a regular JavaScript object, which you can create\n * yourself in any way you want (ES6 class, classic function constructor, object literal etc.). In particular, do\n * not attempt to use any RxJS implementation details to create Observers - you don't need them. Remember also\n * that your object does not have to implement all methods. If you find yourself creating a method that doesn't\n * do anything, you can simply omit it. Note however, if the `error` method is not provided and an error happens,\n * it will be thrown asynchronously. Errors thrown asynchronously cannot be caught using `try`/`catch`. Instead,\n * use the {@link onUnhandledError} configuration option or use a runtime handler (like `window.onerror` or\n * `process.on('error)`) to be notified of unhandled errors. Because of this, it's recommended that you provide\n * an `error` method to avoid missing thrown errors.\n *\n * The second way is to give up on Observer object altogether and simply provide callback functions in place of its methods.\n * This means you can provide three functions as arguments to `subscribe`, where the first function is equivalent\n * of a `next` method, the second of an `error` method and the third of a `complete` method. Just as in case of an Observer,\n * if you do not need to listen for something, you can omit a function by passing `undefined` or `null`,\n * since `subscribe` recognizes these functions by where they were placed in function call. When it comes\n * to the `error` function, as with an Observer, if not provided, errors emitted by an Observable will be thrown asynchronously.\n *\n * You can, however, subscribe with no parameters at all. This may be the case where you're not interested in terminal events\n * and you also handled emissions internally by using operators (e.g. using `tap`).\n *\n * Whichever style of calling `subscribe` you use, in both cases it returns a Subscription object.\n * This object allows you to call `unsubscribe` on it, which in turn will stop the work that an Observable does and will clean\n * up all resources that an Observable used. Note that cancelling a subscription will not call `complete` callback\n * provided to `subscribe` function, which is reserved for a regular completion signal that comes from an Observable.\n *\n * Remember that callbacks provided to `subscribe` are not guaranteed to be called asynchronously.\n * It is an Observable itself that decides when these functions will be called. For example {@link of}\n * by default emits all its values synchronously. Always check documentation for how given Observable\n * will behave when subscribed and if its default behavior can be modified with a `scheduler`.\n *\n * #### Examples\n *\n * Subscribe with an {@link guide/observer Observer}\n *\n * ```ts\n * import { of } from 'rxjs';\n *\n * const sumObserver = {\n * sum: 0,\n * next(value) {\n * console.log('Adding: ' + value);\n * this.sum = this.sum + value;\n * },\n * error() {\n * // We actually could just remove this method,\n * // since we do not really care about errors right now.\n * },\n * complete() {\n * console.log('Sum equals: ' + this.sum);\n * }\n * };\n *\n * of(1, 2, 3) // Synchronously emits 1, 2, 3 and then completes.\n * .subscribe(sumObserver);\n *\n * // Logs:\n * // 'Adding: 1'\n * // 'Adding: 2'\n * // 'Adding: 3'\n * // 'Sum equals: 6'\n * ```\n *\n * Subscribe with functions ({@link deprecations/subscribe-arguments deprecated})\n *\n * ```ts\n * import { of } from 'rxjs'\n *\n * let sum = 0;\n *\n * of(1, 2, 3).subscribe(\n * value => {\n * console.log('Adding: ' + value);\n * sum = sum + value;\n * },\n * undefined,\n * () => console.log('Sum equals: ' + sum)\n * );\n *\n * // Logs:\n * // 'Adding: 1'\n * // 'Adding: 2'\n * // 'Adding: 3'\n * // 'Sum equals: 6'\n * ```\n *\n * Cancel a subscription\n *\n * ```ts\n * import { interval } from 'rxjs';\n *\n * const subscription = interval(1000).subscribe({\n * next(num) {\n * console.log(num)\n * },\n * complete() {\n * // Will not be called, even when cancelling subscription.\n * console.log('completed!');\n * }\n * });\n *\n * setTimeout(() => {\n * subscription.unsubscribe();\n * console.log('unsubscribed!');\n * }, 2500);\n *\n * // Logs:\n * // 0 after 1s\n * // 1 after 2s\n * // 'unsubscribed!' after 2.5s\n * ```\n *\n * @param observerOrNext Either an {@link Observer} with some or all callback methods,\n * or the `next` handler that is called for each value emitted from the subscribed Observable.\n * @param error A handler for a terminal event resulting from an error. If no error handler is provided,\n * the error will be thrown asynchronously as unhandled.\n * @param complete A handler for a terminal event resulting from successful completion.\n * @return A subscription reference to the registered handlers.\n */\n subscribe(\n observerOrNext?: Partial> | ((value: T) => void) | null,\n error?: ((error: any) => void) | null,\n complete?: (() => void) | null\n ): Subscription {\n const subscriber = isSubscriber(observerOrNext) ? observerOrNext : new SafeSubscriber(observerOrNext, error, complete);\n\n errorContext(() => {\n const { operator, source } = this;\n subscriber.add(\n operator\n ? // We're dealing with a subscription in the\n // operator chain to one of our lifted operators.\n operator.call(subscriber, source)\n : source\n ? // If `source` has a value, but `operator` does not, something that\n // had intimate knowledge of our API, like our `Subject`, must have\n // set it. We're going to just call `_subscribe` directly.\n this._subscribe(subscriber)\n : // In all other cases, we're likely wrapping a user-provided initializer\n // function, so we need to catch errors and handle them appropriately.\n this._trySubscribe(subscriber)\n );\n });\n\n return subscriber;\n }\n\n /** @internal */\n protected _trySubscribe(sink: Subscriber): TeardownLogic {\n try {\n return this._subscribe(sink);\n } catch (err) {\n // We don't need to return anything in this case,\n // because it's just going to try to `add()` to a subscription\n // above.\n sink.error(err);\n }\n }\n\n /**\n * Used as a NON-CANCELLABLE means of subscribing to an observable, for use with\n * APIs that expect promises, like `async/await`. You cannot unsubscribe from this.\n *\n * **WARNING**: Only use this with observables you *know* will complete. If the source\n * observable does not complete, you will end up with a promise that is hung up, and\n * potentially all of the state of an async function hanging out in memory. To avoid\n * this situation, look into adding something like {@link timeout}, {@link take},\n * {@link takeWhile}, or {@link takeUntil} amongst others.\n *\n * #### Example\n *\n * ```ts\n * import { interval, take } from 'rxjs';\n *\n * const source$ = interval(1000).pipe(take(4));\n *\n * async function getTotal() {\n * let total = 0;\n *\n * await source$.forEach(value => {\n * total += value;\n * console.log('observable -> ' + value);\n * });\n *\n * return total;\n * }\n *\n * getTotal().then(\n * total => console.log('Total: ' + total)\n * );\n *\n * // Expected:\n * // 'observable -> 0'\n * // 'observable -> 1'\n * // 'observable -> 2'\n * // 'observable -> 3'\n * // 'Total: 6'\n * ```\n *\n * @param next A handler for each value emitted by the observable.\n * @return A promise that either resolves on observable completion or\n * rejects with the handled error.\n */\n forEach(next: (value: T) => void): Promise;\n\n /**\n * @param next a handler for each value emitted by the observable\n * @param promiseCtor a constructor function used to instantiate the Promise\n * @return a promise that either resolves on observable completion or\n * rejects with the handled error\n * @deprecated Passing a Promise constructor will no longer be available\n * in upcoming versions of RxJS. This is because it adds weight to the library, for very\n * little benefit. If you need this functionality, it is recommended that you either\n * polyfill Promise, or you create an adapter to convert the returned native promise\n * to whatever promise implementation you wanted. Will be removed in v8.\n */\n forEach(next: (value: T) => void, promiseCtor: PromiseConstructorLike): Promise;\n\n forEach(next: (value: T) => void, promiseCtor?: PromiseConstructorLike): Promise {\n promiseCtor = getPromiseCtor(promiseCtor);\n\n return new promiseCtor((resolve, reject) => {\n const subscriber = new SafeSubscriber({\n next: (value) => {\n try {\n next(value);\n } catch (err) {\n reject(err);\n subscriber.unsubscribe();\n }\n },\n error: reject,\n complete: resolve,\n });\n this.subscribe(subscriber);\n }) as Promise;\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): TeardownLogic {\n return this.source?.subscribe(subscriber);\n }\n\n /**\n * An interop point defined by the es7-observable spec https://github.com/zenparsing/es-observable\n * @return This instance of the observable.\n */\n [Symbol_observable]() {\n return this;\n }\n\n /* tslint:disable:max-line-length */\n pipe(): Observable;\n pipe(op1: OperatorFunction): Observable;\n pipe(op1: OperatorFunction, op2: OperatorFunction): Observable;\n pipe(op1: OperatorFunction, op2: OperatorFunction, op3: OperatorFunction): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction,\n op9: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction,\n op9: OperatorFunction,\n ...operations: OperatorFunction[]\n ): Observable;\n /* tslint:enable:max-line-length */\n\n /**\n * Used to stitch together functional operators into a chain.\n *\n * ## Example\n *\n * ```ts\n * import { interval, filter, map, scan } from 'rxjs';\n *\n * interval(1000)\n * .pipe(\n * filter(x => x % 2 === 0),\n * map(x => x + x),\n * scan((acc, x) => acc + x)\n * )\n * .subscribe(x => console.log(x));\n * ```\n *\n * @return The Observable result of all the operators having been called\n * in the order they were passed in.\n */\n pipe(...operations: OperatorFunction[]): Observable {\n return pipeFromArray(operations)(this);\n }\n\n /* tslint:disable:max-line-length */\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(): Promise;\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(PromiseCtor: typeof Promise): Promise;\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(PromiseCtor: PromiseConstructorLike): Promise;\n /* tslint:enable:max-line-length */\n\n /**\n * Subscribe to this Observable and get a Promise resolving on\n * `complete` with the last emission (if any).\n *\n * **WARNING**: Only use this with observables you *know* will complete. If the source\n * observable does not complete, you will end up with a promise that is hung up, and\n * potentially all of the state of an async function hanging out in memory. To avoid\n * this situation, look into adding something like {@link timeout}, {@link take},\n * {@link takeWhile}, or {@link takeUntil} amongst others.\n *\n * @param [promiseCtor] a constructor function used to instantiate\n * the Promise\n * @return A Promise that resolves with the last value emit, or\n * rejects on an error. If there were no emissions, Promise\n * resolves with undefined.\n * @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise\n */\n toPromise(promiseCtor?: PromiseConstructorLike): Promise {\n promiseCtor = getPromiseCtor(promiseCtor);\n\n return new promiseCtor((resolve, reject) => {\n let value: T | undefined;\n this.subscribe(\n (x: T) => (value = x),\n (err: any) => reject(err),\n () => resolve(value)\n );\n }) as Promise;\n }\n}\n\n/**\n * Decides between a passed promise constructor from consuming code,\n * A default configured promise constructor, and the native promise\n * constructor and returns it. If nothing can be found, it will throw\n * an error.\n * @param promiseCtor The optional promise constructor to passed by consuming code\n */\nfunction getPromiseCtor(promiseCtor: PromiseConstructorLike | undefined) {\n return promiseCtor ?? config.Promise ?? Promise;\n}\n\nfunction isObserver(value: any): value is Observer {\n return value && isFunction(value.next) && isFunction(value.error) && isFunction(value.complete);\n}\n\nfunction isSubscriber(value: any): value is Subscriber {\n return (value && value instanceof Subscriber) || (isObserver(value) && isSubscription(value));\n}\n", "import { Observable } from '../Observable';\nimport { Subscriber } from '../Subscriber';\nimport { OperatorFunction } from '../types';\nimport { isFunction } from './isFunction';\n\n/**\n * Used to determine if an object is an Observable with a lift function.\n */\nexport function hasLift(source: any): source is { lift: InstanceType['lift'] } {\n return isFunction(source?.lift);\n}\n\n/**\n * Creates an `OperatorFunction`. Used to define operators throughout the library in a concise way.\n * @param init The logic to connect the liftedSource to the subscriber at the moment of subscription.\n */\nexport function operate(\n init: (liftedSource: Observable, subscriber: Subscriber) => (() => void) | void\n): OperatorFunction {\n return (source: Observable) => {\n if (hasLift(source)) {\n return source.lift(function (this: Subscriber, liftedSource: Observable) {\n try {\n return init(liftedSource, this);\n } catch (err) {\n this.error(err);\n }\n });\n }\n throw new TypeError('Unable to lift unknown Observable type');\n };\n}\n", "import { Subscriber } from '../Subscriber';\n\n/**\n * Creates an instance of an `OperatorSubscriber`.\n * @param destination The downstream subscriber.\n * @param onNext Handles next values, only called if this subscriber is not stopped or closed. Any\n * error that occurs in this function is caught and sent to the `error` method of this subscriber.\n * @param onError Handles errors from the subscription, any errors that occur in this handler are caught\n * and send to the `destination` error handler.\n * @param onComplete Handles completion notification from the subscription. Any errors that occur in\n * this handler are sent to the `destination` error handler.\n * @param onFinalize Additional teardown logic here. This will only be called on teardown if the\n * subscriber itself is not already closed. This is called after all other teardown logic is executed.\n */\nexport function createOperatorSubscriber(\n destination: Subscriber,\n onNext?: (value: T) => void,\n onComplete?: () => void,\n onError?: (err: any) => void,\n onFinalize?: () => void\n): Subscriber {\n return new OperatorSubscriber(destination, onNext, onComplete, onError, onFinalize);\n}\n\n/**\n * A generic helper for allowing operators to be created with a Subscriber and\n * use closures to capture necessary state from the operator function itself.\n */\nexport class OperatorSubscriber extends Subscriber {\n /**\n * Creates an instance of an `OperatorSubscriber`.\n * @param destination The downstream subscriber.\n * @param onNext Handles next values, only called if this subscriber is not stopped or closed. Any\n * error that occurs in this function is caught and sent to the `error` method of this subscriber.\n * @param onError Handles errors from the subscription, any errors that occur in this handler are caught\n * and send to the `destination` error handler.\n * @param onComplete Handles completion notification from the subscription. Any errors that occur in\n * this handler are sent to the `destination` error handler.\n * @param onFinalize Additional finalization logic here. This will only be called on finalization if the\n * subscriber itself is not already closed. This is called after all other finalization logic is executed.\n * @param shouldUnsubscribe An optional check to see if an unsubscribe call should truly unsubscribe.\n * NOTE: This currently **ONLY** exists to support the strange behavior of {@link groupBy}, where unsubscription\n * to the resulting observable does not actually disconnect from the source if there are active subscriptions\n * to any grouped observable. (DO NOT EXPOSE OR USE EXTERNALLY!!!)\n */\n constructor(\n destination: Subscriber,\n onNext?: (value: T) => void,\n onComplete?: () => void,\n onError?: (err: any) => void,\n private onFinalize?: () => void,\n private shouldUnsubscribe?: () => boolean\n ) {\n // It's important - for performance reasons - that all of this class's\n // members are initialized and that they are always initialized in the same\n // order. This will ensure that all OperatorSubscriber instances have the\n // same hidden class in V8. This, in turn, will help keep the number of\n // hidden classes involved in property accesses within the base class as\n // low as possible. If the number of hidden classes involved exceeds four,\n // the property accesses will become megamorphic and performance penalties\n // will be incurred - i.e. inline caches won't be used.\n //\n // The reasons for ensuring all instances have the same hidden class are\n // further discussed in this blog post from Benedikt Meurer:\n // https://benediktmeurer.de/2018/03/23/impact-of-polymorphism-on-component-based-frameworks-like-react/\n super(destination);\n this._next = onNext\n ? function (this: OperatorSubscriber, value: T) {\n try {\n onNext(value);\n } catch (err) {\n destination.error(err);\n }\n }\n : super._next;\n this._error = onError\n ? function (this: OperatorSubscriber, err: any) {\n try {\n onError(err);\n } catch (err) {\n // Send any errors that occur down stream.\n destination.error(err);\n } finally {\n // Ensure finalization.\n this.unsubscribe();\n }\n }\n : super._error;\n this._complete = onComplete\n ? function (this: OperatorSubscriber) {\n try {\n onComplete();\n } catch (err) {\n // Send any errors that occur down stream.\n destination.error(err);\n } finally {\n // Ensure finalization.\n this.unsubscribe();\n }\n }\n : super._complete;\n }\n\n unsubscribe() {\n if (!this.shouldUnsubscribe || this.shouldUnsubscribe()) {\n const { closed } = this;\n super.unsubscribe();\n // Execute additional teardown if we have any and we didn't already do so.\n !closed && this.onFinalize?.();\n }\n }\n}\n", "import { Subscription } from '../Subscription';\n\ninterface AnimationFrameProvider {\n schedule(callback: FrameRequestCallback): Subscription;\n requestAnimationFrame: typeof requestAnimationFrame;\n cancelAnimationFrame: typeof cancelAnimationFrame;\n delegate:\n | {\n requestAnimationFrame: typeof requestAnimationFrame;\n cancelAnimationFrame: typeof cancelAnimationFrame;\n }\n | undefined;\n}\n\nexport const animationFrameProvider: AnimationFrameProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n schedule(callback) {\n let request = requestAnimationFrame;\n let cancel: typeof cancelAnimationFrame | undefined = cancelAnimationFrame;\n const { delegate } = animationFrameProvider;\n if (delegate) {\n request = delegate.requestAnimationFrame;\n cancel = delegate.cancelAnimationFrame;\n }\n const handle = request((timestamp) => {\n // Clear the cancel function. The request has been fulfilled, so\n // attempting to cancel the request upon unsubscription would be\n // pointless.\n cancel = undefined;\n callback(timestamp);\n });\n return new Subscription(() => cancel?.(handle));\n },\n requestAnimationFrame(...args) {\n const { delegate } = animationFrameProvider;\n return (delegate?.requestAnimationFrame || requestAnimationFrame)(...args);\n },\n cancelAnimationFrame(...args) {\n const { delegate } = animationFrameProvider;\n return (delegate?.cancelAnimationFrame || cancelAnimationFrame)(...args);\n },\n delegate: undefined,\n};\n", "import { createErrorClass } from './createErrorClass';\n\nexport interface ObjectUnsubscribedError extends Error {}\n\nexport interface ObjectUnsubscribedErrorCtor {\n /**\n * @deprecated Internal implementation detail. Do not construct error instances.\n * Cannot be tagged as internal: https://github.com/ReactiveX/rxjs/issues/6269\n */\n new (): ObjectUnsubscribedError;\n}\n\n/**\n * An error thrown when an action is invalid because the object has been\n * unsubscribed.\n *\n * @see {@link Subject}\n * @see {@link BehaviorSubject}\n *\n * @class ObjectUnsubscribedError\n */\nexport const ObjectUnsubscribedError: ObjectUnsubscribedErrorCtor = createErrorClass(\n (_super) =>\n function ObjectUnsubscribedErrorImpl(this: any) {\n _super(this);\n this.name = 'ObjectUnsubscribedError';\n this.message = 'object unsubscribed';\n }\n);\n", "import { Operator } from './Operator';\nimport { Observable } from './Observable';\nimport { Subscriber } from './Subscriber';\nimport { Subscription, EMPTY_SUBSCRIPTION } from './Subscription';\nimport { Observer, SubscriptionLike, TeardownLogic } from './types';\nimport { ObjectUnsubscribedError } from './util/ObjectUnsubscribedError';\nimport { arrRemove } from './util/arrRemove';\nimport { errorContext } from './util/errorContext';\n\n/**\n * A Subject is a special type of Observable that allows values to be\n * multicasted to many Observers. Subjects are like EventEmitters.\n *\n * Every Subject is an Observable and an Observer. You can subscribe to a\n * Subject, and you can call next to feed values as well as error and complete.\n */\nexport class Subject extends Observable implements SubscriptionLike {\n closed = false;\n\n private currentObservers: Observer[] | null = null;\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n observers: Observer[] = [];\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n isStopped = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n hasError = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n thrownError: any = null;\n\n /**\n * Creates a \"subject\" by basically gluing an observer to an observable.\n *\n * @deprecated Recommended you do not use. Will be removed at some point in the future. Plans for replacement still under discussion.\n */\n static create: (...args: any[]) => any = (destination: Observer, source: Observable): AnonymousSubject => {\n return new AnonymousSubject(destination, source);\n };\n\n constructor() {\n // NOTE: This must be here to obscure Observable's constructor.\n super();\n }\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n lift(operator: Operator): Observable {\n const subject = new AnonymousSubject(this, this);\n subject.operator = operator as any;\n return subject as any;\n }\n\n /** @internal */\n protected _throwIfClosed() {\n if (this.closed) {\n throw new ObjectUnsubscribedError();\n }\n }\n\n next(value: T) {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n if (!this.currentObservers) {\n this.currentObservers = Array.from(this.observers);\n }\n for (const observer of this.currentObservers) {\n observer.next(value);\n }\n }\n });\n }\n\n error(err: any) {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n this.hasError = this.isStopped = true;\n this.thrownError = err;\n const { observers } = this;\n while (observers.length) {\n observers.shift()!.error(err);\n }\n }\n });\n }\n\n complete() {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n this.isStopped = true;\n const { observers } = this;\n while (observers.length) {\n observers.shift()!.complete();\n }\n }\n });\n }\n\n unsubscribe() {\n this.isStopped = this.closed = true;\n this.observers = this.currentObservers = null!;\n }\n\n get observed() {\n return this.observers?.length > 0;\n }\n\n /** @internal */\n protected _trySubscribe(subscriber: Subscriber): TeardownLogic {\n this._throwIfClosed();\n return super._trySubscribe(subscriber);\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n this._throwIfClosed();\n this._checkFinalizedStatuses(subscriber);\n return this._innerSubscribe(subscriber);\n }\n\n /** @internal */\n protected _innerSubscribe(subscriber: Subscriber) {\n const { hasError, isStopped, observers } = this;\n if (hasError || isStopped) {\n return EMPTY_SUBSCRIPTION;\n }\n this.currentObservers = null;\n observers.push(subscriber);\n return new Subscription(() => {\n this.currentObservers = null;\n arrRemove(observers, subscriber);\n });\n }\n\n /** @internal */\n protected _checkFinalizedStatuses(subscriber: Subscriber) {\n const { hasError, thrownError, isStopped } = this;\n if (hasError) {\n subscriber.error(thrownError);\n } else if (isStopped) {\n subscriber.complete();\n }\n }\n\n /**\n * Creates a new Observable with this Subject as the source. You can do this\n * to create custom Observer-side logic of the Subject and conceal it from\n * code that uses the Observable.\n * @return Observable that this Subject casts to.\n */\n asObservable(): Observable {\n const observable: any = new Observable();\n observable.source = this;\n return observable;\n }\n}\n\nexport class AnonymousSubject extends Subject {\n constructor(\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n public destination?: Observer,\n source?: Observable\n ) {\n super();\n this.source = source;\n }\n\n next(value: T) {\n this.destination?.next?.(value);\n }\n\n error(err: any) {\n this.destination?.error?.(err);\n }\n\n complete() {\n this.destination?.complete?.();\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n return this.source?.subscribe(subscriber) ?? EMPTY_SUBSCRIPTION;\n }\n}\n", "import { Subject } from './Subject';\nimport { Subscriber } from './Subscriber';\nimport { Subscription } from './Subscription';\n\n/**\n * A variant of Subject that requires an initial value and emits its current\n * value whenever it is subscribed to.\n */\nexport class BehaviorSubject extends Subject {\n constructor(private _value: T) {\n super();\n }\n\n get value(): T {\n return this.getValue();\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n const subscription = super._subscribe(subscriber);\n !subscription.closed && subscriber.next(this._value);\n return subscription;\n }\n\n getValue(): T {\n const { hasError, thrownError, _value } = this;\n if (hasError) {\n throw thrownError;\n }\n this._throwIfClosed();\n return _value;\n }\n\n next(value: T): void {\n super.next((this._value = value));\n }\n}\n", "import { TimestampProvider } from '../types';\n\ninterface DateTimestampProvider extends TimestampProvider {\n delegate: TimestampProvider | undefined;\n}\n\nexport const dateTimestampProvider: DateTimestampProvider = {\n now() {\n // Use the variable rather than `this` so that the function can be called\n // without being bound to the provider.\n return (dateTimestampProvider.delegate || Date).now();\n },\n delegate: undefined,\n};\n", "import { Subject } from './Subject';\nimport { TimestampProvider } from './types';\nimport { Subscriber } from './Subscriber';\nimport { Subscription } from './Subscription';\nimport { dateTimestampProvider } from './scheduler/dateTimestampProvider';\n\n/**\n * A variant of {@link Subject} that \"replays\" old values to new subscribers by emitting them when they first subscribe.\n *\n * `ReplaySubject` has an internal buffer that will store a specified number of values that it has observed. Like `Subject`,\n * `ReplaySubject` \"observes\" values by having them passed to its `next` method. When it observes a value, it will store that\n * value for a time determined by the configuration of the `ReplaySubject`, as passed to its constructor.\n *\n * When a new subscriber subscribes to the `ReplaySubject` instance, it will synchronously emit all values in its buffer in\n * a First-In-First-Out (FIFO) manner. The `ReplaySubject` will also complete, if it has observed completion; and it will\n * error if it has observed an error.\n *\n * There are two main configuration items to be concerned with:\n *\n * 1. `bufferSize` - This will determine how many items are stored in the buffer, defaults to infinite.\n * 2. `windowTime` - The amount of time to hold a value in the buffer before removing it from the buffer.\n *\n * Both configurations may exist simultaneously. So if you would like to buffer a maximum of 3 values, as long as the values\n * are less than 2 seconds old, you could do so with a `new ReplaySubject(3, 2000)`.\n *\n * ### Differences with BehaviorSubject\n *\n * `BehaviorSubject` is similar to `new ReplaySubject(1)`, with a couple of exceptions:\n *\n * 1. `BehaviorSubject` comes \"primed\" with a single value upon construction.\n * 2. `ReplaySubject` will replay values, even after observing an error, where `BehaviorSubject` will not.\n *\n * @see {@link Subject}\n * @see {@link BehaviorSubject}\n * @see {@link shareReplay}\n */\nexport class ReplaySubject extends Subject {\n private _buffer: (T | number)[] = [];\n private _infiniteTimeWindow = true;\n\n /**\n * @param _bufferSize The size of the buffer to replay on subscription\n * @param _windowTime The amount of time the buffered items will stay buffered\n * @param _timestampProvider An object with a `now()` method that provides the current timestamp. This is used to\n * calculate the amount of time something has been buffered.\n */\n constructor(\n private _bufferSize = Infinity,\n private _windowTime = Infinity,\n private _timestampProvider: TimestampProvider = dateTimestampProvider\n ) {\n super();\n this._infiniteTimeWindow = _windowTime === Infinity;\n this._bufferSize = Math.max(1, _bufferSize);\n this._windowTime = Math.max(1, _windowTime);\n }\n\n next(value: T): void {\n const { isStopped, _buffer, _infiniteTimeWindow, _timestampProvider, _windowTime } = this;\n if (!isStopped) {\n _buffer.push(value);\n !_infiniteTimeWindow && _buffer.push(_timestampProvider.now() + _windowTime);\n }\n this._trimBuffer();\n super.next(value);\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n this._throwIfClosed();\n this._trimBuffer();\n\n const subscription = this._innerSubscribe(subscriber);\n\n const { _infiniteTimeWindow, _buffer } = this;\n // We use a copy here, so reentrant code does not mutate our array while we're\n // emitting it to a new subscriber.\n const copy = _buffer.slice();\n for (let i = 0; i < copy.length && !subscriber.closed; i += _infiniteTimeWindow ? 1 : 2) {\n subscriber.next(copy[i] as T);\n }\n\n this._checkFinalizedStatuses(subscriber);\n\n return subscription;\n }\n\n private _trimBuffer() {\n const { _bufferSize, _timestampProvider, _buffer, _infiniteTimeWindow } = this;\n // If we don't have an infinite buffer size, and we're over the length,\n // use splice to truncate the old buffer values off. Note that we have to\n // double the size for instances where we're not using an infinite time window\n // because we're storing the values and the timestamps in the same array.\n const adjustedBufferSize = (_infiniteTimeWindow ? 1 : 2) * _bufferSize;\n _bufferSize < Infinity && adjustedBufferSize < _buffer.length && _buffer.splice(0, _buffer.length - adjustedBufferSize);\n\n // Now, if we're not in an infinite time window, remove all values where the time is\n // older than what is allowed.\n if (!_infiniteTimeWindow) {\n const now = _timestampProvider.now();\n let last = 0;\n // Search the array for the first timestamp that isn't expired and\n // truncate the buffer up to that point.\n for (let i = 1; i < _buffer.length && (_buffer[i] as number) <= now; i += 2) {\n last = i;\n }\n last && _buffer.splice(0, last + 1);\n }\n }\n}\n", "import { Scheduler } from '../Scheduler';\nimport { Subscription } from '../Subscription';\nimport { SchedulerAction } from '../types';\n\n/**\n * A unit of work to be executed in a `scheduler`. An action is typically\n * created from within a {@link SchedulerLike} and an RxJS user does not need to concern\n * themselves about creating and manipulating an Action.\n *\n * ```ts\n * class Action extends Subscription {\n * new (scheduler: Scheduler, work: (state?: T) => void);\n * schedule(state?: T, delay: number = 0): Subscription;\n * }\n * ```\n */\nexport class Action extends Subscription {\n constructor(scheduler: Scheduler, work: (this: SchedulerAction, state?: T) => void) {\n super();\n }\n /**\n * Schedules this action on its parent {@link SchedulerLike} for execution. May be passed\n * some context object, `state`. May happen at some point in the future,\n * according to the `delay` parameter, if specified.\n * @param state Some contextual data that the `work` function uses when called by the\n * Scheduler.\n * @param delay Time to wait before executing the work, where the time unit is implicit\n * and defined by the Scheduler.\n * @return A subscription in order to be able to unsubscribe the scheduled work.\n */\n public schedule(state?: T, delay: number = 0): Subscription {\n return this;\n }\n}\n", "import type { TimerHandle } from './timerHandle';\ntype SetIntervalFunction = (handler: () => void, timeout?: number, ...args: any[]) => TimerHandle;\ntype ClearIntervalFunction = (handle: TimerHandle) => void;\n\ninterface IntervalProvider {\n setInterval: SetIntervalFunction;\n clearInterval: ClearIntervalFunction;\n delegate:\n | {\n setInterval: SetIntervalFunction;\n clearInterval: ClearIntervalFunction;\n }\n | undefined;\n}\n\nexport const intervalProvider: IntervalProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n setInterval(handler: () => void, timeout?: number, ...args) {\n const { delegate } = intervalProvider;\n if (delegate?.setInterval) {\n return delegate.setInterval(handler, timeout, ...args);\n }\n return setInterval(handler, timeout, ...args);\n },\n clearInterval(handle) {\n const { delegate } = intervalProvider;\n return (delegate?.clearInterval || clearInterval)(handle as any);\n },\n delegate: undefined,\n};\n", "import { Action } from './Action';\nimport { SchedulerAction } from '../types';\nimport { Subscription } from '../Subscription';\nimport { AsyncScheduler } from './AsyncScheduler';\nimport { intervalProvider } from './intervalProvider';\nimport { arrRemove } from '../util/arrRemove';\nimport { TimerHandle } from './timerHandle';\n\nexport class AsyncAction extends Action {\n public id: TimerHandle | undefined;\n public state?: T;\n // @ts-ignore: Property has no initializer and is not definitely assigned\n public delay: number;\n protected pending: boolean = false;\n\n constructor(protected scheduler: AsyncScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n public schedule(state?: T, delay: number = 0): Subscription {\n if (this.closed) {\n return this;\n }\n\n // Always replace the current state with the new state.\n this.state = state;\n\n const id = this.id;\n const scheduler = this.scheduler;\n\n //\n // Important implementation note:\n //\n // Actions only execute once by default, unless rescheduled from within the\n // scheduled callback. This allows us to implement single and repeat\n // actions via the same code path, without adding API surface area, as well\n // as mimic traditional recursion but across asynchronous boundaries.\n //\n // However, JS runtimes and timers distinguish between intervals achieved by\n // serial `setTimeout` calls vs. a single `setInterval` call. An interval of\n // serial `setTimeout` calls can be individually delayed, which delays\n // scheduling the next `setTimeout`, and so on. `setInterval` attempts to\n // guarantee the interval callback will be invoked more precisely to the\n // interval period, regardless of load.\n //\n // Therefore, we use `setInterval` to schedule single and repeat actions.\n // If the action reschedules itself with the same delay, the interval is not\n // canceled. If the action doesn't reschedule, or reschedules with a\n // different delay, the interval will be canceled after scheduled callback\n // execution.\n //\n if (id != null) {\n this.id = this.recycleAsyncId(scheduler, id, delay);\n }\n\n // Set the pending flag indicating that this action has been scheduled, or\n // has recursively rescheduled itself.\n this.pending = true;\n\n this.delay = delay;\n // If this action has already an async Id, don't request a new one.\n this.id = this.id ?? this.requestAsyncId(scheduler, this.id, delay);\n\n return this;\n }\n\n protected requestAsyncId(scheduler: AsyncScheduler, _id?: TimerHandle, delay: number = 0): TimerHandle {\n return intervalProvider.setInterval(scheduler.flush.bind(scheduler, this), delay);\n }\n\n protected recycleAsyncId(_scheduler: AsyncScheduler, id?: TimerHandle, delay: number | null = 0): TimerHandle | undefined {\n // If this action is rescheduled with the same delay time, don't clear the interval id.\n if (delay != null && this.delay === delay && this.pending === false) {\n return id;\n }\n // Otherwise, if the action's delay time is different from the current delay,\n // or the action has been rescheduled before it's executed, clear the interval id\n if (id != null) {\n intervalProvider.clearInterval(id);\n }\n\n return undefined;\n }\n\n /**\n * Immediately executes this action and the `work` it contains.\n */\n public execute(state: T, delay: number): any {\n if (this.closed) {\n return new Error('executing a cancelled action');\n }\n\n this.pending = false;\n const error = this._execute(state, delay);\n if (error) {\n return error;\n } else if (this.pending === false && this.id != null) {\n // Dequeue if the action didn't reschedule itself. Don't call\n // unsubscribe(), because the action could reschedule later.\n // For example:\n // ```\n // scheduler.schedule(function doWork(counter) {\n // /* ... I'm a busy worker bee ... */\n // var originalAction = this;\n // /* wait 100ms before rescheduling the action */\n // setTimeout(function () {\n // originalAction.schedule(counter + 1);\n // }, 100);\n // }, 1000);\n // ```\n this.id = this.recycleAsyncId(this.scheduler, this.id, null);\n }\n }\n\n protected _execute(state: T, _delay: number): any {\n let errored: boolean = false;\n let errorValue: any;\n try {\n this.work(state);\n } catch (e) {\n errored = true;\n // HACK: Since code elsewhere is relying on the \"truthiness\" of the\n // return here, we can't have it return \"\" or 0 or false.\n // TODO: Clean this up when we refactor schedulers mid-version-8 or so.\n errorValue = e ? e : new Error('Scheduled action threw falsy error');\n }\n if (errored) {\n this.unsubscribe();\n return errorValue;\n }\n }\n\n unsubscribe() {\n if (!this.closed) {\n const { id, scheduler } = this;\n const { actions } = scheduler;\n\n this.work = this.state = this.scheduler = null!;\n this.pending = false;\n\n arrRemove(actions, this);\n if (id != null) {\n this.id = this.recycleAsyncId(scheduler, id, null);\n }\n\n this.delay = null!;\n super.unsubscribe();\n }\n }\n}\n", "import { Action } from './scheduler/Action';\nimport { Subscription } from './Subscription';\nimport { SchedulerLike, SchedulerAction } from './types';\nimport { dateTimestampProvider } from './scheduler/dateTimestampProvider';\n\n/**\n * An execution context and a data structure to order tasks and schedule their\n * execution. Provides a notion of (potentially virtual) time, through the\n * `now()` getter method.\n *\n * Each unit of work in a Scheduler is called an `Action`.\n *\n * ```ts\n * class Scheduler {\n * now(): number;\n * schedule(work, delay?, state?): Subscription;\n * }\n * ```\n *\n * @deprecated Scheduler is an internal implementation detail of RxJS, and\n * should not be used directly. Rather, create your own class and implement\n * {@link SchedulerLike}. Will be made internal in v8.\n */\nexport class Scheduler implements SchedulerLike {\n public static now: () => number = dateTimestampProvider.now;\n\n constructor(private schedulerActionCtor: typeof Action, now: () => number = Scheduler.now) {\n this.now = now;\n }\n\n /**\n * A getter method that returns a number representing the current time\n * (at the time this function was called) according to the scheduler's own\n * internal clock.\n * @return A number that represents the current time. May or may not\n * have a relation to wall-clock time. May or may not refer to a time unit\n * (e.g. milliseconds).\n */\n public now: () => number;\n\n /**\n * Schedules a function, `work`, for execution. May happen at some point in\n * the future, according to the `delay` parameter, if specified. May be passed\n * some context object, `state`, which will be passed to the `work` function.\n *\n * The given arguments will be processed an stored as an Action object in a\n * queue of actions.\n *\n * @param work A function representing a task, or some unit of work to be\n * executed by the Scheduler.\n * @param delay Time to wait before executing the work, where the time unit is\n * implicit and defined by the Scheduler itself.\n * @param state Some contextual data that the `work` function uses when called\n * by the Scheduler.\n * @return A subscription in order to be able to unsubscribe the scheduled work.\n */\n public schedule(work: (this: SchedulerAction, state?: T) => void, delay: number = 0, state?: T): Subscription {\n return new this.schedulerActionCtor(this, work).schedule(state, delay);\n }\n}\n", "import { Scheduler } from '../Scheduler';\nimport { Action } from './Action';\nimport { AsyncAction } from './AsyncAction';\nimport { TimerHandle } from './timerHandle';\n\nexport class AsyncScheduler extends Scheduler {\n public actions: Array> = [];\n /**\n * A flag to indicate whether the Scheduler is currently executing a batch of\n * queued actions.\n * @internal\n */\n public _active: boolean = false;\n /**\n * An internal ID used to track the latest asynchronous task such as those\n * coming from `setTimeout`, `setInterval`, `requestAnimationFrame`, and\n * others.\n * @internal\n */\n public _scheduled: TimerHandle | undefined;\n\n constructor(SchedulerAction: typeof Action, now: () => number = Scheduler.now) {\n super(SchedulerAction, now);\n }\n\n public flush(action: AsyncAction): void {\n const { actions } = this;\n\n if (this._active) {\n actions.push(action);\n return;\n }\n\n let error: any;\n this._active = true;\n\n do {\n if ((error = action.execute(action.state, action.delay))) {\n break;\n }\n } while ((action = actions.shift()!)); // exhaust the scheduler queue\n\n this._active = false;\n\n if (error) {\n while ((action = actions.shift()!)) {\n action.unsubscribe();\n }\n throw error;\n }\n }\n}\n", "import { AsyncAction } from './AsyncAction';\nimport { AsyncScheduler } from './AsyncScheduler';\n\n/**\n *\n * Async Scheduler\n *\n * Schedule task as if you used setTimeout(task, duration)\n *\n * `async` scheduler schedules tasks asynchronously, by putting them on the JavaScript\n * event loop queue. It is best used to delay tasks in time or to schedule tasks repeating\n * in intervals.\n *\n * If you just want to \"defer\" task, that is to perform it right after currently\n * executing synchronous code ends (commonly achieved by `setTimeout(deferredTask, 0)`),\n * better choice will be the {@link asapScheduler} scheduler.\n *\n * ## Examples\n * Use async scheduler to delay task\n * ```ts\n * import { asyncScheduler } from 'rxjs';\n *\n * const task = () => console.log('it works!');\n *\n * asyncScheduler.schedule(task, 2000);\n *\n * // After 2 seconds logs:\n * // \"it works!\"\n * ```\n *\n * Use async scheduler to repeat task in intervals\n * ```ts\n * import { asyncScheduler } from 'rxjs';\n *\n * function task(state) {\n * console.log(state);\n * this.schedule(state + 1, 1000); // `this` references currently executing Action,\n * // which we reschedule with new state and delay\n * }\n *\n * asyncScheduler.schedule(task, 3000, 0);\n *\n * // Logs:\n * // 0 after 3s\n * // 1 after 4s\n * // 2 after 5s\n * // 3 after 6s\n * ```\n */\n\nexport const asyncScheduler = new AsyncScheduler(AsyncAction);\n\n/**\n * @deprecated Renamed to {@link asyncScheduler}. Will be removed in v8.\n */\nexport const async = asyncScheduler;\n", "import { AsyncAction } from './AsyncAction';\nimport { Subscription } from '../Subscription';\nimport { QueueScheduler } from './QueueScheduler';\nimport { SchedulerAction } from '../types';\nimport { TimerHandle } from './timerHandle';\n\nexport class QueueAction extends AsyncAction {\n constructor(protected scheduler: QueueScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n public schedule(state?: T, delay: number = 0): Subscription {\n if (delay > 0) {\n return super.schedule(state, delay);\n }\n this.delay = delay;\n this.state = state;\n this.scheduler.flush(this);\n return this;\n }\n\n public execute(state: T, delay: number): any {\n return delay > 0 || this.closed ? super.execute(state, delay) : this._execute(state, delay);\n }\n\n protected requestAsyncId(scheduler: QueueScheduler, id?: TimerHandle, delay: number = 0): TimerHandle {\n // If delay exists and is greater than 0, or if the delay is null (the\n // action wasn't rescheduled) but was originally scheduled as an async\n // action, then recycle as an async action.\n\n if ((delay != null && delay > 0) || (delay == null && this.delay > 0)) {\n return super.requestAsyncId(scheduler, id, delay);\n }\n\n // Otherwise flush the scheduler starting with this action.\n scheduler.flush(this);\n\n // HACK: In the past, this was returning `void`. However, `void` isn't a valid\n // `TimerHandle`, and generally the return value here isn't really used. So the\n // compromise is to return `0` which is both \"falsy\" and a valid `TimerHandle`,\n // as opposed to refactoring every other instanceo of `requestAsyncId`.\n return 0;\n }\n}\n", "import { AsyncScheduler } from './AsyncScheduler';\n\nexport class QueueScheduler extends AsyncScheduler {\n}\n", "import { QueueAction } from './QueueAction';\nimport { QueueScheduler } from './QueueScheduler';\n\n/**\n *\n * Queue Scheduler\n *\n * Put every next task on a queue, instead of executing it immediately\n *\n * `queue` scheduler, when used with delay, behaves the same as {@link asyncScheduler} scheduler.\n *\n * When used without delay, it schedules given task synchronously - executes it right when\n * it is scheduled. However when called recursively, that is when inside the scheduled task,\n * another task is scheduled with queue scheduler, instead of executing immediately as well,\n * that task will be put on a queue and wait for current one to finish.\n *\n * This means that when you execute task with `queue` scheduler, you are sure it will end\n * before any other task scheduled with that scheduler will start.\n *\n * ## Examples\n * Schedule recursively first, then do something\n * ```ts\n * import { queueScheduler } from 'rxjs';\n *\n * queueScheduler.schedule(() => {\n * queueScheduler.schedule(() => console.log('second')); // will not happen now, but will be put on a queue\n *\n * console.log('first');\n * });\n *\n * // Logs:\n * // \"first\"\n * // \"second\"\n * ```\n *\n * Reschedule itself recursively\n * ```ts\n * import { queueScheduler } from 'rxjs';\n *\n * queueScheduler.schedule(function(state) {\n * if (state !== 0) {\n * console.log('before', state);\n * this.schedule(state - 1); // `this` references currently executing Action,\n * // which we reschedule with new state\n * console.log('after', state);\n * }\n * }, 0, 3);\n *\n * // In scheduler that runs recursively, you would expect:\n * // \"before\", 3\n * // \"before\", 2\n * // \"before\", 1\n * // \"after\", 1\n * // \"after\", 2\n * // \"after\", 3\n *\n * // But with queue it logs:\n * // \"before\", 3\n * // \"after\", 3\n * // \"before\", 2\n * // \"after\", 2\n * // \"before\", 1\n * // \"after\", 1\n * ```\n */\n\nexport const queueScheduler = new QueueScheduler(QueueAction);\n\n/**\n * @deprecated Renamed to {@link queueScheduler}. Will be removed in v8.\n */\nexport const queue = queueScheduler;\n", "import { AsyncAction } from './AsyncAction';\nimport { AnimationFrameScheduler } from './AnimationFrameScheduler';\nimport { SchedulerAction } from '../types';\nimport { animationFrameProvider } from './animationFrameProvider';\nimport { TimerHandle } from './timerHandle';\n\nexport class AnimationFrameAction extends AsyncAction {\n constructor(protected scheduler: AnimationFrameScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n protected requestAsyncId(scheduler: AnimationFrameScheduler, id?: TimerHandle, delay: number = 0): TimerHandle {\n // If delay is greater than 0, request as an async action.\n if (delay !== null && delay > 0) {\n return super.requestAsyncId(scheduler, id, delay);\n }\n // Push the action to the end of the scheduler queue.\n scheduler.actions.push(this);\n // If an animation frame has already been requested, don't request another\n // one. If an animation frame hasn't been requested yet, request one. Return\n // the current animation frame request id.\n return scheduler._scheduled || (scheduler._scheduled = animationFrameProvider.requestAnimationFrame(() => scheduler.flush(undefined)));\n }\n\n protected recycleAsyncId(scheduler: AnimationFrameScheduler, id?: TimerHandle, delay: number = 0): TimerHandle | undefined {\n // If delay exists and is greater than 0, or if the delay is null (the\n // action wasn't rescheduled) but was originally scheduled as an async\n // action, then recycle as an async action.\n if (delay != null ? delay > 0 : this.delay > 0) {\n return super.recycleAsyncId(scheduler, id, delay);\n }\n // If the scheduler queue has no remaining actions with the same async id,\n // cancel the requested animation frame and set the scheduled flag to\n // undefined so the next AnimationFrameAction will request its own.\n const { actions } = scheduler;\n if (id != null && id === scheduler._scheduled && actions[actions.length - 1]?.id !== id) {\n animationFrameProvider.cancelAnimationFrame(id as number);\n scheduler._scheduled = undefined;\n }\n // Return undefined so the action knows to request a new async id if it's rescheduled.\n return undefined;\n }\n}\n", "import { AsyncAction } from './AsyncAction';\nimport { AsyncScheduler } from './AsyncScheduler';\n\nexport class AnimationFrameScheduler extends AsyncScheduler {\n public flush(action?: AsyncAction): void {\n this._active = true;\n // The async id that effects a call to flush is stored in _scheduled.\n // Before executing an action, it's necessary to check the action's async\n // id to determine whether it's supposed to be executed in the current\n // flush.\n // Previous implementations of this method used a count to determine this,\n // but that was unsound, as actions that are unsubscribed - i.e. cancelled -\n // are removed from the actions array and that can shift actions that are\n // scheduled to be executed in a subsequent flush into positions at which\n // they are executed within the current flush.\n let flushId;\n if (action) {\n flushId = action.id;\n } else {\n flushId = this._scheduled;\n this._scheduled = undefined;\n }\n\n const { actions } = this;\n let error: any;\n action = action || actions.shift()!;\n\n do {\n if ((error = action.execute(action.state, action.delay))) {\n break;\n }\n } while ((action = actions[0]) && action.id === flushId && actions.shift());\n\n this._active = false;\n\n if (error) {\n while ((action = actions[0]) && action.id === flushId && actions.shift()) {\n action.unsubscribe();\n }\n throw error;\n }\n }\n}\n", "import { AnimationFrameAction } from './AnimationFrameAction';\nimport { AnimationFrameScheduler } from './AnimationFrameScheduler';\n\n/**\n *\n * Animation Frame Scheduler\n *\n * Perform task when `window.requestAnimationFrame` would fire\n *\n * When `animationFrame` scheduler is used with delay, it will fall back to {@link asyncScheduler} scheduler\n * behaviour.\n *\n * Without delay, `animationFrame` scheduler can be used to create smooth browser animations.\n * It makes sure scheduled task will happen just before next browser content repaint,\n * thus performing animations as efficiently as possible.\n *\n * ## Example\n * Schedule div height animation\n * ```ts\n * // html:
\n * import { animationFrameScheduler } from 'rxjs';\n *\n * const div = document.querySelector('div');\n *\n * animationFrameScheduler.schedule(function(height) {\n * div.style.height = height + \"px\";\n *\n * this.schedule(height + 1); // `this` references currently executing Action,\n * // which we reschedule with new state\n * }, 0, 0);\n *\n * // You will see a div element growing in height\n * ```\n */\n\nexport const animationFrameScheduler = new AnimationFrameScheduler(AnimationFrameAction);\n\n/**\n * @deprecated Renamed to {@link animationFrameScheduler}. Will be removed in v8.\n */\nexport const animationFrame = animationFrameScheduler;\n", "import { Observable } from '../Observable';\nimport { SchedulerLike } from '../types';\n\n/**\n * A simple Observable that emits no items to the Observer and immediately\n * emits a complete notification.\n *\n * Just emits 'complete', and nothing else.\n *\n * ![](empty.png)\n *\n * A simple Observable that only emits the complete notification. It can be used\n * for composing with other Observables, such as in a {@link mergeMap}.\n *\n * ## Examples\n *\n * Log complete notification\n *\n * ```ts\n * import { EMPTY } from 'rxjs';\n *\n * EMPTY.subscribe({\n * next: () => console.log('Next'),\n * complete: () => console.log('Complete!')\n * });\n *\n * // Outputs\n * // Complete!\n * ```\n *\n * Emit the number 7, then complete\n *\n * ```ts\n * import { EMPTY, startWith } from 'rxjs';\n *\n * const result = EMPTY.pipe(startWith(7));\n * result.subscribe(x => console.log(x));\n *\n * // Outputs\n * // 7\n * ```\n *\n * Map and flatten only odd numbers to the sequence `'a'`, `'b'`, `'c'`\n *\n * ```ts\n * import { interval, mergeMap, of, EMPTY } from 'rxjs';\n *\n * const interval$ = interval(1000);\n * const result = interval$.pipe(\n * mergeMap(x => x % 2 === 1 ? of('a', 'b', 'c') : EMPTY),\n * );\n * result.subscribe(x => console.log(x));\n *\n * // Results in the following to the console:\n * // x is equal to the count on the interval, e.g. (0, 1, 2, 3, ...)\n * // x will occur every 1000ms\n * // if x % 2 is equal to 1, print a, b, c (each on its own)\n * // if x % 2 is not equal to 1, nothing will be output\n * ```\n *\n * @see {@link Observable}\n * @see {@link NEVER}\n * @see {@link of}\n * @see {@link throwError}\n */\nexport const EMPTY = new Observable((subscriber) => subscriber.complete());\n\n/**\n * @param scheduler A {@link SchedulerLike} to use for scheduling\n * the emission of the complete notification.\n * @deprecated Replaced with the {@link EMPTY} constant or {@link scheduled} (e.g. `scheduled([], scheduler)`). Will be removed in v8.\n */\nexport function empty(scheduler?: SchedulerLike) {\n return scheduler ? emptyScheduled(scheduler) : EMPTY;\n}\n\nfunction emptyScheduled(scheduler: SchedulerLike) {\n return new Observable((subscriber) => scheduler.schedule(() => subscriber.complete()));\n}\n", "import { SchedulerLike } from '../types';\nimport { isFunction } from './isFunction';\n\nexport function isScheduler(value: any): value is SchedulerLike {\n return value && isFunction(value.schedule);\n}\n", "import { SchedulerLike } from '../types';\nimport { isFunction } from './isFunction';\nimport { isScheduler } from './isScheduler';\n\nfunction last(arr: T[]): T | undefined {\n return arr[arr.length - 1];\n}\n\nexport function popResultSelector(args: any[]): ((...args: unknown[]) => unknown) | undefined {\n return isFunction(last(args)) ? args.pop() : undefined;\n}\n\nexport function popScheduler(args: any[]): SchedulerLike | undefined {\n return isScheduler(last(args)) ? args.pop() : undefined;\n}\n\nexport function popNumber(args: any[], defaultValue: number): number {\n return typeof last(args) === 'number' ? args.pop()! : defaultValue;\n}\n", "export const isArrayLike = ((x: any): x is ArrayLike => x && typeof x.length === 'number' && typeof x !== 'function');", "import { isFunction } from \"./isFunction\";\n\n/**\n * Tests to see if the object is \"thennable\".\n * @param value the object to test\n */\nexport function isPromise(value: any): value is PromiseLike {\n return isFunction(value?.then);\n}\n", "import { InteropObservable } from '../types';\nimport { observable as Symbol_observable } from '../symbol/observable';\nimport { isFunction } from './isFunction';\n\n/** Identifies an input as being Observable (but not necessary an Rx Observable) */\nexport function isInteropObservable(input: any): input is InteropObservable {\n return isFunction(input[Symbol_observable]);\n}\n", "import { isFunction } from './isFunction';\n\nexport function isAsyncIterable(obj: any): obj is AsyncIterable {\n return Symbol.asyncIterator && isFunction(obj?.[Symbol.asyncIterator]);\n}\n", "/**\n * Creates the TypeError to throw if an invalid object is passed to `from` or `scheduled`.\n * @param input The object that was passed.\n */\nexport function createInvalidObservableTypeError(input: any) {\n // TODO: We should create error codes that can be looked up, so this can be less verbose.\n return new TypeError(\n `You provided ${\n input !== null && typeof input === 'object' ? 'an invalid object' : `'${input}'`\n } where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.`\n );\n}\n", "export function getSymbolIterator(): symbol {\n if (typeof Symbol !== 'function' || !Symbol.iterator) {\n return '@@iterator' as any;\n }\n\n return Symbol.iterator;\n}\n\nexport const iterator = getSymbolIterator();\n", "import { iterator as Symbol_iterator } from '../symbol/iterator';\nimport { isFunction } from './isFunction';\n\n/** Identifies an input as being an Iterable */\nexport function isIterable(input: any): input is Iterable {\n return isFunction(input?.[Symbol_iterator]);\n}\n", "import { ReadableStreamLike } from '../types';\nimport { isFunction } from './isFunction';\n\nexport async function* readableStreamLikeToAsyncGenerator(readableStream: ReadableStreamLike): AsyncGenerator {\n const reader = readableStream.getReader();\n try {\n while (true) {\n const { value, done } = await reader.read();\n if (done) {\n return;\n }\n yield value!;\n }\n } finally {\n reader.releaseLock();\n }\n}\n\nexport function isReadableStreamLike(obj: any): obj is ReadableStreamLike {\n // We don't want to use instanceof checks because they would return\n // false for instances from another Realm, like an